Perform a function on result of another function if not none - python

I have a small snippet of code with two functions in it.
I want to call the first function if it receives a response then perform a function on that response. Then assign the result to another variable.
In a verbose way it looks like:
result = get_something()
if result:
answer = transform(result)
alternatively I could do
if get_something():
answer = transform(get_something())
but that requires calling the first function twice
is there a way to do all of this on one line a bit like a ternary (maybe as a lambda)
answer = transform(result) if get_something() else None
Obviously in the above there is nothing to state what result is but I need to say basically where result = get_something()
I can do that in a list comprehension but that seems a bit dumb
answer = [transform(x) for x in [get_something()] if x][0]

In the latest Python version (Python 3.8) there's a new assignment that may be useful for you, :=:
There is new syntax := that assigns values to variables as part of a larger expression. It is affectionately known as “walrus operator” due to its resemblance to the eyes and tusks of a walrus.
if (n := len(a)) > 10:
print(f"List is too long ({n} elements, expected <= 10)")
In this example, the assignment expression helps avoid calling len() twice:

We can in Python 3.8 with assignment expressions:
if (result := get_something()) is not None:
# do something with result

Although I don't fully understand the reasons for doing things this way (which is less clear than any of the others), here's an example using lambda:
>>> def get_something(flag): # Added the flag argument, to mimic different return values
... return 5 if flag else None
...
>>> answer = (lambda func, arg: func(arg) if arg else None)(int, get_something(True))
>>> answer
5
>>> answer = (lambda func, arg: func(arg) if arg else None)(int, get_something(False))
>>> answer
>>>

Related

How to integrate 'append' and 'print' Python code in a line?

python newbie here. I wonder if I can write the append and print syntax in a line instead of two. I tried twice but failed, as shown below:
a = [1,2,3,4]
y = 7
a.append(y)
print(a) #correct [1, 2, 3, 4, 7]
(1)
print(a.append(y)) #retuns None
(2)
print(a = a.append(y)) #returns 'a' is an invalid keyword argument for print()
A more important question is: May I know the reason of failure? Thank you for your answer.
.append() returns None, so that's what print() is printing in attempt #1.
In attempt #2, you can't perform an assignment quite like that. If you have Python 3.8 or later, you can use the assignment expression operator := like this:
print(a := a.append(y))
but not only will that still not print a, it replaces a with the None that .append() returns.
As for how to add to a and print a at the same time, since .append() returns None, this construct will work:
print(a.append(y) or a)
Since the left side of the or will evaluate to None, the value of the expression will be the right side, a.
But I wouldn't do that. It's unnecessary and confusing. Clever one-liners are no substitute for readable code.
I think you can't do this using append, but try this:
a = [1,2,3,4]
y = 7
print([*a,y])

Python: general iterator or pure function for testing any condition across list

