在 Python 中,对象是否有自己的 class 变量副本?
In Python, do objects have their own copies of class variables?
这是一个让每个Python中级学习者都感到困惑的问题,所以请给出一个简短的(白痴友好的)答案。
我想创建一个变量,它在创建新对象时将变量 population
递增 1。
class Human:
population = 0
# initialization.
def __init__(self, name, age, gender):
self.name = name
self.age = age
self.gender = gender
def increment_pop(self):
self.population += 1
# class Human ends.
person = Human('Tom', 22, 'M')
person.increment_pop()
person.increment_pop()
print('Population : ', person.population)
person2 = Human('Anna', 24, 'F')
person2.increment_pop()
print('Population : ', person2.population)
print(Human.population)
输出:
Population : 2
Population : 1
0
所以对象和 class 都有变量 population
?变量population
和init()方法里面的变量有什么区别?
我知道 init() 方法中只有实例变量。
self
引用了 class 的特定实例。当您调用 increment_pop 时,您正在使用 self.population,这只会导致该实例的增量。因为在 python 中不需要调用初始值设定项,所以 increment_pop 创建了一个名为 self.population 的新变量,其中 class 变量 population 应该是全局的。
将 increment_pop 更改为 cls.population 而不是 self.population,它应该会按预期工作。请注意,在函数定义中,您需要装饰器 @class 方法和参数 cls.
比那要复杂一点。有 class 变量和实例变量,但在您的示例中发生的是 class 变量被实例变量 覆盖 。
考虑以下几点:
>>> class Foobar:
... my_list = []
...
>>> foo = Foobar()
>>> Foobar.my_list.append('hello')
>>> foo.my_list.append('world')
>>> foo.my_list
['hello', 'world']
如您所见,Foobar
class 和 foo
实例 在此处共享一个变量 。所以不,实例化 class 不会“复制”所有 class 变量。然而,考虑一下:
>>> foo.my_list = ['spam', 'eggs']
>>> foo.my_list
['spam', 'eggs']
>>> Foobar.my_list
['hello', 'world']
现在我们有 两个变量,一个是 class 变量,另一个是实例变量。他们不再一样了。
如果您想始终使用 class 变量,最优雅的方法是使用 class 方法。例如,
class Human:
population = 0
# initialization.
def __init__(self, name, age, gender):
self.name = name
self.age = age
self.gender = gender
@classmethod
def increment_pop(cls):
cls.population += 1
现在因为 increment_pop
是一个 class 方法,它总是在 class 上操作,而不是实例。但是,population
属性将对实例可用,只要它们不覆盖它,它将与 class 变量相同。
需要牢记的几件重要事情:
基元,如整数和字符串,是不可变的。当您执行 foo += 1
时,您正在创建整数的新实例并替换旧实例。您没有就地修改它。
实例可以使用 self.whatnot
访问 class 变量和 class 方法。但是如果你想明确一点,你也可以将它们引用为 ClassName.whatnot
.
Class 方法可以从实例中调用,类似地,像 self.class_method()
.
关于 Python 变量的其他说明
让我们回到这里并考虑 Python 如何在 实例 :
上解决对类似 my_human.population
的请求
- 首先,Python 查找名为
population
的实例属性。如果它存在,那就是你得到的值。
- Second,Python 查找名为
population
的 class 属性。如果存在,那就是你得到的值。
因此,当您的实例上没有分配 population
并且您访问 self.population
时,由于不存在具有该名称的实例属性,您将获得 class 属性。
但是,一旦您将实例属性分配给您的对象,上述第二步就不会发生。
您也可以在运行时检查它:
>>> class Human:
... population = 0
...
>>> my_human = Human()
>>> 'population' in Human.__dict__
True
>>> 'population' in my_human.__dict__
False
所以 population
只存在于 人类 class 上,而不存在于实例上。当我们访问Human.population
时,它找到了class属性:
>>> my_human.population
0
但是如果我创建实例 属性会怎样?
>>> Human.population = 100
>>> my_human.population = 50
>>> Human.population
100
>>> my_human.population
50
现在,因为实例有一个与名称 population
匹配的属性,当您访问 my_human.population
时,它永远不会查找 class.
上的内容
这是一个让每个Python中级学习者都感到困惑的问题,所以请给出一个简短的(白痴友好的)答案。
我想创建一个变量,它在创建新对象时将变量 population
递增 1。
class Human:
population = 0
# initialization.
def __init__(self, name, age, gender):
self.name = name
self.age = age
self.gender = gender
def increment_pop(self):
self.population += 1
# class Human ends.
person = Human('Tom', 22, 'M')
person.increment_pop()
person.increment_pop()
print('Population : ', person.population)
person2 = Human('Anna', 24, 'F')
person2.increment_pop()
print('Population : ', person2.population)
print(Human.population)
输出:
Population : 2
Population : 1
0
所以对象和 class 都有变量 population
?变量population
和init()方法里面的变量有什么区别?
我知道 init() 方法中只有实例变量。
self
引用了 class 的特定实例。当您调用 increment_pop 时,您正在使用 self.population,这只会导致该实例的增量。因为在 python 中不需要调用初始值设定项,所以 increment_pop 创建了一个名为 self.population 的新变量,其中 class 变量 population 应该是全局的。
将 increment_pop 更改为 cls.population 而不是 self.population,它应该会按预期工作。请注意,在函数定义中,您需要装饰器 @class 方法和参数 cls.
比那要复杂一点。有 class 变量和实例变量,但在您的示例中发生的是 class 变量被实例变量 覆盖 。
考虑以下几点:
>>> class Foobar:
... my_list = []
...
>>> foo = Foobar()
>>> Foobar.my_list.append('hello')
>>> foo.my_list.append('world')
>>> foo.my_list
['hello', 'world']
如您所见,Foobar
class 和 foo
实例 在此处共享一个变量 。所以不,实例化 class 不会“复制”所有 class 变量。然而,考虑一下:
>>> foo.my_list = ['spam', 'eggs']
>>> foo.my_list
['spam', 'eggs']
>>> Foobar.my_list
['hello', 'world']
现在我们有 两个变量,一个是 class 变量,另一个是实例变量。他们不再一样了。
如果您想始终使用 class 变量,最优雅的方法是使用 class 方法。例如,
class Human:
population = 0
# initialization.
def __init__(self, name, age, gender):
self.name = name
self.age = age
self.gender = gender
@classmethod
def increment_pop(cls):
cls.population += 1
现在因为 increment_pop
是一个 class 方法,它总是在 class 上操作,而不是实例。但是,population
属性将对实例可用,只要它们不覆盖它,它将与 class 变量相同。
需要牢记的几件重要事情:
基元,如整数和字符串,是不可变的。当您执行
foo += 1
时,您正在创建整数的新实例并替换旧实例。您没有就地修改它。实例可以使用
self.whatnot
访问 class 变量和 class 方法。但是如果你想明确一点,你也可以将它们引用为ClassName.whatnot
.Class 方法可以从实例中调用,类似地,像
self.class_method()
.
关于 Python 变量的其他说明
让我们回到这里并考虑 Python 如何在 实例 :
上解决对类似my_human.population
的请求
- 首先,Python 查找名为
population
的实例属性。如果它存在,那就是你得到的值。 - Second,Python 查找名为
population
的 class 属性。如果存在,那就是你得到的值。
因此,当您的实例上没有分配 population
并且您访问 self.population
时,由于不存在具有该名称的实例属性,您将获得 class 属性。
但是,一旦您将实例属性分配给您的对象,上述第二步就不会发生。
您也可以在运行时检查它:
>>> class Human:
... population = 0
...
>>> my_human = Human()
>>> 'population' in Human.__dict__
True
>>> 'population' in my_human.__dict__
False
所以 population
只存在于 人类 class 上,而不存在于实例上。当我们访问Human.population
时,它找到了class属性:
>>> my_human.population
0
但是如果我创建实例 属性会怎样?
>>> Human.population = 100
>>> my_human.population = 50
>>> Human.population
100
>>> my_human.population
50
现在,因为实例有一个与名称 population
匹配的属性,当您访问 my_human.population
时,它永远不会查找 class.