设为首页 收藏本站
查看: 755|回复: 0

[经验分享] Python中的默认参数值

[复制链接]

尚未签到

发表于 2017-4-26 12:16:54 | 显示全部楼层 |阅读模式
  不要使用可变对象作为函数的默认参数例如 list,dict,因为def是一个可执行语句,只有def执行的时候才会计算默认默认参数的值,所以使用默认参数会造成函数执行的时候一直在使用同一个对象,引起bug。
  基本原理
  在 Python 源码中,我们使用def来定义函数或者方法。在其他语言中,类似的东西往往只是一一个语法声明关键字,但def却是一个可执行的指令。Python代码执行的时候先会使用 compile 将其编译成 PyCodeObject.
  PyCodeObject 本质上依然是一种静态源代码,只不过以字节码方式存储,因为它面向虚拟机。因此 Code 关注的是如何执行这些字节码,比如栈空间大小,各种常量变量符号列表,以及字节码与源码行号的对应关系等等。
  PyFunctionObject 是运行期产生的。它提供一个动态环境,让 PyCodeObject 与运行环境关联起来。同时为函数调用提供一系列的上下文属性,诸如所在模块、全局名字空间、参数默认值等等。这是def语句执行的时候干的活。
  PyFunctionObject 让函数面向逻辑,而不仅仅是虚拟机。PyFunctionObject 和 PyCodeObject 组合起来才是一个完整的函数。
  下文翻译了一篇文章,有一些很好的例子。但是由于水平有限,有些不会翻译或者有些翻译有误,敬请谅解。如果有任何问题请发邮件到 acmerfight圈gmail.com,感激不尽
  主要参考资料 书籍:《深入Python编程》 大牛:shell 和 Topsky
  原文链接
  Python对于函数中默认参数的处理往往会给新手造成困扰(但是通常只有一次)。
  当你使用“可变”的对象作为函数中作为默认参数时会往往引起问题。因为在这种情况下参数可以在不创建新对象的情况下进行修改,例如 list dict。
  >>> def function(data=[]):
  ...     data.append(1)
  ...     return data
  ...
  >>> function()
  [1]
  >>> function()
  [1, 1]
  >>> function()
  [1, 1, 1]
  像你所看到的那样,list变得越来越长。如果你仔细地查看这个list。你会发现list一直是同一个对象。
  >>> id(function())
  12516768
  >>> id(function())
  12516768
  >>> id(function())
  12516768
  原因很简单: 在每次函数调用的时候,函数一直再使用同一个list对象。这么使用引起的变化,非常“sticky”。
  为什么会发生这种情况?
  当且仅当默认参数所在的“def”语句执行的时候,默认参数才会进行计算。请看文档描述
  http://docs.python.org/ref/function.html
  的相关部分。
  "def"是Python中的可执行语句,默认参数在"def"的语句环境里被计算。如果你执行了"def"语句多次,每次它都将会创建一个新的函数对象。接下来我们将看到例子。
  用什么来代替?
  像其他人所提到的那样,用一个占位符来替代可以修改的默认值。None
  def myfunc(value=None):
  if value is None:
  value = []
  # modify value here
  如果你想要处理任意类型的对象,可以使用sentinel
  sentinel = object()
  def myfunc(value=sentinel):
  if value is sentinel:
  value = expression
  # use/modify value here
  在比较老的代码中,written before “object” was introduced,你有时会看到
  sentinel = ['placeholder']
  译者注:太水,真的不知道怎么翻译了。我说下我的理解 有时逻辑上可能需要传递一个None,而你的默认值可能又不是None,而且还刚好是个列表,列表不
  可以写在默认值位置,所以你需要占位符,但是用None,你又不知道是不是调用者传递过来的那个
  正确地使用可变参数
  最后需要注意的是一些高深的Python代码经常会利用这个机制的优势;举个例子,如果在一个循环里创建一些UI上的按钮,你可能会尝试这样去做:
  for i in range(10):
  def callback():
  print "clicked button", i
  UI.Button("button %s" % i, callback)
  但是你却发现callback打印出相同的数字(在这个情况下很可能是9)。原因是Python的嵌套作用域只是绑定变量,而不是绑定数值的,所以callback只看到了变量i绑定的最后一个数值。为了避免这种情况,使用显示绑定。
  for i in range(10):
  def callback(i=i):
  print "clicked button", i
  UI.Button("button %s" % i, callback)
  i=i把callback的参数i(一个局部变量)绑定到了当前外部的i变量的数值上。(译者注:如果不理解这个例子,请看http://stackoverflow.com/questions/233673/lexical-closures-in-python)
  另外的两个用途local caches/memoization
  def calculate(a, b, c, memo={}):
  try:
  value = memo[a, b, c] # return already calculated value
  except KeyError:
  value = heavy_calculation(a, b, c)
  memo[a, b, c] = value # update the memo dictionary
  return value
  (对一些递归算法非常好用)
  对高度优化的代码而言, 会使用局部变量绑全局的变量:
  import math
  def this_one_must_be_fast(x, sin=math.sin, cos=math.cos):
  ...
  这是如何工作的?
  当Python执行一条def语句时, 它会使用已经准备好的东西(包括函数的代码对象和函数的上下文属性),创建了一个新的函数对象。同时,计算了函数的默认参数值。
  不同的组件像函数对象的属性一样可以使用。上文用到的'function'
  >>> function.func_name
  'function'
  >>> function.func_code
  ", line 1>
  >>> function.func_defaults
  ([1, 1, 1],)
  >>> function.func_globals
  {'function': ,
  '__builtins__': ,
  '__name__': '__main__', '__doc__': None}
  这样你可以访问默认参数,你甚至可以修改它。
  >>> function.func_defaults[0][:] = []
  >>> function()
  [1]
  >>> function.func_defaults
  ([1],)
  然而我不推荐你平时这么使用。
  另一个重置默认参数的方法是重新执行相同的def语句,Python将会和代码对象创建一个新的函数对象,并计算默认参数,并且把新创建的函数对象赋值给了和上次相同的变量。但是再次强调,只有你清晰地知道在做什么的情况下你才能这么做。

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-369546-1-1.html 上篇帖子: python 对象内存分析 下篇帖子: Dave Python 练习二
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表