6

Is there a Python equivalent for Ruby's define_method, which would allow dynamic generation of class methods? (as can be seen in Wikipedia's Ruby example code)

Rabarberski
  • 23,854
  • 21
  • 74
  • 96

3 Answers3

14

Functions are first-class objects in Python and can be assigned to attributes of a class or an instance. One way to do the same thing as in the Wikipedia example is:

colours = {"black": "000",
           "red": "f00",
           "green": "0f0",
           "yellow": "ff0",
           "blue": "00f",
           "magenta": "f0f",
           "cyan": "0ff",
           "white": "fff"}

class MyString(str):
    pass

for name, code in colours.iteritems():
    def _in_colour(self, code=code):
        return '<span style="color: %s">%s</span>' % (code, self)
    setattr(MyString, "in_" + name, _in_colour)
Jochen Ritzel
  • 104,512
  • 31
  • 200
  • 194
Sven Marnach
  • 574,206
  • 118
  • 941
  • 841
  • This will *not* work as expected. `code` will be the same for every method. Adding a bogus default parameter `code=code` like `name` will fix it. –  Mar 27 '11 at 12:52
  • Note the `code=code` in the inner function, you have to pass the variable you loop over to the function like this. – Jochen Ritzel Mar 27 '11 at 12:52
  • @delnan: This was a mistake. I meant to pass in `code=code` instead of `name=name`. – Sven Marnach Mar 27 '11 at 12:57
  • Note that you can do this without adding a dummy parameter to the function, but it's beyond the scope of the question (closures). It's just a difference of scoping rules. – Glenn Maynard Mar 27 '11 at 16:36
2

In Python, you can just set a method on a class:

>>> class Spam:
...     def __init__(self, x):
...         self.value = x
...
>>> Spam.getvalue = lambda self: self.value
>>> ham = Spam('ham')
>>> ham.getvalue()
'ham'

Fancier stuff is possible with decorators.

Fred Foo
  • 355,277
  • 75
  • 744
  • 836
0

You just assign a function as a new attribute to a class:

 def replacement_method(self):
     print self.name


 class Foo(object):
     def __init__(self, name):
         self.name = name
     # .... whatever

 setattr(Foo, "printMyName", replacement_method) # assign it
 Foo("Joe").printMyName() # call it

If you don't need a computable name (as are strings in the sample from Wikipedia), you can have it even cleaner:

 Foo.printMyName = replacement_method
9000
  • 39,899
  • 9
  • 66
  • 104