python 在调用时计算默认值


大家都知道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
性能上还过得去....

评论关闭