当你写一个类块,你创建类的属性(或类变量)。在类块中分配的所有名称,包括用def定义的方法都成为类属性。
创建一个类实例后,任何具有实例引用的对象都可以在其上创建实例属性。在内部方法中,“当前”实例几乎总是绑定到名称self,这就是为什么你认为这些作为“自变量”。通常在面向对象设计中,附加到类的代码应该具有对该类的实例的属性的控制,因此几乎所有的实例属性赋值都是在方法中完成的,使用对在self参数中接收的实例的引用方法。
类属性通常与在Java,C#或C语言中发现的静态变量(或方法)进行比较。然而,如果你想要更深入的理解,我会避免将类属性视为静态变量“相同”。虽然它们经常用于相同的目的,但基本概念是完全不同的。更多关于这一点在“高级”部分下面的行。
一个例子!
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和bar_list。
然后我们将创建一个实例:
instance = SomeClass()
当这种情况发生时,SomeClass的__init__方法被执行,在其self参数中接收新的实例。此方法创建两个实例属性:foo和foo_list。然后这个实例被分配到实例变量中,因此它被绑定到一个带有两个实例属性的东西:foo和foo_list。
但:
print instance.bar
给出:
I am a class attribute called bar
这怎么发生的?当我们尝试通过点语法检索属性,并且该属性不存在时,Python通过一系列步骤尝试并满足您的请求。接下来要做的是查看实例类的类属性。在这种情况下,它在SomeClass中找到了一个属性栏,因此它返回了。
这也是方法调用如何工作的方式。例如,当调用mylist.append(5)时,mylist没有名为append的属性。但是mylist的类,它绑定到一个方法对象。该方法对象由mylist.append位返回,然后(5)位调用具有参数5的方法。
这是有用的方法是SomeClass的所有实例将有权访问相同的bar属性。我们可以创建一百万个实例,但我们只需要在内存中存储一个字符串,因为他们都可以找到它。
但你必须有点小心。看看下面的操作:
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检索的bar_list。这通常不是你想要的。
高级研究如下。
为了真正编写Python,来自传统的静态类型的OO语言,如Java和C#,你必须学习重新思考类。
在Java中,类本身不是一个真正的东西。当你写一个类时,你更多的声明一堆东西,该类的所有实例具有共同点。在运行时,只有实例(和静态方法/变量,但是那些只是全局变量和函数在一个类的命名空间中,与OO无关)。类是在源代码中写下在运行时实例将是什么样的方式;他们只“存在”在你的源代码中,而不是在运行的程序。
在Python中,类是没有什么特别的。它是一个对象就像任何其他。所以“类属性”事实上与“实例属性”完全相同;在现实中只有“属性”。形成区别的唯一原因是我们倾向于使用与不是类的对象不同的类。底层的机械是一样的。这就是为什么我说,将类属性看作来自其他语言的静态变量是错误的。
但是真正使Python类与Java风格的类不同的是,就像任何其他对象一样,每个类都是一个类的实例!
在Python中,大多数类都是类型为内建类的实例。它是这个类控制类的常见行为,并使所有的OO东西的方式。默认的OO方式拥有具有自己的属性的类的实例,并且具有由它们的类定义的通用方法/属性,只是Python中的协议。如果你愿意,你可以改变它的大部分。如果你听说过使用元类,所有这一切都是定义一个类,它是一个不同类的实例。
关于类的唯一真正的“特殊”的东西(除了所有的内置机制,使他们按照他们默认的方式工作),是类块语法,使您更容易创建类型的实例。这个:
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的所有内容成为创建的对象的属性。
因此,看起来你可以通过Class.attribute访问类属性,就像i = Class()一样容易。 i属性。 i和Class都是对象,对象具有属性。这也使得很容易理解如何在类创建后修改它;只是分配它的属性与你将与任何其他对象相同的方式!
事实上,实例与用于创建它们的类没有特殊的关系。 Python知道哪个类搜索在实例中找不到的属性的方式是通过隐藏的__class__属性。你可以读它来找出这是什么类是一个实例,就像任何其他属性:c = some_instance .__ class__。现在你有一个变量c绑定到一个类,即使它可能不具有相同的名称作为类。你可以使用它来访问类的属性,甚至调用它来创建更多的实例(即使你不知道它的类是什么!)。
你甚至可以分配到i .__ class__来改变它是一个实例的类。如果你这样做,什么都不会立即发生。这不是毁灭性的。这意味着当你查找实例中不存在的属性时,Python会去查看__class__的新内容。由于这包括大多数方法,并且方法通常期望他们正在操作的实例处于某些状态,这通常导致错误,如果你随机做它,它是非常混乱,但它可以做到。如果你很小心,你存储在__class__的东西甚至不必是一个类对象;所有Python需要做的是在某些情况下查找属性,所以你需要的是一个具有正确类型属性的对象(一些注意事项,即Python确实对某些特定类的类或实例感兴趣)。
这可能已经足够了。希望(如果你甚至读了这么远)我没有混淆你太多。 Python是整洁的,当你学习它是如何工作的。