tags:

views:

218

answers:

4

This code produces an error message, which I found surprising:

class Foo(object):
    custom = 1
    def __init__(self, custom=Foo.custom):
        self._custom = custom

x = Foo()

Can anyone provide enlightenment?

+3  A: 

The class body is executed before the class its self is defined, so default argument values can't reference the class. Just making custom the default (without class qualification) should work.

Benjamin Peterson
+8  A: 

It's Foo that isn't visible, because you're in the middle of building it. But since you're in the same scope as custom, you can just say custom rather than Foo.custom:

class Foo(object):
    custom = 1
    def __init__(self, mycustom=custom):
        self._custom = mycustom

But note that changing Foo.custom later on won't affect the value of custom that subsequently-created Foos see:

class Foo(object):
    custom = 1
    def __init__(self, mycustom=custom):
        self._custom = mycustom

one = Foo()
Foo.custom = 2
two = Foo()
print (two._custom)  # Prints 1

By using a sentinel default value instead, you can get what you want:

class Foo(object):
    custom = 1
    def __init__(self, mycustom=None):
        if mycustom is None:
            self._custom = Foo.custom
        else:
            self._custom = mycustom

one = Foo()
Foo.custom = 2
two = Foo()
print (two._custom)  # Prints 2
RichieHindle
Nice, except comparisons with None should always be done via `is`.
Daniel Roseman
"Though that does mean that changing Foo.custom later on won't affect the value of custom that subsequently-created Foos see": Worth pointing out that it's always the case for functions that default values are created and bound only once, when the function is created, so even *if* it were possible to reference Foo.custom for a default value, it would still be the case that the default value wouldn't change if Foo.custom was changed.
Miles
Why is it allowable to reference Foo.custom in the body of __init__ but not in the initializer list? In both cases "we're in the middle of building it".
@rdm: When you *run* the body of __init__ you're no longer building Foo. It's only when you call __init__ that the reference to 'Foo.custom' in the function body actually tries to look up 'custom' in Foo. Whereas it's when you're *defining* __init__ that the default parameters are looked up, as Miles points out in his comment.
RichieHindle
@rdm: references to global variables are resolved only when the expression containing them is evaluated. Argument default values are evaluated when the function is created. The body of a function is evaluated when it is called. __init__ is called when you create a Foo instance, after the Foo class has been created.
Miles
+2  A: 

I get the following error:

Traceback (most recent call last):
  Line 1, in <module>
    class Foo(object):
  Line 3, in Foo
    def __init__(self, custom=Foo.custom):
NameError: name 'Foo' is not defined

This is because the name Foo is in the process of being defined as the __init__ function is defined, and is not fully available at that time.

The solution is to avoid using the name Foo in the function definition (I also renamed the custom paramter to acustom to distinguish it from Foo.custom):

class Foo(object):
    custom = 1
    def __init__(self, acustom=custom):
        self._custom = acustom
x = Foo()
print x._custom
Greg Hewgill
But you know you can refer to Foo.custom in the _body_ of __init__, not just in the initializer list.This strikes me as inconsistent.
"I also renamed the custom paramter to acustom to distinguish it from Foo.custom": This is probably a good idea, but it's not strictly necessary, and in some situations in Python it's even an idiom to have them be the same (mostly with nested functions inside of a loop, e.g. lambda x=x:...)
Miles
+6  A: 

What we do instead is the following

class Foo( object ):
    custom = 1
    def __init__( self, arg=None )
        self._custom = self.custom if arg is None else arg

This bypasses the confusing issue of whether or not the name Foo has been defined yet.

S.Lott
Why should it be confusing? Before an instance is created, the class should be initialized. __init__ is run when an instance is created, so class variables should be visible.
@rdm: See Miles's comment to my answer. Default parameters are determined during the creation of the function (ie. during the creation of the class in this case), not when you call it.
RichieHindle