Python中的类初始化

Z_J

我发现有些类包含一个__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!您的回答使我对我的问题有了进一步的了解。现在,我了解到它们的区别在于,一个是“类变量”,另一个是“实例变量”。但是,当我进一步尝试时,又遇到了另一个令人困惑的问题。

这就是它。我创建了2个新类来了解您的发言:

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的实例并非如此如果不是INTTEST4类也通过引用改变?

i1 = Test3()

i2 = Test3()
>>> i1.i.append(2)
>>> i2.i
[1, 2]

j1 = Test4()
j2 = Test4()
>>> j1.i = 3
>>> j2.i
1

这是为什么?是否在默认情况下不更改原始的“ Test4.i创建一个名为“ i的“实例变量但是“ append ”方法只处理“类变量”吗?

再次感谢您向Python新手详尽地解释了最无聊的基本概念。我非常感谢!

安蒂·哈帕拉(Antti Haapala)

在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)(即class attribute 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 = []

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_listc2.the_list指向内存中完全相同的列表对象,而i1.the_listi2.the_list是不同的。修改c1.the_list外观,好像外观c2.the_list也会发生变化。

这是因为attribute本身未设置,只是被读取。c1.the_list.append(42)行为与

getattr(c1, 'the_list').append(42)

也就是说,它仅尝试读取the_liston上的attribute的值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在执行类主体(即类中所有代码行)时没有定义-实际上,仅执行了类主体中的所有代码之后才创建该类。类主体与其他任何代码段一样,只有defs和变量赋值将在类上创建方法和属性,而不是设置全局变量。

本文收集自互联网,转载请注明来源。

如有侵权,请联系[email protected] 删除。

编辑于
0

我来说两句

0条评论
登录后参与评论

相关文章