【问题标题】:Potential uses of an empty tuple in PythonPython中空元组的潜在用途
【发布时间】:2014-07-11 19:25:51
【问题描述】:

我在玩 Python 中类级变量的风险,我认为列表作为类级变量的风险可以通过元组来解决,例如空元组。

拍摄:

class BaseClass(object):
    default_sth = []

    def foo(self):
       for sth in self.default_sth:
           pass

这是有风险的,因为:

class SubClass(BaseClass):
    pass

a = SubClass()
a.default_sth.append(3)

print(BaseClass.default_sth) # [3]

使用 None 代替空元组 (tuple()?) 可能会很危险,因为它在迭代它时会失败。

由于不可变性,我认为这是一个不错的选择,但我认为我在任何地方都没有看到任何人谈论这个。你觉得这个推理有什么缺陷吗?是pythonic吗?

【问题讨论】:

  • 嗯。您是否有可能正在寻找通过name mangling __leading_double_underscore names 在 Python 中支持的类私有成员?这些基本上是实际类所独有的类级别属性,并且不能(容易地,或者意外地)被子类更改。有关详细说明,请参阅 Raymond Hettinger 的演讲 Class development toolkit(名称修改从 33:24 左右开始)。
  • 另请参阅this answer,了解有关名称修改工作原理的快速示例。
  • 嗯,这是一个有趣的概念。这样做的问题是我确实希望变量在子类中是可覆盖的,但不改变原始类中的值。

标签: python immutability class-variables immutablearray


【解决方案1】:

您将类变量与实例变量混淆了(这很容易做到,因为在没有初始化的实例变量的情况下,查找会找到类变量)。在这种情况下,您已经明确表示default_sth 是一个类变量,而您的意思是为实例变量设置一个默认值。改用这个:

class BaseClass(object):
    def __init__(self, sth=None):
        if sth is None:
            sth = []

    def foo(self):
       for sth in self.sth:
           pass

您可能对使用“硬编码”默认值有点犹豫,但您只是通过尝试为默认值设置一个单独的名称来增加冗长性。只需评论并记录您的 __init__ 方法以指明 sth 是什么,如果不是,它的默认值应该是什么 传递其他值。除非您有充分的理由,否则不要尝试使默认值本身可配置。

就不变性而言,只有 元组 是不可变的,而不是引用它的名称。没有什么可以阻止某人使用您的原始代码并说

BaseClass.default_sth = []

尽管将 default_sth 设为一个元组,但您又回到了起点。

【讨论】:

  • 这是一个有趣的方法。您知道在类定义中针对初始化执行此操作是否有任何性能差异?
  • self.foo 将始终在对象的字典中查找foo,如果查找失败,则检查类的字典。因此,使用类变量作为真正实例变量的默认值会在每次使用时强加字典查找失败的开销。
  • 实际上,虽然“显式比隐式好”,所以如果某些东西是实例变量,请初始化它,而不是依赖于类变量的回退。
【解决方案2】:

我认为使用元组而不是列表的问题在于,人们通常选择使用列表是因为他们实际上想要列表语义;您不能附加到元组,对其进行就地排序,从中删除项目等。如果您希望您的类级列表属性不与子类共享,则子类可以制作自己的副本:

from copy import deepcopy

class SubClass(BaseClass):
    default_sth = deepcopy(BaseClass.default_sth)  # now SubClass has its own copy.

然后您的示例将输出[],这是您希望的行为。如果您在BaseClass 中使用元组,您的示例将抛出一个AttributeError(因为它试图在一个元组上调用append),并且代码需要更改为此以执行与append 等效的操作:

a = SubClass()
a.default_sth = tuple(list(a.default_sth) + [3])

写起来有点麻烦,效率也不高,因为你要进行大量复制以从元组 -> 列表 -> 元组转换。

【讨论】:

  • 我没想到有人可能想要列表语义。我会考虑到这一点! +1 deepcopy 事情。
【解决方案3】:

可变的默认变量在其他如此漂亮的 Python 中是众所周知的。

例如

http://eli.thegreenplace.net/2009/01/16/python-insight-beware-of-mutable-default-values-for-arguments/

"Least Astonishment" and the Mutable Default Argument

http://pythonconquerstheuniverse.wordpress.com/2012/02/15/mutable-default-arguments/

解决方案包括

  • 初始化 __init__ 中的所有内容 - 不要使用类级别的变量,除非您真的是指全局变量

  • 传入None,第一次访问时转换为list

【讨论】:

  • 这不是默认参数值的问题;它只是一个简单的类变量。
  • 您的意思是说“众所周知的wart”吗? :)
猜你喜欢
  • 2011-06-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多