Tuesday, May 06, 2008

跳进python, Django

一直都想学习java以外的一门新语言,在学习java以前看过perl, php, 但是粗略的看一看,又没有地方来用,最终就淡忘了。 后来开始学python,也是一样的,有空看看书,但是用的机会不多,慢慢又模糊了, 真是浪费生命。 再以后开始用python写一些小工具来辅助开发,比如写了一个破解我老婆博客密码的小东东, 一个根据diff文件构建升级包的工具, 实践是最好的老师,一些模糊的记忆又慢慢开始清晰了。 java世界开始变得开放,比如jython的出现,这样python代码可以直接在java虚拟机上跑了。 更好的事情是grinder,一个开源的java性能测试工具, 它的测试脚本支持用jython来写, 于是就在项目里面小用了一下,很有意思, python可以在日常工作中发挥更大的作用了。
不过这里要写这篇blog是用来作为我学习python的笔记, 一些自己在使用python的过程经常出错的东西都要在这里记录下来,免得同一个石头绊我三次。

[内置的数据类型]
dictionary: key必须是常量 ,key大小写敏感,而且不能重复。 其中的元素是没有秩序的。
list::相当于php中的数组,可以保存不同类型的数据。 其中元素是有秩序的
tuple: 不可变的list, 可以用tuple()来将一个list转化为tuple; 反过来可以用list()将一个tuple转化为list

python是一种强类型的语言,就是说一旦将给一个变量赋值之后,这个值的类型是不能变化的,比如进行显式的强制转换,比如: int('1'), str(1); 对于弱类型的语言,这种转换可以自动完成,比如‘1’ + 2在python中会抛出异常,但是javascript中可以返回‘12’, 或者3,取决于你怎么使用。
python中有一个内置函数isinstance()可以检验某个值的数据类型(int, long, str, dict, list, tuple, float, Class etc),比如: isinstance(1, int) = True, isinstance('1', int) = False

python中有个types模块,其中定义了很多内置的数据类型:
>>> import types
>>> dir(types)

[内置函数]
[getattr]
使用 getattr 函数,可以得到一个直到运行时才知道名称的函数的引用。
>>> li = ["Larry", "Curly"]
>>> li.pop

>>> getattr(li, "pop")

>>> getattr(li, "append")("Moe")
>>> li
["Larry", "Curly", "Moe"]
[isinstance]
是否是某个类型的实例, isinstance(1, int) = true
[callable]
接收任何对象作为参数,如果参数对象是可调用的,返回 True;否则返回 False
[range ]
内置的 range 函数返回一个元素为整数的 list。这个函数的简化调用形式是接收一个上限值,然后返回一个初始值从 0 开始的 list,它依次递增,直到但不包含上限值。(如果您愿意,您可以传入其它的参数来指定一个非 0 的初始值和非 1 的步长。也可以使用 print range.__doc__ 来了解更多的细节。)
(MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY) = range(7)

[filter]

Python 有一个内建 filter 函数,它接受两个参数:一个函数和一个列表,返回一个列表。作为第一个参数传递给 filter 的函数本身应接受一个参数,filter 返回的列表将会包含被传入列表参数传递给 filter 所有可以令函数返回真 (true) 的元素。

>>> def odd(n):                 1
... return n % 2
...
>>> li = [1, 2, 3, 5, 9, 10, 256, -3]
>>> filter(odd, li) 2
[1, 3, 5, 9, -3]

[map]

使用内建 map 函数。它的工作机理和 filter 函数类似。

>>> def double(n):
... return n*2
...
>>> li = [1, 2, 3, 5, 9, 10, 256, -3]
>>> map(double, li) 1
[2, 4, 6, 10, 18, 20, 512, -6]

[other...]

获得定长字符串:

>>>s = 'xiix'

>>>s.ljust(8, '0')

'xiix0000'

>>>s.zfill(8)

'0000xiix'

[映射list]

Python 的强大特性之一是其对 list 的解析,它提供一种紧凑的方法,可以通过对 list 中的每个元素应用一个函数,从而将一个 list 映射为另一个 list。

>>> li = [1, 9, 8, 4]
>>> [elem*2 for elem in li] 1
[2, 18, 16, 8]
[过滤列表]
这种能力同过滤机制结合使用,使列表中的有些元素被映射的同时跳过另外一些元素。
[mapping-expression for element in source-list if filter-expression]
[使用lamba函数]
Python 支持一种有趣的语法,它允许你快速定义单行的最小函数。这些叫做 lambda 的函数,是从 Lisp 借用来的,可以用在任何需要函数的地方。
>>> (lambda x: x*2)(3) 2
6
lambda 函数可以接收任意多个参数 (包括可选参数) 并且返回单个表达式的值。lambda 函数不能包含命令,包含的表达式不能超过一个。

[多变量赋值]
>>>t = (1, 2, 3)
>>>(x, y, z) = t
x = 1, y =2, z =3
>>>l = [1, 2]
>>>[x,y] = l
x = 1, y = 2

[类的定义]
  • 当从你的类中调用一个父类的一个方法时,你必须包括 self 参数。
  • __init__ 方法是可选的,但是一旦你定义了,就必须记得显示调用父类的 __init__ 方法 (如果它定义了的话)。这样更是正确的:无论何时子类想扩展父类的行为,后代方法必须在适当的时机,使用适当的参数,显式调用父类方法。
class FileInfo(UserDict):
    "store file metadata"
