Can I use a class attribute as a default value for an instance method?

Question:

I would like to use a class attribute as a default value for one of the arguments to my class’s __init__ method. This construct raises a NameError exception, though, and I don’t understand why:

class MyClass():
    __DefaultName = 'DefaultName'
    def __init__(self, name = MyClass.__DefaultName):
        self.name = name

Why does this fail, and is there a way to do this that works?

Asked By: manniongeo

||

Answers:

That’s because, according to the documentation:

Default parameter values are evaluated when the function definition
is executed.
This means that the
expression is evaluated once, when the
function is defined

When __init__() is defined, the definition of MyClass is incomplete, as it’s still being parsed, so you can’t refer to MyClass.__DefaultName yet. One way to work around that is to pass a special unique value to __init__(), such as None:

def __init__(self, name=None):
    if name is None:
        name = MyClass.__DefaultName
    # ...
Answered By: Frédéric Hamidi

An important thing to keep in mind with Python is when different bits of code are executed, along with the fact that class and def statements are executed when seen, not just stored away for later. With a def it’s a little easier to understand because the only thing being executed is whatever is between the ()s — so in your code above

def __init__( SELF, name = MyClass.__DefaultName ):

when Python sees MyClass.__DefaultName it tries to find it first in the local namespace, then the module (aka global) namespace, and finally in builtins.

What is the local namespace when executing a class statement? That’s the fun part — it is what will become the class namespace, but at the moment it is anonymous — so you can’t reference it by name (aka MyClass in your code) but you can directly reference anything that has already been defined… and what has been defined? In your class example only one thing — __DefaultName. So your __init__ should look like this:

def __init__( SELF, name=__DefaultName ):

Keep in mind that you can’t change MyClass._MyClass__DefaultName later and have those changes show up in new instances. Why? Because the __init__ def is executed only once, and whatever value __DefaultName had at that moment has been stored away and will always be used — unless you directly specify a new value when you create a new instance:

my_instance = MyClass(name='new name')
Answered By: Ethan Furman
Categories: questions Tags:
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.