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类实例变量和类变量的主要内容,如果未能解决你的问题,请参考以下文章