【问题标题】:Why does Python seem to treat instance variables as shared between objects?为什么 Python 似乎将实例变量视为对象之间共享的?
【发布时间】:2012-02-10 05:44:48
【问题描述】:

今天我正在编写一个简单的脚本时,我注意到 Python 处理实例变量的方式有一个奇怪的怪癖。

假设我们有一个简单的对象:

class Spam(object):
    eggs = {}

    def __init__(self, bacon_type):
        self.eggs["bacon"] = bacon_type

    def __str__(self):
       return "My favorite type of bacon is " + self.eggs["bacon"]

我们用不同的参数创建这个对象的两个实例:

spam1 = Spam("Canadian bacon")
spam2 = Spam("American bacon")

print spam1
print spam2

结果令人费解:

My favorite type of bacon is American bacon
My favorite type of bacon is American bacon

似乎“eggs”字典在所有不同的“垃圾邮件”实例之间共享 - 或者每次创建新实例时都会覆盖它。这在日常生活中并不是真正的问题,因为我们可以通过在初始化函数中声明实例变量来解决它:

class Spam(object):

    def __init__(self, bacon_type):
        self.eggs = {}
        self.eggs["bacon"] = bacon_type

    def __str__(self):
        return "My favorite type of bacon is " + self.eggs["bacon"]

spam1 = Spam("Canadian bacon")
spam2 = Spam("American bacon")

print spam1
print spam2

这样写的代码,结果就是我们所期望的:

My favorite type of bacon is Canadian bacon
My favorite type of bacon is American bacon

因此,虽然我没有被这种行为所困扰,但我不明白 Python 为什么会这样工作。任何人都可以对此有所了解吗?

【问题讨论】:

标签: python oop object


【解决方案1】:

那不是实例变量,那是类变量。通过实例访问它的事实是无关紧要的;它仍然是同一个对象。

【讨论】:

  • 我明白你在说什么。但是,在定义一个类时,如何将变量声明为实例变量或类变量?我的意思是,当您查看此代码时,您怎么知道它是一个类变量?
  • 它是类定义的一部分,即Spam.eggs。当绑定为self的属性时,它不是类的属性而是实例的属性。
  • 好的,如果我理解正确的话,一旦我创建了一个 Spam 实例,我可以直接调用 Spam.eggs 并获取我刚刚创建的 Spam 实例的鸡蛋吗?感谢您的帮助!
  • 没有。这将访问类属性。如果你想尝试访问一个实例属性,那么你需要通过一个实例来访问它,尽管这保证它是一个实例属性,因为类也可能被检查。
【解决方案2】:

正如 Ignacio 所发布的,在 Python 中分配给类范围的变量是类变量。基本上,在 Python 中,一个类只是 class 语句下的语句列表。一旦该语句列表完成执行,Python 会收集在该执行过程中创建的所有变量,并从中创建一个类。如果你想要一个实例变量,你实际上必须将它分配给实例。

另一方面:听起来您可能是从 Java(或类似 Java)的角度来看的。所以也许你知道,因为 Java 需要显式声明变量,所以它需要在类范围内声明实例变量。

class Foo {
    String bar;
    public Foo() {
        this.bar = "xyz";
    }
}

请注意,只有 声明 在类范围内。换句话说,为该变量分配的 内存 是类“模板”的一部分,但变量的实际 不是。

Python 不需要任何变量声明。因此,在 Python 翻译中,您只需删除声明即可。

class Foo:
    # String bar;  <-- useless declaration is useless
    def __init__(self):
        self.bar = "xyz"

内存将在需要时分配;只有作业实际上是写出来的。这在构造函数中,就像在 Java 中一样。

【讨论】:

  • 是的,没错——当我在大学学习 OOP 时,我们是用 Java 完成的。
【解决方案3】:

与编译语言不同,Python 中的class 语句实际上是执行代码,并在内存中创建一个类对象(不是实例!)。

class 块运行时定义的任何符号都属于类本身。这包括变量,例如您的第一个示例中的eggs 变量,以及您定义的__init____str__ 方法。所有这些都是在定义类时创建的,它们都是类的一部分。

在您实际创建对象实例并运行__init__ 方法之前,不会创建实例变量,并且它们必须self 的属性。

所以,当python解释器执行时

class Spam(object):
    eggs = {}

    def __init__(self):
        <stuff>
    def __str__(self):
        <other stuff>

它实际上是在运行时构建一个类对象。它执行代码“eggs={}”,并执行两个def语句,并构建一个具有三个属性的类:eggs__init____str__

稍后,当它执行时

spam1 = Spam()

然后它创建一个新实例,并运行它的__init__ 方法。 __init__ 方法本身当然属于类;它在所有实例之间共享,就像 eggs 属性一样。

实例本身作为self 参数传入,您在其上定义的任何内容都属于该实例。这就是为什么必须将self 传递给每个类方法的原因——在python 中,这些方法实际上属于类本身,而self 是您必须引用实例的唯一方法。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-02-21
    • 1970-01-01
    • 2020-09-07
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多