django+tornado

Django的部署方式有很多种:WSGI,FastCGI等等,当然最主流的还是WSGI。比如官方文档中推荐的django+apache+mod_wsgi, django+gunicorn,django+uWSGI等等。那么什么是WSGI?

其实这个问题也困扰了我很久。每当我想研究一下WSGI到底是何方神圣的时候,google给出的答案就是pep 3333,不过这篇像论文一样的pep 3333比较晦涩难懂,一直都没搞明白。直到后来发现了这篇文章。和其他的文章不一样,文章没有开头就讲WSGI是什么,反而开始就说WSGI不是什么:

What WSGI is not: a server, a python module, a framework, an API or any kind of software. What it is: an interface specification by which server and application communicate.

看到这里心中无比的敞亮,原来WSGI就是一种标准:application和server之间交互的标准。application就是通常所说的framework,server则接受客户端的请求并将请求发给application处理,然后将application的应答返回给客户端。现在应该明白,之前说的apache+mod_wsgi, gunicorn, uWSGI都是扮演WSGI Server的角色,只要兼容WSGI的server都可以用来和任意的python web framework搭配。

今天的主角是Tornado。它包含两部分:一个framework和一个server。由于tornado使用了异步非阻塞的IO模型,所以特别适用于长轮询的场景,性能比较强大。今天主要用tornado的http server来部署django应用。

tornado的wsgi模块包含了两个类WSGIApplication和WSGIContainer。WSGIApplication是把tornado作为framework,用其他的WSGI server来驱动。WSGIContainer则是我们需要的,把其他web framework的应用封装一下,以便在tornado server上运行。
继续阅读django+tornado

Python Argument list Unpacking

今天遇到一个问题,假设有字符串[ccib_python]s = “INSERT INTO tbl VALUES({0}, {1}, {2}, {3}, {4})”[/ccib_python],如何用format函数将一个元组的内容映射到字符串上去呢?当然,最简单的就是:
[ccb_python]
t = (1, 2, 3, 4)
s.format(t[0], t[1], t[2], t[3])
[/ccb_python]
不过这种做法实在是too simple,too naive了。一点也不pythonic。正确的做法是:
[ccb_python]
s.format(*t)
[/ccb_python]
怎么样,这样是不是看起来顺眼多了?

如果m是一个列表或元组,那么f(*m)将会把列表中的元素分别作为函数f的位置参数。同样,如果m是个字典类型,那么f(*m)将把m中的每个key:value对作为函数f的关键字参数。

参考

  1. Unpacking Argument Lists
  2. The Python IAQ

Python Decorator

Python有很多漂亮的特性,decorator就是其中之一。什么是decorator?其他语言里面也有类似于decorator的东西,那就是宏。但是c里面的宏的用法是有点诡异的,Bruce Eckel都说,c里面的宏完全是另外一种语言[1]不能同意更多了,从好多#define都会被当做面试题就可以看出来了:D)。

废话少说,先上一个decorator过把瘾。

[ccb_python]
def my_decorator(f):

def new_f(*args, **kwargs):
print ‘enter new_f’
return f(*args, **kwargs)

return new_f

@my_decorator
def f(a, b):
return a + b
[/ccb_python]

上面的my_decorator只是在调用f之前打印“Enter new_f”出来,然后再返回f的结果。想要达到同样的目的,更普通一点的做法是:

[ccb_python]
def f(a, b):
return a + b

f = my_decorator(f)
[/ccb_python]
你会选择普通点的做法,还是文艺点的[ccib_python]@my_decorator[/ccib_python]呢?

用类做decorator

上面的例子用函数作decorator,类也是可以的,只要类实现了[ccib_python]__call__[/ccib_python]方法。其实用类做decorator是更普遍的做法。

[ccb_python]
class Decorator(object):

def __init__(self, f):
print ‘__init__()’
self.f = f

def __call__(self):
print ‘__call__’
self.f()
[/ccb_python]

用Decorator类修饰函数试下:

[ccb_python]
>>> @Decorator
… def f():
… print “in f()”

__init__()
>>> f()
__call__
in f()
[/ccb_python]
与前面的函数做decorator不同的是,用Decorator类修饰f()时产生了一句输出。其实这也很容易理解,因为python的函数本身也是对象,只是我们无法定义函数初始化时候的操作罢了。类就不同了,我们可以随意定义[ccib_python]__init__[/ccib_python]函数来实现想要的功能。

带参数的decorator

decorator也可以有自己的参数,比如:

[ccb_python]
@decorate(message)
def f(….)
[/ccb_python]

上面的等效于

[ccb_python]
temp = decorate(message)
f = temp(f)
[/ccb_python]

带参数的decorator和不带参数的其实是有很大不同的。不相信?那自己写一个函数实现的decorator,再同下面这个做下对比:

[ccb_python]
def decorate(message):
def wrap(f):
def wrapped_f(*args):
print message
return f(*args)
return wrapped_f
return wrap

