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被绕过。此外,这会使每个实例在内存中都大得多,这将导致严重的扩展。