动态添加静态方法到Python类

11 投票
3 回答
8288 浏览
提问于 2025-04-17 14:25

我在这里找到一个很好的例子,教我们怎么动态地给一个类添加新方法(也就是把方法“移植”到类里):

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!')

但是我不太明白,怎么修改这个例子来添加静态方法。有人能帮我吗?

3 个回答

0

好的,下面的代码可以正常工作,也就是说它在Patient类上添加了一个静态方法,我觉得这正是提问者想要的。

def tell(msg):
   print(msg)

...

funcToMethod(tell, Patient, 'say3')

...

Patient.say3('Bye!')
6

我在这里没有看到静态方法。say这个函数需要两个参数,而第一个参数host看起来是这个类的实例。

所以看起来你只是想给一个类添加一个新方法。其实可以不使用funcToMethod或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')

这样就可以了

Jimmy says Hello

如果你想添加一个静态方法,那么正如MartijnPieters所说的,使用staticmethod装饰器:

def tell(msg):
   print(msg)

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

这样就可以了

Goodbye

上面的内容展示了如何在不使用funcToMethodtransplant的情况下,将新方法添加到一个类中。funcToMethodtransplant都是试图将函数附加到类的实例上,而不是类本身。这种做法是错误的,所以需要一些复杂的操作(比如在jimmy.say2(jimmy, 'Good Bye!')中传递jimmy作为参数)才能让它工作。方法应该在类上定义(例如Patient),而不是在实例上(例如jimmy)。

transplant特别糟糕。它使用了一个类,而其实用一个函数就足够了。它使用了过时的apply,而不是现代的self.method(*nargs, **kwargs)语法,并且忽略了PEP8关于类名使用驼峰命名法的规范。为了给它辩护,它是在十多年前写的。但从根本上说,它之所以被认为是糟糕编程的反面,是因为你根本不需要它。

23

你只需要把这个函数放在一个 staticmethod() 的调用里就可以了:

say = staticmethod(say)

或者把它作为装饰器应用到函数定义上:

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

这两种方式其实是一样的。

记住;@decorator 这种写法只是为了让代码看起来更简洁,其实它的意思是 target = decorator(target),这里的 target 就是被装饰的对象。

撰写回答