class 在 Python 中初始化
class initialization in Python
我发现有些 classes 包含 __init__
功能,有些则没有。我对下面描述的事情感到困惑。
这两段代码有什么区别:
class Test1(object):
i = 1
和
class Test2(object):
def __init__(self):
self.i = 1
我知道这两个 class 创建的结果或任何实例以及获取实例变量的方式几乎相同。但是,当我们没有为 class 定义 __init__
函数时,在幕后是否存在任何类型的 Python 的“默认”或“隐藏”初始化机制?以及为什么我不能这样写第一个代码:
class Test1(object):
self.i = 1
这就是我的问题。非常感谢!
非常感谢 Antti Haapala!你的回答让我对我的问题有了进一步的了解。现在,我了解到它们的不同之处在于一个是 "class variable",另一个是 "instance variable"。但是,当我进一步尝试时,我遇到了另一个令人困惑的问题。
事情是这样的。我创建了 2 个新的 classes 来理解你所说的:
class Test3(object):
class_variable = [1]
def __init__(self):
self.instance_variable = [2]
class Test4(object):
class_variable = 1
def __init__(self):
self.instance_variable = 2
正如你在回答我第一个问题时所说,我理解 class_variable 是 "class variable" 一般为 class,应通过引用传递或更改内存中的相同位置。 instance_variable 将针对不同的实例创建。
但是正如我所尝试的,你所说的对于 Test3 的实例是正确的,它们都共享相同的内存。如果我在一个实例中更改它,无论我在哪里调用它,它的值都会改变。
但是对于 Test4 的实例来说并非如此。 int 中的 Test4 class 是不是也应该改成引用?
i1 = 测试 3()
i2 = Test3()
>>> i1.i.append(2)
>>> i2.i
[1, 2]
j1 = Test4()
j2 = Test4()
>>> j1.i = 3
>>> j2.i
1
这是为什么?那个“=”是否创建了一个名为“i”的“实例变量”而不改变原来的“Test4.i” " 默认情况下?然而“append”方法只是处理“class变量”?
再次感谢您向 Python 的新手详尽解释最无聊的基本概念。非常感谢!
在python中,实例属性(如self.i
)存储在实例字典(i.__dict__
)中。 class 主体中的所有变量声明都存储为 class.
的属性
因此
class Test(object):
i = 1
等同于
class Test(object):
pass
Test.i = 1
如果没有定义__init__
方法,新创建的实例通常以一个空的实例字典开头,即定义了none个属性。
现在,当 Python 执行 get 属性时 (与 print(instance.i)
操作一样,它首先查找名为 i
的属性在 实例 上设置)。如果失败,将在 type(i)
上查找 i
属性(即 class 属性 i
)。
因此您可以执行以下操作:
class Test:
i = 1
t = Test()
print(t.i) # prints 1
t.i += 1
print(t.i) # prints 2
但这实际上做的是:
>>> class Test(object):
... i = 1
...
>>> t = Test()
>>> t.__dict__
{}
>>> t.i += 1
>>> t.__dict__
{'i': 2}
新建的t
根本没有i
属性!因此在 t.i += 1
中 .i
在 Test
class 中被查找以供阅读,但新值被设置到 t
.
如果你使用__init__
:
>>> class Test2(object):
... def __init__(self):
... self.i = 1
...
>>> t2 = Test2()
>>> t2.__dict__
{'i': 1}
新创建的实例 t2
已经设置了属性。
现在,在 int
等不可变值的情况下,差别不大。但是假设您使用了一个列表:
class ClassHavingAList():
the_list = []
对比
class InstanceHavingAList()
def __init__(self):
self.the_list = []
现在,如果您创建两者的 2 个实例:
>>> c1 = ClassHavingAList()
>>> c2 = ClassHavingAList()
>>> i1 = InstanceHavingAList()
>>> i2 = InstanceHavingAList()
>>> c1.the_list is c2.the_list
True
>>> i1.the_list is i2.the_list
False
>>> c1.the_list.append(42)
>>> c2.the_list
[42]
c1.the_list
和 c2.the_list
指的是内存中完全相同的列表对象,而 i1.the_list
和 i2.the_list
是不同的。修改 c1.the_list
看起来好像 c2.the_list
也发生了变化。
这是因为attribute
本身并没有设置,只是读取而已。 c1.the_list.append(42)
的行为与
相同
getattr(c1, 'the_list').append(42)
也就是说,它只尝试读取 c1
上属性 the_list
的值,如果没有找到,则在 superclass 中查找。 append
不改变属性,它只是改变属性指向的值。
现在,如果您要编写一个表面上看起来相同的示例:
c1.the_list += [ 42 ]
它的工作原理与
相同
original = getattr(c1, 'the_list')
new_value = original + [ 42 ]
setattr(c1, 'the_list', new_value)
并做一件完全不同的事情:首先 original + [ 42 ]
会创建一个新的列表对象。然后属性 the_list
将在 c1
中创建,并设置为指向这个新列表。即在instance.attribute
的情况下,如果attribute
是"read from",则可以在class(或superclass)中查找如果没有在实例中设置,但如果它被写入,如instance.attribute = something
,它将始终在实例中设置。
至于这个:
class Test1(object):
self.i = 1
这样的事情在Python中不起作用,因为在class主体(即class中的所有代码行)时没有定义self
被执行 - 实际上,class 仅在 class 主体中的所有代码都已执行后创建。 class 主体与任何其他代码段一样,只有 def
和变量赋值会在 class 上创建方法和属性,而不是设置全局变量。
我理解了我的新问题。感谢 Antti Haapala。
Now, when Python does the get attribute (as in print(instance.i) operation, it first looks for the attribute named i that is set on the instance). If that fails, the i attribute is looked up on type(i) instead (that is, the class attribute i).
我很清楚为什么是:
j1 = Test4()
j2 = Test4()
>>> j1.i = 3
>>> j2.i
1
经过几次测试。代码
j1.3 = 3
实际上是在不改变class变量的情况下为j1创建了一个新的实例变量。这就是“=”和 "append".
等方法之间的区别
我是 Python 的新手,来自 c++。所以,乍一看,这对我来说很奇怪,因为我从来没有想过创建一个新的实例变量,它不是在 class 中创建的,只是使用“=”。 c++和Python.
的区别真的很大
知道了,谢谢大家
我发现有些 classes 包含 __init__
功能,有些则没有。我对下面描述的事情感到困惑。
这两段代码有什么区别:
class Test1(object):
i = 1
和
class Test2(object):
def __init__(self):
self.i = 1
我知道这两个 class 创建的结果或任何实例以及获取实例变量的方式几乎相同。但是,当我们没有为 class 定义 __init__
函数时,在幕后是否存在任何类型的 Python 的“默认”或“隐藏”初始化机制?以及为什么我不能这样写第一个代码:
class Test1(object):
self.i = 1
这就是我的问题。非常感谢!
非常感谢 Antti Haapala!你的回答让我对我的问题有了进一步的了解。现在,我了解到它们的不同之处在于一个是 "class variable",另一个是 "instance variable"。但是,当我进一步尝试时,我遇到了另一个令人困惑的问题。
事情是这样的。我创建了 2 个新的 classes 来理解你所说的:
class Test3(object):
class_variable = [1]
def __init__(self):
self.instance_variable = [2]
class Test4(object):
class_variable = 1
def __init__(self):
self.instance_variable = 2
正如你在回答我第一个问题时所说,我理解 class_variable 是 "class variable" 一般为 class,应通过引用传递或更改内存中的相同位置。 instance_variable 将针对不同的实例创建。
但是正如我所尝试的,你所说的对于 Test3 的实例是正确的,它们都共享相同的内存。如果我在一个实例中更改它,无论我在哪里调用它,它的值都会改变。
但是对于 Test4 的实例来说并非如此。 int 中的 Test4 class 是不是也应该改成引用?
i1 = 测试 3()
i2 = Test3()
>>> i1.i.append(2)
>>> i2.i
[1, 2]
j1 = Test4()
j2 = Test4()
>>> j1.i = 3
>>> j2.i
1
这是为什么?那个“=”是否创建了一个名为“i”的“实例变量”而不改变原来的“Test4.i” " 默认情况下?然而“append”方法只是处理“class变量”?
再次感谢您向 Python 的新手详尽解释最无聊的基本概念。非常感谢!
在python中,实例属性(如self.i
)存储在实例字典(i.__dict__
)中。 class 主体中的所有变量声明都存储为 class.
因此
class Test(object):
i = 1
等同于
class Test(object):
pass
Test.i = 1
如果没有定义__init__
方法,新创建的实例通常以一个空的实例字典开头,即定义了none个属性。
现在,当 Python 执行 get 属性时 (与 print(instance.i)
操作一样,它首先查找名为 i
的属性在 实例 上设置)。如果失败,将在 type(i)
上查找 i
属性(即 class 属性 i
)。
因此您可以执行以下操作:
class Test:
i = 1
t = Test()
print(t.i) # prints 1
t.i += 1
print(t.i) # prints 2
但这实际上做的是:
>>> class Test(object):
... i = 1
...
>>> t = Test()
>>> t.__dict__
{}
>>> t.i += 1
>>> t.__dict__
{'i': 2}
新建的t
根本没有i
属性!因此在 t.i += 1
中 .i
在 Test
class 中被查找以供阅读,但新值被设置到 t
.
如果你使用__init__
:
>>> class Test2(object):
... def __init__(self):
... self.i = 1
...
>>> t2 = Test2()
>>> t2.__dict__
{'i': 1}
新创建的实例 t2
已经设置了属性。
现在,在 int
等不可变值的情况下,差别不大。但是假设您使用了一个列表:
class ClassHavingAList():
the_list = []
对比
class InstanceHavingAList()
def __init__(self):
self.the_list = []
现在,如果您创建两者的 2 个实例:
>>> c1 = ClassHavingAList()
>>> c2 = ClassHavingAList()
>>> i1 = InstanceHavingAList()
>>> i2 = InstanceHavingAList()
>>> c1.the_list is c2.the_list
True
>>> i1.the_list is i2.the_list
False
>>> c1.the_list.append(42)
>>> c2.the_list
[42]
c1.the_list
和 c2.the_list
指的是内存中完全相同的列表对象,而 i1.the_list
和 i2.the_list
是不同的。修改 c1.the_list
看起来好像 c2.the_list
也发生了变化。
这是因为attribute
本身并没有设置,只是读取而已。 c1.the_list.append(42)
的行为与
getattr(c1, 'the_list').append(42)
也就是说,它只尝试读取 c1
上属性 the_list
的值,如果没有找到,则在 superclass 中查找。 append
不改变属性,它只是改变属性指向的值。
现在,如果您要编写一个表面上看起来相同的示例:
c1.the_list += [ 42 ]
它的工作原理与
相同original = getattr(c1, 'the_list')
new_value = original + [ 42 ]
setattr(c1, 'the_list', new_value)
并做一件完全不同的事情:首先 original + [ 42 ]
会创建一个新的列表对象。然后属性 the_list
将在 c1
中创建,并设置为指向这个新列表。即在instance.attribute
的情况下,如果attribute
是"read from",则可以在class(或superclass)中查找如果没有在实例中设置,但如果它被写入,如instance.attribute = something
,它将始终在实例中设置。
至于这个:
class Test1(object):
self.i = 1
这样的事情在Python中不起作用,因为在class主体(即class中的所有代码行)时没有定义self
被执行 - 实际上,class 仅在 class 主体中的所有代码都已执行后创建。 class 主体与任何其他代码段一样,只有 def
和变量赋值会在 class 上创建方法和属性,而不是设置全局变量。
我理解了我的新问题。感谢 Antti Haapala。
Now, when Python does the get attribute (as in print(instance.i) operation, it first looks for the attribute named i that is set on the instance). If that fails, the i attribute is looked up on type(i) instead (that is, the class attribute i).
我很清楚为什么是:
j1 = Test4()
j2 = Test4()
>>> j1.i = 3
>>> j2.i
1
经过几次测试。代码
j1.3 = 3
实际上是在不改变class变量的情况下为j1创建了一个新的实例变量。这就是“=”和 "append".
等方法之间的区别我是 Python 的新手,来自 c++。所以,乍一看,这对我来说很奇怪,因为我从来没有想过创建一个新的实例变量,它不是在 class 中创建的,只是使用“=”。 c++和Python.
的区别真的很大知道了,谢谢大家