I would like to have a function AllTrue that takes three arguments:
List: a list of values
Function: a function to apply to all values
Condition: something to test against the function's output
and return a boolean of whether or not all values in the list match the criteria.
I can get this to work for basic conditions as follows:
def AllTrue(List, Function = "Boolean", Condition = True):
flag = True
condition = Condition
if Function == "Boolean"
for element in List:
if element != condition:
flag = False
break
else:
Map = map(Function, List)
for m in Map:
if m != condition:
flag = False
break
return flag
Since python doesn't have function meant for explicitly returning if something is True, I just make the default "Boolean". One could clean this up by defining TrueQ to return True if an element is True and then just mapping TrueQ on the List.
The else handles queries like:
l = [[0,1], [2,3,4,5], [6,7], [8,9],[10]]
AllTrue(l, len, 2)
#False
testing if all elements in the list are of length 2. However, it can't handle more complex conditions like >/< or compound conditions like len > 2 and element[0] == 15
How can one do this?
Cleaned up version
def TrueQ(item):
return item == True
def AllTrue(List, Function = TrueQ, Condition = True):
flag = True
condition = Condition
Map = map(Function, List)
for m in Map:
if m != condition:
flag = False
break
return flag
and then just call AllTrue(List,TrueQ)
Python already has built-in the machinery you are trying to build. For example to check if all numbers in a list are even the code could be:
if all(x%2==0 for x in L):
...
if you want to check that all values are "truthy" the code is even simpler:
if all(L):
...
Note that in the first version the code is also "short-circuited", in other words the evaluation stops as soon as the result is known. In:
if all(price(x) > 100 for x in stocks):
...
the function price will be called until the first stock is found with a lower or equal price value. At that point the search will stop because the result is known to be False.
To check that all lengths are 2 in the list L the code is simply:
if all(len(x) == 2 for x in L):
...
i.e. more or less a literal translation of the request. No need to write a function for that.
If this kind of test is a "filter" that you want to pass as a parameter to another function then a lambda may turn out useful:
def search_DB(test):
for record in database:
if test(record):
result.append(record)
...
search_DB(lambda rec: all(len(x) == 2 for x in rec.strings))
I want a function that takes a list, a function, and a condition, and tells me if every element in the list matches the condition. i.e. foo(List, Len, >2)
In Python >2 is written lambda x : x>2.
There is (unfortunately) no metaprogramming facility in Python that would allow to write just >2 or things like ·>2 except using a string literal evaluation with eval and you don't want to do that. Even the standard Python library tried going down that path (see namedtuple implementation in collections) but it's really ugly.
I'm not saying that writing >2 would be a good idea, but that it would be nice to have a way to do that in case it was a good idea. Unfortunately to have decent metaprogramming abilities you need a homoiconic language representing code as data and therefore you would be programming in Lisp or another meta-language, not Python (programming in Lisp would indeed be a good idea, but for reasons unknown to me that approach is still unpopular).
Given that, the function foo to be called like
foo(L, len, lambda x : x > 2)
is just
def foo(L, f=lambda x : x, condition=lambda x: x):
return all(condition(f(x)) for x in L)
but no Python programmer would write such a function, because the original call to foo is actually more code and less clear than inlining it with:
all(len(x) > 2 for x in L)
and requires you to also learn about this thing foo (that does what all and a generator expression would do, just slower, with more code and more obfuscated).
You are reinventing the wheel. Just use something like this:
>>> l = [[0,1], [2,3,4,5], [6,7], [8,9],[10]]
>>> def all_true(iterable, f, condition):
... return all(condition(f(e)) for e in iterable)
...
>>> def cond(x): return x == 2
...
>>> all_true(l, len, cond)
False
You can define a different function to check a different condition:
>>> def cond(x): return x >= 1
...
>>> all_true(l, len, b)
True
>>>
And really, having your own function that does this seems like overkill. For example, to deal with your "complex condition" you could simply do something like:
>>> l = [[0,2],[0,1,2],[0,1,3,4]]
>>> all(len(sub) > 2 and sub[0] == 5 for sub in l)
False
>>> all(len(sub) > 1 and sub[0] == 0 for sub in l)
True
>>>
I think the ideal solution in this case may be:
def AllTrue(List, Test = lambda x:x):
all(Test(x) for x in List)
This thereby allows complex queries like:
l = [[0, 1], [1, 2, 3], [2, 5]]
AllTrue(l, lambda x: len(x) > 2 and x[0] == 1)
To adhere to Juanpa's suggestion, here it is in python naming conventions and an extension of what I posted in the question now with the ability to handle simple conditions like x > value.
from operator import *
all_true(a_list, a_function, an_operator, a_value):
a_map = map(a_function, a_list)
return all( an_operator(m, a_value) for m in a_map)
l = [[0,2],[0,1,2],[0,1,3,4]]
all_true(l, len, gt, 2)
#True
Note: this works for single conditions, but not for complex conditions like
len > 2 and element[0] == 5

Python syntax for "if a or b or c but not all of them"

