I have a custom class,
class A:
def __init__(self, a, b):
self.a = a
self.b = b
The class is not iterable or indexable or anything like that. If at all possible, I would like to keep it that way. Is it possible to have something like the following work?
>>> x = A(1, 2)
>>> min(x)
1
>>> max(x)
2
What got me thinking about this is that min
and max
are listed as "Common Sequence Operations" in the docs. 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.
Perhaps there is a magic method that I am not aware of that would enable this?
There are no
__min__
and__max__
special methods*. This is kind of a shame sincerange
has seen some pretty nice optimizations in Python 3. You can do this:But don't try this unless you want to wait a long time:
However creating your own
min
/max
functions is a pretty good idea, as suggested by Lærne.Here is how I would do it. UPDATE: removed the dunder name
__min__
in favor of_min
, as recommended by PEP 8:Code:
I think this way is maybe a little bit better because it handles some corner cases the other answer hasn't considered.
Note that an iterable object with a
_min
method will still be consumed byoldmin
as per usual, but the return value is overridden by the special method.HOWEVER, if the
_min
method requires the iterator to still be available for consumption, this will need to be tweaked because the iterator is getting consumed byoldmin
first.Note also that if the
__min
method is simply implemented by callingoldmin
, things will still work fine (even though the iterator was consumed; this is becauseoldmin
raises aValueError
in this case).* Such methods are often called "magic", but this is not the preferred terminology.
Yes. When
min
takes one arguments it assumes it to be an iterable, iterates over it and takes the minimum value. So,Should work.
Additional Note: If you don't want to use
__iter__
, I don't know of way to do that. You probably want to create your own min function, that calls some__min__
method if there is one in the argument it is passed to and calls the oldmin
else.There's no optimization going on for ranges and there are no specialized magic methods for
min
/max
.If you peek at the implementation for
min
/max
you'll see that after some argument parsing is done, a call toiter(obj)
(i.eobj.__iter__()
) is made to grab an iterator:then calls to
next(it)
(i.eit.__next__
) are performed in a loop to grab values for comparisons:No, if you want to use the built-in
min
* the only option you have is implementing the iterator protocol.*By patching
min
, you can of-course, make it do anything you want. Obviously at the cost of operating in Pythonland. If, though, you think you can utilize some optimizations, I'd suggest you create amin
method rather than re-defining the built-inmin
.In addition, if you only have ints as instance variables and you don't mind a different call, you can always use
vars
to grab theinstance.__dict__
and then supply it's.values()
tomin
: