简体   繁体   中英

Why does Python seem to treat instance variables as shared between objects?

I was working on a simple script today when I noticed a strange quirk in the way Python treats instance variables.

Say we have a simple object:

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

And we create two instances of this object with separate arguments:

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

print spam1
print spam2

The results are puzzling:

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

It seems like the "eggs" dictionary is shared between all the different "Spam" instances -- either that or it is overwritten every time a new instance is created. This isn't really a problem in every day life, since we can solve it by declaring the instance variable in the initialization function:

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

With the code written this way, the result is what we expect:

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

So while I'm not held up by this behavior, I don't understand why Python works this way. Can anyone shed some light on this?

As Ignacio has posted, variables which are assigned to at class scope in Python are class variables. Basically, in Python, a class is just a list of statements under a class statement. Once that list of statements finishes executing, Python scoops up any variables that were created during the course of that execution and makes a class out of them. If you want an instance variable, you actually do have to assign it to the instance.

On another note: it sounds like you may be coming to this from a Java (or Java-like) perspective. So perhaps you know that because Java requires variables to be explicitly declared, it needs to have instance variable declarations at class scope.

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

Note that only the declaration is at class scope. In other words, the memory allocated for that variable is part of the class "template," but the actual value of the variable is not.

Python doesn't have any need for variable declarations. So in the Python translation, you just drop the declaration.

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

The memory will be allocated when it's needed; only the assignment is actually written out. And that goes in the constructor, just like in Java.

That's not an instance variable, that's a class variable. The fact that it is being accessed via an instance is irrelevant; it's still the same object.

Unlike in a compiled language, the class statement in Python is actually code that executes, and creates a class object (not an instance!) in memory.

Any symbols that are defined when the class block runs belong to the class itself. This includes variables, such as the eggs variable in your first example, as well as the __init__ and __str__ methods that you define. All of those are created when the class is defined, and they are all part of the class.

Instance variables are not created until you actually create an instance of the object, and the __init__ method is run, and they have to be attributes of self .

So, when the python interpreter executes

class Spam(object):
    eggs = {}

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

it is actually building a class object at run time. It executes the code " eggs={} ", and it executes the two def statements, and it builds a class that has three attributes: eggs , __init__ and __str__ .

Later, when it executes

spam1 = Spam()

Then it creates a new instance, and runs its __init__ method. The __init__ method itself, of course, belongs to the class; it is shared between all instances, just like the eggs attribute.

The instance itself gets passed in as the self parameter, and anything you define on it belong to that instance alone. That's why self has to be passed into every class method -- in python, the methods actually belong to the class itself, and self is the only way that you have to refer to the instance.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM