fastai.fastcore patch decorator vs simple monkey-patching

2024/10/8 0:34:52

I'm trying to understand the value-added of using fastai's fastcore.basics.patch_to decorator. Here's the fastcore way:

from fastcore.basics import patch_toclass _T3(int):pass@patch_to(_T3)
def func1(self, a):return self + a

And here's the simple monkey-patching approach:

class simple_T3(int):passdef func1(self, a):return self + asimple_T3.func1 = func1

Inspecting the two classes does not reveal any differences. I understand that simple monkey-patching might cause problems in more complex cases, so it would be great to know what such cases are? In other words, what's the value-added of fastcore.basics.patch_to?

Answer

TL;DR

More informative debugging messages, better IDE support.


Answer

patch and patch_to are decorators in the fastcore basics module that are helpful to make the monkey_patched method to look more like as if it was a method originally placed inside the Class, the classical way (pun intended).

If you create a function outside a class and then monkey-patch it, the outsider method typically has different attributes, such as its name, module, and documentation, compared to the original function. This can be confusing and unhelpful when debugging or working with the "outsider" function.

Source: Official documentation: https://github.com/fastai/fastcore/blob/master/nbs/01_basics.ipynb


Usage suggestion

Consider using patch instead of patch_to, because this way you can add type annotations.

from fastcore.basics import patchclass _T3(int):pass@patch
def func1(self: _T3, a):return self + a

What if I don't want to use the library?

Credits: Kai Lichtenberg

fastcore itself is extremely low weight: The only external library used is numpy (and dataclasses if your python is < 3.7).

But if you really want to not use it, here's an implementation with only two built-in dependencies:

import functools
from copy import copy
from types import FunctionTypedef 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 fndef 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 manuallyfor 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 freturn _innerdef 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!"
https://en.xdnf.cn/q/70182.html

Related Q&A

Adding user to group on creation in Django

Im looking to add a User to a group only if a field of this User is specified as True once the User is created. Every User that is created would have a UserProfile associated with it. Would this be the…

imgradient matlab equivalent in Python

I am searching for an imgradient MATLAB equivalent in Python. I am aware of cv2.Sobel() and cv2.Laplacian() but it doesnt work as imgradient works in MATLAB. If I could get source code of imgradient.m…

Error: astype() got an unexpected keyword argument categories

df = pd.DataFrame([A+, A, A-, B+, B, B-, C+, C, C-, D+, D],index=[excellent, excellent, excellent, good, good, good, ok, ok, ok, poor, poor])df.rename(columns={0: Grades}, inplace=True)dfI am trying to…

python: regular expression search pattern for binary files (half a byte)

I am using the following regular expression pattern for searching 0xDEAD4FAD in a binary file:my_pattern = re.compile(b"\xDE\xAD\x4F\xAD")but how do I generalize the search pattern for search…

pandas: selecting rows in a specific time window

I have a dataset of samples covering multiple days, all with a timestamp. I want to select rows within a specific time window. E.g. all rows that were generated between 1pm and 3 pm every day.This is a…

How to visualize (dendrogram) a dictionary of hierarchical items?

This is my first time of doing visualization from hierarchical data in dictionary format with Python. Last part of the data looks like this:d = {^2820: [^391, ^1024], ^2821: [^759, w, ^118, ^51], ^2822…

How to unfocus (blur) Python-gi GTK+3 window on Linux

What I want to do and whyI want my window to unfocus, so the previous focused window is selected.Why? I want to interact with the previously selected window (from other programs). My current plan is: …

SyntaxError: multiple exception types must be parenthesized

I am a beginner and have a problem after installing pycaw for the audio control using python, on putting the basic initialization code for pycaw, i get the following error:- Traceback (most recent call…

Python find difference between file paths

I have a bunch of file paths, such as:path1 = "./base/folder1/subfolder" path2 = "./base/folder2/"I am trying to write a function that can give me the relative difference between th…

Update range of colorbar in matplotlib

I want to update a contourf plot within a function, which works fine. However, the range of the data changes and I therefore also have to update the colorbar. That is where I fail to do so.Please see f…