在使用Python多年以后,我偶然发现了一些我们过去不知道的功能和特性。一些可以说是非常有用,但却没有充分利用。考虑到这一点,我编辑了一些你应该了解的Python功能特色。
带任意数量参数的函数
你可能已经知道了Python允许你定义可选参数。但还有一个方法,可以定义函数任意数量的参数。
首先,看下面是一个只定义可选参数的例子
01 | def function(arg1 = " ",arg2=" "): |
02 | print "arg1: {0}" . format (arg1) |
03 | print "arg2: {0}" . format (arg2) |
04 | |
05 | function( "Hello" , "World" ) |
06 | # prints args1: Hello |
07 | # prints args2: World |
08 | |
09 | function() |
10 | # prints args1: |
11 | # prints args2: |
现在,让我们看看怎么定义一个可以接受任意参数的函数。我们利用元组来实现。
01 | def foo( * args): # just use "*" to collect all remaining arguments into a tuple |
02 | numargs = len (args) |
03 | print "Number of arguments: {0}" . format (numargs) |
04 | for i, x in enumerate (args): |
05 | print "Argument {0} is: {1}" . format (i,x) |
06 | |
07 | foo() |
08 | # Number of arguments: 0 |
09 | |
10 | foo( "hello" ) |
11 | # Number of arguments: 1 |
12 | # Argument 0 is: hello |
13 | |
14 | foo( "hello" , "World" , "Again" ) |
15 | # Number of arguments: 3 |
16 | # Argument 0 is: hello |
17 | # Argument 1 is: World |
18 | # Argument 2 is: Again |
使用Glob()查找文件
大多Python函数有着长且具有描述性的名字。但是命名为glob()的函数你可能不知道它是干什么的除非你从别处已经熟悉它了。
它像是一个更强大版本的listdir()函数。它可以让你通过使用模式匹配来搜索文件。
1 | import glob |
2 | |
3 | # get all py files |
4 | files = glob.glob( '*.py' ) |
5 | print files |
6 | |
7 | # Output |
8 | # ['arg.py', 'g.py', 'shut.py', 'test.py'] |
你可以像下面这样查找多个文件类型:
01 | import itertools as it, glob |
02 | |
03 | def multiple_file_types( * patterns): |
04 | return it.chain.from_iterable(glob.glob(pattern) for pattern in patterns) |
05 | |
06 | for filename in multiple_file_types( "*.txt" , "*.py" ): # add as many filetype arguements |
07 | print filename |
08 | |
09 | # output |
10 | #=========# |
11 | # test.txt |
12 | # arg.py |
13 | # g.py |
14 | # shut.py |
15 | # test.py |
如果你想得到每个文件的绝对路径,你可以在返回值上调用realpath()函数:
01 | import itertools as it, glob, os |
02 | |
03 | def multiple_file_types( * patterns): |
04 | return it.chain.from_iterable(glob.glob(pattern) for pattern in patterns) |
05 | |
06 | for filename in multiple_file_types( "*.txt" , "*.py" ): # add as many filetype arguements |
07 | realpath = os.path.realpath(filename) |
08 | print realpath |
09 | |
10 | # output |
11 | #=========# |
12 | # C:\xxx\pyfunc\test.txt |
13 | # C:\xxx\pyfunc\arg.py |
14 | # C:\xxx\pyfunc\g.py |
15 | # C:\xxx\pyfunc\shut.py |
16 | # C:\xxx\pyfunc\test.py |
调试
下面的例子使用inspect模块。该模块用于调试目的时是非常有用的,它的功能远比这里描述的要多。
这篇文章不会覆盖这个模块的每个细节,但会展示给你一些用例。
01 | import logging, inspect |
02 | |
03 | logging.basicConfig(level = logging.INFO, |
04 | format = '%(asctime)s %(levelname)-8s %(filename)s:%(lineno)-4d: %(message)s' , |
05 | datefmt = '%m-%d %H:%M' , |
06 | ) |
07 | logging.debug( 'A debug message' ) |
08 | logging.info( 'Some information' ) |
09 | logging.warning( 'A shot across the bow' ) |
10 | |
11 | def test(): |
12 | frame,filename,line_number,function_name,lines,index = \ |
13 | inspect.getouterframes(inspect.currentframe())[ 1 ] |
14 | print (frame,filename,line_number,function_name,lines,index) |
15 | |
16 | test() |
17 | |
18 | # Should print the following (with current date/time of course) |
19 | #10-19 19:57 INFO test.py:9 : Some information |
20 | #10-19 19:57 WARNING test.py:10 : A shot across the bow |
21 | #(, 'C:/xxx/pyfunc/magic.py', 16, '', ['test()\n'], 0) |
生成唯一ID
在有些情况下你需要生成一个唯一的字符串。我看到很多人使用md5()函数来达到此目的,但它确实不是以此为目的。
其实有一个名为uuid()的Python函数是用于这个目的的。
1 | import uuid |
2 | result = uuid.uuid1() |
3 | print result |
4 | |
5 | # output => various attempts |
6 | # 9e177ec0-65b6-11e3-b2d0-e4d53dfcf61b |
7 | # be57b880-65b6-11e3-a04d-e4d53dfcf61b |
8 | # c3b2b90f-65b6-11e3-8c86-e4d53dfcf61b |
你可能会注意到,即使字符串是唯一的,但它们后边的几个字符看起来很相似。这是因为生成的字符串与电脑的MAC地址是相联系的。
为了减少重复的情况,你可以使用这两个函数。
01 | import hmac,hashlib |
02 | key = '1' |
03 | data = 'a' |
04 | print hmac.new(key, data, hashlib.sha256).hexdigest() |
05 | |
06 | m = hashlib.sha1() |
07 | m.update( "The quick brown fox jumps over the lazy dog" ) |
08 | print m.hexdigest() |
09 | |
10 | # c6e693d0b35805080632bc2469e1154a8d1072a86557778c27a01329630f8917 |
11 | # 2fd4e1c67a2d28fced849ee1bb76e7391b93eb12 |
序列化
你曾经需要将一个复杂的变量存储在数据库或文本文件中吧?你不需要想一个奇特的方法将数组或对象格转化为式化字符串,因为Python已经提供了此功能。
01 | import pickle |
02 | |
03 | variable = [ 'hello' , 42 , [ 1 , 'two' ], 'apple' ] |
04 | |
05 | # serialize content |
06 | file = open ( 'serial.txt' , 'w' ) |
07 | serialized_obj = pickle.dumps(variable) |
08 | file .write(serialized_obj) |
09 | file .close() |
10 | |
11 | # unserialize to produce original content |
12 | target = open ( 'serial.txt' , 'r' ) |
13 | myObj = pickle.load(target) |
14 | |
15 | print serialized_obj |
16 | print myObj |
17 | |
18 | #output |
19 | # (lp0 |
20 | # S'hello' |
21 | # p1 |
22 | # aI42 |
23 | # a(lp2 |
24 | # I1 |
25 | # aS'two' |
26 | # p3 |
27 | # aaS'apple' |
28 | # p4 |
29 | # a. |
30 | # ['hello', 42, [1, 'two'], 'apple'] |
这是一个原生的Python序列化方法。然而近几年来JSON变得流行起来,Python添加了对它的支持。现在你可以使用JSON来编解码。
01 | import json |
02 | |
03 | variable = [ 'hello' , 42 , [ 1 , 'two' ], 'apple' ] |
04 | print "Original {0} - {1}" . format (variable, type (variable)) |
05 | |
06 | # encoding |
07 | encode = json.dumps(variable) |
08 | print "Encoded {0} - {1}" . format (encode, type (encode)) |
09 | |
10 | #deccoding |
11 | decoded = json.loads(encode) |
12 | print "Decoded {0} - {1}" . format (decoded, type (decoded)) |
13 | |
14 | # output |
15 | |
16 | # Original ['hello', 42, [1, 'two'], 'apple'] - <type 'list'=""> |
17 | # Encoded ["hello", 42, [1, "two"], "apple"] - <type 'str'=""> |
18 | # Decoded [u'hello', 42, [1, u'two'], u'apple'] - <type 'list'=""> |
这样更紧凑,而且最重要的是这样与JavaScript和许多其他语言兼容。然而对于复杂的对象,其中的一些信息可能丢失。
压缩字符
当谈起压缩时我们通常想到文件,比如ZIP结构。在Python中可以压缩长字符,不涉及任何档案文件。
01 | import zlib |
02 | |
03 | string = """ Lorem ipsum dolor sit amet, consectetur |
04 | adipiscing elit. Nunc ut elit id mi ultricies |
05 | adipiscing. Nulla facilisi. Praesent pulvinar, |
06 | sapien vel feugiat vestibulum, nulla dui pretium orci, |
07 | non ultricies elit lacus quis ante. Lorem ipsum dolor |
08 | sit amet, consectetur adipiscing elit. Aliquam |
09 | pretium ullamcorper urna quis iaculis. Etiam ac massa |
10 | sed turpis tempor luctus. Curabitur sed nibh eu elit |
11 | mollis congue. Praesent ipsum diam, consectetur vitae |
12 | ornare a, aliquam a nunc. In id magna pellentesque |
13 | tellus posuere adipiscing. Sed non mi metus, at lacinia |
14 | augue. Sed magna nisi, ornare in mollis in, mollis |
15 | sed nunc. Etiam at justo in leo congue mollis. |
16 | Nullam in neque eget metus hendrerit scelerisque |
17 | eu non enim. Ut malesuada lacus eu nulla bibendum |
18 | id euismod urna sodales. """ |
19 | |
20 | print "Original Size: {0}" . format ( len (string)) |
21 | |
22 | compressed = zlib.compress(string) |
23 | print "Compressed Size: {0}" . format ( len (compressed)) |
24 | |
25 | decompressed = zlib.decompress(compressed) |
26 | print "Decompressed Size: {0}" . format ( len (decompressed)) |
27 | |
28 | # output |
29 | |
30 | # Original Size: 1022 |
31 | # Compressed Size: 423 |
32 | # Decompressed Size: 1022 |
注册Shutdown函数
有可模块叫atexit,它可以让你在脚本运行完后立马执行一些代码。
假如你想在脚本执行结束时测量一些基准数据,比如运行了多长时间:
01 | import atexit |
02 | import time |
03 | import math |
04 | |
05 | def microtime(get_as_float = False ) : |
06 | if get_as_float: |
07 | return time.time() |
08 | else : |
09 | return '%f %d' % math.modf(time.time()) |
10 | start_time = microtime( False ) |
11 | atexit.register(start_time) |
12 | |
13 | def shutdown(): |
14 | global start_time |
15 | print "Execution took: {0} seconds" . format (start_time) |
16 | |
17 | atexit.register(shutdown) |
18 | |
19 | # Execution took: 0.297000 1387135607 seconds |
20 | # Error in atexit._run_exitfuncs: |
21 | # Traceback (most recent call last): |
22 | # File "C:\Python27\lib\atexit.py", line 24, in _run_exitfuncs |
23 | # func(*targs, **kargs) |
24 | # TypeError: 'str' object is not callable |
25 | # Error in sys.exitfunc: |
26 | # Traceback (most recent call last): |
27 | # File "C:\Python27\lib\atexit.py", line 24, in _run_exitfuncs |
28 | # func(*targs, **kargs) |
29 | # TypeError: 'str' object is not callable |
打眼看来很简单。只需要将代码添加到脚本的最底层,它将在脚本结束前运行。但如果脚本中有一个致命错误或者脚本被用户终止,它可能就不运行了。
当你使用atexit.register()时,你的代码都将执行,不论脚本因为什么原因停止运行。
结论
你是否意识到那些不是广为人知Python特性很有用?请在评论处与我们分享。谢谢你的阅读!