趣味编程网站Codecademy新增了Python编程语言的学习课程

趣味编程网站Codecademy今天增加了Python编程语言的编程学习课程。就在上个月,公司刚获得一轮1000万美元的融资,Codecademy网站也新增5个语言的版本,包括中文、俄语、日语、西班牙语和德语。 Codecademy此前就已经支持JavaScript、HTML和CSS三种编程语言的编程学习课程。公司联合创始人Zach Sim表示,支持Python编程语言只是一个开始,Codecademy在未来还将提供更多的编程语言学习课程。不过Sims并没有透露具体还将新增哪些编程语言。 去年年底,Codecademy推出了实验室Labs功能,目的是为了能让人们更方便地学以致用,免去下载安装桌面版本的编辑器或集成开发环境(IDE)的麻烦。除了编辑,Codecademy Labs还能让你运行和下载可执行文件,拿到别的地方去运行。你不仅可以在上面学习磨练你的JavaScript,要是你在别的地方也学了Python和Ruby,你同样可以在Codecademy上面磨刀霍霍地训练起来。不过当时还没有Python课程,今天Codecademy上的Python课程终于推出。 Sims还表示,从一开始,Codecademy用户就对Python编程课程有强烈的需求,其中大部分是中学或大学的老师。对于想学习编程的新手,他们不妨加入Codecademy推出的新年编程项目Code Year,他们在这里可以进行JavaScript、HTML、CSS和jQuery的混合编程练习。

阅读全文 »

谈谈Python函数的默认参数

Python中很奇葩的一个地方是它的函数的默认参数的值,仅仅在def语句执行的时候计算一次。这会导致什么问题呢? 奇葩的例子 我们来看一个例子: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 In [44]: def packitem(item, pkg = []): ....: pkg.append(item) ....: return pkg In [45]: l = [100,200] In [46]: packitem(300, l) Out[46]: [100, 200, 300] In [47]: packitem(1) Out[47]: [1] In [48]: packitem(2) Out[48]: [1, 2] In [49]: packitem(3) Out[49]: [1, 2, 3] 这个可以看到packitem的默认参数pkg=[]仅仅计算了一次。而之后的packitem函数调用时,pkg都指向了最初创建的那个列表。 为什么 为什么会这样呢? 我们此时需要从Python编译出来的字节码中寻求答案。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 In [65]: def main(): ....: def packitem(item, pkg = []): ....: pkg.append(item) ....: return pkg ....: print packitem(1) ....: print packitem(2) ....: print packitem(3) In [66]: main() [1] [1, 2] [1, 2, 3] In [67]: dis.dis(main) 2 0 BUILD_LIST 0 3 LOAD_CONST 1 (code object) 6 MAKE_FUNCTION 1 9 STORE_FAST 0 (packitem) 5 12 LOAD_FAST 0 (packitem) 15 LOAD_CONST 2 (1) 18 CALL_FUNCTION 1 21 PRINT_ITEM 22 PRINT_NEWLINE 6 23 LOAD_FAST 0 (packitem) 26 LOAD_CONST 3 (2) 29 CALL_FUNCTION 1 32 PRINT_ITEM 33 PRINT_NEWLINE 7 34 LOAD_FAST 0 (packitem) 37 LOAD_CONST 4 (3) 40 CALL_FUNCTION 1 43 PRINT_ITEM 44 PRINT_NEWLINE 45 LOAD_CONST 0 (None) 48 RETURN_VALUE 可以看出。packitem函数的默认参数pkg的值是在第一条字节码创建的。随后在MAKE_FUNCTION指令的时候一起和code object打包成一个函数对象,然后通过STORE_FAST 0存在了FAST表的第0位。 后续的函数调用通过LOAD_FAST 0指令将packitem的函数对象取出,然后通过CALL_FUNCTION调用(对于CALL_FUNCTION,我们会在后续的文章进行探讨)。整个函数调用的过程并没有涉及到默认参数值的初始化。 所以,可见,Python函数的默认参数的值仅在函数定义的时候计算,后续的函数调用时的默认参数都是引用最初创建的那个对象。 Hack It 既然Python没有在我们进行函数调用的时候帮我们重新创建的默认参数的值,那我们就自己动手,丰衣足食。 第一种方案是是用不可变的默认值,例如None,然后在函数内部进行判断。此法略显麻烦。 第二种方案是通过装饰器来解决这个问题。 这段脚本是Sean Ross写的,非常感谢他。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 In [74]: def freshdefaults(f): ....: fdefaults = f.func_defaults ....: def refresher(*args, **kwds): ....: f.func_defaults = copy.deepcopy(fdefaults) ....: return f(*args, **kwds) ....: return refresher In [75]: @freshdefaults ....: def packitem(item, pkg = []): ....: pkg.append(item) ....: return pkg In [76]: l = [100,200] In [77]: packitem(300, l) Out[77]: [100, 200, 300] In [78]: packitem(1) Out[78]: [1] In [79]: packitem(2) Out[79]: [2] In [80]: packitem(3) Out[80]: [3] 可以看到,packitem的输出符合我们的预期了。我们通过装饰器freshdefault,完成了对于默认参数的更新。packitem的pkg已经在每次调用的时候更新了。 装饰器等价于 1 myfunc = wrapper(myfunc) 在此例子中 ,等价于在后面加上了一句 1 packitem = freshdefault(packitem) 参考 Python Cookbook Python源码剖析 ...

