Python: 别在迭代容器 list/dict 时候增减容器内的元素

别在迭代容器 list/dict 时候增减容器内的元素:

Python 3.5.0
>>> aa = [1, 2, 3, 4, 5, 6]
>>> for a in aa:
>>>     if 2 < a < 5:
>>>         aa.remove(a)
>>> aa
[1, 2, 4, 5, 6]

反面参考来源:

http://www.oschina.net/translate/playing-around-with-await-async-in-python-3-5

http://benno.id.au/blog/2015/05/25/await1

2013,12,08 PyCon 2013 珠海,笔记

当场笔记:

http://www.douban.com/photos/album/122033509/

等有空再整理,总结。 :-(

Python 嵌套函数本地变量

大家都知道这个:

>>> def g():
>>>    s = 'z'
>>>    def f():
>>>        s = 'k'
>>>        print('f: %s' % s)
>>>    f()
>>>    return s
>>> print(g())

在 Python 2.x/3.x 都会得到:

f: k
z

但是最近同事使用 list 写了一个这样的:

>>> def g():
>>>     s = ['z']
>>>     def f():
>>>         s[0] = 'k'
>>>         print('f: %s' % s[0])
>>>     f()
>>>     return s[0]
>>> print(g())

在 Python 2.x/3.x 都会得到:

f: k
k

这是?


updated on 2013,05,02

其实这个问题主要包括两个方面的原因:

1) Python 调用模型中对参数的传递使用的是传值,不过所传的值不是该参数对象具体的值,而是该参数对象的引用。

http://docs.python.org/2/tutorial/controlflow.html#defining-functions

The actual parameters (arguments) to a function call are introduced in the local symbol table of the called function when it is called; thus, arguments are passed using call by value (where the value is always an object reference, not the value of the object). [1] When a function calls another function, a new local symbol table is created for that call.

...

[1] Actually, call by object reference would be a better description, since if a mutable object is passed, the caller will see any changes the callee makes to it (items inserted into a list).

另外,也有称这种参数传递方式为 call by object 的:

http://effbot.org/zone/call-by-object.htm

2) 在 Python 中变量名只是其所对应的对象的一种引用,而且在对象与变量名的对应关系中使用 copy on write 的方式来优化性能。

要知道一个对象与变量名的关系。在 Python 中变量名与对象是不同的东西。一般来说,通过赋值语句会生成一个对象,但这个对象一般不能直接使用,需要通过一个引用来使用它,即给它一个名字。也就是等号左边是一个变量名,右边是生成对象的代码,因此在执行这条赋值语句后就存在了两个东西,一个是名字,一个是对象。在 Python 中把名字与变量关联起来叫绑定。因此在后面你如果继续对同一个变量名赋值的话,其实是实现了对变量名与新对象的重新绑定,在 Python 中这是允许的,原来绑定的对象如果无人再使用就自动回收,否则就将引用计数减1。而这个对象是无法改变类型的。

copy on write 在重新绑定对象与变量名的时候起作用。

无论对象是不是被作为参数调用并应用到局部作用域,这个 copy on write 方式都发生作用:

>>> # 不可变对象的情况
>>> ss = 'abc'
>>> id(ss)  # -> 36956344
>>> ss = 'xyz'
>>> id(ss)  # -> 59511896
>>> kk = ss  # point to the same object
>>> id(kk)  # -> 59511896
>>> kk = 'rst'  # copy on write
>>> id(kk)  # -> 59547520
>>> id(ss)  # -> 59511896
>>> ss  # -> 'xyz'

>>> # 可变对象的情况
>>> aa = [11,  22]
>>> id(aa)  # -> 59628168
>>> aa.append(33)
>>> id(aa)  # -> 59628168
>>> aa[2] = 44
>>> id(aa)  # -> 59628168
>>> aa = ['x', 'y', 'z']
>>> id(aa)  # -> 60494664
>>> bb = aa  # point to the same object
>>> id(bb)  # -> 60494664
>>> bb[1] = 's'
>>> id(bb)  # -> 60494664
>>> id(aa)  # -> 60494664
>>> bb  # -> ['x', 's', 'z']
>>> aa  # -> ['x', 's', 'z']
>>> bb = [111, 222, 333, 444]  # copy on write
>>> id(bb)  # -> 60483784
>>> id(aa)  # -> 60494664

updated on 2015,12,18

然后,注意这里其实主要是闭包的问题。

在 Python 中闭包的一些文章:

Python闭包详解

http://www.cnblogs.com/ChrisChen3121/p/3208119.html

Python深入04 闭包

http://www.cnblogs.com/vamei/archive/2012/12/15/2772451.html

Python中的闭包

http://www.the5fire.com/closure-in-python.html

有趣的Python闭包(Closures)

http://feilong.me/2012/06/interesting-python-closures

在内部函数bar里面,本地的a = 1定义了在bar函数范围内的新的一个局部变量,因为名字和外部函数foo里面的变量a名字相同,导致外部函数foo里的a在内部函数bar里实际已不可见。

再来说a = a + 1出错是怎么回事,首先a = xxx这种形式,Python解析器认为要在内部函数bar内创建一个新的局部变量a,同时外部函数foo里的a在bar里已不可见,而解析器对接下来对右边的a + 1的解析就是用本地的变量a加1,而这时左边的a即本地的变量a还没有创建(等右边赋值呢),因此就这就产生了一个是鸡生蛋还是蛋生鸡的问题,导致了上面说的UnboundLocalError的错误。

