Python 系统地重新布线功能
我有许多类,其中大多数方法只是对Python 系统地重新布线功能,python,python-3.x,Python,Python 3.x,我有许多类,其中大多数方法只是对self.value的方法调用“重新布线”,然后返回一个新实例: class someClass(): def __init__(self, value): self.value = value def __add__(self, other): return self.__class__( self.value.__add__(other) ) def someMethod(self, *args, *
self.value
的方法调用“重新布线”,然后返回一个新实例:
class someClass():
def __init__(self, value):
self.value = value
def __add__(self, other):
return self.__class__( self.value.__add__(other) )
def someMethod(self, *args, **kwargs):
return self.__class__( self.value.someMethod(*args, **kwargs) )
def someOtherMethod(self, *args, **kwargs):
return self.__class__( self.value.someOtherMethod(*args, **kwargs) )
当然,并不是所有的内部方法都是这样的,但大多数都是这样
不必显式地实现someMethod
、someOtherMethod
和\uuuuuuuuuuuu
,有没有一种方法可以系统地实现这一点?可能使用\uuuu getattr\uuuu
或\uuuu getattribute\uuuuu
下面是将值的类型子类化不可行的原因:
>>> class someClass(int):
pass
>>> a = someClass(5)
>>> isinstance(a+5, someClass)
False #I need this to be True
为了使最后一行返回为真,我必须像以前一样“重新布线”所有运算符:子类化根本无助于部分答案,因为您仍然必须列出要包装的方法,但下面的内容使包装大量方法变得容易得多,因为您只是将它们添加到要包装的方法列表中:
class MyClass():
def __init__(self, value):
self.value = value
def __str__(self):
return f'<MyClass({repr(self.value)})>'
def make_wrappers(cls, methods):
for method in methods:
def wrapper(self, *args, _method=method, **kwargs):
return self.__class__(getattr(self.value, _method)(*args, **kwargs))
setattr(cls, method, wrapper)
make_wrappers(MyClass, ['__add__', '__sub__', 'upper'])
x = MyClass(3)
print(x+2, x-2) # <MyClass(5)> <MyClass(1)>
y = MyClass('hello')
print(y.upper()) # <MyClass('HELLO')>
class MyClass():
定义初始值(自身,值):
自我价值=价值
定义(自我):
返回f“”
def make_包装器(cls、方法):
对于方法中的方法:
def包装器(self,*args,_method=method,**kwargs):
返回self.\uuuu类(getattr(self.value,\u方法)(*args,**kwargs))
setattr(cls、方法、包装器)
制作包装(MyClass、['''''''''''''''.'子'.'''.'上'.'')
x=MyClass(3)
打印(x+2,x-2)#
y=MyClass(“你好”)
打印(y.upper())#
您似乎无法使用\uuuu getattr\uuuu
完全完成您想要的操作。这是因为如果您试图计算a+b
,其中a
是您的类的一个实例,那么如果您没有定义\uuuuuuuuuu添加\uuuuuuu
方法,但是定义了\uuu getattr\uuuuuuu
,那么这将引发类型错误
并忽略(而不是调用)\uuuu getattr\uuuuu
。因此,您不能使用\uuuuu getattr\uuuuu
来包装“神奇方法”,例如\uuuuu add\uuuuuu
(至少在Python 3中是这样)
请注意,\u方法
用于确保变量方法
的值在定义时绑定到包装器中(添加到默认字典)。相反,如果您在使用\u method
的包装器中直接使用方法
,那么您会发现每个包装器在调用时都会使用从外部范围继承的方法
的值,这不是您想要的
还要注意,该解决方案依赖于类的“猴子补丁”。这会奏效的。在Python3中不起作用的是对类的实例进行monkey-patch——例如,如果您试图使用setattr
直接向x
添加一个在MyClass
中不存在的方法,那么调用它将不起作用。我将使用这种非常基本的方法:
class SomeClass():
def __init__(self, value):
self.value = value
for method_name in ("some_method",
"some_other_method"):
new_method = lambda *args, **kwargs: self.__class__(getattr(self.value, method_name)(*args, **kwargs)
setattr(self, method_name, new_method)
现在,您只需要一种方法来为这些方法提供docstring。如果您从“值类”继承而不是将其用作成员,则不必实现这些方法。你可以阅读我关于继承@TheFool的文章,这些“rewres”大多数都是用于运算符的,对值的类进行子类化难道不是意味着像+=或*=这样的运算符将返回该类而不是我的类吗?也许我误解了你在做什么。你能试着用问题中的代码更好地解释它吗?@TheFool我已经添加了一个与你提出的解决方案相关的具体示例!我需要非常仔细地重新阅读并测试它,但它似乎满足了我的要求。如果我把它放在一个模块中并导入它,它会起作用吗?@AmyLucyRose我不明白为什么不起作用。重新阅读后,它确实解决了我的问题。然而,有没有一种方法可以在没有你提到的问题的情况下摆脱kwarg方法?如果没有,也没关系,但我认为这会让实现变得更加混乱。(希望我添加的docstring能够解决这个问题,但是,如果有办法消除它,那就太好了)我想我可以想出一种使用lambda的方法,我也会尝试实现它并测试它。这不起作用,但原因很微妙:对于某些方法(主要是运算符和其他dunder),它们需要存在于类级别,因为uuu getattr_uuu和uu getattribute_uuu被绕过。此外,这会使每个实例在内存中都大得多,这将导致严重的扩展。