假设我已经构建了一个包含Foo
类的库,并支持一些神奇的方法,比如__add__()
和__radd__()
:
>>> class Foo(object):
... def __add__(self, rhs):
... print("Foo.__add__", rhs)
... def __radd__(self, lhs):
... print("Foo.__radd__", lhs)
...
>>> foo = Foo()
>>> foo + 3
Foo.__add__ 3
>>> 3 + foo
Foo.__radd__ 3
在计算3 + foo
时,python首先调用type(3).__add__(3, foo)
,但当它返回NotImplemented
时,它会回退到type(foo).__radd__(foo, 3)
:
>>> type(3).__add__(3, foo)
NotImplemented
我希望开发人员能够在我的库之上构建库,比如一个包含类Bar
的库,我希望它们具有完全控制权。特别是,我想实现一些机制,让其他库决定foo + bar
是应该调用foo.__add__(bar)
还是bar.__radd__(foo)
。
我看到NumPy使用__array_priority__
方案解决了这个问题。但这似乎引起了一些令人头疼的问题(考虑到问题和问题的数量)。还有其他最佳做法吗?
答案 0 :(得分:1)
一个流行的选择是维护LHS支持的类型列表,如果RHS的类型不在列表中,则返回NotImplemented
:
class Foo(object):
SUPPORTED_TYPES = (int, Foo)
def __add__(self, rhs):
if isinstance(type(rhs), SUPPORTED_TYPES):
[...] # compute self + rhs
else:
return NotImplemented
除非rhs
是SUPPORTED_TYPES
之一的智能子类型,否则这很有效:它无法获得控制权。
而且,这种列表类型不是很灵活。依赖鸭子打字可能比在硬编码类型列表上更好。
答案 1 :(得分:1)
一个简单的选择是尝试让LHS做它需要做的任何事情(在下面的示例中它调用RHS的value()
方法)并且如果它引发异常,捕获它并返回{{1 }}:
NotImplemented
尽可能简单,无需维护class Foo(object):
[...]
def __add__(self, rhs):
try:
return self._value + rhs.value()
except AttributeError:
return NotImplemented
列表。但是,RHS可能会实现与此任务无关的SUPPORTED_TYPES
方法,因此可能存在风险。此外,value()
没有简单的方法可以完全控制结果。
在Python中,通常最好是请求宽恕而不是如上所述请求许可,但您可能更愿意检查rhs
是否有rhs
方法:
value()
答案 2 :(得分:0)
另一种选择是使用__foo_priority__
之类的属性,有点像NumPy对其__array_priority__
所做的那样:
class Foo(object):
__foo_priority__ = 0
def __add__(self, rhs):
delegate = True
try:
rhs_prio = type(rhs).__foo_priority__
delegate = (self.__foo_priority__ < rhs_prio)
except AttributeError:
delegate = True
if delegate:
return NotImplemented
else:
return self.value_ + rhs.value()
这个选项稍微复杂一点,但非常灵活。此选项的唯一(次要)问题是它要求rhs
类型具有额外属性,因此如果rhs
无法控制,则无法控制#!/usr/bin/python
import pandas as pd
file = 'sample.xls'
df = pd.read_excel(file, sheetname=0, skiprows=7)
它是没有此属性的现有类型。