python类实例变量和类变量

Posted

技术标签:

【中文标题】python类实例变量和类变量【英文标题】:python class instance variables and class variables 【发布时间】:2012-01-31 20:34:18 【问题描述】:

我在理解类/实例变量如何在 Python 中工作时遇到问题。我不明白为什么当我尝试这段代码时,列表变量似乎是一个类变量

class testClass():
    list = []
    def __init__(self):
        self.list.append('thing')

p = testClass()
print p.list

f = testClass()
print f.list

输出:

['thing']
['thing', 'thing']

当我这样做时,它似乎是一个实例变量

class testClass():
    def __init__(self):
        self.list = []
        self.list.append('thing')

p = testClass()
print p.list

f = testClass()
print f.list

输出:

['thing']
['thing']

【问题讨论】:

如果我理解正确,您是在询问有关 Python 语法的特定设计决策背后的基本原理。对于这样的问题,最好的答案要么是“因为它符合语言的精神”,要么更直接:“问它的创造者”。 好的,所以变量默认是类变量,这就是我的困惑的根源,这应该是我的问题,变量是否默认为类变量 @jonathantopf 不,默认情况下变量不是类变量。在 Python 中,您不声明变量。就此而言,它们实际上并不是变量(python 是一种 variableless 语言),只是名称。而且您不声明或分配名称,您将它们绑定到一个对象。并且运行时中的每个对象都有一个名称到对象的字典。即使两个对象属于同一类,它们也可以有非常不同的字典。 在您的第一个示例中,您在 MyClass 类的所有成员之间创建了一个真正的共享资源。然后,您继续将 MyClass 类的每个成员的共享特征的内容私有化。 How do I avoid having Python class data shared among instances?的可能重复 【参考方案1】:

这是因为 Python 使用 . 解析名称的方式。当您编写 self.list 时,Python 运行时首先尝试通过在实例对象中查找 list 名称来解析它,如果在那里找不到,则在类实例中找到它。

让我们一步一步地研究它

self.list.append(1)
    对象self 中是否有list 名称? 是的:使用它!完成。 否:转至 2。 对象self 的类实例中是否有list 名称? 是的:使用它!完成 否:错误!

但是当你绑定一个名字时,情况就不同了:

self.list = []
    对象self 中是否有list 名称? 是的:覆盖它! 不:绑定它!

所以,这始终是一个实例变量。

您的第一个示例在类实例中创建了一个list,因为这是当时的活动范围(任何地方都没有self)。但是您的第二个示例在self 的范围内显式创建了list

更有趣的是这个例子:

class testClass():
    list = ['foo']
    def __init__(self):
        self.list = []
        self.list.append('thing')

x = testClass()
print x.list
print testClass.list
del x.list
print x.list

这将打印:

['thing']
['foo']
['foo']

在您删除实例名称的那一刻,类名称通过 self 引用可见。

【讨论】:

好的,我关注,在这种情况下,有没有办法在 init 函数之外定义实例变量,我只是尝试在 init 之外执行 self.list[] ,这是有道理的,但有办法吗? @jonathantopf:实例变量只有在有实例时才能创建。它们必须在实例上调用的方法中创建。您的另一个选择是重载__new__ 并在那里创建它们,但这真的很难看,因为它不是__new__ 的真正用途,而且它并不比在__init__ 中创建它们更好。什么,祈祷告诉,你认为你需要这种能力吗?您可以创建一个重载__new__ 的元类来查看类的字典并将内容复制到实例字典中。但这真的丑陋。 :) 点了,这是有道理的,我讨厌问这样的问题,但有时这是找出我的想法是否疯狂的最好方法,现在我知道了! @jonathantopf 你可以在任何函数之外,在全局范围内执行p.list = [],它会正常工作。 @jonathantopf:是的,您正在尝试与 Python 的工作方式作斗争,并试图将其强制纳入 Java 模型。请记住,Python 基本上没有“声明”。都是可执行代码。 class 构造只是对class 实例成员的一系列赋值的语法糖。最接近声明的是对新样式类的类的__slots__ 变量的赋值。【参考方案2】:

Python 有一些关于查找名称的有趣规则。如果你真的想改变主意,试试这个代码:

class testClass():
    l = []
    def __init__(self):
        self.l = ['fred']

这将为每个实例提供一个名为 l 的变量,该变量会屏蔽类变量 l。如果您执行self.__class__.l,您仍然可以获取类变量。

我的想法是这样的...每当您执行instance.variable(即使对于方法名称,它们只是其值恰好是函数的变量)时,它都会在实例的字典中查找它。如果在那里找不到它,它会尝试在实例的类字典中查找它。仅当变量被“读取”时。如果它被分配,它总是在实例字典中创建一个新条目。

【讨论】:

多么狡猾的并发症!这个答案真的很有帮助,因为“阅读与写作”的情况让我很头疼。【参考方案3】:

在您的第一个示例中,list 是该类的一个属性,由它的所有实例共享。这意味着您甚至可以在没有testClass 类型的对象的情况下访问它:

>>> class testClass():
...     list = []
...     def __init__(self):
...             self.list.append("thing")
... 
>>> testClass.list
[]
>>> testClass.list.append(1)
>>> testClass.list
[1]

但是所有对象都与类以及彼此共享list 属性:

>>> testObject = testClass()
>>> testObject.list
[1, 'thing']
>>> testClass.list
[1, 'thing']
>>> 
>>> testObject2 = testClass()
>>> testClass.list
[1, 'thing', 'thing']
>>> testObject2.list
[1, 'thing', 'thing']

【讨论】:

【参考方案4】:

当你实例化一个类时,__init__ 方法会自动执行。

在第一种情况下,您的列表是一个类属性,并由其所有实例共享。你有两个'东西',因为你在实例化 p 时附加了一个,在实例化 f 时附加了另一个(第一个已经在第一次调用时附加)。

【讨论】:

以上是关于python类实例变量和类变量的主要内容,如果未能解决你的问题,请参考以下文章

python 类变量和实例变量

Python中静态方法和类方法的区别

Python类变量和实例变量区别

实例变量和类变量

ruby中的实例变量和类变量

JAVA类与对象---实例变量与类变量的区别,实例方法和类方法的区别