类和实例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__
, bar
和bar_list
。
然后我们将创build一个实例:
instance = SomeClass()
当发生这种情况时, SomeClass
的__init__
方法被执行,在其self
参数中接收新的实例。 这个方法创build两个实例属性: foo
和foo_list
。 然后这个实例被分配到instance
variables中,所以它被绑定到具有这两个实例属性的事物: foo
和foo_list
。
但:
print instance.bar
得到:
I am a class attribute called bar
这怎么发生的? 当我们尝试通过点语法检索一个属性,并且该属性不存在时,Python会经历一系列的步骤来尝试和完成您的请求。 接下来要做的就是查看实例类的类属性 。 在这种情况下,它在SomeClass
find一个属性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.attribute
。 i
和Class
都是对象,对象有属性。 这也使得很容易理解如何在创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
是对该实例的引用。