I have a python script that can receive either zero or three command line arguments. (Either it runs on default behavior or needs all three values specified.)
What's the ideal syntax for something like:
if a and (not b or not c) or b and (not a or not c) or c and (not b or not a):
?
If you mean a minimal form, go with this:
if (not a or not b or not c) and (a or b or c):
Which translates the title of your question.
UPDATE: as correctly said by Volatility and Supr, you can apply De Morgan's law and obtain equivalent:
if (a or b or c) and not (a and b and c):
My advice is to use whichever form is more significant to you and to other programmers. The first means "there is something false, but also something true", the second "There is something true, but not everything". If I were to optimize or do this in hardware, I would choose the second, here just choose the most readable (also taking in consideration the conditions you will be testing and their names). I picked the first.
How about:
conditions = [a, b, c]
if any(conditions) and not all(conditions):
...
Other variant:
if 1 <= sum(map(bool, conditions)) <= 2:
...
This question already had many highly upvoted answers and an accepted answer, but all of them so far were distracted by various ways to express the boolean problem and missed a crucial point:
I have a python script that can receive either zero or three command
line arguments. (Either it runs on default behavior or needs all three
values specified)
This logic should not be the responsibility of library code in the first place, rather it should be handled by the command-line parsing (usually argparse module in Python). Don't bother writing a complex if statement, instead prefer to setup your argument parser something like this:
#!/usr/bin/env python
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('--foo', nargs=3, default=['x', 'y', 'z'])
args = parser.parse_args()
print(args.foo)
And yes, it should be an option not a positional argument, because it is after all optional.
edited: To address the concern of LarsH in the comments, below is an example of how you could write it if you were certain you wanted the interface with either 3 or 0 positional args. I am of the opinion that the previous interface is better style (because optional arguments should be options), but here's an alternative approach for the sake of completeness. Note we're overriding kwarg usage when creating your parser, because argparse will auto-generate a misleading usage message otherwise!
#!/usr/bin/env python
import argparse
parser = argparse.ArgumentParser(usage='%(prog)s [-h] [a b c]\n')
parser.add_argument('abc', nargs='*', help='specify 3 or 0 items', default=['x', 'y', 'z'])
args = parser.parse_args()
if len(args.abc) != 3:
parser.error('expected 3 arguments')
print(args.abc)
Here are some usage examples:
# default case
$ ./three_or_none.py
['x', 'y', 'z']
# explicit case
$ ./three_or_none.py 1 2 3
['1', '2', '3']
# example failure mode
$ ./three_or_none.py 1 2
usage: three_or_none.py [-h] [a b c]
three_or_none.py: error: expected 3 arguments
I'd go for:
conds = iter([a, b, c])
if any(conds) and not any(conds):
# okay...
I think this should short-circuit fairly efficiently
Explanation
By making conds an iterator, the first use of any will short circuit and leave the iterator pointing to the next element if any item is true; otherwise, it will consume the entire list and be False. The next any takes the remaining items in the iterable, and makes sure than there aren't any other true values... If there are, the whole statement can't be true, thus there isn't one unique element (so short circuits again). The last any will either return False or will exhaust the iterable and be True.
note: the above checks if only a single condition is set
If you want to check if one or more items, but not every item is set, then you can use:
not all(conds) and any(conds)
The English sentence:
“if a or b or c but not all of them”
Translates to this logic:
(a or b or c) and not (a and b and c)
The word "but" usually implies a conjunction, in other words "and". Furthermore, "all of them" translates to a conjunction of conditions: this condition, and that condition, and other condition. The "not" inverts that entire conjunction.
I do not agree that the accepted answer. The author neglected to apply the most straightforward interpretation to the specification, and neglected to apply De Morgan's Law to simplify the expression to fewer operators:
not a or not b or not c -> not (a and b and c)
while claiming that the answer is a "minimal form".
What about: (unique condition)
if (bool(a) + bool(b) + bool(c) == 1):
Notice, if you allow two conditions too you could do that
if (bool(a) + bool(b) + bool(c) in [1,2]):
This returns True if one and only one of the three conditions is True. Probably what you wanted in your example code.
if sum(1 for x in (a,b,c) if x) == 1:
To be clear, you want to made your decision based on how much of the parameters are logical TRUE (in case of string arguments - not empty)?
argsne = (1 if a else 0) + (1 if b else 0) + (1 if c else 0)
Then you made a decision:
if ( 0 < argsne < 3 ):
doSth()
Now the logic is more clear.
And why not just count them ?
import sys
a = sys.argv
if len(a) = 1 :
# No arguments were given, the program name count as one
elif len(a) = 4 :
# Three arguments were given
else :
# another amount of arguments was given
If you don't mind being a bit cryptic you can simly roll with 0 < (a + b + c) < 3 which will return true if you have between one and two true statements and false if all are false or none is false.
This also simplifies if you use functions to evaluate the bools as you only evaluate the variables once and which means you can write the functions inline and do not need to temporarily store the variables. (Example: 0 < ( a(x) + b(x) + c(x) ) < 3.)
The question states that you need either all three arguments (a and b and c) or none of them (not (a or b or c))
This gives:
(a and b and c) or not (a or b or c)
As I understand it, you have a function that receives 3 arguments, but if it does not it will run on default behavior. Since you have not explained what should happen when 1 or 2 arguments are supplied I will assume it should simply do the default behavior. In which case, I think you will find the following answer very advantageous:
def method(a=None, b=None, c=None):
if all([a, b, c]):
# received 3 arguments
else:
# default behavior
However, if you want 1 or 2 arguments to be handled differently:
def method(a=None, b=None, c=None):
args = [a, b, c]
if all(args):
# received 3 arguments
elif not any(args):
# default behavior
else:
# some args (raise exception?)
note: This assumes that "False" values will not be passed into this method.
If you work with an iterator of conditions, it could be slow to access. But you don't need to access each element more than once, and you don't always need to read all of it. Here's a solution that will work with infinite generators:
#!/usr/bin/env python3
from random import randint
from itertools import tee
def generate_random():
while True:
yield bool(randint(0,1))
def any_but_not_all2(s): # elegant
t1, t2 = tee(s)
return False in t1 and True in t2 # could also use "not all(...) and any(...)"
def any_but_not_all(s): # simple
hadFalse = False
hadTrue = False
for i in s:
if i:
hadTrue = True
else:
hadFalse = True
if hadTrue and hadFalse:
return True
return False
r1, r2 = tee(generate_random())
assert any_but_not_all(r1)
assert any_but_not_all2(r2)
assert not any_but_not_all([True, True])
assert not any_but_not_all2([True, True])
assert not any_but_not_all([])
assert not any_but_not_all2([])
assert any_but_not_all([True, False])
assert any_but_not_all2([True, False])
When every given bool is True, or when every given bool is False...
they all are equal to each other!
So, we just need to find two elements which evaluates to different bools
to know that there is at least one True and at least one False.
My short solution:
not bool(a)==bool(b)==bool(c)
I belive it short-circuits, cause AFAIK a==b==c equals a==b and b==c.
My generalized solution:
def _any_but_not_all(first, iterable): #doing dirty work
bool_first=bool(first)
for x in iterable:
if bool(x) is not bool_first:
return True
return False
def any_but_not_all(arg, *args): #takes any amount of args convertable to bool
return _any_but_not_all(arg, args)
def v_any_but_not_all(iterable): #takes iterable or iterator
iterator=iter(iterable)
return _any_but_not_all(next(iterator), iterator)
I wrote also some code dealing with multiple iterables, but I deleted it from here because I think it's pointless. It's however still available here.
This is basically a "some (but not all)" functionality (when contrasted with the any() and all() builtin functions).
This implies that there should be Falses and Trues among the results. Therefore, you can do the following:
some = lambda ii: frozenset(bool(i) for i in ii).issuperset((True, False))
# one way to test this is...
test = lambda iterable: (any(iterable) and (not all(iterable))) # see also http://stackoverflow.com/a/16522290/541412
# Some test cases...
assert(some(()) == False) # all() is true, and any() is false
assert(some((False,)) == False) # any() is false
assert(some((True,)) == False) # any() and all() are true
assert(some((False,False)) == False)
assert(some((True,True)) == False)
assert(some((True,False)) == True)
assert(some((False,True)) == True)
One advantage of this code is that you only need to iterate once through the resulting (booleans) items.
One disadvantage is that all these truth-expressions are always evaluated, and do not do short-circuiting like the or/and operators.

