6

I've got a background in Python (though entirely self-taught, so I might have some bad habits or misconceptions), and I'm trying to learn Ruby to broaden my scope.

I was reading through some comparisons, and saw a lot of assertions that "Python can't do metaprogramming" (or, less inflammatorily, "Python can't do metaprogramming so simply as Ruby"). So I went away and quickly read about metaprogramming, and came away with the impression that it is, basically, editing the methods/behaviour of your classes/objects during runtime (please do correct me if I'm incorrect!).

I was under the impression that, since Python is dynamic, that shouldn't be a problem. However, I ran the following test code, which didn't give the response I expected:

>>> class foo:
...     def make_hello_method(self):
...             def hello(obj):
...                     print 'hello'
...             self.hello = hello
...
>>> f = foo()
>>> f.hello()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: foo instance has no attribute 'hello'
>>> f.make_hello_method()
>>> f.hello()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: hello() takes exactly 1 argument (0 given)

I was under the impression that every method of an object was automatically passed the object itself as the first argument (hence the constant requirements to define object methods as (self, [...])). How come f isn't being passed to hello()?

scubbo
  • 4,969
  • 7
  • 40
  • 71

2 Answers2

5

You need to make it an instancemethod. There's a class to do that in the types module. This will work:

self.hello = types.MethodType(hello, self)
Daniel Roseman
  • 588,541
  • 66
  • 880
  • 895
  • Ah, gotcha. Calling type() on `f.make_hello_method` and on `f.hello` made the difference clear. Thank you! – scubbo Apr 05 '14 at 20:51
  • @scubbo: If you wanted to add the method to the class rather than just the one instance, you'd need to instead use something like `setattr(foo, "hello", hello)`, which would add it to class, all existing instances, as well as any new ones that are created. – martineau Apr 05 '14 at 21:13
2

Seeing as you are using Python 2, you could use the new module to make this work:

self.hello = new.instancemethod(hello, self, foo)
anon582847382
  • 19,907
  • 5
  • 54
  • 57