类和实例变量有什么区别?

时间:2012-01-22 05:01:26

标签: python class variables self

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

class Complex:
    a = 1

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

在两种情况下使用呼叫:x = Complex().a将x指定为1。

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

2 个答案:

答案 0 :(得分:100)

编写类块时,可以创建类属性(或类变量)。您在类块中指定的所有名称(包括使用def定义的方法)都将成为类属性。

创建类实例后,任何引用该实例的内容都可以在其上创建实例属性。内部方法,"当前"实例几乎总是绑定到名称self,这就是为什么你将这些视为"自变量"。通常在面向对象的设计中,附加到类的代码应该控制该类的实例的属性,因此几乎所有实例属性赋值都在方法内部完成,使用对{{1}中接收的实例的引用方法的参数。

类属性通常与 static 变量(或方法)进行比较,如Java,C#或C ++等语言。但是,如果你想要更深入的理解,我会避免将类属性视为"同样的"作为静态变量。虽然它们通常用于相同的目的,但基本概念却截然不同。更多关于这个" advanced"这一行以下的部分。

一个例子!

self

执行此块后,有一个类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 = [] ,包含3个类属性:SomeClass__init__bar

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

bar_list

发生这种情况时,会执行instance = SomeClass() SomeClass方法,在其__init__参数中接收新实例。此方法创建两个实例属性:selffoo。然后将此实例分配到foo_list变量中,因此它绑定到具有这两个实例属性的事物:instancefoo

可是:

foo_list

给出:

print instance.bar

这是怎么发生的?当我们尝试通过点语法检索属性,并且该属性不存在时,Python会通过一系列步骤来尝试完成您的请求。它将尝试的下一件事是查看实例类的类属性。在这种情况下,它在I am a class attribute called bar 中找到了一个属性bar,因此返回了该属性。

这也是方法调用如何工作的方式。例如,当您致电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)影响了bar_list通过sc1检索到的type。这通常不是你想要的。


高级研究如下。 :)

要真正理解Python,来自传统的静态类型的OO语言,如Java和C#,你必须学会​​重新思考类。

在Java中,一个类本身并不是的东西。当你写一个课程时,你会更多地宣称这个课程的所有实例都有共同之处。在运行时,只有实例(和静态方法/变量,但这些实际上只是与类关联的命名空间中的全局变量和函数,与OO无关)。类是您在源代码中记下实例在运行时将会是什么样的方式;他们只有#34;存在"在您的源代码中,而不是在正在运行的程序中。

在Python中,一个类并不特别。它就像其他任何东西一样。所以"类属性"实际上与"实例属性完全相同&#34 ;;实际上,只有"属性"。区分的唯一原因是我们倾向于使用对象,这些对象与不是类的对象不同。底层机器都是一样的。这就是为什么我说将类属性视为来自其他语言的静态变量是错误的。

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

在Python中,大多数类都是名为type的内置类的实例。正是这个类控制着类的常见行为,并以它的方式完成所有OO的操作。具有自己的属性的类的实例以及由其类定义的公共方法/属性的默认OO方式只是Python中的协议。如果需要,您可以更改它的大多数方面。如果您曾经听说过使用元类,那就是定义一个类,它是与type不同的类的实例。

唯一真正的"特别"关于类的事情(除了所有内置机制使它们以默认方式工作),是类块语法,使您更容易创建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.attribute可以像i一样轻松地访问类属性,这几乎是微不足道的。 Class__class__都是对象,对象具有属性。这也使您可以轻松了解如何在创建类之后修改类;只需像对待任何其他对象一样分配其属性!

实际上,实例与用于创建它们的类没有特别的特殊关系。 Python知道哪个类搜索在实例中找不到的属性的方式是隐藏的c = some_instance.__class__属性。您可以阅读以查找此实例的类,就像使用任何其他属性一样:c。现在你有一个变量i.__class__绑定到一个类,即使它可能没有与该类相同的名称。您可以使用它来访问类属性,甚至可以调用它来创建更多的实例(即使您不知道它是什么类!)。

你甚至可以分配给__class__来改变它是一个实例的类!如果你这样做,没有特别的事情立即发生。它并不是惊天动地的。所有这一切意味着,当您查找实例中不存在的属性时,Python将查看__class__的新内容。由于这包括大多数方法,并且方法通常期望他们正在操作的实例处于某些状态,如果您随机执行此操作通常会导致错误,并且它非常令人困惑,但它可能是完成。如果你非常小心,你在{{1}}中存储的东西甚至不必是一个类对象;所有Python都会在某些情况下查找属性,所以你需要的只是一个具有正确属性的对象(除了Python对于作为类或实例的事物感到挑剔的一些注意事项)特别的课程)。

现在可能已经够了。希望(如果你甚至读过这篇文章),我并没有太多困惑你。当你了解它的工作原理时,Python很简洁。 :)

答案 1 :(得分:10)

你所谓的“实例”变量实际上并不是一个实例变量;它是类变量。请参阅language reference about classes

在您的示例中,a似乎是一个实例变量,因为它是不可变的。在分配可变对象的情况下,可以看到它作为变量的性质:

>>> 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']

如果您使用self,那么它将是一个真正的实例变量,因此每个实例都拥有它自己的唯一a。对象的__init__函数为called when a new instance is createdself是对该实例的引用。