1

Having a bit of trouble wrapping my head around this one. I'm sure the answer is simple but its been driving me batty.

In the following code:

class f1():
    valnum=1

    def A1(self):
        self.valnum=5
        print self.valnum

    def B2(self):
        self.valnum=10
        print self.valnum


    funlist=[A1,B2]


x=f1
x()

x().A1()
x().funlist[1]()

A1() will execute as expected but not B2(). Ultimately, I want to have the code call a random function from the list, but the TypeError: B2() takes exactly 1 argument (0 given) keeps getting in my way. Why is this and what's the best solution.

Thanks.

Thomas Wouters
  • 130,178
  • 23
  • 148
  • 122
Dan Carreker
  • 13
  • 1
  • 3

2 Answers2

3

x().funlist[1]() isn't doing quite what you think it is.

funlist is a class attribute. It contains references to the instance methods A1 and B1, but doesn't actually associate them with any instance - they are known as unbound methods.

When you call that code, you're presumably trying to say "call the first function in funlist on the instance x". But what it's actually doing is "call the function referenced in the class attribute funlist associated with the class of x", which isn't the same thing at all.

Now, Python doesn't actually make much of a distinction in practice between bound and unbound methods. You can fake the binding by simply passing the instance in as the first parameter - so this would work:

x_instance = x()
x_instance.funlist[1](x_instance)
Daniel Roseman
  • 588,541
  • 66
  • 880
  • 895
  • 1
    Actually, `funlist` contains *functions*, not instance methods or unbound methods. Functions don't become unbound methods until they are retrieved off the class object, and when `funlist` is created the class object doesn't exist yet. – Thomas Wouters Mar 27 '12 at 10:52
  • Thanks, that's a useful clarification. – Daniel Roseman Mar 27 '12 at 11:02
  • Alternatively, move the definition of funlist into `__init__` and have it fetch instance methods and be an instance attribute. – agf Mar 27 '12 at 11:09
  • Both this and larsman's answer were great. Between both of them I got everything I needed. Thanks all. – Dan Carreker Mar 28 '12 at 07:44
0
x().funlist[1]

evaluates to the method B2, but does not bind it to the temporary object x() as x().B2 would do:

>>> f1().B2
<bound method f1.B2 of <__main__.f1 instance at 0x1cb4d40>>
>>> f1().funlist[1]
<function B2 at 0x1c3cb90>

The solution is to explicitly pass the "self" object to the method:

obj = f1()
obj.funlist[1](obj)
Community
  • 1
  • 1
Fred Foo
  • 355,277
  • 75
  • 744
  • 836