python 变量默认值_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

defcompute_default_value_for_each_call(func):

defaults= func.__defaults__

if notdefaults:returnNone

defaults= tuple(copy.copy(x) for x indefaults)

@functools.wraps(func)def wrapper(*args, **kwargs):if func.__defaults__ !=defaults:

func.__defaults__ = tuple(copy.copy(y) for y indefaults)return func(*args, **kwargs)returnwrapper

@compute_default_value_for_each_calldef foo(b, a=[]):ifb:

a.append(3)returnaimport timeit

这样两次调用foo(1), 结果为:

[3]

[3]

这个decorator有对未修改默认参数值做优化, 在我们不对默认值修改的情况下(比如打印变量a的内容), 性能有很大提升:

@compute_default_value_for_each_calldef foo(b, a=[]):ifb:

a.append(3)returnadef foo2(b, a=None):

a= a or[]ifb:

a.append(3)returnaimporttimeitprint 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

性能上还过得去....

觉得这种方法性能低的同学, 还可以使用ast模块, 直接修改函数源代码, 达到a = a or []这样的性能. 但是实现起来略麻烦, 有机会我再试试.

====================================================

修改函数代码版本如下:

importinspectimportdisimportastimportreimporttracebackimporttimeitdefcompute_default_value_for_each_call(func):

source=inspect.getsource(func)

m= re.search(r'^\s+', source)ifm:

m.group(0)

n=len(m.group(0))

lines= [line[n:] for line instr.splitlines(source)]

source= '\n'.join(lines)

root=ast.parse(source)

root.body[0].decorator_list.pop()

arg_names= [arg.id for arg inroot.body[0].args.args]

default_nodes=root.body[0].args.defaults

arg_names= arg_names[len(arg_names) -len(default_nodes):]

body=root.body[0].body

n=len(arg_names)for i inreversed(xrange(n)):

arg_name=arg_names[i]

default_node=default_nodes[i]

lineno=default_node.lineno

col_offset=default_node.col_offset

body.insert(0, ast.Assign(targets=[ast.Name(id=arg_name, ctx=ast.Store(),

lineno=lineno, col_offset=col_offset)],

value=ast.BoolOp(op=ast.Or(), lineno=lineno, col_offset=col_offset,

values=[ast.Name(id=arg_name, ctx=ast.Load(), lineno=lineno, col_offset=col_offset),

default_node]),

lineno=lineno, col_offset=col_offset))

root.body[0].args.defaults= [ast.Name(id='None', ctx=ast.Load(), lineno=old.lineno, col_offset=old.col_offset)for old indefault_nodes]

root.body[0].body=body

l={}exec compile(root, '', mode='exec') inglobals(), l

func= l[func.__name__]returnfuncdefroot2():defmain():

@compute_default_value_for_each_calldef root(a=([])):

a.append(3)returnaprintroot()printroot()

main()print 'used in local function'root2()deffoo():return 42bar_count=0defbar():globalbar_countifbar_count:raiseRuntimeError

bar_count+= 1@compute_default_value_for_each_calldef f1(a=foo()):returnadef f2(a=None):

a= a orfoo()returna

@compute_default_value_for_each_calldef f3(a=bar()):returnadef f4(a=None):

a= a orbar()returnaprint 'f1:'dis.dis(f1)print 'f2:'dis.dis(f2)print 'f2 running time:'

print timeit.timeit('f2', setup='from __main__ import f2')print 'f1 running time:'

print timeit.timeit('f1', setup='from __main__ import f1')print 'f2 running time:'

print timeit.timeit('f2', setup='from __main__ import f2')print 'f3:'dis.dis(f3)print 'f4:'dis.dis(f4)try:

f3()except:printtraceback.format_exc()try:

f4()except:print traceback.format_exc()

输出:

used inlocal function

[3]

[3]

f1:20 LOAD_FAST 0 (a)3 JUMP_IF_TRUE_OR_POP 12

6LOAD_GLOBAL 0 (foo)9CALL_FUNCTION 0>> 12STORE_FAST 0 (a)3 15LOAD_FAST 0 (a)18RETURN_VALUE

f2:830 LOAD_FAST 0 (a)3 JUMP_IF_TRUE_OR_POP 12

6LOAD_GLOBAL 0 (foo)9CALL_FUNCTION 0>> 12STORE_FAST 0 (a)84 15LOAD_FAST 0 (a)18RETURN_VALUE

f2 running time:0.0288169384003f1 running time:0.0251071453094f2 running time:0.025267124176f3:20 LOAD_FAST 0 (a)3 JUMP_IF_TRUE_OR_POP 12

6LOAD_GLOBAL 0 (bar)9CALL_FUNCTION 0>> 12STORE_FAST 0 (a)3 15LOAD_FAST 0 (a)18RETURN_VALUE

f4:930 LOAD_FAST 0 (a)3 JUMP_IF_TRUE_OR_POP 12

6LOAD_GLOBAL 0 (bar)9CALL_FUNCTION 0>> 12STORE_FAST 0 (a)94 15LOAD_FAST 0 (a)18RETURN_VALUE

Traceback (most recent call last):

File"./b/b.py", line 114, in f3()

File"", line 2, inf3

File"./b/b.py", line 73, inbarraiseRuntimeError

RuntimeError

Traceback (most recent call last):

File"./b/b.py", line 118, in f4()

File"./b/b.py", line 93, inf4

a= a orbar()

File"./b/b.py", line 73, inbarraiseRuntimeError

RuntimeError

可以看到性能完全一样了, 在调试信息也还不错

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值