动态添加内置方法以指向 属性 的内置方法
Dynamically adding builtin methods to point to a property's built-ins
我有几个 classes 和一个函数:
from functools import partial
def fn(other, self, name):
print(f"calling {name} with {other}")
func = getattr(self.a, name)
return func(other)
class A:
def __add__(self, other):
return 9
def __mul__(self, other):
return 7
def __sub__(self, other):
return 8
class B:
def __init__(self,a):
self.a = a
for name in ['add', 'sub']:
name = f"__{name}__"
p = partial(fn, self=self,name=name)
setattr(self, name, p)
p.__name__ = name
我希望能够使用将魔术方法转发到现有 属性。我不想继承 class 因为我不想要所有的内置函数。只是一对夫妇。例如,我可能想使用来自不同 class 的乘法。我试图避免像这样编码:
def __add__(self, other):
self.a.__add__(other)
使用上面的代码我收到以下信息:
>>> b = B(A())
>>> b + 3
TypeError Traceback (most recent call last)
<ipython-input-40-fa904b7bb783> in <module>
----> 1 b + 3
2
TypeError: unsupported operand type(s) for +: 'B' and 'int'
>>> b.__add__(3)
calling __add__ with 3
9
也许我遗漏了一些简单的东西,但我找不到动态添加内置函数的方法。
要解决的主要问题是像 __add__
这样的魔术方法是在 class 上查找的,而不是在对象本身上;否则你可以在 __init__
方法中写 self.__add__ = a.__add__
。为了解决这个问题,我们需要在 class B
上声明方法,而不是在它的单个实例上声明方法。
下面定义的函数 delegate
通过向 B
class 添加一个方法来工作。此方法采用 self
,这将是一个 B
实例,因此它必须动态加载 a
属性,然后加载其 __add__
方法。
class A:
def __add__(self, other):
return 9
def __mul__(self, other):
return 7
def __sub__(self, other):
return 8
class B:
def __init__(self, a):
self.a = a
def delegate(cls, attr_name, method_name):
def delegated(self, *vargs, **kwargs):
a = getattr(self, attr_name)
m = getattr(a, method_name)
return m(*vargs, **kwargs)
setattr(cls, method_name, delegated)
delegate(B, 'a', '__add__')
delegate(B, 'a', '__sub__')
示例:
>>> b = B(A())
>>> b + 3
9
>>> b - 4
8
代理 __dunder__
方法很棘手。我会使用描述符对象,与其他方法相比,它可以更干净地处理属性访问的潜在奥秘。
class Proxy:
def __set_name__(self, owner, name):
self.attr = name
def __get__(self, obj, objtype=None):
if obj is None:
return self
try:
proxy = obj._proxy
except AttributeError:
raise AttributeError('tried to access proxy field on object with no _proxy')
return getattr(proxy, self.attr)
class A:
def __add__(self, other):
return 9
def __mul__(self, other):
return 7
def __sub__(self, other):
return 8
class B:
def __init__(self,a):
self.a = a
self._proxy = self.a
__add__ = Proxy()
__sub__ = Proxy()
b = B(A())
ipython 回复中的示例:
In [6]: b = B(A())
In [7]: b + b
Out[7]: 9
In [8]: b - b
Out[8]: 8
In [9]: b * b
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-19-cb34cccc83f5> in <module>
----> 1 b * b
TypeError: unsupported operand type(s) for *: 'B' and 'B'
如果你想扩展这种方法,Proxy
可以使用它代理的字段名,所以你可以有类似的东西:
class Proxy:
def __init__(self, proxy_field):
self.prox_field = proxy_field
def __set_name__(self, owner, name):
self.attr = name
def __get__(self, obj, objtype=None):
if obj is None:
return self
try:
proxy = getattr(obj, self.proxy_field)
except AttributeError:
raise AttributeError(f'tried to access proxy field on object with no {self.proxy_field} attribute')
return getattr(proxy, self.attr)
class B:
def __init__(self, foo, bar):
self.foo = foo
self.bar = bar
__add__ = Proxy('foo')
__sub__ = Proxy('bar')
我有几个 classes 和一个函数:
from functools import partial
def fn(other, self, name):
print(f"calling {name} with {other}")
func = getattr(self.a, name)
return func(other)
class A:
def __add__(self, other):
return 9
def __mul__(self, other):
return 7
def __sub__(self, other):
return 8
class B:
def __init__(self,a):
self.a = a
for name in ['add', 'sub']:
name = f"__{name}__"
p = partial(fn, self=self,name=name)
setattr(self, name, p)
p.__name__ = name
我希望能够使用将魔术方法转发到现有 属性。我不想继承 class 因为我不想要所有的内置函数。只是一对夫妇。例如,我可能想使用来自不同 class 的乘法。我试图避免像这样编码:
def __add__(self, other):
self.a.__add__(other)
使用上面的代码我收到以下信息:
>>> b = B(A())
>>> b + 3
TypeError Traceback (most recent call last)
<ipython-input-40-fa904b7bb783> in <module>
----> 1 b + 3
2
TypeError: unsupported operand type(s) for +: 'B' and 'int'
>>> b.__add__(3)
calling __add__ with 3
9
也许我遗漏了一些简单的东西,但我找不到动态添加内置函数的方法。
要解决的主要问题是像 __add__
这样的魔术方法是在 class 上查找的,而不是在对象本身上;否则你可以在 __init__
方法中写 self.__add__ = a.__add__
。为了解决这个问题,我们需要在 class B
上声明方法,而不是在它的单个实例上声明方法。
下面定义的函数 delegate
通过向 B
class 添加一个方法来工作。此方法采用 self
,这将是一个 B
实例,因此它必须动态加载 a
属性,然后加载其 __add__
方法。
class A:
def __add__(self, other):
return 9
def __mul__(self, other):
return 7
def __sub__(self, other):
return 8
class B:
def __init__(self, a):
self.a = a
def delegate(cls, attr_name, method_name):
def delegated(self, *vargs, **kwargs):
a = getattr(self, attr_name)
m = getattr(a, method_name)
return m(*vargs, **kwargs)
setattr(cls, method_name, delegated)
delegate(B, 'a', '__add__')
delegate(B, 'a', '__sub__')
示例:
>>> b = B(A())
>>> b + 3
9
>>> b - 4
8
代理 __dunder__
方法很棘手。我会使用描述符对象,与其他方法相比,它可以更干净地处理属性访问的潜在奥秘。
class Proxy:
def __set_name__(self, owner, name):
self.attr = name
def __get__(self, obj, objtype=None):
if obj is None:
return self
try:
proxy = obj._proxy
except AttributeError:
raise AttributeError('tried to access proxy field on object with no _proxy')
return getattr(proxy, self.attr)
class A:
def __add__(self, other):
return 9
def __mul__(self, other):
return 7
def __sub__(self, other):
return 8
class B:
def __init__(self,a):
self.a = a
self._proxy = self.a
__add__ = Proxy()
__sub__ = Proxy()
b = B(A())
ipython 回复中的示例:
In [6]: b = B(A())
In [7]: b + b
Out[7]: 9
In [8]: b - b
Out[8]: 8
In [9]: b * b
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-19-cb34cccc83f5> in <module>
----> 1 b * b
TypeError: unsupported operand type(s) for *: 'B' and 'B'
如果你想扩展这种方法,Proxy
可以使用它代理的字段名,所以你可以有类似的东西:
class Proxy:
def __init__(self, proxy_field):
self.prox_field = proxy_field
def __set_name__(self, owner, name):
self.attr = name
def __get__(self, obj, objtype=None):
if obj is None:
return self
try:
proxy = getattr(obj, self.proxy_field)
except AttributeError:
raise AttributeError(f'tried to access proxy field on object with no {self.proxy_field} attribute')
return getattr(proxy, self.attr)
class B:
def __init__(self, foo, bar):
self.foo = foo
self.bar = bar
__add__ = Proxy('foo')
__sub__ = Proxy('bar')