阅读全文 »

在 web.py 中使用装饰器

import web
urls = (
'/', 'hello',
)
app = web.application(urls, globals())
def hold(func):
print 'run hlod'
def f(self):
print 'in hold func'
return 'hold'
return f
class hello:
@hold
def GET(self):
print 'in hello'
return 'hello'<



 

 

现在的问题是,hold会在什么时候调用?

过去我一直以为是每次调用GET函数的时候会调用一遍hold函数
但事实并不是这样的,
事实是,当定义GET函数加载的时候,就会调用一遍hold函数
而且仅仅调用一遍hold,
这时候hold返回的函数就会替代原始的GET函数定义,
所以运行这点python代码,输出结果如下

 

 

 

http://0.0.0.0:8080/
run hlod
in hold func
10.0.2.2:51602 - - [23/Sep/2011 08:04:11] "HTTP/1.1 GET /" - 200 OK
10.0.2.2:51602 - - [23/Sep/2011 08:04:11] "HTTP/1.1 GET /favicon.ico" - 404 Not Found
in hold func
10.0.2.2:51602 - - [23/Sep/2011 08:04:12] "HTTP/1.1 GET /" - 200 OK 

				

阅读全文 »

rsync 的核心算法

rsync是unix/linux下同步文件的一个高效算法,它能同步更新两处计算机的文件与目录,并适当利用查找文件中的不同块以减少数据传输。rsync中一项与其他大部分类似程序或协定中所未见的重要特性是镜像是只对有变更的部分进行传送。rsync可拷贝/显示目录属性,以及拷贝文件,并可选择性的压缩以及递归拷贝。rsync利用由Andrew Tridgell发明的算法。这里不介绍其使用方法,只介绍其核心算法。我们可以看到,Unix下的东西,一个命令,一个工具都有很多很精妙的东西,怎么学也学不完,这就是Unix的文化啊。

本来不想写这篇文章的,因为原先发现有很多中文blog都说了这个算法,但是看了一下,发现这些中文blog要么翻译国外文章翻译地非常烂,要么就是介绍这个算法介绍得很乱让人看不懂,还有错误,误人不浅,所以让我觉得有必要写篇rsync算法介绍的文章。(当然,我成文比较仓促,可能会有一些错误,请指正)

问题

