-From Extension Method (C # Programming Guide)
Extension methods allow you to "add" a method to an existing type without creating a new derived type, recompiling, or modifying the original type.
Since it is difficult to realize an extension method like C #, here we aim to wrap an instance of the target type in an extension class so that object-oriented method execution is possible, or it can be executed as a simple function ( Programming by transfer, not inheritance).
According to the Official Document, by implementing __get__
, it is possible to control whether it behaves as a function or a method, so I will put some work here.
The following code replaces the self
passed to the method with self .__ root __
to implement a pseudo-extension method.
from functools import wraps
from types import MethodType
class ExtensionMethod:
def __init__(self, func):
self.__func__ = func
def __get__(self, obj, objtype=None):
if obj is None:
return self.__func__ #If there is no instance, the function is returned as it is
func = self.__func__
@wraps(func) #Inherit function argument information, etc.
def wrapper(self, *args, **kwargs):
return func(getattr(self, "__root__"), *args, **kwargs) #self to self.__root__Replace with
return MethodType(wrapper, obj) #Returns a method that binds an instance and a function
class MyExtension:
def __init__(self, __root__):
self.__root__ = __root__
@ExtensionMethod
def add_one(self):
return self + 1
print(MyExtension.add_one(0)) # => 1
obj = MyExtension(10)
print(obj.add_one()) # => 11
Is it like this? I'm not so happy (Oy
In many cases, simply inheriting is quicker, but since it is programmed for the __root__
interface, it has the advantage of being able to handle various types as objects without inheriting.
There may be a way to go by combining it with type checkers like mypy and pydantic.
If you give a decorator to the ExtensionMethod
, the decorator works for the ExtensionMethod
instance.
class MyExtension:
def __init__(self, __root__):
self.__root__ = __root__
@your_decorator #Like this
@ExtensionMethod
def add_one(self):
return self + 1
When accessing a method from a class, __get__
works and returns a function or method, but it should not be accessed directly during class generation.
To treat it as a function, you need to implement __call__
or take another step. (I won't introduce it here because it makes the code redundant.)
Also, if you use a static type checker such as mypy, you will get an error because the type of self does not match your own type.
@dataclass
class MyExtension:
__root__: str
@ExtensionMethod
def add_one(self: str): #Since mypy assumes MyExtension for self, an error will occur.
return self + 1
When accessing the attributes of MyExtension, if you map the attributes other than ExtensionMethod to the attributes of __root__
, you may be able to implement the behavior closer to the extension method.
Not very practical.
Recommended Posts