python中静态变量和实例变量之间的差异。他们甚至存在吗?

时间:2009-11-08 17:36:12

标签: python

随机类定义:

class ABC:
    x = 6

首先为abc实例设置一些值,稍后为静态变量设置一些值:

abc = ABC()
abc.x = 2
ABC.x = 5

然后打印结果:

print abc.x
print ABC.x

打印

2
5

现在,我真的不知道发生了什么,因为如果我在类定义x = 6中替换“pass”,它只会输出相同的东西。我的问题是,在python的类定义中定义变量的目的是什么,如果看起来我可以随时设置任何变量而不这样做?

另外,python是否知道实例和静态变量之间的区别?从我看到的情况来看,我会这样说。

6 个答案:

答案 0 :(得分:19)

警告:以下内容过于简单化;我忽略了__new__()和一些其他特殊的类方法,并且手工操作了很多细节。但是这个解释会让你在Python中走得很远。

在Python中创建类的实例时,例如在示例中调用 ABC()

abc = ABC()

Python创建一个新的空对象并将其设置为 ABC 。如果有的话,它会调用__init__()。最后它返回对象。

当您要求对象的属性时,首先它会查看实例。如果找不到它,它会查看实例的类。然后在基类(es)等。如果它从未找到任何定义了属性的人,则会抛出异常。

当您指定对象的属性时,如果该对象尚未具有该属性,则会创建该属性。然后它将属性设置为该值。如果对象已经具有该名称的属性,则会删除对旧值的引用,并引用新值。

这些规则使您观察到的行为易于预测。在这一行之后:

abc = ABC()

只有 ABC 对象(该类)具有名为 x 的属性。 abc 实例还没有自己的 x ,所以如果你要求的话,你将获得 ABC.x 。但是,然后在类和对象上重新分配属性 x 。当您随后检查这些属性时,您会观察到您放置的值仍然存在。

现在您应该能够预测此代码的作用:

class ABC:
  x = 6

a = ABC()
ABC.xyz = 5
print(ABC.xyz, a.xyz)

是的:它会打印两个五。您可能希望它抛出AttributeError异常。但是Python在类中找到了属性 - 即使在创建实例之后添加了

这种行为确实会让你陷入困境。 Python中一个经典的初学者错误:

class ABC:
  x = []

a = ABC()
a.x.append(1)

b = ABC()
print(b.x)

将打印 [1] 。 ABC()的所有实例共享相同的列表。你可能想要的是:

class ABC:
  def __init__(self):
    self.x = []

a = ABC()
a.x.append(1)

b = ABC()
print(b.x)

这将按预期打印一个空列表。

回答您的确切问题:

  

我的问题是,在python的类定义中定义变量的目的是什么,如果看起来我可以随时设置任何变量而不这样做?

我认为这意味着“我为什么要在课堂内分配成员,而不是在__init__方法内?”

实际上,这意味着实例没有自己的属性副本(或者至少还没有)。这意味着实例更小;它还意味着访问属性更慢。它还意味着实例都为该属性共享相同的值,在可变对象的情况下,可能是也可能不是您想要的。最后,这里的赋值意味着值是类的属性,这是在类上设置属性的最直接的方法。

作为一个纯粹的风格问题,它是更短的代码,因为你没有全部 self。的所有实例。除此之外,它没有太大的区别。但是,在__init__方法中分配属性可确保它们是明确的实例变量。

我自己并不是非常一致。我唯一要做的就是分配我不希望在__init__方法中共享的所有可变对象。

  

另外,python是否知道实例和静态变量之间的区别?从我看到的情况来看,我会这样说。

Python类没有像C ++那样的类静态变量。只有属性:类对象的属性和实例对象的属性。如果你要求一个属性,并且实例没有它,你将从类中获取属性。

Python中类静态变量的最接近的近似值是隐藏模块属性,如下所示:

_x = 3
class ABC:
  def method(self):
    global _x
    # ...

