我有一个自定义类,
class A:
def __init__(self, a, b):
self.a = a
self.b = b
该类不可迭代或可索引或类似的东西。如果可能的话,我想保持这种方式。是否可以进行以下工作?
>>> x = A(1, 2)
>>> min(x)
1
>>> max(x)
2
让我思考的是min
和max
被列为" Common Sequence Operations"在docs。由于range
被同一个文档视为序列类型,我认为range
必须进行某种优化,也许我可以利用它。
也许有一种神奇的方法,我不知道会启用这个吗?
答案 0 :(得分:21)
是。当min
接受一个参数时,它假定它是一个可迭代的,迭代它并取最小值。所以,
class A:
def __init__(self, a, b):
self.a = a
self.b = b
def __iter__(self):
yield self.a
yield self.b
应该工作。
附加说明:如果您不想使用__iter__
,我不知道该怎么做。您可能想要创建自己的min函数,如果传递给它的参数中有一个__min__
方法,则调用它,并调用旧的min
。
oldmin = min
def min(*args):
if len(args) == 1 and hasattr(args[0], '__min__'):
return args[0].__min__()
else:
return oldmin(*args)
答案 1 :(得分:6)
由于
range
被认为是同一个文档的序列类型,我认为必须对range
进行某种优化,也许我可以利用它的。
范围没有优化,min
/ max
没有专门的魔术方法。
如果您查看the implementation for min
/max
,您会看到在完成一些参数解析之后,a call to iter(obj)
(即obj.__iter__()
)会抓取迭代器:
it = PyObject_GetIter(v);
if (it == NULL) {
return NULL;
}
然后calls to next(it)
(即it.__next__
)在循环中执行以获取比较值:
while (( item = PyIter_Next(it) )) {
/* Find min/max */
是否可以进行以下工作?
不,如果你想使用内置的min
*,你唯一的选择就是实现迭代器协议。
*通过修补min
,你当然可以做任何你想做的事情。显然以Pythonland的运营成本为代价。但是,如果您认为可以使用某些优化,我建议您创建一个min
方法,而不是重新定义内置min
。
此外,如果您只将int作为实例变量并且不介意使用其他调用,则可以始终使用vars
来抓取instance.__dict__
,然后提供.values()
到min
:
>>> x = A(20, 4)
>>> min(vars(x).values())
4
答案 2 :(得分:6)
没有__min__
和__max__
特殊方法*。这是一种耻辱,因为range
看到了一些pretty nice optimizations in Python 3。你可以这样做:
>>> 1000000000000 in range(1000000000000)
False
但除非你想等待很长时间,否则不要试试这个:
>>> max(range(1000000000000))
然而,根据Lærne的建议,创建自己的min
/ max
函数是个不错的主意。
我将如何做到这一点。更新:根据PEP 8的建议删除了dunder名称__min__
,转而使用_min
:
永远不要发明这样的名字;只使用它们记录
代码:
from functools import wraps
oldmin = min
@wraps(oldmin)
def min(*args, **kwargs)
try:
v = oldmin(*args, **kwargs)
except Exception as err:
err = err
try:
arg, = args
v = arg._min()
except (AttributeError, ValueError):
raise err
try:
return v
except NameError:
raise ValueError('Something weird happened.')
我认为这种方式可能会更好一点,因为它处理了一些角落案例而另一个答案没有被考虑过。
请注意_min
方法仍会按照惯例使用具有oldmin
方法的可迭代对象,但返回值会被特殊方法覆盖。
但是,如果_min
方法要求迭代器仍可供使用,则需要进行调整,因为迭代器首先被oldmin
消耗。
另请注意,如果__min
方法只是通过调用oldmin
来实现,那么事情仍然可以正常工作(即使迭代器已被使用;这是因为oldmin
引发了ValueError
1}}在这种情况下)。
*此类方法通常被称为" magic",但这不是首选术语。