[转]如何像Python高手(Pythonista)一样编程
本文转自: 最近在网上看到一篇介绍Pythonic编程的文章:,其实作者在2006的PyCon会议后就写了这篇文章,写这篇文章的主要原因是作者发现很多有经验的Pythoner写出的代码不够Pythonic。我觉得这篇文章很不错,所以将它用中文写了下来(不是逐字的翻译,中间加了一些自己的理解),分享给大家。另:由于本人平时时间有限,这篇文章翻译了比较长的时间,如果你发现了什么不对的地方,欢迎指出。。 一、Python之禅(The Zen of Python)The Zen of Python是Python语言的指导原则,遵循这些基本原则,你就可以像个Pythonista一样编程。具体内容你可以在Python命令行输入import this看到: Beautiful <span style="color: #0000ff;">is<span style="color: #000000;"> better than ugly.
<span style="color: #008000;">#<span style="color: #008000;"> 优美胜于丑陋(Python以编写优美的代码为目标) <span style="color: #000000;"> Explicit <span style="color: #0000ff;">is<span style="color: #000000;"> better than implicit. <span style="color: #008000;">#<span style="color: #008000;"> 明了胜于晦涩(优美的代码应当是明了的,命名规范,风格相似) <span style="color: #000000;"> Simple <span style="color: #0000ff;">is<span style="color: #000000;"> better than complex. <span style="color: #008000;">#<span style="color: #008000;"> 简洁胜于复杂(优美的代码应当是简洁的,不要有复杂的内部实现) <span style="color: #000000;"> Complex <span style="color: #0000ff;">is<span style="color: #000000;"> better than complicated. <span style="color: #008000;">#<span style="color: #008000;"> 复杂胜于凌乱(如果复杂不可避免,那代码间也不能有难懂的关系,要保持接口简洁) <span style="color: #000000;"> Flat <span style="color: #0000ff;">is<span style="color: #000000;"> better than nested. <span style="color: #008000;">#<span style="color: #008000;"> 扁平胜于嵌套(优美的代码应当是扁平的,不能有太多的嵌套) <span style="color: #000000;"> Sparse <span style="color: #0000ff;">is<span style="color: #000000;"> better than dense. <span style="color: #008000;">#<span style="color: #008000;"> 间隔胜于紧凑(优美的代码有适当的间隔,不要奢望一行代码解决问题) <span style="color: #000000;"> Readability counts. <span style="color: #008000;">#<span style="color: #008000;"> 可读性很重要(优美的代码是可读的) <span style="color: #000000;"> Special cases aren<span style="color: #800000;">'<span style="color: #800000;">t special enough to break the rules. <span style="color: #000000;">Although practicality beats purity. <span style="color: #008000;">#<span style="color: #008000;"> 即便假借特例的实用性之名,也不可违背这些规则(这些规则至高无上) <span style="color: #000000;"> Errors should never <span style="color: #0000ff;">pass<span style="color: #000000;"> silently. Unless explicitly silenced. <span style="color: #008000;">#<span style="color: #008000;"> 不要包容所有错误,除非你确定需要这样做(精准地捕获异常,不写except:pass风格的代码) <span style="color: #000000;"> In the face of ambiguity,refuse the temptation to guess. <span style="color: #008000;">#<span style="color: #008000;"> 当存在多种可能,不要尝试去猜测 <span style="color: #000000;"> There should be one-- <span style="color: #0000ff;">and preferably only one --<span style="color: #000000;">obvious way to do it. <span style="color: #008000;">#<span style="color: #008000;"> 而是尽量找一种,最好是唯一一种明显的解决方案(如果不确定,就用穷举法) <span style="color: #000000;"> Although that way may <span style="color: #0000ff;">not be obvious at first unless you<span style="color: #800000;">'<span style="color: #800000;">re Dutch. <span style="color: #008000;">#<span style="color: #008000;"> 虽然这并不容易,因为你不是 Python 之父(这里的Dutch是指Guido) Now <span style="color: #0000ff;">is<span style="color: #000000;"> better than never. Although never <span style="color: #0000ff;">is often better than right<span style="color: #000000;"> now. <span style="color: #008000;">#<span style="color: #008000;"> 做也许好过不做,但不假思索就动手还不如不做(动手之前要细思量) If the implementation <span style="color: #0000ff;">is hard to explain,it<span style="color: #800000;">'<span style="color: #800000;">s a bad idea. If the implementation <span style="color: #0000ff;">is<span style="color: #000000;"> easy to explain,it may be a good idea. <span style="color: #008000;">#<span style="color: #008000;"> 如果你无法向人描述你的方案,那肯定不是一个好方案;反之亦然(方案测评标准) Namespaces are one honking great idea -- let<span style="color: #800000;">'<span style="color: #800000;">s do more of those! <span style="color: #008000;">#<span style="color: #008000;"> 命名空间是一种绝妙的理念,我们应当多加利用(倡导与号召)
二、PEP8: Python编码规范(PEP8: Style Guide for Python Code)
空格和缩进(WhiteSpace and Indentation) 空格和缩进在Python语言中非常重要,它替代了其他语言中{}的作用,用来区分代码块和作用域。在这方面PEP8有以下的建议: 123456、紧随括号后面或者参数列表前一个字符不要存在空格
Python命名
1、方法 &2、常量:joined_lower 34、类属性:interface,_internal,
5、camelCase only to conform to pre-existing conventions
三、交换变量值(Swap Values)在其他语言中,交换两个变量值的时候,可以这样写: temp === temp
b,a = a,b
1、Python会先将右边的a,b生成一个tuple(元组),存放在内存中; 2、之后会执行赋值操作,这时候会将tuple拆开; 3、然后将tuple的第一个元素赋值给左边的第一个变量,第二个元素赋值给左边第二个变量。 再举个tuple拆分的例子: In [1]: people = [,,In [2]: name,title,phone =<span style="color: #000000;"> people
In [ 3<span style="color: #000000;">]: nameOut[3]: <span style="color: #800000;">'<span style="color: #800000;">David<span style="color: #800000;">'<span style="color: #000000;"> In [ 4<span style="color: #000000;">]: titleOut[4]: <span style="color: #800000;">'<span style="color: #800000;">Pythonista<span style="color: #800000;">'<span style="color: #000000;"> In [5<span style="color: #000000;">]: phone
In [6]: people = [[,],[,,In [7]: <span style="color: #0000ff;">for name,phone <span style="color: #0000ff;">in<span style="color: #000000;"> people:
...: <span style="color: #0000ff;">print<span style="color: #000000;"> name,phone ...: David 15145551234<span style="color: #000000;"> Wu 15101365547
更多tuple的例子: >>> 11>>> (11>>> (11
>>> value = 1>>>1,)
四、Python控制台的"_"(Interactive "_")
>>> >>> math.pi / 3
1.0471975511965976
>>> angle =>>>0.50000000000000011
>>>0.50000000000000011
五、合并字符串(Building Strings from Sub strings)
colors = [,,,result = <span style="color: #800000;">' '
<span style="color: #0000ff;">for s <span style="color: #0000ff;">in<span style="color: #000000;"> colors: result += s
result = .join(colors)
六、使用关键字in(Use in where possible)
d = {: 1,: 2
d.has_key(<span style="color: #0000ff;">for key <span style="color: #0000ff;">in<span style="color: #000000;"> d:
<span style="color: #0000ff;">print<span style="color: #000000;"> key <span style="color: #008000;">#<span style="color: #008000;"> DO NOT USE <span style="color: #0000ff;">for key <span style="color: #0000ff;">in<span style="color: #000000;"> d.keys(): <span style="color: #0000ff;">print key
七、字典(Dictionary)? 1、get 在获取dict中的数据时,我们一般使用index的方式,但是如果KEY不存在的时候会抛出KeyError。这时候你可以使用get方法,使用方法:dict.get(key,default=None),可以避免异常。例如: d = {: 1,: 2 d.get()
d.get(,14)
2、fromkeys dict本身有个fromkeys方法,可以通过一个list生成一个dict,不过得提供默认的value,例如:
>>> dict.fromkeys([,,],1
3、setdefault 有些情况下,我们需要给dict的KEY一个默认值,你可以这样写: equities = (portfolio,equity) portfolio = [equity]
equities = (portfolio,equity)
八、defaultdict
九、字典的组装和拆分(Building & Splitting Dictionaries)
>>> given = [,,,>>> family = [,,,>>> pythons =>>> : ,: ,: ,: }
>>>,,>>>,,]
十、Python的True值(Truth Values)
x ==
<span style="color: #008000;"># <span style="color: #008000;"> 对于list,要这样写<span style="color: #0000ff;">if<span style="color: #000000;"> items: <span style="color: #0000ff;">pass <span style="color: #008000;">#<span style="color: #008000;"> !不要这样写 <span style="color: #0000ff;">if len(items) ==<span style="color: #000000;"> 0: <span style="color: #0000ff;">pass 对于自己声明的class,如果你想明确地指定它的实例是True或False,你可以自己实现class的__nonzero__或__len__方法。当你的class是一个container时,你可以实现__len__方法,如下: </span><span style="color: #0000ff;">def</span> <span style="color: #800080;">__init__</span><span style="color: #000000;">(self,data):
self.data </span>=<span style="color: #000000;"> data
</span><span style="color: #0000ff;">def</span> <span style="color: #800080;">__len__</span><span style="color: #000000;">(self):
</span><span style="color: #800000;">"""</span><span style="color: #800000;"> Return my length. </span><span style="color: #800000;">"""</span>
<span style="color: #0000ff;">return</span> len(self.data)</pre>
</span><span style="color: #0000ff;">def</span> <span style="color: #800080;">__init__</span><span style="color: #000000;">(self,value):
self.value </span>=<span style="color: #000000;"> value
</span><span style="color: #0000ff;">def</span> <span style="color: #800080;">__nonzero__</span><span style="color: #000000;">(self):
</span><span style="color: #800000;">"""</span><span style="color: #800000;"> Return my truth value (True or False). </span><span style="color: #800000;">"""</span>
<span style="color: #008000;">#</span><span style="color: #008000;"> This could be arbitrarily complex:</span>
<span style="color: #0000ff;">return</span> bool(self.value)</pre>
=
十一、enumerate:索引和元素(Index & Item: enumerate)
>>> items = >>> ),(1,),(2,),(3,>>> (index,item) index,item
(index,item) <span style="color: #008000;">#<span style="color: #008000;"> compare:
index =<span style="color: #000000;"> 0 <span style="color: #0000ff;">for item <span style="color: #0000ff;">in<span style="color: #000000;"> items: <span style="color: #0000ff;">print<span style="color: #000000;"> index,item index += 1 <span style="color: #008000;">#<span style="color: #008000;"> compare:
>>>
十二、Python中的变量 & 引用(variables & names)
int a = 1;
a = 2;
int b = a;
盒子"b"是第二个"盒子",里面是整数 2的一个拷贝,盒子"a"中是另外一个拷贝。 在Python中,变量没有数据类型,是附属于对象的标示符名称,如下图:实际,这段表明了像python,PHP这类动态脚本语言中“变量”包含了两个内容:1 标识符名称 2 标识符所对应(引用)的值(对象),也就是说“变量”不在是一个容器。 a = 1
a = 2
b = a
变量 "a"和"b" 是指向同一个整数对象的。 PS:Python中的变量,引用等设计和其他语言不同,这里只是将原文翻译说明了一下,更多的介绍可以参看: 十三、Python方法中参数的默认值(Default Parameter Values)
bad_append(new_item,a_list=>>> <span style="color: #0000ff;">print bad_append(<span style="color: #800000;">'<span style="color: #800000;">one<span style="color: #800000;">'<span style="color: #000000;">)
[<span style="color: #800000;">'<span style="color: #800000;">one<span style="color: #800000;">'<span style="color: #000000;">] >>> <span style="color: #0000ff;">print bad_append(<span style="color: #800000;">'<span style="color: #800000;">two<span style="color: #800000;">'<span style="color: #000000;">) [<span style="color: #800000;">'<span style="color: #800000;">one<span style="color: #800000;">',<span style="color: #800000;">'<span style="color: #800000;">two<span style="color: #800000;">']
good_append(new_item,a_list= a_list = a_list
十四、字符串格式化(String Formatting)
1、操作符 "%" Python中的 "%" 操作符和C语言中的sprintf类似。简单来说,使用 "%" 来格式化字符串的时候,你需要提供一个字符串模板和用来插入的值。模板中有格式符,这些格式符为真实值预留位置,并说明真实数值应该呈现的格式。Python用一个tuple将多个值传递给模板,每个值对应一个格式符。注意:给定的值一定要和模板中的格式符一一对应! name = = 3= ( %<span style="color: #008000;">#<span style="color: #008000;"> Output: Hello xianglong,you have 3 messages
????常用的格式符如下: values = {: name, ( %<span style="color: #008000;">#<span style="color: #008000;"> Output: Hello xianglong,you have 3 messages
( % locals())
在Python中,对象有一个__dict__属性,你可以在格式化字符串的时候使用; ( % self.<span style="color: #008000;">#<span style="color: #008000;"> 等同于
<span style="color: #0000ff;">print (<span style="color: #800000;">"<span style="color: #800000;">We found %d errors<span style="color: #800000;">" % self.error_count)
比如: ( % 10)
( % 5)
( % 2.3)
( % (4,1.2))
str.format()方法是在Python 2.6中引入的,它通过 {} 和 : 来代替 % ,功能非常强大。具体的用法见下面的例子: In [1]: name = 2]: messages = 4
<span style="color: #008000;"># <span style="color: #008000;"> 通过位置In [3]: <span style="color: #800000;">'<span style="color: #800000;">Hello {0},you have {1} messages<span style="color: #800000;">'<span style="color: #000000;">.format(name,messages) Out[3]: <span style="color: #800000;">'<span style="color: #800000;">Hello xianglong,you have 4 messages<span style="color: #800000;">' <span style="color: #008000;">#<span style="color: #008000;"> 通过关键字参数 <span style="color: #008000;">#<span style="color: #008000;"> 通过下标 <span style="color: #008000;">#<span style="color: #008000;"> 格式限定符:填充与对齐<span style="color: #008000;"> <span style="color: #008000;"> ^、<、>分别是居中、左对齐、右对齐,后面带宽度<span style="color: #008000;"><span style="color: #008000;"> :号后面带填充的字符,只能是一个字符,不指定的话默认是用空格填充In [6]: <span style="color: #800000;">'<span style="color: #800000;">Hello {0:>14},you have {1:>14} messages<span style="color: #800000;">'<span style="color: #000000;">.format(name,messages) <span style="color: #008000;">#<span style="color: #008000;"> 格式限定符:精度与类型f <span style="color: #008000;">#<span style="color: #008000;"> 格式限定符:b、d、o、x分别是二进制、十进制、八进制、十六进制 In [9]: <span style="color: #800000;">'<span style="color: #800000;">{:d}<span style="color: #800000;">'.format(14<span style="color: #000000;">) In [10]: <span style="color: #800000;">'<span style="color: #800000;">{:o}<span style="color: #800000;">'.format(14<span style="color: #000000;">) In [11]: <span style="color: #800000;">'<span style="color: #800000;">{:x}<span style="color: #800000;">'.format(14<span style="color: #000000;">) <span style="color: #008000;">#<span style="color: #008000;"> 格式限定符:千位分隔符
十五、迭代器(List comprehensions)
new_list = item
new_list = [fn(item) item a_list condition(item)]
>>> [n ** 2 n range(101,4,9,16,25,36,49,64,81>>> [n ** 2 <span style="color: #0000ff;">for n <span style="color: #0000ff;">in range(10) <span style="color: #0000ff;">if n % 2<span style="color: #000000;">]
[1,81] 十六、生成器(Generator &?Generator expressions)
total = num range(1,101+= num * num
total = sum([num * num num range(1,101<span style="color: #008000;">#<span style="color: #008000;"> 生成器
total = sum(num * num <span style="color: #0000ff;">for num <span style="color: #0000ff;">in xrange(1,101))
在上面的例子中,我们只需要平方和,不需要平方的list,所以我们使用生成器xrange。如果我们计算1 ~ 1000000000的平方和,使用迭代器的话会内存溢出,但是生成器却不会: total = sum(num * num num xrange(1,1000000000))
row data_file = open(path,<span style="color: #800000;">'<span style="color: #800000;">rb<span style="color: #800000;">'<span style="color: #000000;">)
irows =<span style="color: #000000;"> filter_rows(csv.reader(data_file)) <span style="color: #008000;">#<span style="color: #008000;"> 文件读取:open datafile = open(<span style="color: #800000;">'<span style="color: #800000;">datafile<span style="color: #800000;">'<span style="color: #000000;">) <span style="color: #0000ff;">for line <span style="color: #0000ff;">in<span style="color: #000000;"> datafile: do_something(line)
十七、排序(Sorting)
In [1]: a_list = [,,,233]: [,,]
In [1]: a_list = [,2]: b_list =33]: [,44]: [,]
In [1]: cmp((item1[1],item1[3]),(item2[1],item2[32]: a_list = [,344]: [,]
1、DSU排序方法 DSU即Decorate-Sort-Undecorate,中文就是"封装-排序-解封"。DSU方法不会创建自定义的排序方法,而是创建一个辅助的排序列表,然后对这个列表进行默认排序。需要说明的是:DSU方法是一种比较老的方法,现在已经基本上不使用了,不过这里还是给出一个简单的例子说明一下:
to_sort = [(item[1],item[3],item) item <span style="color: #008000;">#<span style="color: #008000;"> Sort:
<span style="color: #000000;">to_sort.sort() <span style="color: #008000;">#<span style="color: #008000;"> Undecorate: a_list = [item[-1] <span style="color: #0000ff;">for item <span style="color: #0000ff;">in to_sort]
这种方法是使用复杂度和内存空间来减少计算时间,比较简单,也比较快,但是我们得复制原列表的数据。 2、KEY方法 自从Python 2.4之后,list.sort()和sorted()都添加了一个key参数用来指定一个函数,这个函数作用于每个list元素,在做cmp之前调用。key参数是一个函数,这个函数有一个参数,返回一个用来排序的关键字。这种排序方法很快,因为key方法在每个输入的record上只执行一次。你可以使用Python内置的函数(len,str.lower)或者自定义函数作为key参数,下面是一个简单的例子: In [1]: a_list = [,2]: (item[1],item[33]: a_list.sort(key=44]: [,]
十八、EAFP vs LBYL
LBYL: Look Before You?Leap,即事先检查; EAFP: It's Easier to Ask?Forgiveness than Permission,即不检查,出了问题由异常处理来处理。 异常处理总是比事先检查容易,因为你很难提前想到所有可能的问题。所以,一般情况下编码时会倾向使用EAFP风格,但它也不是适应所有的情况。两个风格的优缺点如下: d == [,,,,,
w w =+= 1
<span style="color: #008000;"># <span style="color: #008000;"> EAFP<span style="color: #0000ff;">for w <span style="color: #0000ff;">in<span style="color: #000000;"> words: <span style="color: #0000ff;">try<span style="color: #000000;">: d[w] += 1 <span style="color: #0000ff;">except<span style="color: #000000;"> KeyError: d[w] = 1
另外,需要注意的是,如果涉及到原子操作,强烈推荐用EAFP风格。比如我某段程序逻辑是根据redis的key是否存在进行操作。如果先if exists(key),然后do something。这样就变成2步操作,在多线程并发的时候,可能key的状态已经被其他线程改变了。而用EAFP风格则可以确保原子性。 PS:在使用EAFP风格捕获异常时,尽量指明具体的异常,不要直接捕获Exception。否则会捕获到其他未知的异常,如果有问题,你会很难去定位(debug)。 十九、引用(Importing)
module *
通配符引用的方式属于Python中的阴暗面,这种方式会导致命名空间污染的问题。你可能会在本地命名空间中得到意想不到的东西,而且这种方式引入的变量可能将你在本地定义的变量覆盖,在这种情况下,你很难弄清楚变量来自哪里。所以,通配符引用的方式虽然方便,但可能会导致各种各样奇怪的问题,在Python项目中尽量不要用这种方式。 在Python中,大家比较认同的import方式有以下几个规则: 1、通过模块引用变量(Reference names through their module) 这种方式直接import的是模块,然后通过模块访问其中的变量,Class和方法。使用这种方式可以很清晰的知道变量来自哪里: 2、模块名比较长时使用短名字(alias) 3、直接引用你需要的变量名 module
二十、模块与脚本(Modules & Scripts)
==
最好不要在Python文件中直接写可执行的语句,应该将这些代码放在方法或类里面,必要的时候放在"if __name__ ?== '__main__':"中。一个Python文件的结构可以参考下面的: <span style="color: #008000;"># <span style="color: #008000;"> 引用 imports<span style="color: #008000;"> <span style="color: #008000;"> 常量 constants<span style="color: #008000;"> <span style="color: #008000;"> 异常 exception classes<span style="color: #008000;"> <span style="color: #008000;"> 方法 interface functions<span style="color: #008000;"><span style="color: #008000;"> 类 classes<span style="color: #008000;"><span style="color: #008000;"> 内部方法和类 internal functions & classes<span style="color: #0000ff;">def<span style="color: #000000;"> main(...): <span style="color: #0000ff;">if <span style="color: #800080;">name == <span style="color: #800000;">'<span style="color: #800000;">main<span style="color: #800000;">'<span style="color: #000000;">: 二十一、命令行解析(Commend-Line Processing)
<span style="color: #800000;">""" <span style="color: #800000;">Module docstring. <span style="color: #800000;">""" <span style="color: #0000ff;">import<span style="color: #000000;"> sys <span style="color: #0000ff;">def<span style="color: #000000;"> process_command_line(argv):
<span style="color: #0000ff;">def main(argv=<span style="color: #000000;">None): <span style="color: #0000ff;">if <span style="color: #800080;">name == <span style="color: #800000;">'<span style="color: #800000;">main<span style="color: #800000;">'<span style="color: #000000;">: 二十二、包(Packages)
package/
/
package.subpackage package.subpackage.module2 name
absolute_import
相对导入:在不指明 package 名的情况下导入自己这个 package 的模块,比如一个 package 下有 a.py 和 b.py 两个文件,在 a.py 里?from . import b?即是相对导入 b.py。 绝对导入:指明顶层 package 名。比如?import a,Python 会在?sys.path?里寻找所有名为?a?的顶层模块。 引入absolute_import之后不是支持了绝对引入,而是拒绝相对引入。 简单比复杂好
不要重复造轮子
????1、Python标准库 ????2、Python第三方LIB,PYPI(Python Package Index),地址: ????3、搜索引擎,Google 参考(编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |