I am writing a metaclass that amongst other things wants to add a method to the classes it creates. Let's forget about the metaclass for now though and just look at simple adding of methods.
In order to dynamically add an instance method I can do:
class Foo:
def bar(self, x):
print(f"In bar() with {x} and {self}")
def func(self, x):
print(f"In func() with {x} and {self}")
Foo.func = func
After that I can do:
>>> f = Foo()
>>> f.bar(7)
In bar() with 7 and <__main__.Foo object at 0x7f912a7e57f0>
>>> f.func(7)
In func() with 7 and <__main__.Foo object at 0x7f912a7e57f0>
So the methods bar
and func
seem to function identically, but there are some discernible differences, eg:
>>> f.bar.__qualname__
'Foo.bar'
>>> f.func.__qualname__
'func'
f.func.__module__
could also potentially be different from f.bar.__module__
, depending on where everything is defined.
What do I have to change in Construction 2 (below) in order for both constructions to behave exactly the same (no code that uses the Foo
class could change its behaviour depending on which construction is used)?
# Construction 1
class Foo:
def func(self):
pass
# Construction 2
class Foo:
pass
def func(self):
pass
Foo.func = func
I have created a decorator that hopefully implements a sensible version of Construction 2, but what could I still be missing/breaking by monkey patching like that?
class instance_method_of:
def __init__(self, cls, name=None):
self.cls = cls
self.name = name
def __call__(self, func):
if self.name is not None:
func.__name__ = self.name
func.__qualname__ = f'{self.cls.__qualname__}.{func.__name__}'
func.__module__ = self.cls.__module__
setattr(self.cls, func.__name__, func)
return func
class Foo:
pass
@instance_method_of(Foo)
def func(self):
pass
It seems to cover everything: Although I would go with a function decorator like :
from pathlib import Path
def patch(f):
cls = next(iter(f.__annotations__.values()))
name = f.__defaults__[0]
f.__qualname__ = f"{cls.__name__}.{f.__name__}"
f.__module__ = cls.__module__
if name is None:
setattr(cls,f.__name__,f)
else:
f.__qualname__ = f"{cls.__name__}.{name}"
setattr(cls,name,f)
return f
@patch
def new(self:Path, name:str=None):
"new"
return list(self.iterdir())
path = Path()
path.new()
mostly adapted from fastai version
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.