【问题标题】:Python: self vs type(self) and the proper use of class variablesPython:自我与类型(自我)以及类变量的正确使用
【发布时间】:2014-09-15 10:05:28
【问题描述】:

在 Python 中使用类变量时,可以通过“self”(感谢引用)或“type(self)”(直接)访问和(如果它是可变的)直接操作它,而不可变变量(例如,整数) 当您只使用“self”时,显然会被新的实例对象遮蔽。

那么,在处理 Python 类变量时,是否最好/Pythonic 总是简单地使用“type(self)”来处理从类方法中引用的类变量?

(我知道类变量有时不受欢迎,但当我使用它们时,我希望以一致的方式访问它们(如果它们是不可变类型则以一种方式访问​​它们,如果它们是可变类型则以另一种方式访问​​。)

编辑:是的,如果您将值修改为不可变,您将获得一个新对象。修改可变对象的值的副作用是导致这个问题的原因 - “self”将为您提供一个参考,您可以使用它来更改类变量而不隐藏它,但是如果您为其分配一个新对象,它会隐藏它。使用 classname.classvar 或 type(self).classvar 或 self.__class__ 可确保您始终使用类变量,而不仅仅是隐藏它(尽管子类使这变得复杂,如下所述)。

【问题讨论】:

  • 你能展示一些示例代码吗?类方法是什么意思?你是指用@classmethod 修饰的方法,还是只是一个类的方法?
  • 我的意思是类的常规方法。下面有一些示例代码 - 尽管需要进行一些澄清,但似乎问题本身已被理解。

标签: python class-variables


【解决方案1】:

您可能希望使用实际类型名称来访问它们,而不是 selftype(self)

您看到的效果与可变性无关。如果你这样做

class Foo(object):
    x = []
    def __init__(self):
        self.x = [1]

__init__ 中的赋值将创建一个新列表并将其分配给实例属性 x,忽略类属性,即使 Foo.x 是一个可变对象。每当您想分配给一个属性时,您都需要使用它实际定义的对象。

注意在继承的情况下通过type(self)修改属性会失败:

class Foo(object):
    x = 1
    def modify_x(self, x):
        type(self).x = x

class Bar(Foo): pass

Foo().modify_x(2) # modifies Foo.x
Bar().modify_x(3) # modifies Bar.x, the wrong attribute

【讨论】:

  • 您关于显式使用类名的观点很有趣......尽管我想知道是否有比在整个过程中显式地使用类名更简洁的方法。至于示例,是的,如果您分配一个新列表“[1]”,您将隐藏类变量。但是,如果您更改现有列表(例如,追加),那么您不会创建新对象(而更改不可变对象将始终创建新对象 - 仅通过使用类名或“self”来调节阴影)。
  • @MartyMacGyver:您不能更改不可变对象。您只能重新分配它,而 assignment 才是问题的真正根源。
  • 我可以将“更改”或“修改”放在引号中,但是是的,我明白了这个事实。可以单独使用 self 修改可变类变量(因为引用),但我想知道(为了清楚起见)在使用类变量时是否应该始终使用 type(self).classvar (或 classname.classvar)构造,并避免单独使用“self”的任何副作用。
  • @MartyMacGyver:在某些情况下,self 会更可取,例如,如果期望子类覆盖该属性。但是,当您想引用特定类的类属性时,我建议您明确地引用该类。
  • 单独使用“self”是否可以用子类中的新 class 变量覆盖 class 变量?我可以看到它创建了一个实例变量......我能看到的所有工作都是对 subclass.classvar 的赋值。
【解决方案2】:
python -c 'import this'
The Zen of Python, by Tim Peters

Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!

我会推荐这些以供参考

Beautiful is better than ugly.
Simple is better than complex.
Readability counts.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
If the implementation is hard to explain, it's a bad idea.

我的建议是使用直接属性引用,因为这可能是语言设计者的意图。

【讨论】:

  • 那是一个很长的路要走...所以,直接在 classname.classvar 或 type(self).classvar 或其他一些结构?
  • 另外type(self).classvar可能对不同版本的python有不同的影响。 type() 在 python 的整个历史中都经历了变化。
  • 考虑到 2.6、2.7 和 3.x,您知道这些不同的效果吗?再一次,您推荐哪种结构 - classname.classvar 或 type(self).classvar 或其他?
  • 我推荐classname.classvar。具体来说,元类和类型之间的交互在 2.6、2.7 和 3.x 之间发生了变化。即使您不打算自己使用元类,元类也很常见。 python-3-patterns-idioms-test.readthedocs.org/en/latest/…
  • @AndrewJohnson - 感谢您提供详细信息和建议!我还将仔细研究元类。
【解决方案3】:

在与其他人离线交谈后(并且根据@wwii 对此处其中一个答案的评论),事实证明最好的方法明确嵌入类名是使用 self.__class__.attribute.

(虽然有些人使用 type(self).attribute 它会导致其他问题。)

【讨论】:

    【解决方案4】:

    https://bitbucket.org/larry/cpython350/pull-requests/15/issue-24912-prevent-class-assignment/diff

    在常见情况下,它们的工作方式相同(可能您应该使用 YouClass.NameAttribute 以避免以后继承出现问题)。 简而言之,差异一直到 Python 3.5。问题是问题 #24912(不允许在所有情况下进行评估)。示例:像 int 这样的不可变类型被静态分配,并且 HEAPTYPE 意外规则在停止允许 class assingment 的地方停止。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-04-26
      • 1970-01-01
      • 2013-01-18
      • 1970-01-01
      • 2016-05-04
      • 2011-01-29
      相关资源
      最近更新 更多