Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dynamically added static methods to a Python class

I found really good example how to add new method to the class dynamically (transplant class):

def say(host, msg):
   print '%s says %s' % (host.name, msg)

def funcToMethod(func, clas, method_name=None):
   setattr(clas, method_name or func.__name__, func)

class transplant:
   def __init__(self, method, host, method_name=None):
      self.host = host
      self.method = method
      setattr(host, method_name or method.__name__, self)

   def __call__(self, *args, **kwargs):
      nargs = [self.host]
      nargs.extend(args)
      return apply(self.method, nargs, kwargs)

class Patient:
   def __init__(self, name):
      self.name = name

if __name__ == '__main__':
   jimmy = Patient('Jimmy')
   transplant(say, jimmy, 'say1')
   funcToMethod(say, jimmy, 'say2')

   jimmy.say1('Hello')
   jimmy.say2(jimmy, 'Good Bye!')

But I don't understand, how to modify it for adding static methods. Can someone help me?

like image 572
SkyFox Avatar asked Feb 01 '13 11:02

SkyFox


2 Answers

I don't see a staticmethod here. The say function is expecting two arguments, and the first argument, host, appears to be the instance of the class.

So it seems like you are simply trying to attach a new method to a class. That can be done without funcToMethod or transplant:

def say(self, msg):
   print '%s says %s' % (self.name, msg)

class Patient:
   def __init__(self, name):
      self.name = name

if __name__ == '__main__':
   jimmy = Patient('Jimmy')
   Patient.say = say
   jimmy.say('Hello')

yields

Jimmy says Hello

If you did want to attach a staticmethod, then, as MartijnPieters answered, use the staticmethod decorator:

def tell(msg):
   print(msg)

if __name__ == '__main__':
   jimmy = Patient('Jimmy')
   Patient.tell = staticmethod(tell)
   jimmy.tell('Goodbye')

yields

Goodbye

The above shows how new methods can be attached to a class without funcToMethod or transplant. Both funcToMethod and transplant try to attach functions to instances of the class rather than the class itself. This is wrong-headed, which is why it requires contortions (like having to pass jimmy as an argument in jimmy.say2(jimmy, 'Good Bye!')) to make it work. Methods should be defined on the class (e.g. Patient), not on the instance (e.g. jimmy).

transplant is particularly horrible. It uses a class when a function would suffice. It uses the archaic apply instead of the modern self.method(*nargs, **kwargs) syntax, and ignores the PEP8 convention for camelCasing class names. In its defense, it was written over ten years ago. But fundamentally, what makes it an anathema to good programming is that you just don't need it.

like image 116
unutbu Avatar answered Oct 31 '22 02:10

unutbu


All you need to do is wrap the function in a staticmethod() call:

say = staticmethod(say)

or apply it as a decorator to the function definition:

@staticmethod
def say(host, msg):
    # ...

which comes down to the same thing.

Just remember; the @decorator syntax is just syntactic sugar for writing target = decorator(target), where target is the decorated object.

like image 24
Martijn Pieters Avatar answered Oct 31 '22 01:10

Martijn Pieters