Print "Foo" if an element is in a list

I have tried:
>>> l = [1,2,3]
>>> x = 1
>>> x in l and lambda: print("Foo")
x in l && print "Horray"
^
SyntaxError: invalid syntax
A bit of googling revealed that print is a statement in python2 whereas it's a function in python3. But, I have tried the above snipped in python3 and it throws SyntaxError exception.
Any idea on how can I do it in one line? (Readability or google programming practice is not an issue here)
l = [1, 2, 3]
x = 1
if x in l: print "Foo"
I'm not being a smart ass, this is the way to do it in one line. Or, if you're using Python3:
if x in l: print("Foo")
lambda creates, well a lambda. It needs to be called to execute it. You cannot do this that way, because Python doesn't allow statements in this context, only expressions (including function calls).
To make print a function in Python 2.x, try:
from __future__ import print_function
x in l and print('foo')
Be wary though. If you try:
x in l and print('foo') or print('bar')
it won't work, because print returns None, so the first and expression is False, so both prints will be executed. In Python 3.x you don't need the import.
If you won't have complex short-circuiting (i.e. just one and or or), or you know your functions or expressions won't surprise the short-circuiting logic, there's nothing wrong with the code. Otherwise, try the non-short-circuiting 1-liner:
print('foo') if x in l else print('bar')
This form is recommended only if the probability/expectation of the conditional to be True is vastly higher than being False. Otherwise, plain good-old if-else is the way to go.
Getting rid of the shortcomings of print as a statement in Python2.x using from __future__ import print_function is the first step. Then the following all work:
x in l and (lambda: print("yes"))() # what an overkill!
(x in l or print("no")) and print("yes") # note the order, print returns None
print("yes") if x in l else print("no") # typical A if Cond else Y
print("yes" if x in l else "no") # a more condensed form
For even more fun, if you're into this, you can consider this - prints and returns True or False, depending on the x in l condition (to get the False I used the double not):
def check_and_print(x, l):
return x in l and not print("yes") or not not print("no")
That was ugly. To make the print transparent, you could define 2 other version
of print, which return True or False. This could actually be useful for logging:
def trueprint(*args, **kwargs):
print(*args, **kwargs)
return True
def falseprint(*args, **kwargs):
return not trueprint(*args, **kwargs)
result = x in l and trueprint("yes") or falseprint("no")
If you want to print something different in both true and false cases, use a conditional expression to create the value to print: print ('foo' if x in l else 'bar').
If you just want a function in Python 2 that outputs, you can try sys.stdout.write (after you first import sys of course), but keep in mind that this is nowhere near as flexible; here you're treating the standard output as a file-like object (which it is).
lambda almost certainly buys you nothing here.
Using and-or chaining tricks is incredibly un-Pythonic. The fact that people struggled with these hacks anyway, knowing how awful they were, was exactly why those conditional expressions from point 1 were added to the language. There was a lot of discussion regarding syntax.