@decorate(‘message’)
def f(a, b):
print ‘add a and b’
return a + b
[/ccb_python]
猜你的反应肯定是:“WTF!怎么会有这么多层函数!”。先别急,把最外面的一层函数去掉,不就是最简单的没有参数的decorator了吗?其实wrap只是在decoration阶段接收message参数,wrapped_f才是真正会调用的函数。

用类实现的就是下面这个样子的了:

[ccb_python]
class Decorator(object):

def __init__(self, message):
self.message = message

def __call__(self, f):

def wrapped_f(*args):
print self.message
return f(*args)

return wrapped_f
[/ccb_python]

Real word decorators

理解decorator的基础之后,再看几个例子。

我们都知道c++的类可以有static方法,这类方法并不属于某一个具体的实例,而是存在于整个类的命名范围之内的。所有的实例都共用这个方法。在python中,我们也可以实现。方法就是是借助[ccib_python]@staticmethod[/ccib_python]这个decorator:

[ccb_python]
class Foo(object):
@staticmethod
def add(x,y):
return x + y

x = Foo.add(3,4)
[/ccb_python]

另外一个常见的decorator就是[ccib_python]@classmethod[/ccib_python]。用classmethod修饰的方法和普通的方法不同,普通的方法第一个参数都是self,而classmethod的第一个参数则是类。例如:

[ccb_python]
class Times(object):
factor = 1
@classmethod
def mul(cls,x):
return cls.factor*x

class TwoTimes(Times):
factor = 2

x = TwoTimes.mul(4)
[/ccb_python]
还有更多的用法,参见这里

参考

  1. Decorators I: Introduction to Python Decorators
  2. Python Decorators II: Decorator Arguments
  3. PythonDecoratorLibrary

*args and **kwargs in Python

Python里面*args和**kwargs的用途和c里面[ccib_c]**argv[/ccib_c]的用法类似,都是用来表示可变参数。[ccib_c]int main (int argc, char **argv)//Variable-length arguments in c[/ccib_c]

*args和**kwargs的区别在于前者用于表示非关键字可变长度参数,后者表示关键字可变长度参数。

*args

[ccb_python]
def func_args(arg1, *args):
print ‘Normal argument:’, arg1
for arg in args:
print ‘variable arg:’, arg

>>> func_args(1, 2, ‘3’)
Normal argument: 1
variable arg: 2
variable arg: 3
[/ccb_python]

**kwargs

[ccb_python]
def func_kwargs(arg1, **kwargs):
print ‘Normal argument:’, arg1
for key in kwargs:
print ‘Keyworded variable arg:’, key, kwargs[key]

>>> func_kwargs(arg1=1, karg1=’2′, karg2=3)
Normal argument: 1
Keyworded variable arg: karg2 3
Keyworded variable arg: karg1 2
[/ccb_python]

除了定义函数,*args和**kwargs还可以用来调用函数。

[ccb_python]
def func(arg1, arg2, arg3):
print ‘arg1:’, arg1
print ‘arg2:’, arg2
print ‘arg3:’, arg3

>>> args = (2, ‘3’)
>>> kwargs = {‘arg2’:2, ‘arg3′:’3’}
>>> func(1, *args)
arg1: 1
arg2: 2
arg3: 3
>>> func(1, **kwargs)
arg1: 1
arg2: 2
arg3: 3
[/ccb_python]

Python调用c函数

如果你已经写好了一个c程序,但是又需要用脚本来实现同样的功能,应该怎么做?用python重新实现一次当然可以。但这样又得耗费不少精力,而且重复的劳动也没有意义。这种情况下就应该用ctypes模块了。

比如我想在脚本里面调用jenkins hash方法来计算某个序列的哈希值。python没有这样的模块可用。不过手头倒是有jenkins hash的c程序。看这段代码,如果用python再实现一次,是有点复杂的。所以最好的方式是把这段程序拿过来直接用。

[ccb_c height=”500″]
#include
#include
#define mix(a,b,c)
{
a -= b; a -= c; a ^= (c >> 13);
b -= c; b -= a; b ^= (a << 8); c -= a; c -= b; c ^= (b >> 13);
a -= b; a -= c; a ^= (c >> 12);
b -= c; b -= a; b ^= (a << 16); c -= a; c -= b; c ^= (b >> 5);
a -= b; a -= c; a ^= (c >> 3);
b -= c; b -= a; b ^= (a << 10); c -= a; c -= b; c ^= (b >> 15);
}

