1

It seems that in Python, to declare a variable in a class, it is static (keeps its value in the next instances). What better way to get around this problem?

class Foo():
  number = 0
  def set(self):
    self.number = 1


>>> foo = Foo()
>>> foo.number
0
>>> foo.set()
>>> foo.number
1
>>> new_foo = Foo()
>>> new_foo.number
1
6
  • 1
    While there is a shared Foo.number and you usually don't want that, it's not as bad as in your example. At the end new_foo.number will be 0. Commented Aug 28, 2012 at 14:49
  • 2
    Did you test the code you posted? new_foo.number should not be 1 in this case. Commented Aug 28, 2012 at 14:49
  • 1
    If I run your code new_foo.number is zero. As you want it to be! Where is the problem? Commented Aug 28, 2012 at 14:51
  • "What better way to get around this problem?" - Which part of this behaviour do you consider a problem? And what is the desired result after getting around it? Commented Aug 28, 2012 at 14:56
  • foo = Foo() provides an instance without a number attribute. When you ask for self.number since there is not the instance one you get the class one. When you set self.number the attribute number is given to self and Foo.number is kind of overriden... Commented Aug 28, 2012 at 14:57

3 Answers 3

8

Variables defined at the class level are indeed "static", but I don't think they work quite the way you think they do. There are 2 levels here which you need to worry about. There are attributes at the class level, and there are attributes at the instance level. Whenever you do self.attribute = ... inside a method, you're setting an attribute at the instance level. Whenever python looks up an attribute, it first looks at the instance level, if it doesn't find the attribute, it looks at the class level.

This can be a little confusing (especially if the attribute is a reference to a mutable object). consider:

class Foo(object):
    attr = []  #class level attribute is Mutable
    def __init__(self):
        # in the next line, self.attr references the class level attribute since
        # there is no instance level attribute (yet)
        self.attr.append('Hello')
        self.attr = []
        # Now, we've created an instance level attribute, so further appends will
        # append to the instance level attribute, not the class level attribute.
        self.attr.append('World')

a = Foo()
print (a.attr)  #['World']
print (Foo.attr) #['Hello']
b = Foo()
print (b.attr)  #['World']
print (Foo.attr) #['Hello', 'Hello']

As others have mentioned, if you want an attribute to be specific to an instance, just initialize it as an instance attribute in __init__ (using self.attr = ...). __init__ is a special method which is run whenever a class is initialized (with a few exceptions that we won't discuss here).

e.g.

class Foo(object):
   def __init__(self):
       self.attr = 0
Sign up to request clarification or add additional context in comments.

1 Comment

Good answer. To clarify, to "if you want an attribute to be specific to an instance, just initialize it as an instance attribute in init." I would add "and make it an instance attribute using 'self.'". Otherwise, the attribute will "disappear" when init exits.
1

Just leave the declaration out. If you want to provide default values for the variables, initialize them in the __init__ method instead.

class Foo(object):
    def __init__(self):
        self.number = 0

    def set(self):
        self.number = 1

>>> foo = Foo()
>>> foo.number
0
>>> foo.set()
>>> foo.number
1
>>> new_foo = Foo()
>>> new_foo.number
0

Edit: replaced last line of the above snippet; it used to read 1 although it was just a typo on my side. Seems like it has caused quite a bit of confusion while I was away.

12 Comments

This doesn't work as advertised - new_foo.number is 0, not 1. Are you trying to make it static?
But not the way your example says it does.
@delnan, my example? I suppose Shankar wanted the 0 1 1 behavior... and I do provide it
I did. Several times. I also copied and pasted it into the interactive prompt. I get 0 1 0, and no language change in the last ten years of so has any effect on the outcome. Now I even put it onto ideone for you to see. Try it out instead of claiming. I'd downvote you again if I could.
@ShankarCabus Your code it not equivalent to jimifiki (Foo.number instead of self.number in set). It is probably not the cleanest solution for what you actually want, but it does give 0 1 0.
|
0

You maybe want to change the class attribute:

class Foo():
    number = 0
    def set(self):
        Foo.number = 1

instead of overriding it!

1 Comment

Same problem, @jimifiki. When instantiate a new Foo, number is 1, not 0.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.