def __init__(self, filename=None):
UserDict.__init__(self) 1
self["name"] = filename
2
  • 要在类的内部引用一个数据属性,我们使用 self 作为限定符。习惯上,所有的数据属性都在 __init__ 方法中初始化为有意义的值。然而,这并不是必须的,因为数据属性,像局部变量一样,当你首次赋给它值的时候突然产生
  • 专用类方法。Python 有一个与 __getitem__ 类似的 __setitem__ 专用方法,比如f['name']这个看上去就像你用来得到一个字典值的语法,事实上它返回你期望的值。下面是隐藏起来的一个环节:暗地里,Python 已经将这个语法转化为 f.__getitem__("name") 的方法调用。这就是为什么 __getitem__ 是一个专用类方法的原因,不仅仅是你可以自已调用它,还可以通过使用正确的语法让 Python 来替你调用。
  • 类属性和数据属性。 数据属性在__init__方法中通过'self.XXX'来定义,是一个特定类实例所拥有的变量;类属性是在class中直接定义, 是由类拥有的属性,当然所有该类的实例都拥有该属性。
[高级专用类方法]
  • __repr__ 是一个专用的方法,在当调用 repr(instance) 时被调用。repr 函数是一个内置函数,它返回一个对象的字符串表示。它可以用在任何对象上,不仅仅是类的实例。你已经对 repr 相当熟悉了,尽管你不知道它。在交互式窗口中,当你只敲入一个变量名,接着按ENTER,Python 使用 repr 来显示变量的值。自已用一些数据来创建一个字典 d ,然后用 print repr(d) 来看一看吧。(java.lang.Object#toString())
  • __cmp__ 在比较类实例时被调用。通常,你可以通过使用 == 比较任意两个 Python 对象,不只是类实例。有一些规则,定义了何时内置数据类型被认为是相等的,例如,字典在有着全部相同的关键字和值时是相等的。对于类实例,你可以定义 __cmp__ 方法,自已编写比较逻辑,然后你可以使用 == 来比较你的类,Python 将会替你调用你的 __cmp__ 专用方法。(java.lang.Object#equals(o, o)
  • __len__ 在调用 len(instance) 时被调用。len 是一个内置函数,可以返回一个对象的长度。它可以用于任何被认为理应有长度的对象。字符串的 len 是它的字符个数;字典的 len 是它的关键字的个数;列表或序列的 len 是元素的个数。对于类实例,定义 __len__ 方法,接着自已编写长度的计算,然后调用 len(instance),Python 将替你调用你的 __len__ 专用方法。
  • __delitem__ 在调用 del instance[key] 时调用 ,你可能记得它作为从字典中删除单个元素的方法。当你在类实例中使用 del 时,Python 替你调用 __delitem__ 专用方法。
这些其实就和java中的Object类相似, 因为java中所有类都是从Object继承的,那些方法都定义在Object中,而python没有这样的基类,通过这些专用方法来达到类似的效果。或者有些象设计模式中的模板方法(template method)

[xml解析]
  • 如果你打算在你的 Python 代码中保存非 ASCII 字符串,你需要在每个文件的顶端加入编码声明来指定每个 .py 文件的编码。这个声明定义了 .py 文件的编码为 UTF-8:
#!/usr/bin/env python
# -*- coding: UTF-8 -*-

  • 通过sys.getdefaultencoding()得到当前的默认编码形式, 在python启动以后就不能调用sys.setdefaultencoding('latin-1')来设定编码。 这里介绍一些简单的历史。ASCIi是英语字符的编码,只需要0-127, 但是欧洲,尤其是西欧有很多古怪的字符,比如西班牙语里有字符在n上面加一个波浪线,为了增加对这些字符的支持,出现了ISO-8859-1也就是Latin-1标准,所有那些字符都在128-255之间。 而unicode是一个想要统一天下所有字符的编码标准,地球上所有语言的任何一个字符都可以找到一个唯一的编码,使用两个字节,从0到65535(仅仅中日韩就不止65535个字符了吧,不够用)。 utf-8是对unicode进行的演化,因为对于很多iso-8859-1标准内定义的字符,它们用一个字符就可以表示了,如果用unicode来表示的话就相当于浪费一个字节,所以utf-8对ASCII等单字节的字符仍然只用一个字节表示,对其他的还是双字节。 而utf-16就是对所有字符都使用双字节。
  • iso-8859-1是ascii的超集,unicode是iso-8859-1的超集。 而中文又存在GBK和GB2312两个标准, gbk是gb2312的超集,其中增加了对繁体字,以及一些没有收录到gb2312中的汉字字符。 gb是中国的标准,在一定程度上,比如0-127段的定义和ascii是一致的。 更多信息可以参考维基(http://en.wikipedia.org/wiki/GBK)
[动态导入]
>>> sys = __import__('sys')           1
>>> os = __import__('os')
>>> re = __import__('re')
>>> unittest = __import__('unittest')
>>> sys 2
>>>
>>> os
>>>
1 内建 __import__ 函数与 import 语句的既定目标相同,但它是一个真正的函数,并接受一个字符串参数。
2 变量 sys 现在是 sys 模块,和 import sys 的结果完全相同。变量 os 现在是一个 os 模块,等等。

因此 __import__ 导入一个模块,但是是通过一个字符串参数来做到的。依此处讲,你用以导入的仅仅是一个硬编码性的字符串,但它可以是一个变量,或者一个函数调用的结果。并且你指向模块的变量也不必与模块名匹配。你可以导入一系列模块并把它们指派给一个列表。


[参考资源]

3 comments:

Unknown said...
This comment has been removed by the author.
Unknown said...

哪里可以找到site-packages目录(python的lib目录)?
The location of the site-packages directory depends on the operating system, and the location in which Python was installed. To find out your system’s site-packages location, execute the following:
python -c "from distutils.sysconfig import get_python_lib; print get_python_lib()"

Anonymous said...

Thanks for writing this.