python中静态变量和实例变量之间的差异。它们是否存在?

Differences between static and instance variables in python. Do they even exist?

随机类定义:

1
2
class ABC:
    x = 6

先为ABC实例设置一些值,然后为静态变量设置:

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

然后打印结果:

1
2
print abc.x
print ABC.x

哪些版画

1
2
2
5

现在,我真的不知道发生了什么,因为如果我在类定义x=6中替换"pass",它将只输出相同的东西。我的问题是,在Python的类定义中定义一个变量的目的是什么?如果没有这样做,任何人都可以在任何时候设置任何变量?

另外,python知道实例变量和静态变量之间的区别吗?据我所见,我会这么说。


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

在python中创建类的实例时,例如在示例中调用abc():

1
abc = ABC()

python创建一个新的空对象,并将其类设置为abc。如果有的话,它会叫__init__()。最后返回对象。

当您请求一个对象的属性时,它首先在实例中查找。如果找不到它,它将在实例的类中查找。然后在基类中,依此类推。如果它从未找到任何定义了属性的人,它就会抛出异常。

当分配给对象的属性时,如果对象还没有该属性,它将创建该属性。然后将属性设置为该值。如果对象已经有一个具有该名称的属性,它将删除对旧值的引用,并获取对新值的引用。

这些规则使你观察到的行为很容易预测。在这行之后:

1
abc = ABC()

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

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

1
2
3
4
5
6
class ABC:
  x = 6

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

是的:它印两张五张的。您可能期望它抛出一个attributeError异常。但python在类中找到了属性——即使它是在创建实例之后添加的。

这种行为真的会给你带来麻烦。Python中的一个典型初学者错误:

1
2
3
4
5
6
7
8
class ABC:
  x = []

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

b = ABC()
print(b.x)

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

1
2
3
4
5
6
7
8
9
class ABC:
  def __init__(self):
    self.x = []

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

b = ABC()
print(b.x)

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

要回答您的确切问题:

My question is, what is the purpose of defining a variable in the class definition in python if it seems like i can anyone set at any time any variable without doing so?

我假设这意味着"为什么我应该在类内而不是在__init__方法内分配成员?"

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

作为一个纯粹的风格问题,它是较短的代码,因为你没有所有的自我实例。到处都是。除此之外,没有什么区别。但是,在__init__方法中分配属性可以确保它们是明确的实例变量。

我自己也不太一致。我唯一确定要做的就是分配我不想在__init__方法中共享的所有可变对象。

Also, does python know the difference between instance and static variables? From what I saw, I'd say so.

Python类没有类静态变量,如C++。只有属性:类对象的属性和实例对象的属性。如果您请求一个属性,而实例没有该属性,那么您将从类中获得该属性。

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

1
2
3
4
5
_x = 3
class ABC:
  def method(self):
    global _x
    # ...

从本质上讲,这不是课程的一部分。但这是一个常见的Python习惯用法。


1
2
3
4
5
class SomeClass:
  x=6  # class variable

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

声明类范围的变量有一个优点:它是类范围变量的默认值。像您在其他一些语言中所想的"静态"变量一样,考虑类范围变量。


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

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

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

1
2
3
4
5
6
7
In [4]: ABC.__dict__
Out[4]: {'__doc__': None, '__module__': '__main__', 'x': 6}

In [5]: abc=ABC()

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

注意,如果"x"不在ABC.__dict__中,那么将搜索ABC超类的__dict__。因此,abc.x是从ABC继承的:

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

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

1
2
3
4
5
6
7
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}

当然,如果我们希望:

1
2
3
4
In [10]: ABC.x = 5

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

类级变量(在其他语言中称为"static")归类所有,并由类的所有实例共享。

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

然而。

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

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

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


python区分了这两者。目的可以是多个,但一个例子是:

1
2
3
4
5
6
7
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中。它们都存储在不同的地方——在类对象中,或者在实例对象中,您确实可以将它与一些类似于C++或C语言的OO语言中的静态和实例变量进行比较。

在该示例中,如果执行以下操作:

1
print token.id

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

1
2
x = token(10)
print x.id

将提供该实例的ID。

每个人都可以在实例或类中放置其他属性,这是正确的,但这并不有趣,因为类代码并不打算使用它们。上面一个例子的意思是类代码使用它们。


"static"或python中的"class属性"的好处是类的每个实例都可以访问同一个class属性。对于您可能知道的实例属性,这不是真的。

例如:

1
2
3
4
5
6
7
8
9
10
11
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属性来设置。

棘手的部分是当你有一个类属性和实例属性命名相同的东西。这需要了解发动机罩下发生了什么。

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

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

如果您随后使用相同的名称设置实例属性,则可能会得到一些令人困惑的输出。例如:

1
2
3
4
inst.b = 10
inst.__dict__  #=> {'b': 10}
A.b         # => 5
inst.b      # => 10

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