zoukankan      html  css  js  c++  java
  • 不要使用可变类型作为参数的默认值

    class HauntedBus:
    """备受幽灵乘客折磨的校车"""
    def __init__(self, passengers=[]): ➊
      self.passengers = passengers ➋
    def pick(self, name):
      self.passengers.append(name) ➌
    def drop(self, name):
      self.passengers.remove(name)
    ❶ 如果没传入 passengers 参数,使用默认绑定的列表对象,一开始 是空列表。
    ❷ 这个赋值语句把 self.passengers 变成 passengers 的别名,而没 有传入 passengers 参数时,后者又是默认列表的别名。
    ❸ 在 self.passengers 上调用 .remove() 和 .append() 方法时,修 改的其实是默认列表,它是函数对象的一个属性。
     
    HauntedBus 的诡异行为如示例 8-13 所示。
    >>> bus1 = HauntedBus(['Alice', 'Bill'])
    >>> bus1.passengers
    ['Alice', 'Bill']
    >>> bus1.pick('Charlie')
    >>> bus1.drop('Alice')
    >>> bus1.passengers ➊
    ['Bill', 'Charlie']
    >>> bus2 = HauntedBus() ➋
    >>> bus2.pick('Carrie')
    >>> bus2.passengers
    ['Carrie']
    >>> bus3 = HauntedBus() ➌
    >>> bus3.passengers ➍
    ['Carrie']
    >>> bus3.pick('Dave')
    >>> bus2.passengers ➎
    ['Carrie', 'Dave']
    >>> bus2.passengers is bus3.passengers ➏
    True
    >>> bus1.passengers ➐
    ['Bill', 'Charlie']
    ❶ 目前没什么问题,bus1 没有出现异常。
    ❷ 一开始,bus2 是空的,因此把默认的空列表赋值给 self.passengers。
    ❸ bus3 一开始也是空的,因此还是赋值默认的列表。
    ❹ 但是默认列表不为空!
    ❺ 登上 bus3 的 Dave 出现在 bus2 中。
    ❻ 问题是,bus2.passengers 和 bus3.passengers 指代同一个列 表。
    ❼ 但 bus1.passengers 是不同的列表。
    表。 这种问题很难发现。如示例 8-13 所示,实例化 HauntedBus 时,如果 传入乘客,会按预期运作。但是不为 HauntedBus 指定乘客的话,奇怪 的事就发生了,这是因为 self.passengers 变成了 passengers 参数 默认值的别名。出现这个问题的根源是,默认值在定义函数时计算(通 常在加载模块时),因此默认值变成了函数对象的属性。因此,如果默 认值是可变对象,而且修改了它的值,那么后续的函数调用都会受到影 响。
    运行示例 8-13 中的代码之后,可以审查 HauntedBus.__init__ 对 象,看看它的 __defaults__ 属性中的那些幽灵学生:
    >>> dir(HauntedBus.__init__) # doctest: +ELLIPSIS
    ['__annotations__', '__call__', ..., '__defaults__', ...]
    >>> HauntedBus.__init__.__defaults__
    (['Carrie', 'Dave'],)
    最后,我们可以验证 bus2.passengers 是一个别名,它绑定到 HauntedBus.__init__.__defaults__ 属性的第一个元素上:
    >>> HauntedBus.__init__.__defaults__[0] is bus2.passengers
    True
    可变默认值导致的这个问题说明了为什么通常使用 None 作为接收可变 值的参数的默认值。在示例 8-8 中,__init__ 方法检查 passengers 参数的值是不是 None,如果是就把一个新的空列表赋值给 self.passengers。下一节会说明,如果 passengers 不是 None,正 确的实现会把 passengers 的副本赋值给 self.passengers。下面详 解。
    人生就是要不断折腾
  • 相关阅读:
    awk 字符串连接操作(字符串转数字,数字转字符串)
    Jenkins配置自动发送邮件,成功!
    可嵌入到网页的实用查询代码
    Windows中打开和关闭FSO文件读写权限的方法
    网站项目模型及业务流程分析
    2个JS版的MD5加密脚本
    申请@msn.com的邮箱最新网址
    成功激励格言精选
    随机切换广告图片
    译自MSDN非常详细的IMG,IFRAME的属性参考手册
  • 原文地址:https://www.cnblogs.com/xiangxiaolin/p/11719549.html
Copyright © 2011-2022 走看看