fastai.fastcore 补丁装饰器与简单的猴子补丁

问题描述 投票:0回答:1

我正在尝试了解使用

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
的附加值是多少?

python python-3.x monkeypatching fast-ai
1个回答
3
投票

TL;博士

更丰富的调试消息,更好的 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!"
© www.soinside.com 2019 - 2024. All rights reserved.