1

I have the following python snippet:

class myClass:
    myVar = 'a'

    def __init__(self):
        self.myOtherVar = 'b'
        myVar = 'c'  # Gets assigned but only as a local variable.

    print myVar            # prints 'a' !
    print self.myOtherVar  # says 'self' not found

My question is this; What's the proper way to print the contents of myVar from within myClass and/or re-assign them from init?

10
  • 2
    duplicate of Static class variables in Python Commented Jul 1, 2014 at 8:41
  • For me it prints 'a' for print myVar. Commented Jul 1, 2014 at 8:46
  • Are you sure your indentation is correct? Commented Jul 1, 2014 at 9:01
  • There is nothing special about self; it is just the conventional name for the the first argument to a method, which automatically gets assigned to the instance being called. Outside of methods, you can access class-level attributes directly. Commented Jul 1, 2014 at 9:03
  • What you describe is not true. That code prints a and then an error since self doesn't exist. But myVar is not None and does not raise an error. Also, if the line with myVar raises an error then how you know that the following line raises an error too, since it wont be executed? Commented Jul 1, 2014 at 9:09

3 Answers 3

9

The problem you are facing is because you don't understand how the scoping of class declarations work. A class declaration is executed in its own scope. After the execution is completed a new class object is created and the obtained scope is attached to the class as its __dict__.

Note: the class scope is not searched from within the methods scopes! This means that you have to reference class attributes as MyClass.attribute when inside a method definition.

For example:

class MyClass:
    var = 1

    # we are executing this code as a single block
    # so you must reference the variable as is usual
    print(var)

    # default values are *not* inside the definition.
    # they are evaluated in the outer scope, so use plain "var" here
    def method(self, a_default=var):
        print(a_default)

    def other_method(self):

        # inside methods you are in a different scope
        print(MyClass.var)

        # equivalent *if* no "var" instance attributes exists
        print(self.var)

Note: since the class doesn't still exist when executing its declaration you cannot refer to MyClass at the "top level" of MyClass declaration:

class MyClass:
    var = 1
    print(MyClass.var)   # error: MyClass still doesn't exist.

A side effect of this, is that the following code:

class MyClass:
    x = 1
    results = list(i+x for i in range(10))

Produces:

NameError                                 Traceback (most recent call last)
<ipython-input-6-f1d4417b2e52> in <module>()
----> 1 class MyClass:
      2     x = 1
      3     results = list(i+x for i in range(10))
      4 

<ipython-input-6-f1d4417b2e52> in MyClass()
      1 class MyClass:
      2     x = 1
----> 3     results = list(i+x for i in range(10))
      4 

<ipython-input-6-f1d4417b2e52> in <genexpr>(.0)
      1 class MyClass:
      2     x = 1
----> 3     results = list(i+x for i in range(10))
      4 

NameError: name 'x' is not defined

Because generator expressions (and list-comprehensions in python3) are, in fact, considered functions with their own scope. Since the class scope isn't searched from inner function scopes the x cannot be found.

You can word around this using a function definition and default values:

class MyClass:
    x = 1
    def _make_results(x=x):
        return list(i+x for i in range(10))
    results = _make_results()
    del _make_results    # otherwise it would be added as a method.
    # or:
    results = (lambda x=x: list(i+x for i in range(10)))()

This isn't usually a problem since class definitions rarely contain anything other than method definitions and a few constants.


There are already a few questions on SO about class scopes:

Sign up to request clarification or add additional context in comments.

3 Comments

An exemplary answer by all means. However I need one last clarification; You said that I cannot refer to MyClass at the "top level" of MyClass declaration but instead I can do it from within one of its method declarations. This simply begs the question: When exactly is a class considered alive so that I can start refering to it? And I iterate - not the instance but the class.
As soon as the class block ends. You can use it it in methods, because a function's body is only evaluated once you call it. Which is after the class definition's parsed and executed.
@Konos5 A class is "alive" after its body is evaluated. The evaluation of a method definition means that its code is compiled. So inside a method there is no problem referring to MyClass because, since the method isn't executed, the reference will be only evaluated after the class was completely created. As far as the method is concerned MyClass is just a global variable. In fact you can access the class also as self.__class__ without referencing MyClass. However self.__class__ may not be MyClass if you create some subclasses.
0

self.var will:

  1. give var in self.__dict__ if present
  2. give var in self.__class__.__dict__ if present
  3. AttributeError

So use this or self.__class__.var if you want to access the static variable minding inheritance. If you extend myClass, the children instances will access the static variable in the child class.

If you want to access the static variable in myClass even when called from descendants, use myClass.var.

As for reassigning them, this must be done explicitly on the class object, or the assignment will just target the instance.

4 Comments

No. From within myClass I can't do print myClass.var. Python spits out an error saying the myClass is not defined.
@Konos5 You can't print within a class, but not within its method. Could just please read the duplicate I've given link to? It contains everything there is to know on static variables in Python.
@BartoszKP I'm looking at it as we speak. However I still can't seem to find a clear way of reassigning the static variavle myVar from within myClass's __init__ method...
@Konos5 It's the same syntax as everywhere else in that thread: myClass.myVar. It doesn't matter from where you do it. It's a variable inside that class, . always means reaching inside of something.
-4
class myClass:
    myVar = 'a'

    def __init__(self):
        self.myOtherVar = 'b'

    print myVar  # -> 'a'

    class EmptyClass: pass
    s = EmptyClass()
    __init__(s)

    myVar = s.myOtherVar
    print myVar  # -> 'b'

print myClass.myVar  # -> 'b'

2 Comments

Care to explain what exactly did you do here? I am looking for the functionality you demostrate but in a more "clean" way. Also at some point you wrote __init__(s)... which class that init refers to?
We have to call myClass.__init__ with some instance to get 'b'. So I created an empty class called EmptyClass then I used instance of EmptyClass to call myClass.__init__ by __init__(s) so __init__ refers to myClass

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.