Shortcut OR-chain applied on list - python

I'd like to do something like this:
x = f(a[0]) or f(a[1]) or f(a[2]) or f(a[3]) or …
with a given list a and a given function f. Unlike the built-in any function I need to get the first value of the list which is considered to be true; so for 0 or "foo" or 3.2 I need to get "foo", not just True.
Of course, I could write a small function like
def returnFirst(f, a):
for i in a:
v = f(i)
if v:
return v
return False
x = returnFirst(f, a)
but that's probably not the nicest solution, for reasons also given in this SO question. As I mention this other thread, I could of course use code based on the
solution given there, e.g.
x = next((f(x) for x in a if f(x)), False)
But I don't see a simple way to circumvent the doubled calling of f then.
Is there any simple solution I am missing or just don't know? Something like an
OR((f(x) for x in a))
maybe?
I tried to find other questions concerning this, but searching for keywords like or is a bit problematic in SO, so maybe I just didn't find something appropriate.

This should work:
next((x for y in a for x in (f(y),) if x),False)

I'm now using this:
x = next((v for v in (f(x) for x in a) if v), False)
It's a working idiom without doubling the call of f and without introducing a hacky local, but it still is not very readable (especially if x, f, a, and v are longer than one letter).
I'd be happy to hear of a better solution.

Related

Python how to reduce this two-liner to one line?

x = f1(x)
x = f2(x, x)
How do I write this in a single line? Obviously I don't want to write x = f2(f1(x), f1(x)) since it performs the same operation twice, but do I really have to do a two-liner here?
You should probably just keep it as two lines, it is perfectly clear that way. But if you must you can use an assignment expression:
>>> def f1(a): return a + 42
...
>>> def f2(b, c): return b + c
...
>>> f2(x:=f1(1), x)
86
>>>
But again, don't try to cram your code into one line. Rarely is a code improved by trying to make a "one-liner". Write clear, readable, and maintainable code. Don't try to write the shortest code possible. That is maybe fun if you are playing code-golf, but it isn't what you should do if you are trying to write software that is actually going to be used.
This is horrendous, and 2 clear lines is better than 1 obfuscated line, but...
x = f2(*itertools.repeat(f1(x), 2))
Example of use:
import itertools
def f1(x):
return 2*x
def f2(x1, x2):
return x1+x2
x = 1
x = f2(*itertools.repeat(f1(x), 2))
print(x)
Prints 4.
This really doesn't seem like a good place to condense things down to one line, but if you must, here's the way I would go about it.
Let's take the function f2. Normally, you'd pass in parameters like this:
x = f2("foo", "bar")
But you can also use a tuple containing "foo" and "bar" and extract the values as arguments for your function using this syntax:
t = ("foo", "bar")
x = f2(*t)
So if you construct a tuple with two of the same element, you can use that syntax to pass the same value to both arguments:
t = (f1(x),) * 2
x = f2(*t)
Now just eliminate the temporary variable t to make it a one-liner:
x = f2(*(f1(x),) * 2)
Obviously this isn't very intuitive or readable though, so I'd recommend against it.
One other option you have if you're using Python 3.8 or higher is to use the "walrus operator", which assigns a value and acts as an expression that evaluates to that value. For example, the below expression is equal to 5, but also sets x to 2 in the process of its evaluation:
(x := 2) + 3
Here's your solution for a one-liner using the walrus operator:
x = f2(x := f1(x), x)
Basically, x is set to f1(x), then reused for the second parameter of f2. This one might be a little more readable but it still isn't perfect.

How to annotate variably in dict comprehension? [duplicate]

