I just wrote a bit of code where I wanted to do:
def foo(container)
return any((some_obj.attr <= 0 for some_obj in container))
where foo
would return the first some_obj
where some_obj.attr
is zero or less. The alternative, I suppose, would be
def foo(container):
return next((some_obj for some_obj in container if some_obj.attr <= 0), False)
but that feels very hacky.
I ended up writing it out, but I don't like how deeply nested it got.
def foo(container):
for some_obj in container:
if some_obj.attr <= 0:
return some_obj
return False
To clarify: container
in this case is likely no more than 6 objects (and often 2 objects), though a general case is more useful. I'm also trying to optimize for ease of reading, not for performance.
Is there some better construct than this?
Just for fun, to extend Stefan Pochmann's answer to handle obj.attr <= 0
, still without needing a lambda:
from operator import attrgetter
from functional import compose
next(filter(compose(0..__ge__, attrgetter('attr')), [3, 2, 1, -1, -2]), False)
If you don't have the functional
module (which you probably don't, because the version on PyPI hasn't worked since Python 2.4 or so…) and don't want to search for a modern replacement, you can write compose
yourself (and slightly better):
def compose(f, g):
@functools.wraps(f):
def wrapper(x):
return f(g(x))
return wrapper
About once/year, there's a proposal to add compose
to the stdlib, and maybe even give it an infix operator. With @
being added for matrix multiplication, you can guess the latest proposal.* So, if that happens (which it probably won't), you can do this:
from operator import attrgetter
next(filter(0..__ge__ @ attrgetter('attr'), [3, 2, 1, -1, -2]), False)
Now the only thing we need is Haskell-style operator sectioning so we can get rid of the bound method, the ..
hack, and the need for an attrgetter
function (assuming you consider dot-attribution an operator, which it really isn't, but let's pretend…). Then:
next(filter((<= 0) @ (.attr), [3, 2, 1, -1, -2]), False)
* In fact, it was proposed, twice, during the initial PEP 465 discussion, which is why the PEP mentions, "During discussions of this PEP, a similar suggestion was made to define @
as a general purpose function composition operator, and this suffers from the same problem; functools.compose
isn't even useful enough to exist."
next(filter
should do it, and here's a funny way to test <= 0
:
>>> next(filter((0).__ge__, [3,2,1,-1,-2]), False)
-1
Ha, even tricker:
>>> next(filter(0..__ge__, [3,2,1,-1,-2]), False)
-1
Or, as abarnert pointed out:
>>> next(filter(0 .__ge__, [3,2,1,-1,-2]), False)
-1
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With