在Python3.x中引入了一个nonloacal的关键字来解决这个问题,只要在a = a + 1前加一句nonloacal a即可,即显式的指定a不是内部函数bar内的本地变量,这样就可以在bar内正常的使用和再赋值外部函数foo内的变量a了。


updated on 2016,01,06

当在内部函数使用 globals()locals() 时候,可能外部函数里的变量尚未被放入闭包中:

via: https://www.python.org/dev/peps/pep-0498/#id25

In the discussions on python-dev [4] , a number of solutions where presented that used locals() and globals() or their equivalents. All of these have various problems. Among these are referencing variables that are not otherwise used in a closure. Consider:

>>> def outer(x):
...     def inner():
...         return 'x={x}'.format_map(locals())
...     return inner
...
>>> outer(42)()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in inner
KeyError: 'x'

This returns an error because the compiler has not added a reference to x inside the closure. You need to manually add a reference to x in order for this to work:

>>> def outer(x):
...     def inner():
...         x
...         return 'x={x}'.format_map(locals())
...     return inner
...
>>> outer(42)()
'x=42'

In addition, using locals() or globals() introduces an information leak. A called routine that has access to the callers locals() or globals() has access to far more information than needed to do the string interpolation.

Guido stated [5] that any solution to better string interpolation would not use locals() or globals().

