我正在尝试了解使用
fastai
的 fastcore.basics.patch_to
装饰器 的附加值。这是fastcore
的方法:
from fastcore.basics import patch_to
class _T3(int):
pass
@patch_to(_T3)
def func1(self, a):
return self + a
这是简单的猴子修补方法:
class simple_T3(int):
pass
def func1(self, a):
return self + a
simple_T3.func1 = func1
检查这两个类没有发现任何差异。我知道简单的猴子修补可能会在更复杂的情况下导致问题,所以很高兴知道这些情况是什么?换句话说,
fastcore.basics.patch_to
的附加值是多少?
更丰富的调试消息,更好的 IDE 支持。
patch
和patch_to
是fastcore basics
模块中的装饰器,有助于使monkey_patched方法看起来更像是最初放置在类中的方法,这是经典的方式(双关语)。
如果您在类外部创建一个函数,然后对其进行猴子修补,则与原始函数相比,外部方法通常具有不同的属性,例如其名称、模块和文档。在调试或使用“外部”函数时,这可能会造成混乱且无益。
来源:官方文档:https://github.com/fastai/fastcore/blob/master/nbs/01_basics.ipynb
考虑使用
patch
而不是 patch_to
,因为这样您可以添加类型注释。
from fastcore.basics import patch
class _T3(int):
pass
@patch
def func1(self: _T3, a):
return self + a
致谢:凯·利希滕贝格
fastcore 本身的重量极低:唯一使用的外部库是 numpy(如果你的 Python 是 < 3.7).
,还可以使用数据类)但是如果你真的不想使用它,这里有一个只有两个内置依赖项的实现:
import functools
from copy import copy
from types import FunctionType
def copy_func(f):
"Copy a non-builtin function (NB `copy.copy` does not work for this)"
if not isinstance(f,FunctionType): return copy(f)
fn = FunctionType(f.__code__, f.__globals__, f.__name__, f.__defaults__, f.__closure__)
fn.__dict__.update(f.__dict__)
return fn
def patch_to(cls, as_prop=False):
"Decorator: add `f` to `cls`"
if not isinstance(cls, (tuple,list)): cls=(cls,)
def _inner(f):
for c_ in cls:
nf = copy_func(f)
# `functools.update_wrapper` when passing patched function to `Pipeline`, so we do it manually
for o in functools.WRAPPER_ASSIGNMENTS: setattr(nf, o, getattr(f,o))
nf.__qualname__ = f"{c_.__name__}.{f.__name__}"
setattr(c_, f.__name__, property(nf) if as_prop else nf)
return f
return _inner
def patch(f):
"Decorator: add `f` to the first parameter's class (based on f's type annotations)"
cls = next(iter(f.__annotations__.values()))
return patch_to(cls)(f)
class MyClass():
def __init__(self):
pass
@patch
def new_fun(self:MyClass):
print("I'm a patched function!")
MyInstance = MyClass()
MyInstance.new_fun()
"I'm a patched function!"