我一辈子都在写代码,但从来没有掌握编码的精髓。大部分情况下使用Visual Basic,因为我用VB最舒服。同时还略微了解一点其他语言(R、C、JavaScript、Applescript、Hypertext和1979年学习的BASIC)。几年前,我决定只用Python,以此来提高我的编码能力。在此过程中重复发明了许多轮子,但我并不介意,因为我享受解决问题的乐趣。同时有时能发现更有效、Python式的解决方案。时间长了以后,会有顿悟的时刻,意识到根本没必要用困难且冗长的方式处理问题。下面列出10条Python用法,如果我早点发现,也许能节省很多时间。
这里没有列表推导和lambda函数。虽然这两个用法都是Python式的,效率高也非常酷,但由于经常在StackOverflow或其他地方碰到,所以学Python的应该都知道这两个东西。同时也没有三元运算符、装饰器和生成器,因为我很少用到。
本文还有一个IPython notebook nbviewer版本。
1. 在Python 2中使用Python 3式的输出
Python 2与Python 3不兼容,这让我不知道该选择哪个版本的Python。最终我选择了Python 2,因为当时许多我需要用的库都与Python 3不兼容。
但实际上,日常使用中最大的版本差异是输出(print)和除法行为。现在我在Python 2的代码中都用import from future来导入Python 3的输出和除法。现在我用到的几乎所有库都支持Python 3,因此会很快迁移到Python 3中。
?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | mynumber = 5 print "Python 2:" print "The number is %d" % (mynumber) print mynumber / 2 , print mynumber / / 2 from __future__ import print_function from __future__ import division print ( 'nPython 3:' ) print ( "The number is {}" . format (mynumber)) print (mynumber / 2 , end = ' ' ) print (mynumber / / 2 ) Python 2 : The number is 5 2 2 Python 3 : The number is 5 2.5 2 |
对了,对于C系的那些更喜欢括号而不是缩进的开发者,这里还有一个彩蛋:
?
1 2 3 4 | from __future__ import braces File "", line 1 from __future__ import braces SyntaxError: not a chance |
2. enumerate(list)
很明显,迭代列表时,应该同时迭代其中的元素及其索引,但在很长一段时间内,我都尴尬的使用计数变量或切片。
?
1 2 3 4 5 6 7 8 9 | mylist = [ "It's" , 'only ', ' a ', ' model'] for index, item in enumerate (mylist): print (index, item) 0 It's 1 only 2 a 3 model |
3. 链式比较操作符
由于我以前使用的是静态语言(在这些语言中该用法有二义性),从来没有将两个比较操作符放在一个表达式中。在许多语言中,4 > 3 > 2会返回False,因为4 > 3的结果是布尔值,而True > 2将得出False。
?
1 2 3 4 5 6 7 8 | mynumber = 3 if 4 > mynumber > 2 : print ( "Chained comparison operators work! n" * 3 ) Chained comparison operators work! Chained comparison operators work! Chained comparison operators work! |
4. collections.Counter
Python的集合库看上去是最好的。在计算需要集合中元素的个数时,StackOverflow找到的答案是创建有序字典,但我坚持使用一个代码片段来创建字典,计算结果中元素出现的频率。直到有一天,我发现可以用collections.deque。
?
1 2 3 4 5 6 7 8 9 10 11 12 13 | from collections import Counter from random import randrange import pprint mycounter = Counter() for i in range ( 100 ): random_number = randrange( 10 ) mycounter[random_number] + = 1 for i in range ( 10 ): print (i, mycounter[i]) |
?
1 2 3 4 5 6 7 8 9 10 | 0 10 1 10 2 13 3 6 4 6 5 11 6 10 7 14 8 12 9 8 |
5. 字典推导
Python开发者的一个重要标志就是理解列表推导,但最终我发现字典推导也很有用,特别是在交换字典的键和值的时候。
?
1 2 3 4 5 | my_phrase = [ "No" , "one" , "expects" , "the" , "Spanish" , "Inquisition" ] my_dict = {key: value for value, key in enumerate (my_phrase)} print (my_dict) reversed_dict = {value: key for key, value in my_dict.items()} print (reversed_dict) |
?
1 2 | { 'Inquisition' : 5 , 'No' : 0 , 'expects' : 2 , 'one' : 1 , 'Spanish' : 4 , 'the' : 3 } { 0 : 'No' , 1 : 'one' , 2 : 'expects' , 3 : 'the' , 4 : 'Spanish' , 5 : 'Inquisition' } |
6. 用subprocess执行shell命令
以前,我使用os库调用外部命令处理文件,而现在我可以在Python中以编码的方式执行诸如ffmpeg这样的复杂命令进行视频编辑。
(是的,我和我的客户都使用Windows,如果你们因此鄙视我,我会大方地接受!)
注意,用os库完成这个特定命令比用subprocess更好。我只想有一个大家都熟悉的命令。同时,一般来说,在subprocess中使用shell=True参数是非常糟糕的主意,在这里使用这个参数仅仅是为了能在一个IPython notebook单元中放置命令的输出。不要自己使用这个参数!
?
1 2 3 | import subprocess output = subprocess.check_output( 'dir' , shell = True ) print (output) |
?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | Volume in drive C is OS Volume Serial Number is [REDACTED] Directory of C:UsersDavidDocuments[REDACTED] 2014 - 11 - 26 06 : 04 AM < DIR > . 2014 - 11 - 26 06 : 04 AM < DIR > .. 2014 - 11 - 23 11 : 47 AM < DIR > .git 2014 - 11 - 26 06 : 06 AM < DIR > .ipynb_checkpoints 2014 - 11 - 23 08 : 59 AM < DIR > CCCma 2014 - 09 - 03 06 : 58 AM 19 , 450 colorbrewdict.py 2014 - 09 - 03 06 : 58 AM 92 , 175 imagecompare.ipynb 2014 - 11 - 23 08 : 41 AM < DIR > Japan_Earthquakes 2014 - 09 - 03 06 : 58 AM 1 , 100 LICENSE 2014 - 09 - 03 06 : 58 AM 5 , 263 monty_monte.ipynb 2014 - 09 - 03 06 : 58 AM 31 , 082 pocket_tumblr_reddit_api.ipynb 2014 - 11 - 26 06 : 04 AM 3 , 211 README.md 2014 - 11 - 26 06 : 14 AM 19 , 898 top_10_python_idioms.ipynb 2014 - 09 - 03 06 : 58 AM 5 , 813 tree_convert_mega_to_gexf.ipynb 2014 - 09 - 03 06 : 58 AM 5 , 453 tree_convert_mega_to_json.ipynb 2014 - 09 - 03 06 : 58 AM 1 , 211 tree_convert_newick_to_json.py 2014 - 09 - 03 06 : 58 AM 55 , 970 weather_ML.ipynb 11 File (s) 240 , 626 bytes 6 Dir (s) 180 , 880 , 490 , 496 bytes free |
7. 字典的.get()和.iteritems()方法
字典的get()方法可以设置默认值,当用get()查找的键不存在时,返回方法中的默认值参数是很有用的。与列表中的enumerate()相同,可以用键值元组迭代字典中的元素。
?
1 2 3 4 5 6 7 8 9 10 11 12 | my_dict = { 'name' : 'Lancelot' , 'quest' : 'Holy Grail' , 'favourite_color' : 'blue' } print (my_dict.get( 'airspeed velocity of an unladen swallow' , 'African or European?n' )) for key, value in my_dict.iteritems(): print (key, value, sep = ": " ) African or European? quest: Holy Grail name: Lancelot favourite_color: blue |
8. 用于交换元素的元组解包
在VB中,每当需要交换两个变量时,都要用要一个愚蠢的临时变量:c = a; a = b; b = c
?
1 2 3 4 5 6 7 8 9 10 11 | a = 'Spam' b = 'Eggs' print (a, b) a, b = b, a print (a, b) Spam Eggs Eggs Spam |
9. 内省工具Introspection tools
我知道dir()方法,我本以为help()方法和IPython中的?魔法命令是一样的,但help()的功能更强大。
?
1 2 3 | my_dict = { 'That' : 'an ex-parrot!' } help (my_dict) |
?
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 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 | Help on dict object : class dict ( object ) | dict () - > new empty dictionary | dict (mapping) - > new dictionary initialized from a mapping object 's | (key, value) pairs | dict (iterable) - > new dictionary initialized as if via: | d = {} | for k, v in iterable: | d[k] = v | dict ( * * kwargs) - > new dictionary initialized with the name = value pairs | in the keyword argument list . For example: dict (one = 1 , two = 2 ) | | Methods defined here: | | __cmp__(...) | x.__cmp__(y) < = = > cmp (x,y) | | __contains__(...) | D.__contains__(k) - > True if D has a key k, else False | | __delitem__(...) | x.__delitem__(y) < = = > del x[y] | | __eq__(...) | x.__eq__(y) < = = > x = = y | [TRUNCATED FOR SPACE] | | update(...) | D.update([E, ] * * F) - > None . Update D from dict / iterable E and F. | If E present and has a .keys() method, does: for k in E: D[k] = E[k] | If E present and lacks .keys() method, does: for (k, v) in E: D[k] = v | In either case, this is followed by: for k in F: D[k] = F[k] | | values(...) | D.values() - > list of D's values | | viewitems(...) | D.viewitems() - > a set - like object providing a view on D's items | | viewkeys(...) | D.viewkeys() - > a set - like object providing a view on D's keys | | viewvalues(...) | D.viewvalues() - > an object providing a view on D's values | | - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | Data and other attributes defined here: | | __hash__ = None | | __new__ = | T.__new__(S, ...) - > a new object with type S, a subtype of T |
10. PEP-8兼容的字符串连接
PEP8是Python编码样式指南。撇开其他的不看,PEP8要求每行不能超过80个字符,超过的部分要换行并缩进。
可以通过反斜杠、带逗号“,”的圆括号“()”、或者额外的加号“+”来完成换行。但对于多行字符串,这些解决方案都不够优雅。Python有个多行字符串记号,即三个引号,但这样无法换行后保持缩进。
还有一个方法,那就是不带逗号的圆括号。我不知道为什么这种方式能工作,但能用就行。
?
1 2 3 4 | my_long_text = ( "We are no longer the knights who say Ni! " "We are now the knights who say ekki-ekki-" "ekki-p'tang-zoom-boing-z'nourrwringmm!" ) print (my_long_text) |