• Fluent Python: Mutable Types as Parameter Defaults: Bad Idea


      在Fluent Python一书第八章有一个示例,未看书以先很难理解这个示例运行的结果,我们先看结果,然后再分析问题原因:

    定义了如下Bus类:

    1 class Bus:
    2     def __init__(self, passengers=[]):
    3         self.passengers = passengers
    4 
    5     def pick(self, name):
    6         self.passengers.append(name)
    7 
    8     def drop(self, name):
    9         self.passengers.remove(name)

    创建两个Bus 类实例,bus1, bus2

    >>> import Example8_12
    >>> bus1 = Example8_12.Bus()
    >>> bus2 = Example8_12.Bus()

    假如bus1接到一个一名乘客Alice:

    >>> bus1.pick('Alice')

    此时我们看看bus2里的乘客:

    >>> bus2.passengers
    ['Alice']

    bus2本应该是空的,但是此时却有bus1 pick的乘客'Alice', 这是什么原因呢?

    出现这个问题的根源是:默认值在定义函数时计算(通常是在import 模块时),并且默认值变成了函数对象的属性:

    我们可以省察下类Bus __init__方法的属性

    >>> dir(Example8_12.Bus.__init__)
    ['__annotations__', '__call__', '__class__', '__closure__', '__code__', '__defaults__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__get__', '__getattribute__', '__globals__', '__gt__', '__hash__', '__init__', '__kwdefaults__', '__le__', '__lt__', '__module__', '__name__', '__ne__', '__new__', '__qualname__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__']

    留意其中有个__defaults__属性,我们看看其中的内容:

    >>> Example8_12.Bus.__init__.__defaults__
    (['Alice'],)

    我们可以看到'Alice'正在其中,这也是为什么bus2里会有乘客'Alice'的原因.

    我们可以验证bus2.passengers是一个别名,它绑定到Bus1.__init__.__defaults__属性的第一个元素上了:

    >>> Example8_12.Bus.__init__.__defaults__[0] is bus2.passengers
    True

    这个示例说明了为什么通常使用None作为接收可变值的参数的默认值。

    这个类正确的书写应该如下:

     1 class Bus:
     2     def __init__(self, passengers=None):
     3         if passengers is None:
     4             self.passengers = []
     5         else:
     6             self.passengers = list(passengers)
     7 
     8     def pick(self, name):
     9         self.passengers.append(name)
    10 
    11     def drop(self, name):
    12         self.passengers.remove(name)

    这里有一个原则,如果定义的函数接收可变参数,应该谨慎考虑调用方是否期望修改传入的参数。

  • 相关阅读:
    pythonsys.exit()
    Python字符串格式化
    Json概述以及python对json的相关操作
    python学习笔记——异常
    Python:sys.argv[]用法
    python学习笔记——字符串,列表,字典,集合,数值,sorted
    python class 的属性
    Python模块——unittest 单元测试
    从sql2000 复制数据到sql2005
    abstract、virtual及override
  • 原文地址:https://www.cnblogs.com/z-joshua/p/7590952.html
Copyright © 2020-2023  润新知