Python隐藏(使用)技巧
Table of Contents
- 1. Function argument unpacking
- 2. 串联比较运算(Chaining comparison operators)
- 3. 装饰器(Decorators)
- 4. 小心使用可变参数作为默认参数
- 5. 字典的
get()
函数 - 6. Doctest:文档和单元测试同时进行
- 7. 省略号语法
- 8. enumerate
- 9. for…else 语法
- 10.
import this
- 11. 位置交换赋值(in-place value swapping)
- 12. 按照步长获取切片列表
- 13. 命名格式化
- 14. 单行嵌套 for 循环,生成列表
- 15. try.except.else.finally 语法
- 16. 上下文管理器和
with
语法
1. Function argument unpacking
你可以通过 *
和 **
将列表或者字典解包作为函数参数,比如:
def draw_point(x, y): print(x, y) point_foo = (3, 4) point_bar = {'y': 3, 'x': 2} draw_point(*point_foo) draw_point(**point_bar)
2. 串联比较运算(Chaining comparison operators)
>>> x = 5 >>> 1 < x < 10 True >>> 10 < x < 20 False >>> x < 10 < x*10 < 100 True >>> 10 > x <= 9 True >>> 5 == x > 4 True
3. 装饰器(Decorators)
装饰器是在 Python 函数或方法的基础上包装了一层,你可以添加功能,修改参数或者返回值等等。已我们统计函数执行时间的装饰器为例:
def fprofiler(output=True): """函数执行时间监视器 output: 是否输出信息,线上业务中如果全部打开会产生很多日志,所以需要一个开关 """ class SimpleObj: def __init__(self, func_module, func_name): self.func_module = func_module self.func_name = func_name self.s = time.time() def __del__(self): logger.debug('fprofiler|%s.%s|%s', self.func_module, self.func_name, round(time.time()-self.s, 3)) def _fprofiler(func): def _wrapper(*args, **kwargs): if output: _ = SimpleObj(func.__module__, func.__name__) return func(*args, **kwargs) else: return func(*args, **kwargs) return _wrapper return _fprofiler
使用:
# config.py PROFILER = True # views @fprofiler(PROFILER) def do_something(request): # do something
4. 小心使用可变参数作为默认参数
def foo(x=[]): x.append(1) print(x) foo() foo() foo() # output [1] [1, 1] [1, 1, 1]
正确的做法应该是:
def foo(x=None): x = x or [] x.append(1) print(x) foo() foo() foo() # output 1 1 1
比较合理的一种解释:
Actually, this is not a design flaw, and it is not because of internals, or performance. It comes simply from the fact that functions in Python are first-class objects, and not only a piece of code. As soon as you get to think into this way, then it completely makes sense: a function is an object being evaluated on its definition; default parameters are kind of "member data" and therefore their state may change from one call to the other - exactly as in any other object. In any case, Effbot has a very nice explanation of the reasons for this behavior in Default Parameter Values in Python. I found it very clear, and I really suggest reading it for a better knowledge of how function objects work.
翻译过来就是:把函数当成一个对象,参数可以理解为他们的成员数据,只被定义一次。所以不管函数调用多少次参数都是同一个引用。
5. 字典的 get()
函数
当 key
不存在时,你可以设置一个默认值:=s.get(key, 0)=。
6. Doctest:文档和单元测试同时进行
def factorial(n): """Return the factorial of n, an exact integer >= 0. If the result is small enough to fit in an int, return an int. Else return a long. >>> [factorial(n) for n in range(6)] [1, 1, 2, 6, 24, 120] >>> factorial(-1) Traceback (most recent call last): ... ValueError: n must be >= 0 Factorials of floats are OK, but the float must be an exact integer: """ import math if not n >= 0: raise ValueError("n must be >= 0") if math.floor(n) != n: raise ValueError("n must be exact integer") if n+1 == n: # catch a value like 1e300 raise OverflowError("n too large") result = 1 factor = 2 while factor <= n: result *= factor factor += 1 return result def _test(): import doctest doctest.testmod() if __name__ == "__main__": _test() # output ********************************************************************** File "test.py", line 7, in __main__.factorial Failed example: [factorial(n) for n in range(6)] Expected: [1, 1, 2, 6, 24, 120] Got: [None, None, 2, 2, 2, 2] ********************************************************************** 1 items had failures: 1 of 2 in __main__.factorial ***Test Failed*** 1 failures.
能理解,但是我不会这么玩…
7. 省略号语法
def print_sth(item): if item is Ellipsis: print('all data') else: print(item) print_sth(...) print_sth(1) # output all data 1
8. enumerate
a = [5, 4, 3, 2, 1] for index, item in enumerate(a): print(index, item)
很实用的功能。
9. for…else 语法
foo = (1, 2, 3) for i in foo: if i == 0: break else: print('i was never 0')
else
会在 for
循环结束之后再执行。上面的代码相当于:
found = False foo = (1, 2, 3) for i in foo: if i == 0: found = True break if not found: print('i was never 0')
10. import this
你懂的。
11. 位置交换赋值(in-place value swapping)
a, b = b, a
12. 按照步长获取切片列表
>>> a = [1, 2, 3, 4, 5] >>> a[::2] [1, 3, 5] >>> a[::-1] [5, 4, 3, 2, 1] >>>
13. 命名格式化
%
传入字典。
>>> print "The %(foo)s is %(bar)i." % {'foo': 'answer', 'bar':42} The answer is 42.
新的格式化风格:
>>> print("The {foo} is {bar}".format(foo='answer', bar=42)) The answer is 42
那也就可以这么用:
a = { 'foo': 'answer', 'bar': 42 } print('{foo} - {bar}'.format(**a))
14. 单行嵌套 for 循环,生成列表
[(i,j) for i in range(3) for j in range(i) ] ((i,j) for i in range(4) for j in range(i) )
15. try.except.else.finally 语法
- =except=:异常捕捉
- =else=:在没有任何异常的时候执行的
- =finally=:无论是否有异常都执行
16. 上下文管理器和 with
语法
上下文管理器(context
manager)是一个扮演者管理一系列语句运行时上下文的对象,一般用于在异常情况下正确的释放资源,比如自愿加锁解锁、文件打开关闭,数据库事务。而
with
是上下文管理器的广泛案例。
with open('foo.txt', 'w') as f: f.write('hello!')
with
会在 f
文件对象上自动调用 __enter__
和 __exit__
方法。在
with body
中的任何异常触发之后也会调用 __exit__
方法,这样保证了即便有异常情况文件也会正常的被关闭。