大家都知道python的默认值是在函数定义时计算出来的, 也就是说默认值只会计算一次, 之后函数调用时, 如果参数没有给出,
同一个值会赋值给变量, 这会导致, 如果我们想要一个list默认值, 新手通常这么写:
def foo(a=[]): a.append(3) print a
其实是错误的,两次调用会这样的结果:
[3]
[3, 3]
其实应该这么写
def baz(a=None):
a = a or [] a.append(3) print a
两次调用输出以下结果:
[3]
[3]
这样好挫啊, 搞的虽然有默认值用法,但是我们却需要写的和js,lua一样, 我们不能像c++一样, 在函数运行时每次执行默认值么.
用decorator可以模拟下
import functools
import copy
def compute_default_value_for_each_call(func): defaults = func.__defaults__ if not defaults: return None defaults = tuple(copy.copy(x) for x in defaults) @functools.wraps(func) def wrapper(*args, **kwargs): if func.__defaults__ != defaults: func.__defaults__ = tuple(copy.copy(y) for y in defaults) return func(*args, **kwargs) return wrapper @compute_default_value_for_each_call def foo(b, a=[]): if b: a.append(3) return a import timeit
这样两次调用foo(1), 结果为:
[3]
[3]
这个decorator有对未修改默认参数值做优化, 在我们不对默认值修改的情况下(比如打印变量a的内容), 性能有很大提升:
@compute_default_value_for_each_call def foo(b, a=[]): if b: a.append(3) return a def foo2(b, a=None): a = a or [] if b: a.append(3) return a import timeit print timeit.timeit(‘foo(1)‘, setup=‘from __main__ import foo‘) print timeit.timeit(‘foo(0)‘, setup=‘from __main__ import foo‘) print timeit.timeit(‘foo2(1)‘, setup=‘from __main__ import foo2‘) print timeit.timeit(‘foo2(0)‘, setup=‘from __main__ import foo2‘)
执行结果(调用1000000次的总时间)
4.32704997063 0.630109071732 0.445858955383 0.26370882988
性能上还过得去....