最近尝试用django开发一个叫做vmm的虚拟机管理网站。以下是我在第一次使用django开发较完整网站时学到的东西。
TIME_ZONE
首先是发现django在显示datetime时与预期不太一样,比如下午2点时页面上直接输出datetime显示为早上6点。多多少少决定和时区设置有关系。从Dash看了django的时区配置,发现在网站的settings中有TIMEZONE这一选项,默认是UTC。据文档上上默认是加拿大的时区……所以把TIMEZONE改成上海就行了。
Box Cat Happy Day
最近尝试用django开发一个叫做vmm的虚拟机管理网站。以下是我在第一次使用django开发较完整网站时学到的东西。
首先是发现django在显示datetime时与预期不太一样,比如下午2点时页面上直接输出datetime显示为早上6点。多多少少决定和时区设置有关系。从Dash看了django的时区配置,发现在网站的settings中有TIMEZONE这一选项,默认是UTC。据文档上上默认是加拿大的时区……所以把TIMEZONE改成上海就行了。
老实说这是老东西了,自己之前用scala写过,用ruby写过,这次是用python第二次实现,github上这种生成脚本也挺多的,自己全当是练手。
这个脚本其实就是把apnic提供的数据过滤出指定数据并解析然后生成路由表更新脚本的程序。用途相信各位也清楚,以下是自己重复造轮子的过程:
程序的主逻辑是读取apnic数据,用正则表达式过滤和解析,用解析出来的数据生成路由表修改脚本和恢复脚本。
首先是读取apnic数据。个人的方式是查找当前目录,然后脚本所在目录,如果再没有自动下载文件到脚本所在目录并返回。这里没有read on fly,即边下载边读,也没有比对并确保最新的逻辑。之后可以考虑加上。
看《Python Linux Administrator》这本书时,我并不是按照从头到尾的方式读的,而是看了一遍目录,选择性地读了部分章节。以下是我选择的章节:
有GUI肯定更直观一些,特别是在一些报表展示上。书中提到主要是三种方式,基于pygtk/wxPython的桌面程序,基于cursers的CLI方式和基于django的web方式。个人觉得web方式综合下面比较好一些,而且django并不比想象中要难。我也把django作为定期学习的一个候选。
unix/linux下网络相关命令很多,python也有很多针对网络的库。书中介绍了从底层socket,到上层httplib/ftplib/paramiko(sftp),urllib/urllib2,xmlprclib/Pyro和强大的twisted。针对不同协议场景用不同的库,前提是你要了解足够多的网络库。
通用的有platform,可以用来判断操作系统。接下来都是一些指定系统下的库,比如mac的dscl/asr,windows的zenoss,还有libvert, libvirt, cobbler, virt-factory, func等。这块和系统相关,个人认为要根据实际环境选择性的学习。
较多篇幅介绍了easy_install。同时提到了buildout, virtualenv, epm。个人对virutalenv有点兴趣,因为可以多版本python共存。
一些使用python管理系统的实例。和之前介绍内容可能有交集。个人感兴趣的是python查询dns,还有python管理ldap等。
主要是操作文件。比如常用的os模块,还有glob,shutil,filecmp。用于产生摘要的hashlib等。更多的在python标准库文档中。
以下内容来自这里的学习。
理论上动态语言不建议判断类型,不过python还是有提供判断的方法。
1 2 3 4 |
import types type(1) == types.IntType isinstance(1, int) |
第一种是使用types模块,第二种是使用内建的isinstance方法。
因为Java有BigInteger和BigDecimal,所以不会陌生。
1 2 3 4 5 6 |
import decimal decimal.getcontext().prec = 7 # set precision to 7, default is 28 decimal.Decimal(1) # create from int decimal.Decimal('3.14') # create from string decimal.Decimal(1) / decimal.Decimal(7) |
默认decimal精度为28,可以通过getcontext()修改配置。带小数的建议使用字符串创建,否则你用decimal.Decimal(3.14)试试看。
就是那种几分之几的分数。
1 2 3 4 5 |
import fractions fractions.Fraction(16, -10) # - 8/5 fractions.Fraction(decimal.Decimal('1.1')) # 11/10 fractions.gcd(60, 56) # 4 |
fractions会帮你做约分,事实上这个库还有一个叫做gcd的函数。通过之前的decimal创建也是可以的。
1 2 3 4 5 6 7 8 |
import random # or os.urandom, SystemRandom random.random() # 0.0 <= x < 1.0 random.uniform(1, 10) # 1.0 <= x < 10.0 random.randint(1, 10) # [1, 10] random.randrange(0, 101, 2) # even integer from 0 to 100 random.choice('abcdefghij') # choose a random element random.shuffle([1, 2, 3, 4, 5, 6, 7]) # shuffle a list random.sample([1, 2, 3, 4, 5, 6, 7], 3) # select three items |
这块基本照搬标准库介绍的例子,因为我觉得每个方法都很有用。除了基础的随机数,还有典型的几种随机数应用。
映射标准操作。个人暂时能想到使用的地方如下:
1 2 |
import operator reduce(operator.add, [1, 2, 3, 4], 0) |
以上就是今天学习python标准库了解到的东西。
题目是这样的,给定一个整数序列,比如[1, 2, -1, 8, -9],要求找出一个子序列,其所有数字的和为0,比如子序列[2, -1, 8, -9],并输出子序列在主序列中的位置,比如[2, -1, 8, 9]在主序列[1, 2, -1, 8, -9]中的位置为[1, 4]。
以下是我想到的一个方法,暂时不清楚怎么计算这个的时间复杂度。原理是这样的,对于序列[1, 2, -1, 8, -9],设置一个和序列。初始为[0, 0, 0, 0, 0],长度和输入序列一致。
顺序读取输入序列,第一次遇到1,尝试在和序列的[0, 0]区别内增加1,结果为[1, 0, 0, 0, 0]。为什么这么做呢,这样,输入序列区间[0, 0]的和就计算出来了。
第二次遇到2, 尝试在和序列[0, 1]区间内增加2,结果为[3, 2, 0, 0, 0],这样,输入序列区间[0, 1], [1, 1]的和就计算出来了。没有发现0和序列。
接下来依此类推,发现[1, 4]区间的和为0,输出为(1, 4)。代码如下(python):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
#!/usr/bin/env python import sys def find_zero_sum_seq(seq): sums = [0] * len(seq) for i, x in enumerate(seq): for j in range(0, i + 1): sums[j] += seq[i] if sums[j] == 0: return (j, i) print 'debug', sums if __name__ == '__main__': seq = [int(x) for x in sys.argv[1:]] print find_zero_sum_seq(seq) or 'not found' |
执行和输出,debug为和序列的内容。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
$ python zero-sum.py 4 1 1 1 1 2 4 -1 -6 10 debug [4, 0, 0, 0, 0, 0, 0, 0, 0, 0] debug [5, 1, 0, 0, 0, 0, 0, 0, 0, 0] debug [6, 2, 1, 0, 0, 0, 0, 0, 0, 0] debug [7, 3, 2, 1, 0, 0, 0, 0, 0, 0] debug [8, 4, 3, 2, 1, 0, 0, 0, 0, 0] debug [10, 6, 5, 4, 3, 2, 0, 0, 0, 0] debug [14, 10, 9, 8, 7, 6, 4, 0, 0, 0] debug [13, 9, 8, 7, 6, 5, 3, -1, 0, 0] (4, 8) $ python zero-sum.py 1 2 -1 8 -9 debug [1, 0, 0, 0, 0] debug [3, 2, 0, 0, 0] debug [2, 1, -1, 0, 0] debug [10, 9, 7, 8, 0] (1, 4) |
继续接上篇。
理论上来说,rsync是最好的工程同步方式。只是和fabric自身的ssh登录没有整合,现在我找到了一个简单的解决方法。同时我也准备了其他两种方法:打包上传和远程svn update,这样加上不同步总共有四个部署策略。
解决rsync依赖主机名和密码的方法很简单:因为rsync是在本地运行,只要在rsync之前执行一个简单的远程命令就能获得主机名和密码。代码如下:
1 2 3 4 |
remote_srcdir = 'source' run('hostname') # simple workaround for host prompt local('sshpass -p %s rsync -pthrvz --delete --exclude=.git --exclude=*fabfile* --exclude=target . %s:%s' % (env.password, env.host_string, remote_srcdir)) |
这里写死是密码登录了,理论还有证书登录,暂时不考虑。
打包上传的方式,改造自upload_project
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
import tempfile, os remote_srcdir = 'source' _, archive_path = tempfile.mkstemp() _, archive_name = os.path.split(archive_path) try: local('tar -cz --exclude-vcs --exclude=*fabfile* --exclude=target -f %s .' % archive_path) put(archive_path, '.') run('rm -rf %s' % remote_srcdir) run('mkdir %s' % remote_srcdir) run('tar -xzf %s -C %s' % (archive_name, remote_srcdir)) run('rm -f %s' % archive_name) finally: local('rm -f %s' % archive_path) |
使用临时文件。本地打包,上传到远程服务器。远程服务器上解压缩后删除,最后删除本地临时文件。
远程svn update方式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
import subprocess remote_srcdir = 'source' if _test_svn_info(remote_srcdir): run('svn update %s' % remote_srcdir) else: run('rm -rf %s' % remote_srcdir) git_svn_url = _call_shell("git svn info | fgrep URL | cut -d ' ' -f 2") svn_url = _call_shell("svn info | fgrep URL | cut -d ' ' -f 2") run('svn checkout %s %s' % ((git_svn_url or svn_url).strip(), remote_srcdir)) def _test_svn_info(srcdir): with settings(warn_only = True): return run('svn info %s' % srcdir).return_code == 0 def _call_shell(cmd): p = subprocess.Popen(cmd, shell = True, stdout = subprocess.PIPE, stderr = subprocess.PIPE) out, err = p.communicate() # ignore error return out |
考虑到本地可能是git-svn而不是纯的svn,所以先判断远程代码代码是否是svn目录,如果不是则尝试从本地读取URL并在远程服务器上签出。如果是则直接执行svn update。
至此,个人认为工程同步这块算是基本完美了。若有更好的同步方法,欢迎指出。
接上篇。
最近学习rsync,突然想到自己之前使用fabric上传工程时与其用tar上传再解压缩还不如用rsync增量传输,后者可以更快。查了下fabric的文档,发现有提供几个方法:
1 2 |
fabric.contrib.project.rsync_project(*args, **kwargs) fabric.contrib.project.upload_project(local_dir=None, remote_dir='') |
第一个就是使用rsync,第二个其实和我之前的方法一样采用压缩包传输的。rsync_project个人认为实际上就是local(“rsync xxx”)的模板,参数并不是很全。
但是使用了之后我还是考虑使用第二个方法upload_project,原因是rsync和fabric的ssh登录是分离的:rsync_project需要我重新输入一次密码,不输入的话就需要退化成local加上sshpass。再考虑到一开始可能没有输入密码,代码可能会变成这样:
1 2 3 4 5 |
rsync_cmd = 'rsync --exclude=.git --exclude=*fabfile* -pthrvz . -e ssh %s:source' % env.host_string if env.password: local('sshpass -p %s %s' % (env.password, rsync_cmd)) else: local(rsync_cmd) |
如果不想输入两次密码的话,就必须在代码上或者命令行上写上密码,不是很安全。考虑再三,还是使用fabric提供的upload_project,虽然压缩可能慢一点。
说来惭愧,个人是最近才知道fabric的。但是一上手就觉得fabric非常不错,解决了我长久以来的部署问题。
原因我的部署方式是:
后来觉得把未测试的代码签入代码库不是很好,想直接复制本地代码到远程服务器上再部署,于是第一步变成了:
pexpect是一个用python实现的类似expect的库,支持ssh,ftp等协议的交互。
以前自己写了一篇使用expect+python拉取数据的文章,现在发现了pexpect,便想要完全用python来写。总觉得用expect生成中间文件不是很好。
以下是作为有Java背景的人阅读《Python基础教程》做得一些笔记。
python支持类似数学上的区间比较
1 2 |
age = 22 0 < age < 100 |
比较有趣的功能,免得自己写循环比较了
1 2 |
[1, 2, 3] < [1, 2, 4] # True [1, [2, 3]] < [1, [2, 5]] # True |