首先, 我们先来想一下rsync要解决的问题,如果我们要同步的文件只想传不同的部分,我们就需要对两边的文件做diff,但是这两个问题在两台不同的机器上,无法做diff。如果我们做diff,就要把一个文件传到另一台机器上做diff,但这样一来,我们就传了整个文件,这与我们只想传输不同部的初衷相背。

于是我们就要想一个办法,让这两边的文件见不到面,但还能知道它们间有什么不同。这就出现了rsync的算法。

算法

rsync的算法如下:(假设我们同步源文件名为fileSrc,同步目的文件叫fileDst)

1)分块Checksum算法。首先,我们会把fileDst的文件平均切分成若干个小块,比如每块512个字节(最后一块会小于这个数),然后对每块计算两个checksum,

一个叫rolling checksum,是弱checksum,32位的checksum,其使用的是Mark Adler发明的adler-32算法, 另一个是强checksum,128位的,以前用md4,现在用md5 hash算法。 为什么要这样?因为若干年前的硬件上跑md4的算法太慢了,所以,我们需要一个快算法来鉴别文件块的不同,但是弱的adler32算法碰撞概率太高了,所以我们还要引入强的checksum算法以保证两文件块是相同的。也就是说,弱的checksum是用来区别不同,而强的是用来确认相同。(checksum的具体公式可以参看这篇文章)

2)传输算法。同步目标端会把fileDst的一个checksum列表传给同步源,这个列表里包括了三个东西,rolling checksum(32bits),md5 checksume(128bits),文件块编号。

我估计你猜到了同步源机器拿到了这个列表后,会对fileSrc做同样的checksum,然后和fileDst的checksum做对比,这样就知道哪些文件块改变了。

但是,聪明的你一定会有以下两个疑问:

如果我fileSrc这边在文件中间加了一个字符,这样后面的文件块都会位移一个字符,这样就完全和fileDst这边的不一样了,但理论上来说,我应该只需要传一个字符就好了。这个怎么解决?

如果这个checksum列表特别长,而我的两边的相同的文件块可能并不是一样的顺序,那就需要查找,线性的查找起来应该特别慢吧。这个怎么解决? 很好,让我们来看一下同步源端的算法。

3)checksum查找算法。同步源端拿到fileDst的checksum数组后,会把这个数据存到一个hash table中,用rolling checksum做hash,以便获得O(1)时间复杂度的查找性能。这个hash table是16bits的,所以,hash table的尺寸是2的16次方,对rolling checksum的hash会被散列到0 到 2^16 – 1中的某个整数值。(对于hash table,如果你不清楚,建议回去看大学时的数据结构教科书)

顺便说一下,我在网上看到很多文章说,“要对rolling checksum做排序”(比如这篇和这篇),这两篇文章都引用并翻译了原作者的这篇文章,但是他们都理解错了,不是排序,就只是把fileDst的checksum数据,按rolling checksum做存到2^16的hash table中,当然会发生碰撞,把碰撞的做成一个链表就好了。这就是原文中所说的第二步——搜索有碰撞的情况。

4)比对算法。这是最关键的算法,细节如下:

4.1)取fileSrc的第一个文件块(我们假设的是512个长度),也就是从fileSrc的第1个字节到第512个字节,取出来后做rolling checksum计算。计算好的值到hash表中查。

4.2)如果查到了,说明发现在fileDst中有潜在相同的文件块,于是就再比较md5的checksum,因为rolling checksume太弱了,可能发生碰撞。于是还要算md5的128bits的checksum,这样一来,我们就有 2^-(32+128) = 2^-160的概率发生碰撞,这太小了可以忽略。如果rolling checksum和md5 checksum都相同,这说明在fileDst中有相同的块,我们需要记下这一块在fileDst下的文件编号。

