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__
方法,这样保证了即便有异常情况文件也会正常的被关闭。