unsigned int bob_hash(void *val, unsigned int length)
{
char *k = (char *)val;
unsigned long a,b,c,len;

/* Set up the internal state */
len = length;
a = b = c = 0x9e3779b9; /* the golden ratio; an arbitrary value */

/* Handle most of the key */
while (len >= 12)
{
a += (k[0] +((unsigned long)k[1] << 8) +((unsigned long)k[2] << 16) +((unsigned long)k[3] << 24)); b += (k[4] +((unsigned long)k[5] << 8) +((unsigned long)k[6] << 16) +((unsigned long)k[7] << 24)); c += (k[8] +((unsigned long)k[9] << 8) +((unsigned long)k[10]<< 16)+((unsigned long)k[11] << 24)); mix(a,b,c); k += 12; len -= 12; } /* Handle the last 11 bytes */ c += length; switch(len) /* all the case statements fall through */ { case 11: c+=((unsigned long)k[10] << 24); case 10: c+=((unsigned long)k[9] << 16); case 9 : c+=((unsigned long)k[8] << 8); /* the first byte of c is reserved for the length */ case 8 : b+=((unsigned long)k[7] << 24); case 7 : b+=((unsigned long)k[6] << 16); case 6 : b+=((unsigned long)k[5] << 8); case 5 : b+=k[4]; case 4 : a+=((unsigned long)k[3] << 24); case 3 : a+=((unsigned long)k[2] << 16); case 2 : a+=((unsigned long)k[1] << 8); case 1 : a+=k[0]; } mix(a,b,c); return c; } int hash_string(void *tmpstr) { int hash = (int)bob_hash(tmpstr, strlen(tmpstr)); if(hash < 0) { hash = -hash; } return (int)(hash % 200); } int main() { printf("%d", hash_string("15882917276")); } [/ccb_c] 首先把这段程序编译为so文件。 [ccb_bash] gcc -fPIC -shared bob_hash.c -o bob_hash.so [/ccb_bash] 然后在python里面用ctypes加载刚才的动态库。argtypes和restype分别对应函数的参数和返回值的类型。这样就可以直接调用了。 [ccb_python] >>> from ctypes import CDLL, c_int, c_void_p
>>> bob_hash = CDLL(‘/home/jxq/code/bob_hash.so’)
>>> hash_string = bob_hash.hash_string
>>> hash_string.argtypes = [c_void_p]
>>> hash_string.restype = c_int
>>> hash_string(‘123’)
90
[/ccb_python]

Python修改函数参数?

有很多次尝试修改函数参数的值,因为不知道究竟能不能改,所以每次都写个函数测试一下。经过若干次的“测试”,发现还是没有明确的结论。好像对于不同类型的参数,结果不同。最终,在stackoverflow上面找到了问题的答案

先猜下这段代码的输出:
[ccb_python]
def f(n, x):
n = 2
x.append(4)
print ‘In f():’, n, x

def main():
n = 1
x = [0,1,2,3]
print ‘Before:’, n, x
f(n, x)
print ‘After: ‘, n, x

main()
[/ccb_python]

c语言的函数修改参数,参数类型必须为指针才可以。c++则是通过传递“引用”的方式。那么Python有类似的处理方式吗?
继续阅读Python修改函数参数?

理解python生成器

先看一个函数:
[ccb_python]
def group(seq, size):
“””
Returns an iterator over a series of lists of length size from iterable.

>>> list(group([1,2,3,4], 2))
[[1, 2], [3, 4]]
>>> list(group([1,2,3,4,5], 2))
[[1, 2], [3, 4], [5]]
“””
def take(seq, n):
for i in xrange(n):
yield seq.next()

if not hasattr(seq, ‘next’):
seq = iter(seq)
while True:
x = list(take(seq, size))
if x:
yield x
else:
break
[/ccb_python]
函数中没有常见的return,而是使用了yield。yield和return相同的地方是都用来向函数的调用者返回值。下面这个简单的例子展示了yield与return的不同。
继续阅读理解python生成器

用python和redis打造短网址服务

这篇文章的启发,用python+redis实现了文中的短网址服务。其中使用了web.py作为web框架,redis作为后台存储。部署在dotcloud上面。访问http://81aca6aa.dotcloud.com查看效果。由于使用了几个css3的效果,建议使用firefox、chrome或者ie 8以上的浏览器打开。代码放在GitHub

Web.py比django精简很多,使用起来也相对容易些。基本上对着文档,几分钟就可以写个服务出来。Django给人的感觉则是非常庞大,但是其功能是相当完善的。在框架的选择上,没有最好的,只有最适合的。这个短网址的服务不需要太复杂的功能,而且仅仅是作为一个demo写来玩玩而已,就选择了web.py。
继续阅读用python和redis打造短网址服务

浅拷贝与深拷贝

对象赋值实际上是只是对象的引用,如果想要复制某个对象,需要通过完全切片或者工厂函数以及使用copy模块的方法来完成。但是这三种方法都是所谓的“浅拷贝”。首先明确,“浅拷贝”和“深拷贝”的概念仅仅是针对容器类型的对象,因为非容器类型没有拷贝这一说法。他们的区别在于:

  • 浅拷贝将创建一个新的容器对象,然后把对原容器中元素的引用插入到新容器中。
  • 深拷贝将创建一个新的容器对象,然后递归地把原容器中元素的“拷贝”插入到新容器中。

继续阅读浅拷贝与深拷贝