I have a list comprehension which approximates to:
[f(x) for x in l if f(x)]
Where l is a list and f(x) is an expensive function which returns a list.
I want to avoid evaluating f(x) twice for every non-empty occurance of f(x). Is there some way to save its output within the list comprehension?
I could remove the final condition, generate the whole list and then prune it, but that seems wasteful.
Edit:
Two basic approaches have been suggested:
An inner generator comprehension:
[y for y in (f(x) for x in l) if y]
or memoization.
I think the inner generator comprehension is elegant for the problem as stated. In actual fact I simplified the question to make it clear, I really want:
[g(x, f(x)) for x in l if f(x)]
For this more complicated situation, I think memoization produces a cleaner end result.
[y for y in (f(x) for x in l) if y]
Will do.
Starting Python 3.8, and the introduction of assignment expressions (PEP 572) (:= operator), it's possible to use a local variable within a list comprehension in order to avoid calling twice the same function:
In our case, we can name the evaluation of f(x) as a variable y while using the result of the expression to filter the list but also as the mapped value:
[y for x in l if (y := f(x))]
A solution (the best if you have repeated value of x) would be to memoize the function f, i.e. to create a wrapper function that saves the argument by which the function is called and save it, than return it if the same value is asked.
a really simple implementation is the following:
storage = {}
def memoized(value):
if value not in storage:
storage[value] = f(value)
return storage[value]
[memoized(x) for x in l if memoized(x)]
and then use this function in the list comprehension. This approach is valid under two condition, one theoretical and one practical. The first one is that the function f should be deterministic, i.e. returns the same results given the same input, and the other is that the object x can be used as a dictionary keys. If the first one is not valid than you should recompute f each timeby definition, while if the second one fails it is possible to use some slightly more robust approaches.
You can find a lot of implementation of memoization around the net, and I think that the new versions of python have something included in them too.
On a side note, never use the small L as a variable name, is a bad habit as it can be confused with an i or a 1 on some terminals.
EDIT:
as commented, a possible solution using generators comprehension (to avoid creating useless duplicate temporaries) would be this expression:
[g(x, fx) for x, fx in ((x,f(x)) for x in l) if fx]
You need to weight your choice given the computational cost of f, the number of duplication in the original list and memory at you disposition. Memoization make a space-speed tradeoff, meaning that it keep tracks of each result saving it, so if you have huge lists it can became costly on the memory occupation front.
You should use a memoize decorator. Here is an interesting link.
Using memoization from the link and your 'code':
def memoize(f):
""" Memoization decorator for functions taking one or more arguments. """
class memodict(dict):
def __init__(self, f):
self.f = f
def __call__(self, *args):
return self[args]
def __missing__(self, key):
ret = self[key] = self.f(*key)
return ret
return memodict(f)
#memoize
def f(x):
# your code
[f(x) for x in l if f(x)]
[y for y in [f(x) for x in l] if y]
For your updated problem, this might be useful:
[g(x,y) for x in l for y in [f(x)] if y]
Nope. There's no (clean) way to do this. There's nothing wrong with a good-old-fashioned loop:
output = []
for x in l:
result = f(x)
if result:
output.append(result)
If you find that hard to read, you can always wrap it in a function.
As the previous answers have shown, you can use a double comprehension or use memoization. For reasonably-sized problems it's a matter of taste (and I agree that memoization looks cleaner, since it hides the optimization). But if you're examining a very large list, there's a huge difference: Memoization will store every single value you've calculated, and can quickly blow out your memory. A double comprehension with a generator (round parens, not square brackets) only stores what you want to keep.
To come to your actual problem:
[g(x, f(x)) for x in series if f(x)]
To calculate the final value you need both x and f(x). No problem, pass them both like this:
[g(x, y) for (x, y) in ( (x, f(x)) for x in series ) if y ]
Again: this should be using a generator (round parens), not a list comprehension (square brackets). Otherwise you will build the whole list before you start filtering the results. This is the list comprehension version:
[g(x, y) for (x, y) in [ (x, f(x)) for x in series ] if y ] # DO NOT USE THIS
There have been a lot of answers regarding memoizing. The Python 3 standard library now has a lru_cache, which is a Last Recently Used Cache. So you can:
from functools import lru_cache
#lru_cache()
def f(x):
# function body here
This way your function will only be called once. You can also specify the size of the lru_cache, by default this is 128. The problem with the memoize decorators shown above is that the size of the lists can grow well out of hand.
You can use memoization. It is a technique which is used in order to avoid doing the same computation twice by saving somewhere the result for each calculated value.
I saw that there is already an answer that uses memoization, but I would like to propose a generic implementation, using python decorators:
def memoize(func):
def wrapper(*args):
if args in wrapper.d:
return wrapper.d[args]
ret_val = func(*args)
wrapper.d[args] = ret_val
return ret_val
wrapper.d = {}
return wrapper
#memoize
def f(x):
...
Now f is a memoized version of itself.
With this implementation you can memoize any function using the #memoize decorator.
Use map() !!
comp = [x for x in map(f, l) if x]
f is the function f(X), l is the list
map() will return the result of f(x) for each x in the list.
Here is my solution:
filter(None, [f(x) for x in l])
How about defining:
def truths(L):
"""Return the elements of L that test true"""
return [x for x in L if x]
So that, for example
> [wife.children for wife in henry8.wives]
[[Mary1], [Elizabeth1], [Edward6], [], [], []]
> truths(wife.children for wife in henry8.wives)
[[Mary1], [Elizabeth1], [Edward6]]

