1.基本思想:使用额外的类来保存方法
我找到了一种有意义的工作方式:
首先,我们定义这样一个BaseClass:
class MethodPatcher:
@classmethod
def patch(cls, target):
for k in cls.__dict__:
obj = getattr(cls, k)
if not k.startswith('_') and callable(obj):
setattr(target, k, obj)
现在我们有了一个原始类:
class MyClass(object):
def a(self):
print('a')
然后我们定义要在新Patcher
类上添加的新方法:
_
(在这种情况下,不要让方法名称以 an 开头)
class MyPatcher(MethodPatcher):
def b(self):
print('b')
然后调用:
MyPatcher.patch(MyClass)
因此,您会发现新方法b(self)
已添加到原始方法中MyClass
:
obj = MyClass()
obj.a() # which prints an 'a'
obj.b() # which prints a 'b'
2. 让语法不那么冗长,我们使用类装饰器
现在,如果我们有MethodPatcher
贴花,我们需要做两件事:
- 定义一个子类
ChildClass
,ModelPatcher
其中包含要添加的额外方法
- 称呼
ChildClass.patch(TargetClass)
所以我们很快发现第二步可以通过使用装饰器来简化:
我们定义一个装饰器:
def patch_methods(model_class):
def do_patch(cls):
cls.patch(model_class)
return do_patch
我们可以像这样使用它:
@patch_methods(MyClass)
class MyClassPatcher(MethodPatcher):
def extra_method_a(self):
print('a', self)
@classmethod
def extra_class_method_b(cls):
print('c', cls)
# !!ATTENTION!! the effect on declaring staticmethod here may not work as expected:
# calling this method on an instance will take the self into the first argument.
# @staticmethod
# def extra_static_method_c():
# print('c')
3. 包在一起
因此,我们现在可以将MethodPatcher
and的定义patch_method
放入单个模块中:
# method_patcher.py
class MethodPatcher:
@classmethod
def patch(cls, target):
for k in cls.__dict__:
obj = getattr(cls, k)
if not k.startswith('_') and callable(obj):
setattr(target, k, obj)
def patch_methods(model_class):
def do_patch(cls):
cls.patch(model_class)
return do_patch
所以我们可以自由使用它:
from method_patcher import ModelPatcher, patch_model
4.最终解决方案:更简单的声明
很快我发现MethodPatcher
类不是必需的,而@patch_method
装饰器可以完成工作,所以最后我们只需要一个patch_method
:
def patch_methods(model_class):
def do_patch(cls):
for k in cls.__dict__:
obj = getattr(cls, k)
if not k.startswith('_') and callable(obj):
setattr(model_class, k, obj)
return do_patch
用法变为:
@patch_methods(MyClass)
class MyClassPatcher:
def extra_method_a(self):
print('a', self)
@classmethod
def extra_class_method_b(cls):
print('c', cls)
# !!ATTENTION!! the effect on declaring staticmethod here may not work as expected:
# calling this method on an instance will take the self into the first argument.
# @staticmethod
# def extra_static_method_c():
# print('c')