4.3)如果fileSrc的rolling checksum 没有在hash table中找到,那就不用算md5 checksum了。表示这一块中有不同的信息。总之,只要rolling checksum 或 md5 checksum 其中有一个在fileDst的checksum hash表中找不到匹配项,那么就会触发算法对fileSrc的rolling动作。于是,算法会住后step 1个字节,取fileSrc中字节2-513的文件块要做checksum,go to (4.1) - 现在你明白什么叫rolling checksum了吧。

4.4)这样,我们就可以找出fileSrc相邻两次匹配中的那些文本字符,这些就是我们要往同步目标端传的文件内容了。

阅读全文 »

Python 的闭包和装饰器(转载)

Part I 原文地址: http://blaag.haard.se/Python-Closures-and-Decorators–Pt–1/ 回想起来,当初我做出了错误的选择,把 Python 的课程削减到了4个小时以至于把装饰器的部分搞砸了,我答应大家我稍后会对闭包和装饰器做一个更好的解说 —— 我是这么打算的。 函数也是对象。实际上,在 Python 中函数是一级对象——也就是说,他们可以像其他对象一样使用而没有什么特别的限制。这给了我们一些有趣的选择,我会由浅到深解释这个问题。 关于函数就是对象的一个最常见的例子就是 C 中的函数指针;将函数传递到其他的将要使用它的函数。为了说明这一点,我们来看看一个重复函数的实现 —— 也就是,一个函数接受另外一个函数以及一个数字当作参数,并且重复调用指定函数指定次数: #A very simple function def greeter(): … print("Hello") … #An implementation of a repeat function def repeat(fn, times): … for i in range(times): … fn() … repeat(greeter, 3) Hello Hello Hello 这种模式在很多情况下都有用 —— 比如向一个排序算法传递比较函数,向一个语法分析器传递一个装饰器函数,通常情况下这些做法可以使一个函数的行为 更专一化 ,或者向已经抽象了工作流的函数传递一个待办的特定部分(比如, sort() 知道怎么排序, compare() 知道怎么比较元素)。 函数也可以在其他函数的内部声明,这给了我们另一个很重要的工具。在一般情况下,这可以用来隐藏实用函数的实现细节: def print_integers(values): … def is_integer(value): … try: … return value == int(value) … except: … return False … for v in values: … if is_integer(v): … print(v) … print_integers([1,2,3,"4", "parrot", 3.14]) 1 2 3 这可能是有用的,但它本身并不算是个强大的工具。相比函数可以当作参数被传递而言,我们可以将它们包装(wrap)在另外的函数中,从而向已经构建好的函数增加新的行为。一个简单的例子是向一个函数增加跟踪输出: def print_call(fn): … def fn_wrap(*args, **args): #take any arguments … print ("Calling %s" % (fn.func_name)) … return fn(*args, **kwargs) #pass any arguments to fn() … return fn_wrap … greeter = print_call(greeter) #wrap greeter repeat(greeter, 3) Calling fn_wrap Hello Calling fn_wrap Hello Calling fn_wrap Hello greeter.func_name 'fn_wrap' 正如你看到的那样,我们可以使用带日志的函数来替换掉现有函数相应的部分,然后调用原来的函数。在例子的最后两行,函数的名字已经反映出了它已经被改变,这个改变可能是我们想要的,也可能不是。如果我们想包装一个函数同时保持它原来的名字,我们可以增加一行 print_call 函数,代码如下: def print_call(fn): … def fn_wrap(*args, **kwargs): #take any arguments … print("Calling %s" % (fn.func_name)) … return fn(*args, **kwargs) #pass any arguments to fn() … fn_wrap.func_name = fn.func_name #Copy the original name … return fn_wrap 因为这是一个很长的话题,我明天会来更新第二部分,我们会讲讲闭包,偏函数(partial),还有(终于到它了)装饰器。 至此,如果这些你之前全部没有接触过,可以先用 print_call 函数作为基础,来创建一个能够在正常调用函数之前先打印出这个函数名字的一个修饰器。 Part II 原文地址: http://blaag.haard.se/Python-Closures-and-Decorators–Pt–2/ 在第一部分中,我们学习了以函数作为参数调用其他的函数,还有嵌套函数,最终我们把一个函数包装在另外的函数中。我们先把第一部分的答案给出: def print_call(fn): … def fn_wrap(*args, **kwargs): … print("Calling %s with arguments: \n\targs: %s\n\tkwargs:%s" %fn.__name__, args, kwargs)) … retval = fn(*args, **kwargs) … print("%s returning '%s'" % (fn.func_name, retval)) … return retval … fn_wrap.func_name = fn.func_name … return fn_wrap … def greeter(greeting, what='world'): … return "%s %s!" % (greeting, what) … greeter = print_call(greeter) greeter("Hi") Calling greeter with arguments: args: ('Hi',) kwargs:{} greeter returning 'Hi world!' 'Hi world!' greeter("Hi", what="Python") Calling greeter with arguments: args: ('Hi',) kwargs:{'what': 'Python'} greeter returning 'Hi Python!' 'Hi Python!' 这稍微有那么点儿用了,但它可以变的更好!你可能听说过或者没有听说过*闭包*,你可能听说过成千上万种闭包定义中的某一种或者某几种 —— 我不会那么挑剔,我只是说闭包就是一个捕捉了(或者关闭)非本地变量(自由变量)的代码块(比如一个函数)。如果你不清楚我在说什么,你可能需要进修一下 CS 的相关课程,但是不要担心 —— 我会给你演示例子。闭包的概念很简单:一个可以引用在函数闭合范围内变量的函数。 比如说,看一下这个代码: a = 0 def get_a(): … return a … get_a() 0 a = 3 get_a ...