How can a function return a dynamic value that depends on the number of receivers in Python?

I was trying to do a "strange" (but useful in my case) function that can return a dynamic list whose len depends on the amount of receiver.
For example:
f() returns a dynamic list of None, so I can do the following:
a = f() => a = None
a, b = f() => a=b= None
(a, b) = f() => a=b= None
(a, b, c, d, e, f) = f() => a=b=c=d=e=f= None
I think this might be done via generator comprehension or iterator, but I was blocked on how to get the amount of recevier. Maybe I was in the wrong direction. Would you advise me some tips?
Any helps will be appreciated.
Many Thank,
Tiezhen
This is not possible in Python. The function on the right hand site has no knowledge of the context it was called in. The right hand site is evaluated before any of the name bindings take place.
Unfortunately, Python unpacks returned tuples using the Pythonic "it's easier to ask forgiveness than permission" approach. That is, if you have a statement:
a,b,c = f()
Behind the scenes, it's doing something along the lines of:
try:
a = returned[0]
b = returned[1]
c = returned[2]
except IndexError:
raise ValueError('need more than k values to unpack')
try:
_ = returned[4]
except IndexError:
pass
else:
raise ValueError('too many values to unpack')
So it's discovering dynamically the number of values returned. Unfortunately, that precludes us from being clever and creating a new type for handling variable returns:
class VariableReturn(object):
def __getitem__(self, index):
return ...
In Python 3, you can sort of do what you're asking, but the burden is on the caller, not the function being called. The function should always return the same number of results, but we'll trap the remaining results using extended tuple unpacking, as shown in this StackOverflow question.
Using this approach, you can return as many results as you'd like, but you need to always return at least as many as you need in the maximal case. The rest get packed into a trailing tuple.
a,*others = f()
a,b,*others = f()
a,b,c,*others = f()
If you don't mind using Python 3, you can ignore what you don't need, for example:
a, b, c, d, *_ = (x for x in range(100))
Try this:
def f(n):
return (None, ) * n
For example:
a, b, c = f(3)
... That's about as far as you can get, since in Python there's no way to know how many variables are in the left-hand side of an assignment.
Can't be done.
Functions in Python return one value, only. While it may sometimes look like more, it's still just one value: a tuple. Multiple assignment is then a process of tuple unpacking.
Your question then can be restated: can we create an object that acts like a tuple of varying length, depending on how many values need to be unpacked? And that's simply not made available as an option.
Probably the closest I can think of is to use a generator and get the desired number of items with itertools.islice:
a = itertools.count()
x, y, z = itertools.islice(a, 3) # 0, 1, 2
u, v = itertools.islice(a, 2) # 3, 4
But that's pretty far from what was hoped for.
pretty not nice but perhaps this helps you:
def f(x):
for i in x:
globals()[i] = None
f(['a','b','c'])

Accessing Class Variables from a List in a nice way in Python

Suppose I have a list X = [a, b, c] where a, b, c are instances of the same class C.
Now, all these instances a,b,c, have a variable called v, a.v, b.v, c.v ...
I simply want a list Y = [a.v, b.v, c.v]
Is there a nice command to do this?
The best way I can think of is:
Y = []
for i in X
Y.append(i.v)
But it doesn't seem very elegant ~ since this needs to be repeated for any given "v"
Any suggestions? I couldn't figure out a way to use "map" to do this.
That should work:
Y = [x.v for x in X]
The list comprehension is the way to go.
But you also said you don't know how to use map to do it. Now, I would not recommend to use map for this at all, but it can be done:
map( lambda x: x.v, X)
that is, you create an anonymous function (a lambda) to return that attribute.
If you prefer to use the python library methods (know thy tools...) then something like:
map(operator.attrgetter("v"),X)
should also work.
I would do a list comprehension:
Y = [ i.v for i in X ]
It is shorter and more conveniant.

