我发现有些类包含__init__
函数,有些则没有。我对下面描述的内容感到困惑。
这两段代码之间有什么区别:
class Test1(object):
i = 1
和
class Test2(object):
def __init__(self):
self.i = 1
我知道这两个类创建的结果或任何实例以及获取实例变量的方式几乎相同。但是当我们没有为类定义__init__
函数时,是否有任何类型的“默认”或“隐藏”的Python初始化机制?为什么我不能用这种方式编写第一个代码:
class Test1(object):
self.i = 1
这是我的问题。非常感谢你!
非常感谢Antti Haapala!你的回答让我对我的问题有了进一步的了解。现在,我理解它们的不同之处在于一个是“类变量”,另一个是“实例变量”。但是,当我进一步尝试时,我又遇到了另一个令人困惑的问题。
这是什么。我创建了两个新课程来理解你所说的内容:
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 是一个“类变量”一般来自类,应该通过或改变引用内存中的相同位置。 instance_variable 将针对不同的实例进行明确创建。
但是当我试用时,你所说的 Test3 的实例是正确的,它们都共享相同的内存。如果我在一个实例中更改它,它的值会随着我调用而改变。
但 Test4 的情况并非如此。不应该通过引用更改 Test4 类中的 int 吗?
i1 = Test3()
i2 = Test3()
>>> i1.i.append(2)
>>> i2.i
[1, 2]
j1 = Test4()
j2 = Test4()
>>> j1.i = 3
>>> j2.i
1
为什么?那个“=”会创建一个名为“ i ”的“实例变量”,而不会默认更改原来的“ Test4.i ”吗?然而,“追加”方法只处理“类变量”?
再次感谢您对Python新手最无聊的基本概念的详尽解释。我真的很感激!
答案 0 :(得分:3)
在python中,实例属性(例如self.i
)存储在实例字典(i.__dict__
)中。类体中的所有变量声明都存储为类的属性。
因此
class Test(object):
i = 1
相当于
class Test(object):
pass
Test.i = 1
如果没有定义__init__
方法,则新创建的实例通常以空实例字典开头,这意味着没有定义任何属性。
现在,当Python执行 get属性时(如在print(instance.i)
操作中,它首先查找在实例<上设置的名为i
的属性/强>)。如果失败,则会在i
上查找type(i)
属性(即类属性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}
新创建的i
上没有t
属性!因此,在t.i += 1
.i
类中查找了Test
以进行阅读,但新值已设置为t
。
如果您使用__init__
:
>>> class Test2(object):
... def __init__(self):
... self.i = 1
...
>>> t2 = Test2()
>>> t2.__dict__
{'i': 1}
新创建的实例t2
已经设置了属性。
现在,在int
之类的不可变值的情况下,没有太大的区别。但是假设你使用了一个列表:
class ClassHavingAList():
the_list = []
vs
class InstanceHavingAList()
def __init__(self):
self.the_list = []
现在,如果您创建两个实例:
>>> 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)
也就是说,它只尝试在the_list
上读取属性c1
的值,如果没有在那里找到,那么在超类中查找它。 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
是“读取”,则可以在类(或超类)中查找如果未在实例中设置,但如果它被写入,如在instance.attribute = something
中,它将始终在实例上设置。
至于此:
class Test1(object):
self.i = 1
这样的东西在Python中不起作用,因为当类体(即类中的所有代码行)被执行时没有定义self
- 实际上,该类仅在之后创建已经执行了类体中的所有代码。类主体就像任何其他代码一样,只有def
和变量赋值将在类上创建方法和属性,而不是设置全局变量。
答案 1 :(得分:0)
我理解我新添加的问题。感谢Antti Haapala。
现在,当Python执行get属性时(如在print(instance.i)操作中),它首先查找在实例上设置的名为i的属性。如果失败,则在类型(i)上查找i属性(即,类属性i)。
我清楚为什么会这样:
j1 = Test4()
j2 = Test4()
>>> j1.i = 3
>>> j2.i
1
几次测试后。代码
j1.3 = 3
实际上为j1创建了一个新的实例变量而不更改类变量。这是&#34; =&#34;之间的区别。以及&#34;追加&#34;。
等方法我是来自c ++的Python的新手。所以,乍一看,这对我来说很奇怪,因为我从未想过创建一个新的实例变量,而这个变量不是仅仅使用&#34; =&#34;在类中创建的。它在c ++和Python之间确实存在很大差异。
现在我明白了,谢谢大家。