开发者

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

A random class definition:

class ABC:
    x = 6

Setting some values, first for the a开发者_运维百科bc instance, later for the static variable:

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

and then print the results:

print abc.x
print ABC.x

which prints

2
5

Now, I don't really get what is going on, because if i replace in the class definition x = 6 for "pass", it will just output the same thing. 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?

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


Warning: the following is an oversimplification; I'm ignoring __new__() and a bunch of other special class methods, and handwaving a lot of details. But this explanation will get you pretty far in Python.

When you create an instance of a class in Python, like calling ABC() in your example:

abc = ABC()

Python creates a new empty object and sets its class to ABC. Then it calls the __init__() if there is one. Finally it returns the object.

When you ask for an attribute of an object, first it looks in the instance. If it doesn't find it, it looks in the instance's class. Then in the base class(es) and so on. If it never finds anybody with the attribute defined, it throws an exception.

When you assign to an attribute of an object, it creates that attribute if the object doesn't already have one. Then it sets the attribute to that value. If the object already had an attribute with that name, it drops the reference to the old value and takes a reference to the new one.

These rules make the behavior you observe easy to predict. After this line:

abc = ABC()

only the ABC object (the class) has an attribute named x. The abc instance doesn't have its own x yet, so if you ask for one you're going to get the value of ABC.x. But then you reassign the attribute x on both the class and the object. And when you subsequently examine those attributes you observe the values you put there are still there.

Now you should be able to predict what this code does:

class ABC:
  x = 6

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

Yes: it prints two fives. You might have expected it to throw an AttributeError exception. But Python finds the attribute in the class--even though it was added after the instance was created.

This behavior can really get you in to trouble. One classic beginner mistake in Python:

class ABC:
  x = []

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

b = ABC()
print(b.x)

That will print [1]. All instances of ABC() are sharing the same list. What you probably wanted was this:

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

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

b = ABC()
print(b.x)

That will print an empty list as you expect.

To answer your exact questions:

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?

I assume this means "why should I assign members inside the class, instead of inside the __init__ method?"

As a practical matter, this means the instances don't have their own copy of the attribute (or at least not yet). This means the instances are smaller; it also means accessing the attribute is slower. It also means the instances all share the same value for that attribute, which in the case of mutable objects may or may not be what you want. Finally, assignments here mean that the value is an attribute of the class, and that's the most straightforward way to set attributes on the class.

As a purely stylistic matter it's shorter code, as you don't have all those instances of self. all over. Beyond that it doesn't make much difference. However, assigning attributes in the __init__ method ensures they are unambiguously instance variables.

I'm not terribly consistent myself. The only thing I'm sure to do is assign all the mutable objects that I don't want shared in the __init__ method.

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

Python classes don't have class static variables like C++ does. There are only attributes: attributes of the class object, and attributes of the instance object. And if you ask for an attribute, and the instance doesn't have it, you'll get the attribute from the class.

The closest approximation of a class static variable in Python would be a hidden module attribute, like so:

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

It's not part of the class per se. But this is a common Python idiom.


class SomeClass:
  x=6  # class variable

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

There is virtue in declaring a class scoped variable: it serves as default for one. Think of class scoped variable as you would think of "static" variables in some other languages.


Python makes a distinction between the two. The purpose could be multiple, but one example is this:

class token(object):
    id = 0

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

Here, the class variable token.id is automatically incremented at each new instance, and this instance can take a unique ID at the same time, which will be put in self.id. Both are stored at different places - in the class object, or in the instance object, you can indeed compare that to static and instance variables in some OO languages like C++ or C#.

In that example, if you do:

print token.id

you will see the next ID to be assigned, whereas:

x = token(10)
print x.id

will give the id of that instance.

Everyone can also put other attributes in an instance or in a class, that's right, but that wouldn't be interesting since the class code is not intended to use them. The interest with an exemple as above is that the class code uses them.


A class-level variable (called "static" in other languages) is owned by the class, and shared by all instances of the class.

A instance variable is part of by each distinct instance of the class.

However.

You can add a new instance variable any time you want.

So getting abc.x requires first checking for an instance variable. If there is no instance variable, it will try the class variable.

And setting abc.x will create (or replace) an instance variable.


Every object has a __dict__. The class ABC and its instance, abc, are both objects, and so each has their own separate __dict__:

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

Notice ABC.__dict__ has a 'x' key:

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

In [5]: abc=ABC()

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

Notice that if 'x' is not in abc.__dict__, then the __dict__'s of abc's superclass(es) are searched. So abc.x is "inherited" from ABC:

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

But if we set abc.x then we are changing abc.__dict__, not 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}

Of course, we can change ABC.__dict__ if we wish:

In [10]: ABC.x = 5

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


The benefit of a "static" or in Python a "class attribute" is that each instance of the class will have access to the same class attribute. This is not true for instance attributes as you may be aware.

Take for example:

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

As you can see the instance of the class has access to the class attribute which can be set by specifying the class name and then the class attribute.

The tricky part is when you have a class attribute and instance attribute named the same thing. This requires an understanding of what is going on under the hood.

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

Notice how the instance does not have b as an attribute? Above, when we called inst.b Python actually checks inst.__dict__ for the attribute, if it cannot be found, then it searches A.__dict__ (the class's attributes). Of course, when Python looks up b in the class's attributes it is found and returned.

You can get some confusing output if you then set an instance attribute with the same name. For example:

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

You can see that the instance of the class now has the b instance attribute and therefore Python returns that value.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