大家都知道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

性能上还过得去....

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

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

修改函数代码版本如下:

import inspect
import dis
import ast
import re
import traceback
import timeit def compute_default_value_for_each_call(func):
source = inspect.getsource(func)
m = re.search(r'^\s+', source)
if m:
m.group(0)
n = len(m.group(0))
lines = [line[n:] for line in str.splitlines(source)]
source = '\n'.join(lines)
root = ast.parse(source)
root.body[0].decorator_list.pop()
arg_names = [arg.id for arg in root.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 in reversed(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 in default_nodes]
root.body[0].body = body
l = {}
exec compile(root, '<string>', mode='exec') in globals(), l
func = l[func.__name__]
return func def root2():
def main(): @compute_default_value_for_each_call
def root(a=([])):
a.append(3)
return a
print root()
print root()
main() print 'used in local function'
root2() def foo():
return 42 bar_count = 0 def bar():
global bar_count
if bar_count:
raise RuntimeError
bar_count += 1 @compute_default_value_for_each_call
def f1(a=foo()):
return a def f2(a=None):
a = a or foo()
return a @compute_default_value_for_each_call
def f3(a=bar()):
return a def f4(a=None):
a = a or bar()
return a print '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:
print traceback.format_exc()
try:
f4()
except:
print traceback.format_exc()

输出:

used in local function
[3]
[3]
f1:
2 0 LOAD_FAST 0 (a)
3 JUMP_IF_TRUE_OR_POP 12
6 LOAD_GLOBAL 0 (foo)
9 CALL_FUNCTION 0
>> 12 STORE_FAST 0 (a) 3 15 LOAD_FAST 0 (a)
18 RETURN_VALUE
f2:
83 0 LOAD_FAST 0 (a)
3 JUMP_IF_TRUE_OR_POP 12
6 LOAD_GLOBAL 0 (foo)
9 CALL_FUNCTION 0
>> 12 STORE_FAST 0 (a) 84 15 LOAD_FAST 0 (a)
18 RETURN_VALUE
f2 running time:
0.0288169384003
f1 running time:
0.0251071453094
f2 running time:
0.025267124176
f3:
2 0 LOAD_FAST 0 (a)
3 JUMP_IF_TRUE_OR_POP 12
6 LOAD_GLOBAL 0 (bar)
9 CALL_FUNCTION 0
>> 12 STORE_FAST 0 (a) 3 15 LOAD_FAST 0 (a)
18 RETURN_VALUE
f4:
93 0 LOAD_FAST 0 (a)
3 JUMP_IF_TRUE_OR_POP 12
6 LOAD_GLOBAL 0 (bar)
9 CALL_FUNCTION 0
>> 12 STORE_FAST 0 (a) 94 15 LOAD_FAST 0 (a)
18 RETURN_VALUE
Traceback (most recent call last):
File "./b/b.py", line 114, in <module>
f3()
File "<string>", line 2, in f3
File "./b/b.py", line 73, in bar
raise RuntimeError
RuntimeError Traceback (most recent call last):
File "./b/b.py", line 118, in <module>
f4()
File "./b/b.py", line 93, in f4
a = a or bar()
File "./b/b.py", line 73, in bar
raise RuntimeError
RuntimeError

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

python 在调用时计算默认值的更多相关文章

  1. input 默认值为灰色,输入时清楚默认值

    input 默认值为灰色,输入时清楚默认值 <input value="please input your name" onFocus="if(value==def ...

  2. python定义函数时的默认返回值

    python定义函数时,一般都会有指定返回值,如果没有显式指定返回值,那么python就会默认返回值为None, 即隐式返回语句: return None 执行如下代码 def now(): prin ...

  3. python学习Day12 函数的默认值、三元表达式、函数对象(函数名)的应用场景、名称空间与作用域

    复习 1.字符串的比较: -- 按照从左往右比较每一个字符,通过字符对应的ascii进行比较 2. 函数的参数 : 1)实参与形参:       -- 形参:在函数定义时()中出现的参数       ...

  4. .NET DateTime类型变量作为参数时设置默认值

    一个小的 Tips. .NET 中函数参数的默认值需要是编译时常量.如果参数是引用类型,可以设置Null,如果是值类型,可以设置相应的编译时常量,如整型可以用整数,但对于DateTime(结构体,值类 ...

  5. 其他函数:值为NULL时的默认值NVL,DECODE

    NVL(列,默认数字值),此函数返回值为数值型,非NULL时返回原始值,NULL时返回默认数字值. DECODE:

  6. JavaScript 中对象解构时指定默认值

    待解构字段为原始值 正常情况下, const obj = { a: 1, b: 2, }; const { a, b } = obj; console.log(a, b); // 1 2 当被解构字段 ...

  7. ODOO的命令行调用以及config默认值

    通过odoo-bin 可以启动odoo server ,启动的过程中需要提供一些args,包括数据库设置,ip设置等 如果不想每次输入这些参数,可以直接修改odoo/tools/config.py中的 ...

  8. 001. 为input type=text 时设置默认值

    1. 前端HTML代码 <%@ Page Language="C#" AutoEventWireup="true" CodeFile="Defa ...

  9. select2 插件编辑时设置默认值

    function htDate(selectCustomerId, val) { var customerId = selectCustomerId; var values = val; ajaxJs ...

随机推荐

  1. Fibonacci数列小程序

    Fibonacci数列小程序 问题分析:Fibonacci数列特征是前两项数均为1,从第三项起,前两项的和为第三项的数的数值用公式归纳起来为:f1=f2=1.f1=f1+f2.f2=f1+f2. 程序 ...

  2. POJ-3678 Katu Puzzle 2sat

    题目链接:http://poj.org/problem?id=3678 分别对and,or,xor推出相对应的逻辑关系: 逻辑关系 1 0  A and B     A'->A,B'->B ...

  3. X265编码效率仍然低

    本次测试软件环境:Intel Celeron双核 2.60 Ghz CPU; 4GB 内存:安装 Ubuntu 13.04 hzsx@hzsx-server:~$ lsb_release -a No ...

  4. LLVM在静态分析上的增强 @ WWDC 2013

    在代码还没有真正跑起来的时候,可以利用Clang对代码进行静态分析. 1. 可以应用快捷键Shift+Command+B对项目代码进行分析: 2. 也可以针对某个文件进行分析(现有版本貌似不能针对特定 ...

  5. Java-Web监听器

    在WEB端实现监听实质: 实现一系列的监听接口(实现相应的接口,覆写各接口中相应的方法,在相应的事件触发的时候会执行自己的监听器中的覆写的方法,在各个方法中完成自己想要的操作,从而实现了监听) 监听- ...

  6. Thinkpad SL400安装黑苹果10.8.4全纪录

    提要 还在为学习苹果开发的装备发愁么 ,也许这篇文章会给你带来一些启发. 关于黑苹果:从苹果采用intel的处理器之后,mac os被黑客破解之后可以安装在PC上,从而出现了一大批未购买苹果机而使用苹 ...

  7. 算法 - 求和为n的连续正整数序列(C++)

    //************************************************************************************************** ...

  8. android访问asset目录下的资源

    android提供了AssetManager来访问asset目录下的资源, 在activity中通过getAssets()获取AssetManager 常用的api如下: 1.列举路径下的资源Stri ...

  9. 【剑指offer】包括min函数的栈

    转载请注明出处:http://blog.csdn.net/ns_code/article/details/26064213 剑指offer上的第21题,之前在Cracking the Coding i ...

  10. cocos2d-x 3.0 Armature jsb 初体验

    有段时间没有写游戏代码了,这回来主要任务是要用jsb构建一个aprg动作游戏,看到3.0官方已经绑定好了armature的js函数,先来体验一把 3.0新建项目比2.2方便了很多,在终端运行tools ...