Why do the examples below behave differently?
为什么下面的例子表现不一样?
Example 1: foo
seems to behave like a class variable that is specific for various objects
示例1:foo似乎表现为特定于各种对象的类变量
class A:
foo = 1
a, b = A(), A()
a.foo = 5
print b.foo
----------------
Output: 1
Example 2: foo
seems to behave like a static class variable that is the same for all object. Perhaps the behavior has something to do with lists working as pointers.
示例2:foo似乎表现得像一个静态类变量,对所有对象都是相同的。也许这种行为与作为指针的列表有关。
class A:
foo = []
a, b = A(), A()
a.foo.append(5)
print b.foo
----------------
Output: [5]
Example 3: Doesn't work
示例3:不起作用
class A:
self.foo = []
a, b = A(), A()
a.foo.append(5)
print b.foo
----------------
Output: Error
3 个解决方案
#1
6
The first two examples are both class attributes. The reason they seem different is because you're not doing the same thing in both cases: you're assigning a new value in the first case and modifying the existing value in the second case.
前两个示例都是类属性。它们看起来不同的原因是因为在两种情况下你都没有做同样的事情:你在第一种情况下分配一个新值,在第二种情况下修改现有值。
Notice that you are not doing the same thing in the first two examples. In the first example you do a.foo = 5
, assigning a new value. In the second example, if you did the analogous thing, assigning, a.foo = [5]
, you would see the same kind of result as in the first example. But instead you altered the existing list with a.foo.append(5)
, so the behavior is different. a.foo = 5
changes only the variable (i.e., what value it points to); a.foo.append(5)
changes the value itself.
请注意,前两个示例中您没有做同样的事情。在第一个示例中,您执行a.foo = 5,分配新值。在第二个例子中,如果你做了类似的事情,分配,a.foo = [5],你会看到与第一个例子中相同的结果。但是你用a.foo.append(5)改变了现有的列表,所以行为是不同的。 a.foo = 5只改变变量(即它指向的值); a.foo.append(5)更改值本身。
(Notice that there is no way to do the equivalent of the second example in the first example. That is, there's nothing like a.foo.add(1)
to add 1 to 5. That's because integers are not mutable but lists are. But what matters is not that lists "are" mutable, but that you mutated one. In other words, it doesn't matter what you can do with a list, it matters what you actually do in the specific code.)
(请注意,在第一个示例中没有办法完成第二个示例的等效。也就是说,没有像a.foo.add(1)那样添加1到5.这是因为整数不可变但是列表是。但重要的不是列表“是”可变的,而是你突变了一个。换句话说,无论你用列表做什么都没关系,重要的是你在特定的代码中实际做了什么。)
Also, notice that although the foo
you defined in the class definition is a class attribute, when you do a.foo = 5
, you are creating a new attribute on the instance. It happens to have the same name as the class attribute, but it doesn't change the value of the class attribute, which is what b.foo
still sees.
另请注意,虽然您在类定义中定义的foo是类属性,但是当您执行a.foo = 5时,您将在实例上创建新属性。它恰好与class属性具有相同的名称,但它不会更改class属性的值,这也是b.foo仍然看到的值。
The last example doesn't work because, just like in the first two examples, code inside the class
block is at the class scope. There is no self
because there are no instances yet at the time the class is defined.
最后一个示例不起作用,因为,就像在前两个示例中一样,类块内的代码位于类范围内。没有自我,因为在定义类时还没有实例。
There are many, many other questions about this on * and I urge you to search and read a bunch of them to gain a fuller understanding of how this works.
*上有很多很多其他问题,我敦促你搜索和阅读其中的一些问题,以便更全面地了解它是如何工作的。
#2
1
This doesn't work:
这不起作用:
class A:
self.foo = []
Which raises an error.
这引起了一个错误。
NameError: name 'self' is not defined
Because self
is not a keyword in Python, it's just a variable name commonly assigned to the instance of the class that is passed to a method of the class when the class is called.
因为self不是Python中的关键字,所以它只是一个变量名,通常分配给在调用类时传递给类方法的类实例。
Here's an example:
这是一个例子:
class A(object):
def __init__(self):
self.foo = []
a, b = A(), A()
a.foo.append(5)
print(b.foo)
Then returns:
然后返回:
[]
When each one is initialized, they each get their own list which can be accessed by the attribute foo
, and when one is modified, the other, being a separate list stored at a different place in memory, is not affected.
当每个初始化时,它们各自得到它们自己的列表,可以通过属性foo访问,并且当一个被修改时,另一个,即存储在存储器中不同位置的单独列表,不受影响。
#3
1
The difference has not to do with mutability/immutability, but what operations are performed.
差异与可变性/不变性无关,而是执行了哪些操作。
In example 1, the class has an attribute foo
. After object creation, you give the object another attribute foo
which shadows the former one. So the class attribute acts as a kind of "default" or "fallback".
在示例1中,类具有属性foo。创建对象后,为对象提供另一个属性foo,该属性隐藏前一个属性。因此class属性充当一种“默认”或“后备”。
In example 2, you have one object which you perform an operation on (which, admittedly, only works on mutable objects). So the object referred to by A.foo
, which can be accessed as well via a.foo
and b.foo
due to the lack of an instance attribute with the same name, gets added a 5
.
在示例2中,您有一个对象可以执行操作(当然,它只适用于可变对象)。所以A.foo引用的对象,由于缺少具有相同名称的实例属性,也可以通过a.foo和b.foo访问,添加了5。
Example 3 doesn't work because self
doesn't exist where you use it.
示例3不起作用,因为在您使用它的地方不存在self。
Note that example 1 would as well work with mutable objects, such as lists:
请注意,示例1也适用于可变对象,例如列表:
class A:
foo = []
a, b = A(), A()
a.foo = []
a.foo.append(5)
b.foo.append(10)
print a.foo # [5]
print b.foo # [10]
print A.foo # [10]
Here a.foo
gets a new, empty list. b.foo
, lacking an instance attribute, continues to refer to the class attribute. So we have two empty lists which are independent of each other, as we see when .append()
ing.
这里a.foo获得一个新的空列表。缺少实例属性的b.foo继续引用class属性。所以我们有两个相互独立的空列表,正如我们在.append()时看到的那样。
#1
6
The first two examples are both class attributes. The reason they seem different is because you're not doing the same thing in both cases: you're assigning a new value in the first case and modifying the existing value in the second case.
前两个示例都是类属性。它们看起来不同的原因是因为在两种情况下你都没有做同样的事情:你在第一种情况下分配一个新值,在第二种情况下修改现有值。
Notice that you are not doing the same thing in the first two examples. In the first example you do a.foo = 5
, assigning a new value. In the second example, if you did the analogous thing, assigning, a.foo = [5]
, you would see the same kind of result as in the first example. But instead you altered the existing list with a.foo.append(5)
, so the behavior is different. a.foo = 5
changes only the variable (i.e., what value it points to); a.foo.append(5)
changes the value itself.
请注意,前两个示例中您没有做同样的事情。在第一个示例中,您执行a.foo = 5,分配新值。在第二个例子中,如果你做了类似的事情,分配,a.foo = [5],你会看到与第一个例子中相同的结果。但是你用a.foo.append(5)改变了现有的列表,所以行为是不同的。 a.foo = 5只改变变量(即它指向的值); a.foo.append(5)更改值本身。
(Notice that there is no way to do the equivalent of the second example in the first example. That is, there's nothing like a.foo.add(1)
to add 1 to 5. That's because integers are not mutable but lists are. But what matters is not that lists "are" mutable, but that you mutated one. In other words, it doesn't matter what you can do with a list, it matters what you actually do in the specific code.)
(请注意,在第一个示例中没有办法完成第二个示例的等效。也就是说,没有像a.foo.add(1)那样添加1到5.这是因为整数不可变但是列表是。但重要的不是列表“是”可变的,而是你突变了一个。换句话说,无论你用列表做什么都没关系,重要的是你在特定的代码中实际做了什么。)
Also, notice that although the foo
you defined in the class definition is a class attribute, when you do a.foo = 5
, you are creating a new attribute on the instance. It happens to have the same name as the class attribute, but it doesn't change the value of the class attribute, which is what b.foo
still sees.
另请注意,虽然您在类定义中定义的foo是类属性,但是当您执行a.foo = 5时,您将在实例上创建新属性。它恰好与class属性具有相同的名称,但它不会更改class属性的值,这也是b.foo仍然看到的值。
The last example doesn't work because, just like in the first two examples, code inside the class
block is at the class scope. There is no self
because there are no instances yet at the time the class is defined.
最后一个示例不起作用,因为,就像在前两个示例中一样,类块内的代码位于类范围内。没有自我,因为在定义类时还没有实例。
There are many, many other questions about this on * and I urge you to search and read a bunch of them to gain a fuller understanding of how this works.
*上有很多很多其他问题,我敦促你搜索和阅读其中的一些问题,以便更全面地了解它是如何工作的。
#2
1
This doesn't work:
这不起作用:
class A:
self.foo = []
Which raises an error.
这引起了一个错误。
NameError: name 'self' is not defined
Because self
is not a keyword in Python, it's just a variable name commonly assigned to the instance of the class that is passed to a method of the class when the class is called.
因为self不是Python中的关键字,所以它只是一个变量名,通常分配给在调用类时传递给类方法的类实例。
Here's an example:
这是一个例子:
class A(object):
def __init__(self):
self.foo = []
a, b = A(), A()
a.foo.append(5)
print(b.foo)
Then returns:
然后返回:
[]
When each one is initialized, they each get their own list which can be accessed by the attribute foo
, and when one is modified, the other, being a separate list stored at a different place in memory, is not affected.
当每个初始化时,它们各自得到它们自己的列表,可以通过属性foo访问,并且当一个被修改时,另一个,即存储在存储器中不同位置的单独列表,不受影响。
#3
1
The difference has not to do with mutability/immutability, but what operations are performed.
差异与可变性/不变性无关,而是执行了哪些操作。
In example 1, the class has an attribute foo
. After object creation, you give the object another attribute foo
which shadows the former one. So the class attribute acts as a kind of "default" or "fallback".
在示例1中,类具有属性foo。创建对象后,为对象提供另一个属性foo,该属性隐藏前一个属性。因此class属性充当一种“默认”或“后备”。
In example 2, you have one object which you perform an operation on (which, admittedly, only works on mutable objects). So the object referred to by A.foo
, which can be accessed as well via a.foo
and b.foo
due to the lack of an instance attribute with the same name, gets added a 5
.
在示例2中,您有一个对象可以执行操作(当然,它只适用于可变对象)。所以A.foo引用的对象,由于缺少具有相同名称的实例属性,也可以通过a.foo和b.foo访问,添加了5。
Example 3 doesn't work because self
doesn't exist where you use it.
示例3不起作用,因为在您使用它的地方不存在self。
Note that example 1 would as well work with mutable objects, such as lists:
请注意,示例1也适用于可变对象,例如列表:
class A:
foo = []
a, b = A(), A()
a.foo = []
a.foo.append(5)
b.foo.append(10)
print a.foo # [5]
print b.foo # [10]
print A.foo # [10]
Here a.foo
gets a new, empty list. b.foo
, lacking an instance attribute, continues to refer to the class attribute. So we have two empty lists which are independent of each other, as we see when .append()
ing.
这里a.foo获得一个新的空列表。缺少实例属性的b.foo继续引用class属性。所以我们有两个相互独立的空列表,正如我们在.append()时看到的那样。