[4]: Formatting using locals() and globals() ( https://mail.python.org/pipermail/python-ideas/2015-July/034671.html )

[5]: Avoid locals() and globals() ( https://mail.python.org/pipermail/python-ideas/2015-July/034701.html )

Tornado HTTPServer

Tornado 官方文档和源代码中的几个例子都是运行在单进程模式的, 但是一样可以很简单地运行在多进程模式下。

参看:Tornado: 2. 源码分析 (2)

文章中虽然分析的是 0.2 版本的代码, 但是直到现在最新的 1.2.1 版本中,这些内容也基本没变。

httpserver.py:

If num_processes is None or <= 0, we detect the number of cores available on this machine and fork that number of child processes. If num_processes is given and > 1, we fork that specific number of sub-processes.

没有测试过多进程模式下的性能和稳定性。

Tornado 官方文档: Tornado Web Server Document 官方文档的中文翻译: python_Tornado文档翻译

官方文档中并没有提到使用多进程模式运行 Tornado 实例, 而是提出:

We ran 4 single-threaded Tornado frontends behind an nginx reverse proxy, which is how we recommend running Tornado in production (our load test machine had four cores, and we recommend 1 frontend per core).

整篇文档完全没有提及 pre-forking .

估计是因为: (httpserver.py)

Since we use processes and not threads, there is no shared memory between any server code.

所以在官方文档中提及的使用 "1 frontend per core" 的部署方式, 在性能和资源消耗上应该是和 pre-forking 没什么差别的?

而分别在 nginx 后部署多个 Tornado 单进程实例也还是有些好处的, 比如可以自己控制单进程实例的数量。

Python 中 List Comprehensions 的陷阱?

使用 Python 版本: 2.6.6

对于 List Comprehensions 中涉及到的变量,将会修改上下文相同的变量 ::

>>> user_name = "zhangkc"
>>> users = [user_name for user_name in ["zhangkc", "lixc", "caoxj"]]
>>> user_name
'caoxj'

update on 2011,04,21

其实在遇到这个问题的时候的两天后就看到有位外国技术人员在 PyCon 上的相关发布:

PyCon: Cooking with Python 3

其中写道:

If you write "[x * x for x in nums]; print x", x won't be in scope. This is a bit surprising.

在 Python 3.1 和 Python 3.2 解释器中测试过了,果然在 Python 3 中已去掉了这个“陷阱”。


update on 2012,09,16

嗯,这是 Python 早期的一个设计上的问题。在: From List Comprehensions to Generator Expressions 上有记载,解释了早期之所以这样设计的原因:

This was an artifact of the original implementation of list comprehensions; it was one of Python's "dirty little secrets" for years. It started out as an intentional compromise to make list comprehensions blindingly fast, and while it was not a common pitfall for beginners, it definitely stung people occasionally.

一道简单的笔试题:打印一个菱形

这是一道简单的笔试题,然而还是花了我一些时间。 暂时是这个粗糙的样子:

#!/usr/bin/env python2

__author__ = "zhangkaizhao <zhangkaizhao@gmail.com>"

def print_lozenge(xnum):
    alen = xnum * 2 - 1
    for i in range(alen):
        if i % 2:
            print " "
        else:
            start = (i < xnum) and (xnum - 1 - i) or (i - xnum + 1)
            end = alen - 1 - start
            xjs = range(start, end + 2, 2)
            print "".join([i in xjs and "X" or " " for i in range(alen)])

if __name__ == "__main__":
    import sys
    args = sys.argv
    if len(args) != 2:
        print "Usage: python print_lozenge.py number"
        sys.exit(1)
    xnum = args[1]
    try:
        xnum = int(xnum)
    except:
        print "number must be an odd number"
        sys.exit(1)
    if not xnum % 2:
        print "number must be an odd number"
        sys.exit(1)
    print_lozenge(xnum)

Dropbox client

Dropbox client

2010,11,02 zhangkaizhao

dropbox client 版本:0.7.79 winxp

最近在开发类似的项目。 昨晚无聊,想探探这个客户端的内容, 结果发现其实应该非常简单, 整个 dropbox client 完全使用 Python 2.5 开发而成的。

Windows XP 下 Dropbox 默认安装在用户家目录的 Application Data/Dropbox/ 目录下。 主要分析文件:

bin/Dropbox.exe
dropbox.db

主文件 Dropbox.exe

不要被 Dropbox.exe 文件有 20M 大小而吓到, 当你使用解压缩软件查看这个文件的时候, 你将惊奇的发现,这原来不过是一个压缩文件!

$ mkdir /tmp/dropbox.exe
$ 7z x Dropbox.exe -o/tmp/dropbox.exe
$ ls -l /tmp/dropbox.exe

你将看到很多已编译的 .pyc 文件和 .pyd 文件以及动态链接库 .dll 文件:

总用量 10848
drwx------  2 zkc zkc    4096 11月  2 09:27 adodbapi
drwx------  5 zkc zkc    4096 11月  2 09:27 arch
-rw-rw-r--  1 zkc zkc    9121 12月 11 2009 asynchat.pyc
-rw-rw-r--  1 zkc zkc   18417 12月 11 2009 asyncore.pyc
-rw-rw-r--  1 zkc zkc    2209 12月 11 2009 atexit.pyc
-rw-rw-r--  1 zkc zkc   11681 12月 11 2009 base64.pyc
-rw-rw-r--  1 zkc zkc   20785 12月 11 2009 bdb.pyc
-rw-rw-r--  1 zkc zkc    2961 12月 11 2009 bisect.pyc
drwx------  2 zkc zkc    4096 11月  2 09:27 build_number
-rw-rw-r--  1 zkc zkc   77824 12月 23 2008 bz2.pyd
-rw-rw-r--  1 zkc zkc   31697 12月 11 2009 calendar.pyc
-rw-rw-r--  1 zkc zkc   35649 12月 11 2009 cgi.pyc
drwx------  2 zkc zkc    4096 11月  2 09:27 client_api
-rw-rw-r--  1 zkc zkc   14657 12月 11 2009 cmd.pyc
-rw-rw-r--  1 zkc zkc   37105 12月 11 2009 codecs.pyc
-rw-rw-r--  1 zkc zkc    6977 12月 11 2009 codeop.pyc
-rw-rw-r--  1 zkc zkc   10929 12月 11 2009 code.pyc
-rw-rw-r--  1 zkc zkc   45617 12月 11 2009 commctrl.pyc
-rw-rw-r--  1 zkc zkc   14241 12月 11 2009 common.pyc
drwx------  2 zkc zkc    4096 11月  2 09:27 common_util
drwx------  5 zkc zkc    4096 11月  2 09:27 comtypes
-rw-rw-r--  1 zkc zkc    4417 12月 11 2009 contextlib.pyc
-rw-rw-r--  1 zkc zkc   61825 12月 11 2009 cookielib.pyc
-rw-rw-r--  1 zkc zkc   12273 12月 11 2009 copy.pyc
-rw-rw-r--  1 zkc zkc    5569 12月 11 2009 copy_reg.pyc
drwx------  3 zkc zkc    4096 11月  2 09:27 core
-rw-rw-r--  1 zkc zkc    6433 12月 11 2009 cProfile.pyc
drwx------  3 zkc zkc    4096 11月  2 09:27 ctypes
-rw-rw-r--  1 zkc zkc   86016 12月 23 2008 _ctypes.pyd
-rw-rw-r--  1 zkc zkc  160097 12月 11 2009 decimal.pyc
-rw-rw-r--  1 zkc zkc   63585 12月 11 2009 difflib.pyc
-rw-rw-r--  1 zkc zkc    6673 12月 11 2009 dis.pyc
drwx------  2 zkc zkc    4096 11月  2 09:27 distutils
-rw-rw-r--  1 zkc zkc   87121 12月 11 2009 doctest.pyc
-rw-rw-r--  1 zkc zkc   18529 12月 11 2009 dropsyncore.pyc
-rw-rw-r--  1 zkc zkc    1393 12月 11 2009 dummy_threading.pyc
-rw-rw-r--  1 zkc zkc    5825 12月 11 2009 dummy_thread.pyc
drwx------  3 zkc zkc    4096 11月  2 09:27 email
drwx------  2 zkc zkc    4096 11月  2 09:27 encodings
-rw-rw-r--  1 zkc zkc    7680  9月 29 2009 _fastdetails.pyd
-rw-rw-r--  1 zkc zkc    8704  9月 29 2009 fastpath.pyd
-rw-rw-r--  1 zkc zkc   10433 12月 11 2009 filecmp.pyc
-rw-rw-r--  1 zkc zkc    3377 12月 11 2009 fnmatch.pyc
drwx------  2 zkc zkc    4096 11月  2 09:27 formencode
-rw-rw-r--  1 zkc zkc   29393 12月 11 2009 ftplib.pyc
-rw-rw-r--  1 zkc zkc    1969 12月 11 2009 functools.pyc
-rw-rw-r--  1 zkc zkc    4065 12月 11 2009 __future__.pyc
-rw-rw-r--  1 zkc zkc    7073 12月 11 2009 getopt.pyc
-rw-rw-r--  1 zkc zkc    3537 12月 11 2009 getpass.pyc
-rw-rw-r--  1 zkc zkc   17825 12月 11 2009 gettext.pyc
-rw-rw-r--  1 zkc zkc    2337 12月 11 2009 glob.pyc
-rw-rw-r--  1 zkc zkc    6545 12月 11 2009 gopherlib.pyc
-rw-rw-r--  1 zkc zkc   15809 12月 11 2009 gzip.pyc
-rw-rw-r--  1 zkc zkc    2017 12月 11 2009 hard_trace.pyc
-rw-rw-r--  1 zkc zkc    4385 12月 11 2009 hashlib.pyc
-rw-rw-r--  1 zkc zkc  323584 12月 23 2008 _hashlib.pyd
-rw-rw-r--  1 zkc zkc   11569 12月 11 2009 heapq.pyc
-rw-rw-r--  1 zkc zkc   43425 12月 11 2009 httplib.pyc
-rw-rw-r--  1 zkc zkc   37841 12月 11 2009 inspect.pyc
drwx------  2 zkc zkc    4096 11月  2 09:27 installer
-rw-rw-r--  1 zkc zkc    2145 12月 11 2009 keyword.pyc
-rw-rw-r--  1 zkc zkc    3457 12月 11 2009 linecache.pyc
-rw-rw-r--  1 zkc zkc   45185 12月 11 2009 locale.pyc
drwx------  2 zkc zkc    4096 11月  2 09:27 logging
-rw-rw-r--  1 zkc zkc    6033 12月 11 2009 _LWPCookieJar.pyc
-rw-rw-r--  1 zkc zkc   10273 12月 11 2009 macpath.pyc
-rw-rw-r--  1 zkc zkc    2945 12月 11 2009 macurl2path.pyc
-rw-rw-r--  1 zkc zkc     225 12月 11 2009 md5.pyc
-rw-rw-r--  1 zkc zkc    8561 12月 11 2009 mimetools.pyc
-rw-rw-r--  1 zkc zkc   17745 12月 11 2009 mimetypes.pyc
-rw-rw-r--  1 zkc zkc   12288  2月 16 2009 mmapfile.pyd
-rw-rw-r--  1 zkc zkc    4769 12月 11 2009 _MozillaCookieJar.pyc
drwx------  2 zkc zkc    4096 11月  2 09:27 ncrypt
-rw-rw-r--  1 zkc zkc  909312  9月 29 2009 _ncrypt.pyd
-rw-rw-r--  1 zkc zkc    9265 12月 11 2009 netbios.pyc
-rw-rw-r--  1 zkc zkc     721 12月 11 2009 new.pyc
-rw-rw-r--  1 zkc zkc   12529 12月 11 2009 ntpath.pyc
-rw-rw-r--  1 zkc zkc   22657 12月 11 2009 ntsecuritycon.pyc
-rw-rw-r--  1 zkc zkc    1857 12月 11 2009 nturl2path.pyc
-rw-rw-r--  1 zkc zkc   61505 12月 11 2009 optparse.pyc
-rw-rw-r--  1 zkc zkc   11009 12月 11 2009 os2emxpath.pyc
-rw-rw-r--  1 zkc zkc   26097 12月 11 2009 os.pyc
-rw-rw-r--  1 zkc zkc   45185 12月 11 2009 pdb.pyc
-rw-rw-r--  1 zkc zkc   43185 12月 11 2009 pickle.pyc
-rw-rw-r--  1 zkc zkc   32609 12月 11 2009 platform.pyc
-rw-rw-r--  1 zkc zkc   10641 12月 11 2009 popen2.pyc
-rw-rw-r--  1 zkc zkc   13089 12月 11 2009 posixpath.pyc
-rw-rw-r--  1 zkc zkc   10337 12月 11 2009 pprint.pyc
-rw-rw-r--  1 zkc zkc   26433 12月 11 2009 pstats.pyc
-rw-rw-r--  1 zkc zkc    6737 12月 11 2009 py_compile.pyc
-rw-rw-r--  1 zkc zkc  356352  3月 31 2009 pythoncom25.dll
drwx------  4 zkc zkc    4096 11月  2 09:27 pywin
-rw-rw-r--  1 zkc zkc  122880  2月 16 2009 pywintypes25.dll
-rw-rw-r--  1 zkc zkc    8385 12月 11 2009 Queue.pyc
-rw-rw-r--  1 zkc zkc    7025 12月 11 2009 quopri.pyc
-rw-rw-r--  1 zkc zkc   25617 12月 11 2009 random.pyc
-rw-rw-r--  1 zkc zkc    6017 12月 11 2009 repr.pyc
-rw-rw-r--  1 zkc zkc   13553 12月 11 2009 re.pyc
-rw-rw-r--  1 zkc zkc   34273 12月 11 2009 rfc822.pyc
-rw-rw-r--  1 zkc zkc    7680 12月 23 2008 select.pyd
-rw-rw-r--  1 zkc zkc   20257 12月 11 2009 sets.pyc
-rw-rw-r--  1 zkc zkc     257 12月 11 2009 sha.pyc
-rw-rw-r--  1 zkc zkc    8289 12月 11 2009 shlex.pyc
-rw-rw-r--  1 zkc zkc    7553 12月 11 2009 shutil.pyc
drwx------  2 zkc zkc    4096 11月  2 09:27 simplejson
-rw-rw-r--  1 zkc zkc   42577 12月 11 2009 socket.pyc
-rw-rw-r--  1 zkc zkc   53248 12月 23 2008 _socket.pyd
-rw-rw-r--  1 zkc zkc   24576  9月 29 2009 _speedups.pyd
drwx------  2 zkc zkc    4096 11月  2 09:27 sqlite3
-rw-rw-r--  1 zkc zkc  514570 10月 14 2009 sqlite3.dll
-rw-rw-r--  1 zkc zkc   53248 10月 16 2009 _sqlite3.pyd
drwx------ 10 zkc zkc    4096 11月  2 09:27 sqlobject
-rw-rw-r--  1 zkc zkc   12417 12月 11 2009 sre_compile.pyc
-rw-rw-r--  1 zkc zkc    6161 12月 11 2009 sre_constants.pyc
-rw-rw-r--  1 zkc zkc   21601 12月 11 2009 sre_parse.pyc
-rw-rw-r--  1 zkc zkc     513 12月 11 2009 sre.pyc
-rw-rw-r--  1 zkc zkc  663552 10月 16 2009 _ssl.pyd
-rw-rw-r--  1 zkc zkc    2625 12月 11 2009 stat.pyc
-rw-rw-r--  1 zkc zkc   12177 12月 11 2009 StringIO.pyc
-rw-rw-r--  1 zkc zkc   16353 12月 11 2009 stringprep.pyc
-rw-rw-r--  1 zkc zkc   17841 12月 11 2009 string.pyc
-rw-rw-r--  1 zkc zkc   15713 12月 11 2009 _strptime.pyc
-rw-rw-r--  1 zkc zkc    3841 12月 11 2009 struct.pyc
-rw-rw-r--  1 zkc zkc   33873 12月 11 2009 subprocess.pyc
-rw-rw-r--  1 zkc zkc   70881 12月 11 2009 tarfile.pyc
-rw-rw-r--  1 zkc zkc   16113 12月 11 2009 tempfile.pyc
-rw-rw-r--  1 zkc zkc   11505 12月 11 2009 textwrap.pyc
-rw-rw-r--  1 zkc zkc    6849 12月 11 2009 _threading_local.pyc
-rw-rw-r--  1 zkc zkc   30913 12月 11 2009 threading.pyc
-rw-rw-r--  1 zkc zkc   11953 12月 11 2009 tokenize.pyc
-rw-rw-r--  1 zkc zkc    3857 12月 11 2009 token.pyc
-rw-rw-r--  1 zkc zkc   12641 12月 11 2009 traceback.pyc
-rw-rw-r--  1 zkc zkc    1313 12月 11 2009 trace_report.pyc
-rw-rw-r--  1 zkc zkc    2705 12月 11 2009 types.pyc
drwx------  2 zkc zkc    4096 11月  2 09:27 ui
-rw-rw-r--  1 zkc zkc  479232 12月 23 2008 unicodedata.pyd
-rw-rw-r--  1 zkc zkc   36929 12月 11 2009 unittest.pyc
-rw-rw-r--  1 zkc zkc   51073 12月 11 2009 urllib2.pyc
-rw-rw-r--  1 zkc zkc   54977 12月 11 2009 urllib.pyc
-rw-rw-r--  1 zkc zkc   13281 12月 11 2009 urlparse.pyc
-rw-rw-r--  1 zkc zkc    9761 12月 11 2009 UserDict.pyc
-rw-rw-r--  1 zkc zkc   22305 12月 11 2009 uuid.pyc
-rw-rw-r--  1 zkc zkc    4433 12月 11 2009 uu.pyc
-rw-rw-r--  1 zkc zkc    8993 12月 11 2009 warnings.pyc
-rw-rw-r--  1 zkc zkc   14737 12月 11 2009 weakref.pyc
-rw-rw-r--  1 zkc zkc   21041 12月 11 2009 webbrowser.pyc
-rw-rw-r--  1 zkc zkc   98304  3月 31 2009 win32api.pyd
-rw-rw-r--  1 zkc zkc   14848  3月 31 2009 win32clipboard.pyd
drwx------  5 zkc zkc    4096 11月  2 09:27 win32com
-rw-rw-r--  1 zkc zkc  140577 12月 11 2009 win32con.pyc
-rw-rw-r--  1 zkc zkc   14848  3月 31 2009 win32event.pyd
-rw-rw-r--  1 zkc zkc  114688  3月 31 2009 win32file.pyd
-rw-rw-r--  1 zkc zkc  159744  3月 31 2009 win32gui.pyd
-rw-rw-r--  1 zkc zkc   22865 12月 11 2009 win32gui_struct.pyc
-rw-rw-r--  1 zkc zkc   20992  3月 31 2009 win32pipe.pyd
-rw-rw-r--  1 zkc zkc   45056  3月 31 2009 win32process.pyd
-rw-rw-r--  1 zkc zkc   13824  2月 16 2009 win32profile.pyd
-rw-rw-r--  1 zkc zkc  114688  3月 31 2009 win32security.pyd
-rw-rw-r--  1 zkc zkc    5632  3月 31 2009 _win32sysloader.pyd
-rw-rw-r--  1 zkc zkc   19456  3月 31 2009 win32ts.pyd
-rw-rw-r--  1 zkc zkc  761856  3月 31 2009 win32ui.pyd
-rw-rw-r--  1 zkc zkc   20480  2月 16 2009 win32wnet.pyd
-rw-rw-r--  1 zkc zkc  113345 12月 11 2009 winerror.pyc
-rw-rw-r--  1 zkc zkc  176128  3月 31 2009 winxpgui.pyd
drwx------  3 zkc zkc    4096 11月  2 09:27 wx
-rw-rw-r--  1 zkc zkc 3096576  9月 29 2009 wxmsw28uh_vc.dll
-rw-rw-r--  1 zkc zkc    4545 12月 11 2009 zipextimporter.pyc
-rw-rw-r--  1 zkc zkc   29025 12月 11 2009 zipfile.pyc

这其实就是一个使用类似 py2exe/pyinstaller 等 Python 打包程序生成的自解压可执行程序, 这里我们关注两个地方,一个是使用 wxPython 作为其 GUI 解决方案,所以有:

drwx------  3 zkc zkc    4096 11月  2 09:27 wx
-rw-rw-r--  1 zkc zkc 3096576  9月 29 2009 wxmsw28uh_vc.dll

另外一个地方是其最关键文件:

-rw-rw-r--  1 zkc zkc   18529 12月 11 2009 dropsyncore.pyc

我想 dropbox 客户端的大部分程序逻辑都在此了。 这个已编译的 .pyc 文件也不大, 其编译前的 .py 文件估计大小为 16Kb 左右, 其行数大概就是几百行。 结合使用类似 [DePython]http://www.depython.com/ 等方式, 估计很快就可完全掌握 dropbsyncore.pyc 文件的源文件内容了。 Python 拿来做客户端程序看来还是令人难以放心啊?

数据库文件 dropbox.db

这其实就是一个 sqlite3 的数据库文件, Python 自从 2.5 版本开始内置了完整的 sqlite3 标准模块, 所以一切显得那么的简单高效。 查看这个数据库文件:

$ sqlite3 dropbox.db
sqlite> .tables
block_cache   config        file_journal
sqlite> .schema block_cache
CREATE TABLE block_cache (
    id INTEGER PRIMARY KEY,
    hash VARCHAR(43) NOT NULL UNIQUE,
    sig TEXT,
    size INT,
    delete_after INT,
    needed_for INT
);
sqlite> .schema config
CREATE TABLE config (
    id INTEGER PRIMARY KEY,
    key TEXT NOT NULL UNIQUE,
    value TEXT
);
sqlite> .schema file_journal
CREATE TABLE file_journal (
    id INTEGER PRIMARY KEY,
    server_path TEXT NOT NULL UNIQUE,
    active_server_path TEXT,
    active_blocklist TEXT,
    active_mtime INT,
    active_size INT,
    active_sjid INT UNIQUE,
    active_dir INT,
    active_attrs TEXT,
    updated_server_path TEXT,
    updated_blocklist TEXT,
    updated_mtime INT,
    updated_size INT,
    updated_sjid INT,
    updated_dir INT,
    updated_attrs TEXT,
    on_disk TINYINT
);
sqlite> select * from config;
1|schema_version|STYKLg==

2|last_revision|KGRwMQpMMzU3Mzc2N0wKTDM1MTkyOTU1NTgxMzMwNDMyN0wKcy4=

3|proxy_server|
4|startupitem|STAwCi4=

5|proxy_requires_auth|STAwCi4=

6|show_bubbles|STAwCi4=

7|proxy_port|STgwODAKLg==

8|max_download_kBs|RjAKLg==

9|proxy_mode|STEKLg==

10|proxy_username|
11|max_upload_kBs|SS0xCi4=

12|proxy_type|VkhUVFAKcDEKLg==

13|shadowed_proxy_password|UyItOWtceDgyZ1x4YjFceGUzXHhhY1x4ZjNceGQ5XHgwYkxceGVhJ1x4OTFceGU5IgpwMQou

14|host_id|VmJjNGQ1ODYxOTIzMmVmZTc2OTlkNjI4YjBlM2NjZjUxCnAxCi4=

15|dropbox_path|VkU6XHUwMDVDZGF0YVx1MDA1Q2Ryb3Bib3hcdTAwNUNNeSBEcm9wYm94CnAxCi4=

16|email|VnpoYW5na2Fpemhhb0BnbWFpbC5jb20KcDEKLg==

17|root_ns|TDM1NzM3NjdMCi4=

18|ns_p2p_key_map|KGRwMQpMMzU3Mzc2N0wKKFYtLS0tLUJFR0lOIENFUlRJRklDQVRFLS0tLS1cdTAwMEFNSUlDSkRD
Q0FVMmdBd0lCQXdJQkFUQU5CZ2txaGtpRzl3MEJBUXNGQURBWk1SY3dGUVlEVlFRREV3NWtjbTl3
XHUwMDBBWW05NExXTnNhV1Z1ZERBZUZ3MDNNREF4TURFd01EQXdNREJhRncweE1ERXlNVFV3TlRV
NU5UWmFNQmt4RnpBVlx1MDAwQUJnTlZCQU1URG1SeWIzQmliM2d0WTJ4cFpXNTBNSUhkTUEwR0NT
cUdTSWIzRFFFQkFRVUFBNEhMQURDQnh3S0JcdTAwMEF3UUN4NWxnOUtkbVNaNVpmZDZERFFSaHlP
R1YrdUFaTU90YkE5RXJzU1d6QnRQeitQNFpJelg1cWlXdUxMVkluXHUwMDBBSFVMVWR0NFNnM1Av
MmdTK0s5eG03M3MrU0paRnJHTk40dWVDSjZvditZVXN5ditSVVRycnBCTlBPeU9DN2pDTFx1MDAw
QWpVaFRGK0Y0ZFBHUXpuTjZodzhlZWtOd20vR1c3SjJNdTA3UmtCZHhmbVBaRE1rRzlvdlppem1x
bGlxTm9EaDZcdTAwMEE2L3FOSjdHc1NMdjFoUzExUmVBVEZXRjF6eTgxcGdHT3AxNk1kOW5kSGN3
UmRoTkt2N3ZZckhLdTM0SEhjY20wXHUwMDBBdHo4Q0FRVXdEUVlKS29aSWh2Y05BUUVMQlFBRGdj
RUFyR3psaUFXRnUrR3lJdGdZUUZGc0xuS2ZDelFtSkg0ZVx1MDAwQXhrZ3VsQ253L3ZxWndVeXZO
UDQzWjBpeTV0cXIybGYzenlxeldiSnpSNXFrTXlzMFlWS2F4Y04wdDBtUWoxeUpcdTAwMEE0ajlt
OXdZTjZ2TEs4ZUFuaWpINnliTThicEVncHc5eW4yKytqelRWUDQ3Rm9OcXpUSnBKejV4OTcrVDlz
T2k5XHUwMDBBNzYxbE4wbE9pb2FrQW0zNTlUN1NHL0NhWThQYlUwV2pwRUZybXg4ZXE1cVFEbE1l
QWxlcWZlS2s0TXZMSkE4R1x1MDAwQUthK09UTjc0T1NNaUlrMWZXMG5jWVBHQVpPazZyT3NyXHUw
MDBBLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLVx1MDAwQQpWLS0tLS1CRUdJTiBSU0EgUFJJVkFU
RSBLRVktLS0tLVx1MDAwQU1JSURld0lCQUFLQndRQ3g1bGc5S2RtU1o1WmZkNkREUVJoeU9HVit1
QVpNT3RiQTlFcnNTV3pCdFB6K1A0WklcdTAwMEF6WDVxaVd1TExWSW5IVUxVZHQ0U2czUC8yZ1Mr
Szl4bTczcytTSlpGckdOTjR1ZUNKNm92K1lVc3l2K1JVVHJyXHUwMDBBcEJOUE95T0M3akNMalVo
VEYrRjRkUEdRem5ONmh3OGVla053bS9HVzdKMk11MDdSa0JkeGZtUFpETWtHOW92Wlx1MDAwQWl6
bXFsaXFOb0RoNjYvcU5KN0dzU0x2MWhTMTFSZUFURldGMXp5ODFwZ0dPcDE2TWQ5bmRIY3dSZGhO
S3Y3dllcdTAwMEFySEt1MzRISGNjbTB0ejhDQVFVQ2djQkhLUEFZZHlQVUtXOVpZdzBhNXRhVUZv
Ny9mTTlSc1NLemxPckU2aXVBXHUwMDBBcnN1WTVqVzJ1SmozYWlzRWVJZEMySUVoeVNXaEFXR1pp
bWhNRVl0Y1g4cmx0cUtDUlBTRmpmWTBEOTJzeWpWRlx1MDAwQUhmL1R1aGVSZE5TR0Y2Zk4rT0Ez
MGh4Q3ptWTdmdTByNUIwckpnSUkyRDBESUhPeVFnVlk2OXJ6b2g3QzkrR3dcdTAwMEFETVFKc1I3
Qk8wb0MxZ0RuK0dxemNzTDdFK1RUT3kvem14b2cyMVZJdUl6aUQ2OUd3NkREVDJScGsrTzg4UmZk
XHUwMDBBVHNEZWhVckZQT1FEUlhyV3A5czdUSjBDWVFEbWVwd1FuT3diVjM5S2l4eHczQzhpMFJH
a0loQWc4VzM5cUhkYVx1MDAwQVVrK0dIa1lVaGhMbEJPK3l5MXM3MlNTdWxEUFo0MzQ0M3IzZjB2
SXpZRWRQaWlpdmFzUmdGZVlQRzBPaTNhUTZcdTAwMEFEVWVWaW91bU9iTHNVMUQvR2lmZDZ1WVdr
eE1DWVFERm1VWFRHclNIdkt0RUhPMlhnWHZGK2I2MUpZQU5mRDF5XHUwMDBBMGxNdndHQWF6cGph
TmF3UmNoRHdzODBOMmdrTHVOOC9rbmRqVmdZMHJubnZ3VU9OdmRpUlBUU2twd21YUlovaFx1MDAw
QXFIeEt0Y2pTcHFWNE9NMy9RT2VubDZiUTR6K0o1S1VDWVFDNFloWnpzTHppckdYVlBCYkFzQ1cx
ZEE0YzZBem5cdTAwMEFKNHN4VTVLdXFEK2VmcDUyMGFpM055WW9vcTlqRkIxWWRvL2hnc3Rnc2pG
LzIvVDE1cDl5MU8xWklqYXpSTGdNXHUwMDBBRmMrMWZoejdQZExlQ0crNExpaTlEM1AvU0Iva3Ur
dEZRblVDWUU4S0crNEtycHl4M2hzK3hXL05aTFdYR1JWQ1x1MDAwQU16aVlHSlJVSVVaTTh6M3NQ
U1FWZUFiNmJTMFV1R3Z3MEdzV3Y3TUhZdlNJejBoRnlsL201OUpMOERvWWUzVVBcdTAwMEFuVysx
Y3ljUU1iZDc2ZTNjUWpBV3VNeUFYS2x2M0ZPT0dXcGJkUUpoQUlCRjZ1QkZxT3pJTmNsWFhxWWpY
QUJHXHUwMDBBakYzM0sxZVA0OVduNDNNM0pMbEQrNHdheFc2V3g3WVlUaU9JbHVBQzBOUFBvL3ZH
YWxka1Y5bXBSVkVXN003OVx1MDAwQXUyeVZOekN4c1lvN0ZMcElpQXJyNjJqTFZqWHhCS3NMcDJN
dFJkV2lIZz09XHUwMDBBLS0tLS1FTkQgUlNBIFBSSVZBVEUgS0VZLS0tLS1cdTAwMEEKUycwZGJh
YjMzMzZhNTg2NzU4ZTlmY2RiYzRlOGQ2NTg1NycKcDIKdHAzCnMu

19|p2p_enabled|STAwCi4=
sqlite> .quit

好家伙,东西全在这里了!


update 2010,12,13

Dropbox 的客户端逻辑其实分开好几个模块实现, dropsyncore.pyc 只是很小的一部分。 将近一年多没有运行 Dropbox ,一运行居然自动更新到最近版,从防火墙 Comodo Firewall 查看其更新下载的数据大概有 20 多M, 看来应该是将整个安装程序都下载到本地了。 新的 Dropbox.exe 中内容更多了,不过还是使用 Python 2.5 而不是目前主流的 Python 2.6 。不知道为什么。

最近 UbuntuOne 也开始推出支持 Windows 平台的客户端(beta),下载安装测试了觉得没什么值得期待的。 在自家 Ubuntu 下都那么地慢吞吞,在 Windows 下也是这德行。。。 另外, Ubuntu 使用的单点登录机制好像性能有问题。

Plone4

Plone 4.0 在几天前终于正式发布了。
这个应该是个重要的版本,然而已经没有多少人(国内)关注了。

Planet Plone 上有篇文章 Jon Stahl: 5 Things That Rock About Plone 4 看上去比较有意思。

Plone 及其底层开发框架 Zope 终于作出了很多重大的改变。虽然外表还是那么的丑,外表功能也似乎还是那些。

kupu

kupu 这个符合 Zope 复杂风格的奇怪的在线编辑器终于可以退位让贤了。

chameleon

计划中的 chameleon 模板技术即将替代落后的 ZPT 了。

Zope 的强大的面向对象机制是其优点,也是其“缺点”。 为什么这么说呢? 比如 Zope 发布机制,URL dispatch ,ZPT 等均以此为基础, 看上去很“自然”,看上去很“强大”,看上去很“完美”。 然而,她也为此付出了代价。
Zope 发布机制及 URL dispatch 导致定位最终对象的过程经过了一个漫长的对象容器搜索过程, ZODB 由此也可能被判定为性能的瓶颈。
而 ZPT 的强大的上下文对象也使得渲染模板的时候可能导致多次的回调其视图view 。

如今使用正则表达式来解决 URL dispatch 已完全占据了主流地位, 无论是 Java web容器的 url-mapping 还是 Ruby on Rails 的 Routes mapping , 或者各个 Python 的大大小小的框架,除了 Quxiote 等类 PHP 的文件目录结构方式等等, 几乎都使用了正则表达式了。 总结上越是抽象越是脱离计算机机器原理的东西,越是会产生性能问题,虽然看上去真的很“美”。

而现在大部分的 web 框架都把上下文定义为一个类字典的对象,或者干脆就是个字典, 直接传递给模板进行渲染使用,这样简单明了的做法,看上去虽然很原始,但却很高效简洁。 更避免了多次回调视图的问题。

ZODB 中的 BLOB 支持

这个或许真如文章所说的那样,可能将使得很多基于 Zope/Plone 的文件存储管理系统抛弃原来那种依靠操作系统的文件系统来组织和管理文件及文件目录的方式。
汗,说得好绕。。。

--

最后,Zope3 已死,留下一堆的 Zope 组件。Zope2 依靠 Plone 苟延残存,Zope3 的大部分组件重归 Zope2 并继续被改进。。。

而 repoze.bfg 已经越来越远离 zope 了,然后 grok 差不多真的成了一个笑话了。。。

自己编译的mod_wsgi需要注意python的prefix

Common mod_wsgi installation problems

如果你的 Python 和 mod_wsgi 是自己手工编译的,例如使用:

Python 的编译前配置:

./configure --prefix=/opt/py26

mod_wsgi 的编译前配置:

./configure --with-apxs=/usr/bin/apxs2 --with-python=/opt/py26/bin/python

那么在配置 apache 的时候,需要指明 WSGIPythonHome 参数:

WSGIPythonHome /opt/py26

即类似这样:

WSGIPythonHome /opt/py26
WSGIScriptAlias /pyblog /path/to/yourapplication.wsgi