>>> class Complex:
>>> a = []
>>>
>>> b = Complex()
>>> c = Complex()
>>>
>>> # What do they look like?
>>> b.a
[]
>>> c.a
[]
>>>
>>> # Change b...
>>> b.a.append('Hello')
>>> b.a
['Hello']
>>> # What does c look like?
>>> c.a
['Hello']
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 = []
您所称的“实例”变量实际上不是实例变量;它是类变量。请参阅language reference about classes。
在您的示例中,
a
似乎是一个实例变量,因为它是不可变的。这是一个类变量的性质,在分配可变对象时可以看到:如果使用
self
,那么它将是一个真正的实例变量,因此每个实例都有自己的惟一a
。对象的__init__
函数是called when a new instance is created,而self
是对该实例的引用。编写类块时,创建类属性(或类变量)。在类块中分配的所有名称(包括用
def
定义的方法)都成为类属性。创建类实例后,任何引用该实例的对象都可以在其上创建实例属性。在方法内部,“current”实例几乎总是绑定到名称
self
,这就是为什么您将它们看作“自变量”的原因。通常在面向对象的设计中,附加到类的代码应该控制该类实例的属性,因此几乎所有的实例属性分配都是在方法内部完成的,使用方法的self
参数中接收到的实例的引用。类属性通常与在java、C++、C++等语言中发现的静态EME>变量(或方法)进行比较。但是,如果您希望深入了解,我将避免将类属性视为“相同”的静态变量。虽然它们通常用于相同的目的,但其基本概念却大不相同。在下面的“高级”部分有更多关于这个的内容。
一个例子!
执行此块后,有一个类
SomeClass
,具有3个类属性:__init__
、bar
和bar_list
。然后我们将创建一个实例:
当这种情况发生时,将执行
SomeClass
的__init__
方法,并在其self
参数中接收新实例。此方法创建两个实例属性:foo
和foo_list
。然后这个实例被分配到instance
变量中,因此它被绑定到具有这两个实例属性的对象:foo
和foo_list
。但是:
给出:
这是怎么发生的?当我们试图通过dot语法检索属性时,如果该属性不存在,Python将通过一系列步骤来尝试满足您的请求。接下来,它将尝试查看实例类的类属性。在本例中,它在
SomeClass
中找到了一个属性bar
,因此返回了该属性。顺便说一下,这也是方法调用的工作方式。例如,当调用
mylist.append(5)
时,mylist
没有名为append
的属性。但是mylist
的类是这样的,并且它绑定到一个方法对象。该方法对象由mylist.append
位返回,然后(5)
位使用参数5
调用该方法。这是有用的,因为
SomeClass
的所有实例都可以访问相同的bar
属性。我们可以创建一百万个实例,但是我们只需要将一个字符串存储在内存中,因为它们都可以找到它。但你得小心一点。请查看以下操作:
你觉得这是什么指纹?
这是因为每个实例都有自己的
foo_list
副本,所以它们被分别附加到。但是所有实例共享对同一bar_list
的访问。所以当我们做了sc1.bar_list.append(2)
它影响了sc2
,即使sc2
还不存在!同样地,sc2.bar_list.append(20)
影响了通过sc1
检索到的bar_list
。这通常不是你想要的。接下来是高级研究。:)
要真正搞糟Python,来自Java和C#等传统的静态类型OO语言,您必须学会稍微重新思考类。
在Java中,类本身并不是真正的对象。当你写一个类的时候,你更多的是声明一堆类的所有实例都有共同点的东西。在运行时,只有实例(以及静态方法/变量,但它们实际上只是与类关联的命名空间中的全局变量和函数,与OO无关)。类是在源代码中写下实例在运行时的样子的方式;它们只在so中“存在”urce代码,不在正在运行的程序中。
在Python中,类没有什么特别的。它是一个物体,就像其他任何东西一样。所以“类属性”实际上和“实例属性”完全相同;实际上只有“属性”。唯一的区别是我们倾向于使用与非类对象不同的类对象。基本的机器都是一样的。这就是为什么我说把类属性看作其他语言的静态变量是错误的。
但真正使Python类不同于Java风格类的是,与任何其他对象一样,每个类都是某个类的实例!
在Python中,大多数类都是名为
type
的内置类的实例。正是这个类控制了类的常见行为,并使所有的OO都像它那样。在Python中,默认的OO方式是让类的实例具有自己的属性,并且具有由类定义的公共方法/属性,这只是一种协议。如果你愿意,你可以改变它的大部分方面。如果您曾经听说过使用元类,那么所要做的就是定义一个类,该类是与type
不同的类的实例。类唯一真正“特别”的地方是类块语法(除了默认情况下让它们按自己的方式工作的所有内置机制之外),它使您更容易创建
type
的实例。这:大致相当于:
它将安排
classdict
的所有内容成为所创建对象的属性。因此,通过
Class.attribute
可以像i = Class(); i.attribute
一样轻松地访问类属性,这一点变得非常简单。i
和Class
都是对象,并且对象具有属性。这也使您很容易理解如何在创建类之后修改它;只需像对任何其他对象一样分配它的属性!事实上,实例与用来创建它们的类没有特殊的关系。Python知道要搜索实例中未找到的属性的类的方式是通过隐藏的
__class__
属性。您可以读取它来找出这是哪个类的实例,就像任何其他属性一样:c = some_instance.__class__
。现在有一个变量c
绑定到一个类,尽管它可能与该类的名称不同。您可以使用它来访问类属性,甚至调用它来创建更多的类实例(即使您不知道它是什么类!)。你甚至可以分配给
i.__class__
来改变它是哪个类的实例!如果你这样做,什么都不会马上发生。这不是惊天动地。这意味着,当您查找实例中不存在的属性时,Python将查看__class__
的新内容。因为这包括了大多数方法,而且方法通常希望它们操作的实例处于特定的状态,所以如果您随机执行,这通常会导致错误,而且非常混乱,但这是可以完成的。如果您非常小心,那么存储在__class__
中的东西甚至不必是类对象;Python所要做的就是在某些情况下查找属性,因此,您所需要的只是一个具有正确类型属性的对象(除了Python对类或特定类的实例有挑剔的地方,还有一些警告)。现在可能已经够了。希望(如果你读了这么多的话)我没有把你弄糊涂。当你学习Python是如何工作的时候,它是很整洁的。:)
相关问题 更多 >
编程相关推荐