views:

326

answers:

4

Why doesn't the following work in Python?

def make_class(a):
    class A(object):
        a=a
    return A
+2  A: 

Both appear to work fine (in Python 2.5, at least):

>>> def make_class(a):
...     class A(object):
...             _a = a
...     return A
... 
>>> make_class(10)._a
10
>>> def make_class(b):
...     class B(object):
...             def get_b(self):
...                     return b
...     return B
... 
>>> make_class(10)().get_b()
10
dcrosta
I was trying to do a=a in the class, as in a default function argument, and that does not work.
joeforker
+9  A: 

works just fine:

>>> def make_class(a):
    class A(object):
        _a=a
    return A

>>> make_class('df')
<class '__main__.A'>
>>> make_class('df')._a
'df'

btw, function is not a reserved keyword in Python.

SilentGhost
my bad. I do know Python, honest!
joeforker
@joeforker, so accept SilentGhost's answer already!-)
Alex Martelli
+2  A: 

Try

def make_class(a):
    class A(object): pass
    A.a=a
    return A

The error you get (NameError: name 'a' is not defined) is because the name a in the class shadows the parameter a of the function; hence there is no a defined when you try "a=a" in your code. In other words, the right side a is not the a from the def; instead Python looks for it in the class A since a was already mentioned on the left side of the assignment.

This becomes more clean with functions:

x = 1
def a(x):
    print 'a:',x
    x = 3
    def b():
        print 'b:',x
    b()
a(2)
def c():
    x = x

Obviously, the first print should print 2, not 1, so the parameter x of a must shadow the global variable x. b is defined in a scope where x is known as a parameter of a, so the print works.

If you try to call c, however, you get UnboundLocalError: local variable 'x' referenced before assignment since Python doesn't bind global variables automatically. To fix this, you must add global x before the assignment.

Your case looks more like this:

x = 1
def a(x):
    print 'a:',x
    x = 3
    def b():
        x = x
        print 'b:',x
    b()
a(2)

While printing x worked in the example above, assignment doesn't work. This is a safety measure to make sure that variables don't leak. The solution is to use a default parameter to "copy" the variable into b's scope:

x = 1
def a(x):
    print 'a:',x
    x = 3
    def b(x=x):
        x = x
        print 'b:',x
    b()
a(2)

To solve your problem, you would need to tell Python "make the parameter a of make_class visible in A" and you would need to do that before you try to assign the field a of the class. This is not possible in Python. If you could make a visible, the assignment would change the parameter, not the field, since Python has no way to distinguish the two.

Since you can't make it visible, there is no a to read from, hence the NameError.

See here for an explanation of the scope of a name in Python.

Aaron Digulla
-1 You will get a syntax error with that code, because you open class A and don't indent the following line. Put in an indented pass line and you'll be okay.
David Berger
Oops. Added the missing "pass".
Aaron Digulla
+4  A: 

Let's use a simpler example for the same problem:

a = 'something'
def boo():
    a = a
boo()

This fails because assignments in python, without an accompanying global or nonlocal statement, means that the assigned name is local to the current scope. This happens not just in functions but also in class definitions.

This means that you can't use the same name for a global and local variable and use them both. You can use the workaround from Aaron Digulia's answer, or use a different name:

def make_class(_a):
    class A(object):
        a = _a
    return A
yairchu