以下代码让我感到困扰: -
class mytest:
name="test1"
tricks=list()
def __init__(self,name):
self.name=name
#self.tricks=[name]
self.tricks.append(name)
t1=mytest("hello world")
t2=mytest("bye world")
print t1.name,t2.name
print t1.tricks,t2.tricks
输出结果为: -
hello world bye world
['hello world', 'bye world'] ['hello world', 'bye world']
意味着列表tricks
由两个实例t1和t2共享,这已在https://docs.python.org/3/tutorial/classes.html
但是,如果我执行以下代码: -
class mytest:
name="test1"
tricks=list()
def __init__(self,name):
self.name=name
self.tricks=[name]
self.tricks.append(name)
t1=mytest("hello world")
t2=mytest("bye world")
x=t1.tricks
if type(x) is list:
print 'a list'
elif type(x) is tuple:
print 'a tuple'
else:
print 'neither a tuple or a list'
print t1.name,t2.name
print t1.tricks,t2.tricks
输出如下: -
a list
hello world bye world
['hello world', 'hello world'] ['bye world', 'bye world']
现在似乎这两个实例t1和t2不再共享列表tricks
。
我的问题是,这里的机制是什么?
答案 0 :(得分:8)
不同之处在于,在第二个示例中,您要创建一个新列表self.tricks作为对象的属性:
def __init__(self,name):
self.name=name
self.tricks=[name] # <-- this is creating the new attribute for the object
self.tricks.append(name)
第一个例子起作用是因为Python解析名称的方法:如果在对象中找不到self.tricks(因为它还没有被创建),那么它会尝试将其作为一个班上的成员。由于存在技巧,因此您可以访问它。
如果您尝试在第二个示例中使用mytest.tricks,可能会很清楚:
def __init__(self,name):
self.name=name
mytest.tricks=[name] # <-- this is accesing the class attribute instead
self.tricks.append(name)
这将输出您实际期望的内容。
答案 1 :(得分:2)
在第一种情况下,您没有在对象范围上创建tricks
属性,因此Python使用了类中的属性;在第二种情况下,您创建了一个新列表并将其与对象本身相关联,因此Python使用了它。
如需更全面的解释,请查看:Python Class Attributes: An Overly Thorough Guide
答案 2 :(得分:2)
您的第一个案例创建一个类变量,第二个案例创建一个实例变量。
当您引用self.foo
时,Python首先检查实例的命名空间字典中的foo
元素,然后检查类中的foo
元素&#39 ; s命名空间字典。
在第一种情况下,由于您创建了一个名为tricks
且具有可变类型(列表)的类变量,并且没有专门为该方法重新分配它,因此可以对该列表进行修改到班级的每个实例。
在你的第二种情况下,事情是相同的,除了你用类名的实例变量隐藏类变量,所以从那时起,对self.tricks
的所有引用都引用实例变量而不是类变量
第二个案例说明:
mytest.__dict__ = {
'name': 'test1',
'tricks': [], # this doesn't get updated
}
t1.__dict__ = {
'name': 'some_passed_name'
'tricks': ['some_passed_name'],
}
答案 3 :(得分:2)
在您的第一个示例中,类mytest
有一个tricks
成员,由其所有实例共享:
class mytest:
name = "test1"
tricks = list()
def __init__(self,name):
...
self.tricks.append(name)
但是,在第二个示例中,mytest
个实例另外还有一个tricks
成员:
class mytest:
name = "test1"
tricks = list()
def __init__(self,name):
...
self.tricks = [name]
...
self.tricks = [name]
语句将名为tricks
的属性提供给self
,即mytest
实例。
该课程仍然有一个共同的tricks
成员。
在调用instance.tricks
时,Python首先在tricks
中查找instance.__dict__
成员。如果找不到,则会在tricks
中找到type(instance).__dict__
成员。
因此,您的第一个示例的实例没有tricks
属性,但Python会为您提供所有共享的mytest.tricks
。
另一方面,第二个示例的实例确实有自己的tricks
属性,Python将返回此属性。
答案 4 :(得分:1)
这个问题需要注意一点。
当您传入名称并将其附加到现有的共享tricks
列表时,正如您所见,它是由所有值共享的,因为它是该列表。
但是,当您在第二个示例中执行self.tricks=[name]
时,您正在删除self.tricks
的对象实例,并将其替换为列表[name]
这类似于父母和子女;当子类没有为现有函数赋予不同的定义时,调用它会调用父函数。但如果你这样做,就会调用孩子的功能。
答案 5 :(得分:0)
在这两种情况下,您都将self.name
替换为新值。
在第一种情况下,您要改变self.tricks
列表,改变列表不要替换它。所以在整个执行期间,你有一个单独的列表,正在变异。
在第二种情况下,行self.tricks=[name]
正在更改列表,创建一个新的列表对象。
您可以轻松地反省这一点:
class mytest:
name = "test1"
tricks = list()
print("tricks id during class definition is: {}".format(id(tricks)))
def __init__(self, name):
self.name = name
print("self.tricks id at the beginning of __init__ is: {}".format(id(self.tricks)))
self.tricks = [name]
print("self.tricks id after being replaced is: {}".format(id(self.tricks)))
print("but mytest.tricks id is still: {}".format(id(mytest.tricks)))
self.tricks.append(name)
t1=mytest("hello world")
t2=mytest("bye world")
,并提供:
tricks id during class definition is: 140547174832328
self.tricks id at the beginning of __init__ is: 140547174832328
self.tricks id after being replaced is: 140547174831432
but mytest.tricks id is still: 140547174832328
self.tricks id at the beginning of __init__ is: 140547174832328
self.tricks id after being replaced is: 140547174830600
but mytest.tricks id is still: 140547174832328