Python中的类和实例变量有什么区别?
class Complex:
a = 1
和
class Complex:
def __init__(self):
self.a = 1
在两种情况下使用呼叫:x = Complex().a
将x指定为1。
有关__init__()
和self
的更深入的答案将不胜感激。
答案 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__
参数中接收新实例。此方法创建两个实例属性:self
和foo
。然后将此实例分配到foo_list
变量中,因此它绑定到具有这两个实例属性的事物:instance
和foo
。
可是:
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 created,self
是对该实例的引用。