阅读全文 »

OOREDIS:一个 Pythonic 的 Redis 库

用Redis的朋友们应该会发现,Redis的很多客户端都只是Redis命令的一个简单包装。 举个例子,在Redis的Python客户端redis-py中,设置一个String键的方法如下: from redis import Redis r = Redis r.set('key_name', 'value') 而要取得一个列表的所有元素,则要使用lrange命令: r.lrange('list', 0, -1) 这种操作方式有几个问题: 1.大量的Redis命令聚在一起,妨碍了对客户端对象的使用。 2.每次操作都要将将key name和命令参数(比如lrange的0和-1)显式地传入方法当中,容易出错。 3.命令之间没有限制,可以互相覆盖而没有错误提示。 比如你可以用set命令覆盖一个Redis列表,Redis本身不会报错。 4.客户端没有利用到语言提供的方便机制。 比如r.lrange('list', 0, -1)在Python中就没有for item in list语句来得直观。 5.Redis只储存字符串值,虽然可以储存整数或浮点数,但每次取出值都要显式类型转换,很不方便。 --- 为了解决以上问题,更好地使用Redis,我用Python写了一个Redis库,基于redis-py,名叫ooredis。 ooredis有以下目标: 1.以Key对象为单位操作Redis的数据结构 在ooredis中,Redis的函数被按类型及作用归为了一个个Python类,每个ooredis类有不同的操作。 比如在ooredis中,将Redis的Hash类函数包裹成了Dict类型,它可以以类似Python内置dict类型的方式,操纵Redis数据。 又比如,Redis的List类函数,在ooredis中被包裹成了List类型,它可以以类似Python内置list类型的方式,操纵Redis数据。 如果ooredis类尝试覆盖不同类型的数据,ooredis将抛出异常。 这样就解决了包括命名空间污染、跨类型覆盖等问题。 2.提供一组Pythonic的API 刚才我们说“以类型Python内置的dict类型的方式来操纵Redis的Hash类型数据“,我们还没详细说明这是什么意思。 比如说,在ooredis中,你可以通过传给Dict类一个key name,之后就可以操纵这个Dict对象,来完成Redis中的各种命令,像这样: form ooredis import * project = Dict('ooredis-project') project['name'] = 'ooredis' project['version'] = 1.0 project['author'] = 'huangz' 以上的语句就相当于执行Redis的命令: redis HSET ooredis name ooredis redis HSET ooredis version 1.0 redis HSET ooredis author huangz 也可以用redis-py来完成上面的任务: r.hset('ooredis-project', 'name', 'ooredis') r.hset('ooredis-project', 'version', 1.0) r.hset('ooredis-project', 'autohr', 'huangz) 可以看到,使用Dict对象比单纯的Redis命令更直观。 又比如在Dict对象中,你可以用Python内置类型set的全部命令:items、keys、values、pop,等等。 project.items() [('name', u'ooredis'), ('version', 1.0), ('author', u'huangz')] 'version' in project True project.pop('name') u'ooredis' 不仅是Dict类,ooredis的所有类都大量使用了Python的魔法方法,致力于让Redis数据的操作更直观、清晰和Pythonic。 3.提供方便的类型转换机制 至于类型问题,ooredis通过使用传入TypeCase的方式,来对Redis数据进行类型转换。 比如如果你需要一个只保存整数对象(int/long)类型的列表,只需要这样做就可: numbers = List('numbers', type_case=IntTypeCase) 如果你需要一个只接受Json对象的字典对象,只需要使用以下语句: json_only_dict = Dict('json_dict', type_case=JsonTypeCase) 其中IntTypeCase和JsonTypeCase都是ooredis默认提供的TypeCase类之一,ooredis总共提供了以下常用TypeCase: GenericTypeCase,接受Python常量值,比如int,long,float,str和unicode。为了世界的和平与正义,传入的字符串值总被转换成unicode类型。 IntTypeCase,接受int和long。 FloatTypeCase,接受浮点数。 StringTypeCase,接受str和unicode类型的值,而且总被转换成unicode。 JsonTypeCase,接受所有可被转换成Json对象的值,比如Python的dict类型。 SerializeTypeCase,使用Pickle的dumps和loads,可以对Python的class进行序列化。 当然,除了以上的TypeCase之外,你也可以很方便地定义自己的TypeCase类,像如下代码: class MyTypeCase: @staticmethod def to_redis(value): pass @staticmethod def to_python(value): pass to_redis将值转换成Redis能接受的类型,to_python则将从Redis取出的数据转回原来的类型。 --- 好的,以上就是关于ooredis的基本介绍了,抱歉因为时间关系我不能写一篇更短的文章。 如果你对ooredis有兴趣,可以访问以下地址,获得更多信息: ooredis的更详细介绍,幻灯: http://bit.ly/rbgn3Z ooredis的项目主页: https://github.com/huangz1990/ooredis ...

阅读全文 »

__init__.py文件的用途

__init__.py 有两个用途 :

1、是表示目录下面的python 程序是module 的一部分

2、module 自身,module 自身以及submodule 的初始化、声明了。

例如:

--- breakfast

|

|- spam.py

|- toast.py

|- jam.py

 

包的结构:

package1/

__init__.py

subPack1/

__init__.py

module_11.py

module_12.py

module_13.py

subPack2/

__init__.py

module_21.py

module_22.py

  ……如果用户 import breakfast.spam 来引入 spam ,这样是不行的,因为在breakfast 目录下面没有 __init__.py 这个文件, 如果在breakfast 目录下面加入 __init__.py 就可以了。

阅读全文 »

python 菜谱(python cookbook) 1.4 字符串对齐

任务:实现字符串对齐:左对齐,居中对齐,或者右对齐 这就是字符串内置函数ljust,rjust,center所能实现的功能。函数类似于S.ljust(width[, fillchar]),默认的fillchar是空格,如果想指定填充字符的话,需要给出指定字符。 x=' hej ' print '|', 'hej'.ljust(20), '|', 'hej'.center(20), 'hej'.rjust(20)

阅读全文 »