这不是班级本身的一部分。但这是一种常见的Python习语。

答案 1 :(得分:12)

class SomeClass:
  x=6  # class variable

  def __init__(self):
    self.y = 666  # instance variable

声明一个类范围的变量是有道理的:它是一个默认值。考虑类范围的变量,就像在其他一些语言中想到“静态”变量一样。

答案 2 :(得分:3)

类级变量(在其他语言中称为“静态”)由类拥有,并由类的所有实例共享。

实例变量是该类的每个不同实例的一部分。

然而。

您可以随时添加新的实例变量。

因此,获取abc.x需要首先检查实例变量。如果没有实例变量,它将尝试类变量。

设置abc.x将创建(或替换)实例变量。

答案 3 :(得分:3)

每个对象都有一个__dict__。 ABC类及其实例abc都是对象,因此每个都有自己独立的__dict__

In [3]: class ABC:
   ...:     x=6

注意ABC.__dict__有一个'x'键:

In [4]: ABC.__dict__
Out[4]: {'__doc__': None, '__module__': '__main__', 'x': 6}

In [5]: abc=ABC()

In [6]: abc.__dict__
Out[6]: {}

请注意,如果'{'不在abc.__dict__中,则会搜索abc超类的__dict__。因此abc.xABC的“继承”:

In [14]: abc.x
Out[14]: 6

但是如果我们设置abc.x,那么我们正在更改abc.__dict__,而不是ABC.__dict__

In [7]: abc.x = 2

In [8]: abc.__dict__
Out[8]: {'x': 2}

In [9]: ABC.__dict__
Out[9]: {'__doc__': None, '__module__': '__main__', 'x': 6}

当然,如果我们愿意,我们可以更改ABC.__dict__

In [10]: ABC.x = 5

In [11]: ABC.__dict__
Out[11]: {'__doc__': None, '__module__': '__main__', 'x': 5}

答案 4 :(得分:2)

Python对两者进行了区分。目的可能是多重的,但一个例子就是:

class token(object):
    id = 0

    def __init__(self, value):
        self.value = value
        self.id = token.id
        token.id += 1

这里,类变量token.id在每个新实例中自动递增,并且此实例可以同时获取唯一ID,该ID将放在self.id中。两者都存储在不同的地方 - 在类对象中,或者在实例对象中,您确实可以将它与一些OO语言(如C ++或C#)中的静态和实例变量进行比较。

在该示例中,如果您这样做:

print token.id

您将看到要分配的下一个ID,而:

x = token(10)
print x.id

将给出该实例的id。

每个人也可以在实例或类中放置其他属性,这是正确的,但这不会有趣,因为类代码不打算使用它们。如上所述的例子是类代码使用它们。

答案 5 :(得分:0)

“静态”或Python中的“类属性”的好处是类的每个实例都可以访问相同的类属性。对于您可能知道的实例属性,情况并非如此。

以例如:

class A(object):
    b = 1

A.b        # => 1
inst = A()
inst2 = A()
inst.b     # => 1
inst2.b    # => 1
A.b = 5
inst.b     # => 5
inst2.b    # => 5

正如您所看到的,该类的实例可以访问class属性,该属性可以通过指定类名称然后指定class属性来设置。

棘手的部分是当你有一个类属性和实例属性命名相同的东西。这需要了解幕后发生的事情。

inst.__dict__  # => {}
A.__dict__     # => {..., 'b': 5}

注意实例没有b作为属性?上面,当我们调用inst.b Python实际上检查inst.__dict__属性时,如果找不到它,则它会搜索A.__dict__(类的属性)。当然,当Python在类的属性中查找b时,会找到并返回它。

如果然后设置具有相同名称的实例属性,则可能会出现一些令人困惑的输出。 例如:

inst.b = 10
inst.__dict__  #=> {'b': 10}
A.b         # => 5
inst.b      # => 10

您可以看到该类的实例现在具有b实例属性,因此Python返回该值。