Most pythonic form for mapping a series of statements?

This is something that has bugged me for some time. I learnt Haskell before I learnt Python, so I've always been fond of thinking of many computations as a mapping onto a list. This is beautifully expressed by a list comprehension (I'm giving the pythonic version here):
result = [ f(x) for x in list ]
In many cases though, we want to execute more than a single statement on x, say:
result = [ f(g(h(x))) for x in list ]
This very quickly gets clunky, and difficult to read.
My normal solution to this is to expand this back into a for loop:
result = []
for x in list:
x0 = h(x)
x1 = g(x0)
x2 = f(x1)
result.append(x2)
One thing about this that bothers me no end is having to initialize the empty list 'result'. It's a triviality, but it makes me unhappy. I was wondering if there were any alternative equivalent forms. One way may be to use a local function(is that what they're called in Python?)
def operation(x):
x0 = h(x)
x1 = g(x0)
x2 = f(x1)
return x2
result = [ operation(x) for x in list ]
Are there any particular advantages/disadvantages to either of the two forms above? Or is there perhaps a more elegant way?
You can easily do function composition in Python.
Here's a demonstrates of a way to create a new function which is a composition of existing functions.
>>> def comp( a, b ):
def compose( args ):
return a( b( args ) )
return compose
>>> def times2(x): return x*2
>>> def plus1(x): return x+1
>>> comp( times2, plus1 )(32)
66
Here's a more complete recipe for function composition. This should make it look less clunky.
Follow the style that most matches your tastes.
I would not worry about performance; only in case you really see some issue you can try to move to a different style.
Here some other possible suggestions, in addition to your proposals:
result = [f(
g(
h(x)
)
)
for x in list]
Use progressive list comprehensions:
result = [h(x) for x in list]
result = [g(x) for x in result]
result = [f(x) for x in result]
Again, that's only a matter of style and taste. Pick the one you prefer most, and stick with it :-)
If this is something you're doing often and with several different statements you could write something like
def seriesoffncs(fncs,x):
for f in fncs[::-1]:
x=f(x)
return x
where fncs is a list of functions. so seriesoffncs((f,g,h),x) would return
f(g(h(x))).
This way if you later in your code need to workout h(q(g(f(x)))) you would simply do seriesoffncs((h,q,g,f),x) rather than make a new operations function for each combination of functions.
If your only concerned with the last result, your last answer is the best. It's clear for anyone looking at it what your doing.
I often take any code that starts to get complex and move it to a function. This basically serves as a comment for that block of code. (any complex code probably needs a re-write anyway, and putting it in a function I can go back and work on it later)
def operation(x):
x0 = h(x)
x1 = g(x0)
x2 = f(x1)
return x2
result = [ operation(x) for x in list]
A variation of dagw.myopenid.com's function:
def chained_apply(*args):
val = args[-1]
for f in fncs[:-1:-1]:
val=f(val)
return val
Instead of seriesoffncs((h,q,g,f),x) now you can call:
result = chained_apply(foo, bar, baz, x)
As far as I know there's no built-in/native syntax for composition in Python, but you can write your own function to compose stuff without too much trouble.
def compose(*f):
return f[0] if len(f) == 1 else lambda *args: f[0](compose(*f[1:])(*args))
def f(x):
return 'o ' + str(x)
def g(x):
return 'hai ' + str(x)
def h(x, y):
return 'there ' + str(x) + str(y) + '\n'
action = compose(f, g, h)
print [action("Test ", item) for item in [1, 2, 3]]
Composing outside the comprehension isn't required, of course.
print [compose(f, g, h)("Test ", item) for item in [1, 2, 3]]
This way of composing will work for any number of functions (well, up to the recursion limit) with any number of parameters for the inner function.
There are cases where it's best to go back to the for-loop, yes, but more often I prefer one of these approaches:
Use appropriate line breaks and indentation to keep it readable:
result = [blah(blah(blah(x)))
for x in list]
Or extract (enough of) the logic into another function, as you mention. But not necessarily local; Python programmers prefer flat to nested structure, if you can see a reasonable way of factoring the functionality out.
I came to Python from the functional-programming world, too, and share your prejudice.

Categories

Resources