1

我在玩 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吗?

4

3 回答 3

2

您将类变量与实例变量混淆(这很容易做到,因为在没有初始化的实例变量的情况下,查找会找到类变量)。在这种情况下,当您的意思是实例变量具有默认值时,您已经明确表示这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一个元组开始。

于 2014-07-11T20:07:43.027 回答
1

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

from copy import deepcopy

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

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

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

写起来有点痛苦而且效率不高,因为你正在做一堆复制以从元组 -> 列表 -> 元组转换。

于 2014-07-11T19:55:29.530 回答
0

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

例如

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

“Least Astonishment”和可变默认参数

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

解决方案包括

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

  • None首次访问时传入并转换为列表

于 2014-07-11T19:40:31.987 回答