类和实例variables有什么区别?

Python中的类和实例variables有什么区别?

class Complex: a = 1 

 class Complex: def __init__(self): self.a = 1 

使用调用: x = Complex().a在两种情况下都将x赋值为1。

有关__init__()self的更深入的答案将不胜感激。

当您编写一个类块时,您可以创build类属性 (或类variables)。 您在类块中分配的所有名称(包括使用def定义的方法)都将成为类属性。

创build类实例后,任何引用实例的东西都可以在其上创build实例属性。 在内部方法中,“当前”实例几乎总是绑定到自己的名字,这就是为什么你把它们当作“自variables”的原因。 通常在面向对象devise中,附加到类的代码应该控制该类的实例的属性,因此几乎所有的实例属性赋值都是在方法内部完成的,使用对self参数中接收到的实例的引用方法。

类属性通常与Java,C#或C ++等语言中的静态variables(或方法)进行比较。 然而,如果你想要更深入的理解,我会避免将类属性看作与静态variables“相同”。 虽然它们经常被用于相同的目的,但其基本概念是完全不同的。 线条下方的“高级”部分详细介绍了这一点。

一个例子!

 class SomeClass: def __init__(self): self.foo = 'I am an instance attribute called foo' self.foo_list = [] bar = 'I am a class attribute called bar' bar_list = [] 

在执行这个块之后,有一个SomeClass类,它有3个类属性: __init__ bar_list __init__barbar_list

然后我们将创build一个实例:

 instance = SomeClass() 

当发生这种情况时, SomeClass__init__方法被执行,在其self参数中接收新的实例。 这个方法创build两个实例属性: foofoo_list 。 然后这个实例被分配到instancevariables中,所以它被绑定到具有这两个实例属性的事物: foofoo_list

但:

 print instance.bar 

得到:

 I am a class attribute called bar 

这怎么发生的? 当我们尝试通过点语法检索一个属性,并且该属性不存在时,Python会经历一系列的步骤来尝试和完成您的请求。 接下来要做的就是查看实例类的类属性 。 在这种情况下,它在SomeClassfind一个属性bar ,所以它返回。

这也是方法调用工作的方式。 例如,当您调用mylist.append(5)mylist没有名为append的属性。 但是mylist ,并且绑定到一个方法对象。 该方法对象由mylist.append位返回,然后(5)位使用参数5调用该方法。

这是有用的方式是SomeClass 所有实例将有权访问相同的bar属性。 我们可以创build一百万个实例,但是我们只需要将这个string存储在内存中,因为它们都可以find它。

但是你必须小心点。 看看下面的操作:

 sc1 = SomeClass() sc1.foo_list.append(1) sc1.bar_list.append(2) sc2 = SomeClass() sc2.foo_list.append(10) sc2.bar_list.append(20) print sc1.foo_list print sc1.bar_list print sc2.foo_list print sc2.bar_list 

你认为这打印什么?

 [1] [2, 20] [10] [2, 20] 

这是因为每个实例都有它自己的foo_list副本,所以它们被分开追加。 但是所有的实例都可以访问同一个bar_list 。 所以当我们做了sc1.bar_list.append(2)它影响了sc2 ,即使sc2还不存在! 同样, sc2.bar_list.append(20)影响了通过sc1检索到的sc2.bar_list.append(20) 。 这往往不是你想要的。


高级研究如下。 🙂

要真正理解来自传统的静态types的面向对象语言(如Java和C#)的Python,你必须学会​​重新思考一下类。

在Java中,一个类本身并不是一件真正的事情 。 当你写一个类的时候,你更多地声明了这个类的所有实例都有共同的东西。 在运行时,只有实例(和静态方法/variables,但是这些实际上只是全局variables和函数在与类相关的命名空间中,与OO无关)。 类是您在源代码中logging的实例在运行时会是什么样子的方式; 他们只在您的源代码中“存在”,而不在正在运行的程序中。

在Python中,一个类没有什么特别的。 这是一个对象,就像其他任何东西。 所以“类属性”实际上与“实例属性”完全一样; 在现实中只有“属性”。 绘制区别的唯一原因是我们倾向于使用不同于不是类的对象的类。 基本的机器都是一样的。 这就是为什么我认为把类属性当作其他语言的静态variables来考虑是错误的。

但是真正使Python类与Java风格类不同的东西就像任何其他对象一样, 每个类都是某个类的实例

在Python中,大多数类都是内build类的实例,称为type 。 正是这个类控制了类的常见行为,并且使得OO的所有东西都像它一样。 拥有自己属性的类实例,以及由它们的类定义的通用方法/属性的默认OO方式,只是Python中的一个协议。 如果你愿意,你可以改变它的大部分内容。 如果您曾经听说过使用元类 ,那么所有这一切都是定义一个类,而不是type一个实例。

关于类的唯一真正“特殊”的事情(除了所有的内置机制,使它们按默认的方式工作),就是类块语法,以便于您创buildtype实例。 这个:

 class Foo(BaseFoo): def __init__(self, foo): self.foo = foo z = 28 

大致相当于以下内容:

 def __init__(self, foo): self.foo = foo classdict = {'__init__': __init__, 'z': 28 } Foo = type('Foo', (BaseFoo,) classdict) 

它将安排所有classdict的内容成为创build的对象的属性。

那么看到你可以通过Class.attribute访问一个类属性就像i = Class(); i.attribute一样简单i = Class(); i.attribute i = Class(); i.attributeiClass都是对象,对象有属性。 这也使得很容易理解如何在创build类后对其进行修改; 只需要使用与其他对象相同的方式来赋值它的属性!

事实上,实例与用于创build它们的类没有特别的特殊关系。 Python知道哪个类要search的实例中找不到的属性是隐藏的__class__属性。 你可以阅读它来找出这是什么类的实例,就像其他属性一样: c = some_instance.__class__ 。 现在你有一个绑定到一个类的variablesc ,即使它可能没有与类相同的名字。 你可以用它来访问类属性,甚至可以调用它来创build更多的实例(即使你不知道它是什么类)。

你甚至可以分配给i.__class__来改变它是一个实例的类! 如果你这样做,没有什么特别的事情立即发生。 这不是惊天动地的。 所有这一切意味着,当您查找实例中不存在的属性时,Python将查看__class__的新内容。 由于这包括大多数方法,并且方法通常期望它们正在操作的实例处于某些状态,所以如果随机执行这样的操作,通常会导致错误,这是非常混乱的,但是可以完成。 如果你非常小心,你存储在__class__中的东西甚至不必是一个类对象。 所有Python的工作就是在某些情况下查找属性,所以你所需要的只是一个具有正确types属性的对象(除了Python对特定类的类或实例进行挑剔之外,还有一些注意事项)。

现在可能够了。 希望(如果你已经读了这么多)我没有把你搞糊涂。 当你学习如何工作时,Python是整洁的。 🙂

你所调用的“实例”variables实际上不是一个实例variables; 这是一个类variables 。 请参阅关于类的语言参考 。

在你的例子中, a似乎是一个实例variables,因为它是不可变的。 当你指定一个可变对象时,就可以看到类的variables是自然的:

 >>> class Complex: >>> a = [] >>> >>> b = Complex() >>> c = Complex() >>> >>> # What do they look like? >>> ba [] >>> ca [] >>> >>> # Change b... >>> baappend('Hello') >>> ba ['Hello'] >>> # What does c look like? >>> ca ['Hello'] 

如果你使用self ,那么它将是一个真正的实例variables,因此每个实例都有它自己独特的a 。 创build新实例时会调用对象的__init__函数,而self是对该实例的引用。