有没有办法 return 在 Python 中自定义最小值和最大值?
Is there a way to return a custom value for min and max in Python?
我有一个自定义 class,
class A:
def __init__(self, a, b):
self.a = a
self.b = b
class 不可迭代或可索引或类似的东西。如果可能的话,我想保持这种状态。有没有可能有类似下面的作品?
>>> x = A(1, 2)
>>> min(x)
1
>>> max(x)
2
让我想到这一点的是 min
and max
are listed as "Common Sequence Operations" in the docs. Since range
被相同的文档认为是序列类型,我在想一定有某种可能的优化 range
,也许我可以利用它。
也许有一种我不知道的神奇方法可以实现这一点?
是的。当 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
else.
oldmin = min
def min(*args):
if len(args) == 1 and hasattr(args[0], '_min_'):
return args[0]._min_()
else:
return oldmin(*args)
Since range
is considered to be a sequence type by the very same docs, I was thinking that there must be some sort of optimization that is possible for range
, and that perhaps I could take advantage of it.
范围没有进行优化,min
/max
也没有专门的魔术方法。
如果你偷看the implementation for min
/max
you'll see that after some argument parsing is done, 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 */
Is it possible to have something like the following work?
不,如果您想使用内置的 min
*,您唯一的选择就是实现迭代器协议。
*通过修补 min
,你当然可以让它做任何你想做的事。显然是以在 Pythonland 中运行为代价的。但是,如果您认为可以利用一些优化,我建议您创建一个 min
方法,而不是重新定义内置的 min
.
此外,如果您只有整数作为实例变量并且您不介意不同的调用,您总是可以使用 vars
获取 instance.__dict__
然后提供它的 .values()
到 min
:
>>> x = A(20, 4)
>>> min(vars(x).values())
4
没有 __min__
和 __max__
特殊方法*。这有点遗憾,因为 range
看过一些 。你可以这样做:
>>> 1000000000000 in range(1000000000000)
False
但是除非你想等很长时间,否则不要尝试这个:
>>> max(range(1000000000000))
然而,正如 所建议的那样,创建您自己的 min
/max
函数是一个不错的主意。
这是我的做法。更新:根据 PEP 8:
的建议,删除了名称 __min__
以支持 _min
Never invent such names; only use them as documented
代码:
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
使用,但 return 值会被特殊方法覆盖。
但是,如果 _min
方法要求迭代器仍可用于使用,则需要对此进行调整,因为迭代器首先被 oldmin
使用。
还要注意,如果 __min
方法只是通过调用 oldmin
来实现,事情仍然会正常工作(即使迭代器被消耗;这是因为 oldmin
引发了一个ValueError
在这种情况下)。
* 此类方法通常称为 "magic",但这不是首选术语。
我有一个自定义 class,
class A:
def __init__(self, a, b):
self.a = a
self.b = b
class 不可迭代或可索引或类似的东西。如果可能的话,我想保持这种状态。有没有可能有类似下面的作品?
>>> x = A(1, 2)
>>> min(x)
1
>>> max(x)
2
让我想到这一点的是 min
and max
are listed as "Common Sequence Operations" in the docs. Since range
被相同的文档认为是序列类型,我在想一定有某种可能的优化 range
,也许我可以利用它。
也许有一种我不知道的神奇方法可以实现这一点?
是的。当 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
else.
oldmin = min
def min(*args):
if len(args) == 1 and hasattr(args[0], '_min_'):
return args[0]._min_()
else:
return oldmin(*args)
Since
range
is considered to be a sequence type by the very same docs, I was thinking that there must be some sort of optimization that is possible forrange
, and that perhaps I could take advantage of it.
范围没有进行优化,min
/max
也没有专门的魔术方法。
如果你偷看the implementation for min
/max
you'll see that after some argument parsing is done, 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 */
Is it possible to have something like the following work?
不,如果您想使用内置的 min
*,您唯一的选择就是实现迭代器协议。
*通过修补 min
,你当然可以让它做任何你想做的事。显然是以在 Pythonland 中运行为代价的。但是,如果您认为可以利用一些优化,我建议您创建一个 min
方法,而不是重新定义内置的 min
.
此外,如果您只有整数作为实例变量并且您不介意不同的调用,您总是可以使用 vars
获取 instance.__dict__
然后提供它的 .values()
到 min
:
>>> x = A(20, 4)
>>> min(vars(x).values())
4
没有 __min__
和 __max__
特殊方法*。这有点遗憾,因为 range
看过一些
>>> 1000000000000 in range(1000000000000)
False
但是除非你想等很长时间,否则不要尝试这个:
>>> max(range(1000000000000))
然而,正如 min
/max
函数是一个不错的主意。
这是我的做法。更新:根据 PEP 8:
的建议,删除了名称__min__
以支持 _min
Never invent such names; only use them as documented
代码:
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
使用,但 return 值会被特殊方法覆盖。
但是,如果 _min
方法要求迭代器仍可用于使用,则需要对此进行调整,因为迭代器首先被 oldmin
使用。
还要注意,如果 __min
方法只是通过调用 oldmin
来实现,事情仍然会正常工作(即使迭代器被消耗;这是因为 oldmin
引发了一个ValueError
在这种情况下)。
* 此类方法通常称为 "magic",但这不是首选术语。