Python idiom to return first item or None

I'm calling a bunch of methods that return a list. The list may be empty. If the list is non-empty, I want to return the first item; otherwise, I want to return None. This code works:
def main():
my_list = get_list()
if len(my_list) > 0:
return my_list[0]
return None
but it seems to me that there should be a simple one-line idiom for doing this. Is there?
Python 2.6+
next(iter(your_list), None)
If your_list can be None:
next(iter(your_list or []), None)
Python 2.4
def get_first(iterable, default=None):
if iterable:
for item in iterable:
return item
return default
Example:
x = get_first(get_first_list())
if x:
...
y = get_first(get_second_list())
if y:
...
Another option is to inline the above function:
for x in get_first_list() or []:
# process x
break # process at most one item
for y in get_second_list() or []:
# process y
break
To avoid break you could write:
for x in yield_first(get_first_list()):
x # process x
for y in yield_first(get_second_list()):
y # process y
Where:
def yield_first(iterable):
for item in iterable or []:
yield item
return
The best way is this:
a = get_list()
return a[0] if a else None
You could also do it in one line, but it's much harder for the programmer to read:
return (get_list()[:1] or [None])[0]
(get_list() or [None])[0]
That should work.
BTW I didn't use the variable list, because that overwrites the builtin list() function.
The most python idiomatic way is to use the next() on a iterator since list is iterable. just like what #J.F.Sebastian put in the comment on Dec 13, 2011.
next(iter(the_list), None) This returns None if the_list is empty. see next() Python 2.6+
or if you know for sure the_list is not empty:
iter(the_list).next() see iterator.next() Python 2.2+
If you find yourself trying to pluck the first thing (or None) from a list comprehension you can switch to a generator to do it like:
next((x for x in blah if cond), None)
Pro: works if blah isn't indexable Con: it's unfamiliar syntax. It's useful while hacking around and filtering stuff in ipython though.
The OP's solution is nearly there, there are just a few things to make it more Pythonic.
For one, there's no need to get the length of the list. Empty lists in Python evaluate to False in an if check. Just simply say
if list:
Additionally, it's a very Bad Idea to assign to variables that overlap with reserved words. "list" is a reserved word in Python.
So let's change that to
some_list = get_list()
if some_list:
A really important point that a lot of solutions here miss is that all Python functions/methods return None by default. Try the following below.
def does_nothing():
pass
foo = does_nothing()
print foo
Unless you need to return None to terminate a function early, it's unnecessary to explicitly return None. Quite succinctly, just return the first entry, should it exist.
some_list = get_list()
if some_list:
return list[0]
And finally, perhaps this was implied, but just to be explicit (because explicit is better than implicit), you should not have your function get the list from another function; just pass it in as a parameter. So, the final result would be
def get_first_item(some_list):
if some_list:
return list[0]
my_list = get_list()
first_item = get_first_item(my_list)
As I said, the OP was nearly there, and just a few touches give it the Python flavor you're looking for.
Python idiom to return first item or None?
The most Pythonic approach is what the most upvoted answer demonstrated, and it was the first thing to come to my mind when I read the question. Here's how to use it, first if the possibly empty list is passed into a function:
def get_first(l):
return l[0] if l else None
And if the list is returned from a get_list function:
l = get_list()
return l[0] if l else None
New in Python 3.8, Assignment Expressions
Assignment expressions use the in-place assignment operator (informally called the walrus operator), :=, new in Python 3.8, allows us to do the check and assignment in-place, allowing the one-liner:
return l[0] if (l := get_list()) else None
As a long-time Python user, this feels like we're trying to do too much on one line - I feel it would be better style to do the presumptively equally performant:
if l := get_list():
return l[0]
return None
In support of this formulation is Tim Peter's essay in the PEP proposing this change to the language. He didn't address the first formulation, but based on the other formulations he did like, I don't think he would mind.
Other ways demonstrated to do this here, with explanations
for
When I began trying to think of clever ways to do this, this is the second thing I thought of:
for item in get_list():
return item
This presumes the function ends here, implicitly returning None if get_list returns an empty list. The below explicit code is exactly equivalent:
for item in get_list():
return item
return None
if some_list
The following was also proposed (I corrected the incorrect variable name) which also uses the implicit None. This would be preferable to the above, as it uses the logical check instead of an iteration that may not happen. This should be easier to understand immediately what is happening. But if we're writing for readability and maintainability, we should also add the explicit return None at the end:
some_list = get_list()
if some_list:
return some_list[0]
slice or [None] and select zeroth index
This one is also in the most up-voted answer:
return (get_list()[:1] or [None])[0]
The slice is unnecessary, and creates an extra one-item list in memory. The following should be more performant. To explain, or returns the second element if the first is False in a boolean context, so if get_list returns an empty list, the expression contained in the parentheses will return a list with 'None', which will then be accessed by the 0 index:
return (get_list() or [None])[0]
The next one uses the fact that and returns the second item if the first is True in a boolean context, and since it references my_list twice, it is no better than the ternary expression (and technically not a one-liner):
my_list = get_list()
return (my_list and my_list[0]) or None
next
Then we have the following clever use of the builtin next and iter
return next(iter(get_list()), None)
To explain, iter returns an iterator with a .next method. (.__next__ in Python 3.) Then the builtin next calls that .next method, and if the iterator is exhausted, returns the default we give, None.
redundant ternary expression (a if b else c) and circling back
The below was proposed, but the inverse would be preferable, as logic is usually better understood in the positive instead of the negative. Since get_list is called twice, unless the result is memoized in some way, this would perform poorly:
return None if not get_list() else get_list()[0]
The better inverse:
return get_list()[0] if get_list() else None
Even better, use a local variable so that get_list is only called one time, and you have the recommended Pythonic solution first discussed:
l = get_list()
return l[0] if l else None
Regarding idioms, there is an itertools recipe called nth.
From itertools recipes:
def nth(iterable, n, default=None):
"Returns the nth item or a default value"
return next(islice(iterable, n, None), default)
If you want one-liners, consider installing a library that implements this recipe for you, e.g. more_itertools:
import more_itertools as mit
mit.nth([3, 2, 1], 0)
# 3
mit.nth([], 0) # default is `None`
# None
Another tool is available that only returns the first item, called more_itertools.first.
mit.first([3, 2, 1])
# 3
mit.first([], default=None)
# None
These itertools scale generically for any iterable, not only for lists.
for item in get_list():
return item
Frankly speaking, I do not think there is a better idiom: your is clear and terse - no need for anything "better". Maybe, but this is really a matter of taste, you could change if len(list) > 0: with if list: - an empty list will always evaluate to False.
On a related note, Python is not Perl (no pun intended!), you do not have to get the coolest code possible.
Actually, the worst code I have seen in Python, was also very cool :-) and completely unmaintainable.
By the way, most of the solution I have seen here do not take into consideration when list[0] evaluates to False (e.g. empty string, or zero) - in this case, they all return None and not the correct element.
my_list[0] if len(my_list) else None
Not sure how pythonic this is but until there is a first function in the library I include this in the source:
first = lambda l, default=None: next(iter(l or []), default)
It's just one line (conforms to black) and avoids dependencies.
Out of curiosity, I ran timings on two of the solutions. The solution which uses a return statement to prematurely end a for loop is slightly more costly on my machine with Python 2.5.1, I suspect this has to do with setting up the iterable.
import random
import timeit
def index_first_item(some_list):
if some_list:
return some_list[0]
def return_first_item(some_list):
for item in some_list:
return item
empty_lists = []
for i in range(10000):
empty_lists.append([])
assert empty_lists[0] is not empty_lists[1]
full_lists = []
for i in range(10000):
full_lists.append(list([random.random() for i in range(10)]))
mixed_lists = empty_lists[:50000] + full_lists[:50000]
random.shuffle(mixed_lists)
if __name__ == '__main__':
ENV = 'import firstitem'
test_data = ('empty_lists', 'full_lists', 'mixed_lists')
funcs = ('index_first_item', 'return_first_item')
for data in test_data:
print "%s:" % data
for func in funcs:
t = timeit.Timer('firstitem.%s(firstitem.%s)' % (
func, data), ENV)
times = t.repeat()
avg_time = sum(times) / len(times)
print " %s:" % func
for time in times:
print " %f seconds" % time
print " %f seconds avg." % avg_time
These are the timings I got:
empty_lists:
index_first_item:
0.748353 seconds
0.741086 seconds
0.741191 seconds
0.743543 seconds avg.
return_first_item:
0.785511 seconds
0.822178 seconds
0.782846 seconds
0.796845 seconds avg.
full_lists:
index_first_item:
0.762618 seconds
0.788040 seconds
0.786849 seconds
0.779169 seconds avg.
return_first_item:
0.802735 seconds
0.878706 seconds
0.808781 seconds
0.830074 seconds avg.
mixed_lists:
index_first_item:
0.791129 seconds
0.743526 seconds
0.744441 seconds
0.759699 seconds avg.
return_first_item:
0.784801 seconds
0.785146 seconds
0.840193 seconds
0.803380 seconds avg.
try:
return a[0]
except IndexError:
return None
def head(iterable):
try:
return iter(iterable).next()
except StopIteration:
return None
print head(xrange(42, 1000) # 42
print head([]) # None
BTW: I'd rework your general program flow into something like this:
lists = [
["first", "list"],
["second", "list"],
["third", "list"]
]
def do_something(element):
if not element:
return
else:
# do something
pass
for li in lists:
do_something(head(li))
(Avoiding repetition whenever possible)
Borrowing more_itertools.first_true code yields something decently readable:
def first_true(iterable, default=None, pred=None):
return next(filter(pred, iterable), default)
def get_first_non_default(items_list, default=None):
return first_true(items_list, default, pred=lambda x: x!=default)
Following code covers several scenarios by using lambda:
l1 = [1,2,3]
l2 = []
l3 = None
first_elem = lambda x: x[0] if x else None
print(first_elem(l1))
print(first_elem(l2))
print(first_elem(l3))
Using the and-or trick:
a = get_list()
return a and a[0] or None
Probably not the fastest solution, but nobody mentioned this option:
dict(enumerate(get_list())).get(0)
if get_list() can return None you can use:
dict(enumerate(get_list() or [])).get(0)
Advantages:
-one line
-you just call get_list() once
-easy to understand
My use case was only to set the value of a local variable.
Personally I found the try and except style cleaner to read
items = [10, 20]
try: first_item = items[0]
except IndexError: first_item = None
print first_item
than slicing a list.
items = [10, 20]
first_item = (items[:1] or [None, ])[0]
print first_item
How about this:
(my_list and my_list[0]) or None
Note: This should work fine for lists of objects but it might return incorrect answer in case of number or string list per the comments below.
You could use Extract Method. In other words extract that code into a method which you'd then call.
I wouldn't try to compress it much more, the one liners seem harder to read than the verbose version. And if you use Extract Method, it's a one liner ;)
Several people have suggested doing something like this:
list = get_list()
return list and list[0] or None
That works in many cases, but it will only work if list[0] is not equal to 0, False, or an empty string. If list[0] is 0, False, or an empty string, the method will incorrectly return None.
I've created this bug in my own code one too many times !
isn't the idiomatic python equivalent to C-style ternary operators
cond and true_expr or false_expr
ie.
list = get_list()
return list and list[0] or None
if mylist != []:
print(mylist[0])
else:
print(None)

Categories