Related
I'm trying to learn python and came across some code that is nice and short but doesn't totally make sense
the context was:
def fn(*args):
return len(args) and max(args)-min(args)
I get what it's doing, but why does python do this - ie return the value rather than True/False?
10 and 7-2
returns 5. Similarly, changing the and to or will result in a change in functionality. So
10 or 7 - 2
Would return 10.
Is this legit/reliable style, or are there any gotchas on this?
TL;DR
We start by summarising the two behaviour of the two logical operators and and or. These idioms will form the basis of our discussion below.
and
Return the first Falsy value if there are any, else return the last
value in the expression.
or
Return the first Truthy value if there are any, else return the last
value in the expression.
The behaviour is also summarised in the docs, especially in this table:
Operation
Result
x or y
if x is false, then y, else x
x and y
if x is false, then x, else y
not x
if x is false, then True, else False
The only operator returning a boolean value regardless of its operands is the not operator.
"Truthiness", and "Truthy" Evaluations
The statement
len(args) and max(args) - min(args)
Is a very pythonic concise (and arguably less readable) way of saying "if args is not empty, return the result of max(args) - min(args)", otherwise return 0. In general, it is a more concise representation of an if-else expression. For example,
exp1 and exp2
Should (roughly) translate to:
r1 = exp1
if r1:
r1 = exp2
Or, equivalently,
r1 = exp2 if exp1 else exp1
Similarly,
exp1 or exp2
Should (roughly) translate to:
r1 = exp1
if not r1:
r1 = exp2
Or, equivalently,
r1 = exp1 if exp1 else exp2
Where exp1 and exp2 are arbitrary python objects, or expressions that return some object. The key to understanding the uses of the logical and and or operators here is understanding that they are not restricted to operating on, or returning boolean values. Any object with a truthiness value can be tested here. This includes int, str, list, dict, tuple, set, NoneType, and user defined objects. Short circuiting rules still apply as well.
But what is truthiness?
It refers to how objects are evaluated when used in conditional expressions. #Patrick Haugh summarises truthiness nicely in this post.
All values are considered "truthy" except for the following, which are
"falsy":
None
False
0
0.0
0j
Decimal(0)
Fraction(0, 1)
[] - an empty list
{} - an empty dict
() - an empty tuple
'' - an empty str
b'' - an empty bytes
set() - an empty set
an empty range, like range(0)
objects for which
obj.__bool__() returns False
obj.__len__() returns 0
A "truthy" value will satisfy the check performed by if or while
statements. We use "truthy" and "falsy" to differentiate from the
bool values True and False.
How and Works
We build on OP's question as a segue into a discussion on how these operators in these instances.
Given a function with the definition
def foo(*args):
...
How do I return the difference between the minimum and maximum value
in a list of zero or more arguments?
Finding the minimum and maximum is easy (use the inbuilt functions!). The only snag here is appropriately handling the corner case where the argument list could be empty (for example, calling foo()). We can do both in a single line thanks to the and operator:
def foo(*args):
return len(args) and max(args) - min(args)
foo(1, 2, 3, 4, 5)
# 4
foo()
# 0
Since and is used, the second expression must also be evaluated if the first is True. Note that, if the first expression is evaluated to be truthy, the return value is always the result of the second expression. If the first expression is evaluated to be Falsy, then the result returned is the result of the first expression.
In the function above, If foo receives one or more arguments, len(args) is greater than 0 (a positive number), so the result returned is max(args) - min(args). OTOH, if no arguments are passed, len(args) is 0 which is Falsy, and 0 is returned.
Note that an alternative way to write this function would be:
def foo(*args):
if not len(args):
return 0
return max(args) - min(args)
Or, more concisely,
def foo(*args):
return 0 if not args else max(args) - min(args)
If course, none of these functions perform any type checking, so unless you completely trust the input provided, do not rely on the simplicity of these constructs.
How or Works
I explain the working of or in a similar fashion with a contrived example.
Given a function with the definition
def foo(*args):
...
How would you complete foo to return all numbers over 9000?
We use or to handle the corner case here. We define foo as:
def foo(*args):
return [x for x in args if x > 9000] or 'No number over 9000!'
foo(9004, 1, 2, 500)
# [9004]
foo(1, 2, 3, 4)
# 'No number over 9000!'
foo performs a filtration on the list to retain all numbers over 9000. If there exist any such numbers, the result of the list comprehension is a non-empty list which is Truthy, so it is returned (short circuiting in action here). If there exist no such numbers, then the result of the list comp is [] which is Falsy. So the second expression is now evaluated (a non-empty string) and is returned.
Using conditionals, we could re-write this function as,
def foo(*args):
r = [x for x in args if x > 9000]
if not r:
return 'No number over 9000!'
return r
As before, this structure is more flexible in terms of error handling.
Quoting from Python Docs
Note that neither and nor or restrict the value and type they return
to False and True, but rather return the last evaluated argument. This
is sometimes useful, e.g., if s is a string that should be replaced by
a default value if it is empty, the expression s or 'foo' yields the
desired value.
So, this is how Python was designed to evaluate the boolean expressions and the above documentation gives us an insight of why they did it so.
To get a boolean value just typecast it.
return bool(len(args) and max(args)-min(args))
Why?
Short-circuiting.
For example:
2 and 3 # Returns 3 because 2 is Truthy so it has to check 3 too
0 and 3 # Returns 0 because 0 is Falsey and there's no need to check 3 at all
The same goes for or too, that is, it will return the expression which is Truthy as soon as it finds it, cause evaluating the rest of the expression is redundant.
Instead of returning hardcore True or False, Python returns Truthy or Falsey, which are anyway going to evaluate to True or False. You could use the expression as is, and it will still work.
To know what's Truthy and Falsey, check Patrick Haugh's answer
and and or perform boolean logic, but they return one of the actual values when they are comparing. When using and, values are evaluated in a boolean context from left to right. 0, '', [], (), {}, and None are false in a boolean context; everything else is true.
If all values are true in a boolean context, and returns the last value.
>>> 2 and 5
5
>>> 2 and 5 and 10
10
If any value is false in a boolean context and returns the first false value.
>>> '' and 5
''
>>> 2 and 0 and 5
0
So the code
return len(args) and max(args)-min(args)
returns the value of max(args)-min(args) when there is args else it returns len(args) which is 0.
Is this legit/reliable style, or are there any gotchas on this?
This is legit, it is a short circuit evaluation where the last value is returned.
You provide a good example. The function will return 0 if no arguments are passed, and the code doesn't have to check for a special case of no arguments passed.
Another way to use this, is to default None arguments to a mutable primitive, like an empty list:
def fn(alist=None):
alist = alist or []
....
If some non-truthy value is passed to alist it defaults to an empty list, handy way to avoid an if statement and the mutable default argument pitfall
Gotchas
Yes, there are a few gotchas.
fn() == fn(3) == fn(4, 4)
First, if fn returns 0, you cannot know if it was called without any parameter, with one parameter or with multiple, equal parameters :
>>> fn()
0
>>> fn(3)
0
>>> fn(3, 3, 3)
0
What does fn mean?
Then, Python is a dynamic language. It's not specified anywhere what fn does, what its input should be and what its output should look like. Therefore, it's really important to name the function correctly. Similarly, arguments don't have to be called args. delta(*numbers) or calculate_range(*numbers) might describe better what the function is supposed to do.
Argument errors
Finally, the logical and operator is supposed to prevent the function to fail if called without any argument. It still fails if some argument isn't a number, though:
>>> fn('1')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 2, in fn
TypeError: unsupported operand type(s) for -: 'str' and 'str'
>>> fn(1, '2')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 2, in fn
TypeError: '>' not supported between instances of 'str' and 'int'
>>> fn('a', 'b')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 2, in fn
TypeError: unsupported operand type(s) for -: 'str' and 'str'
Possible alternative
Here's a way to write the function according to the "Easier to ask for forgiveness than permission." principle:
def delta(*numbers):
try:
return max(numbers) - min(numbers)
except TypeError:
raise ValueError("delta should only be called with numerical arguments") from None
except ValueError:
raise ValueError("delta should be called with at least one numerical argument") from None
As an example:
>>> delta()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 7, in delta
ValueError: delta should be called with at least one numerical argument
>>> delta(3)
0
>>> delta('a')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 5, in delta
ValueError: delta should only be called with numerical arguments
>>> delta('a', 'b')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 5, in delta
ValueError: delta should only be called with numerical arguments
>>> delta('a', 3)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 5, in delta
ValueError: delta should only be called with numerical arguments
>>> delta(3, 4.5)
1.5
>>> delta(3, 5, 7, 2)
5
If you really don't want to raise an exception when delta is called without any argument, you could return some value which cannot be possible otherwise (e.g. -1 or None):
>>> def delta(*numbers):
... try:
... return max(numbers) - min(numbers)
... except TypeError:
... raise ValueError("delta should only be called with numerical arguments") from None
... except ValueError:
... return -1 # or None
...
>>>
>>> delta()
-1
Is this legit/reliable style, or are there any gotchas on this?
I would like to add to this question that it not only legit and reliable but it also ultra practical. Here is a simple example:
>>>example_list = []
>>>print example_list or 'empty list'
empty list
Therefore you can really use it at your advantage. In order to be conscise this is how I see it:
Or operator
Python's or operator returns the first Truth-y value, or the last value, and stops
And operator
Python's and operator returns the first False-y value, or the last value, and stops
Behind the scenes
In python, all numbers are interpreted as True except for 0. Therefore, saying:
0 and 10
is the same as:
False and True
Which is clearly False. It is therefore logical that it returns 0
Yes. This is the correct behaviour of and comparison.
At least in Python, A and B returns B if A is essentially True including if A is NOT Null, NOT None NOT an Empty container (such as an empty list, dict, etc). A is returned IFF A is essentially False or None or Empty or Null.
On the other hand, A or B returns A if A is essentially True including if A is NOT Null, NOT None NOT an Empty container (such as an empty list, dict, etc), otherwise it returns B.
It is easy to not notice (or to overlook) this behaviour because, in Python, any non-null non-empty object evaluates to True is treated like a boolean.
For example, all the following will print "True"
if [102]:
print "True"
else:
print "False"
if "anything that is not empty or None":
print "True"
else:
print "False"
if {1, 2, 3}:
print "True"
else:
print "False"
On the other hand, all the following will print "False"
if []:
print "True"
else:
print "False"
if "":
print "True"
else:
print "False"
if set ([]):
print "True"
else:
print "False"
to understand in simple way,
AND : if first_val is False return first_val else second_value
eg:
1 and 2 # here it will return 2 because 1 is not False
but,
0 and 2 # will return 0 because first value is 0 i.e False
and => if anyone false, it will be false. if both are true then only it will become true
OR : if first_val is False return second_val else first_value
reason is, if first is false it check whether 2 is true or not.
eg:
1 or 2 # here it will return 1 because 1 is not False
but,
0 or 2 # will return 2 because first value is 0 i.e False
or => if anyone false, it will be true. so if first value is false no matter what 2 value suppose to be.
so it returns second value what ever it can be.
if anyone is true then it will become true. if both are false then it will become false.
For this function for some reason I get errors, and I can't figure out what's wrong with it.
def bubbleSort(lis):
for pas in lis:
for i in pas:
if lis[i] > lis[i+1]:
lis[i],lis[i+1] = lis[i+1],lis[i]
I get the following errors:
Traceback (most recent call last):
File "<pyshell#5>", line 1, in <module>
bubbleSort(hello)
File "C:/Users/albert/Desktop/test.py", line 4, in bubbleSort
for i in pas:
TypeError: 'int' object is not iterable
Assuming lis is a list of integers, pas will be a single integer. for i in pas: fails because there are no is in a single integer.
Bubble sort is typically done with an outer loop that goes while there are any changes, and an inner loop that iterates over n-1 indices, not list elements. You can find a standard implementation in many places, here's the rosetta code one:
def bubble_sort(seq):
"""Inefficiently sort the mutable sequence (list) in place.
seq MUST BE A MUTABLE SEQUENCE.
As with list.sort() and random.shuffle this does NOT return
"""
changed = True
while changed:
changed = False
for i in xrange(len(seq) - 1):
if seq[i] > seq[i+1]:
seq[i], seq[i+1] = seq[i+1], seq[i]
changed = True
return None
What's the most pythonic way to take the single item of a 1-sized list in python?
I usually go for
item = singlet_list[0]
This would fail for an empty list, but I would like a way to make it fail even if the list is longer, something like:
assert(len(singlet_list) == 1)
item = singlet_list[0]
but I find this ugly. Is there anything better?
This blog post suggests an elegant solution I fell in love with:
(item,) = singlet_list
I find it much more readable, and plus it works with any iterable, even if it is not indexable.
EDIT: Let me dig a little more
This construct is called sequence unpacking or multiple assignment throughout the python documentation, but here I'm using a 1-sized tuple on the left of the assignment.
This has actually a behaviour that is similar to the 2-lines in the initial question: if the list/iterable singlet_list is of length 1, it will assign its only element to item. Otherways, it will fail with an appropriate ValueError (rather than an AssertionError as in the question's 2-liner):
>>> (item,) = [1]
>>> item
1
>>> (item,) = [1, 2]
ValueError: too many values to unpack
>>> (item,) = []
ValueError: need more than 0 values to unpack
As pointed out in the blog post, this has some advantages:
This will not to fail silently, as required by the original question.
It is much more readable than the [0] indexing, and it doesn't pass unobserved even in complex statements.
It works for any iterable object.
(Of course) it uses only one line, with respect to explicitly using assert
You could use an inline if...else and define a default value like this:
If singlet_list contains one or more values:
singlet_list = [2]
item = singlet_list[0] if singlet_list else False
print item
output:
2
If singlet_list is empty:
singlet_list = []
item = singlet_list[0] if singlet_list else False
print item
output:
False
This uses the fact that an empty list evaluates to False.
Similarly, if you would like a default value to be assigned if the list doesn't contain exactly one element, you could check the length:
item = singlet_list[0] if len(singlet_list) == 1 else False
I'm trying to return 1 instead of None when I pass an empty list through reduce(mul, a). My code:
from operator import mul
def product_list(a):
for b in a:
b = reduce(mul, a)
if b == None:
return 1
return b
print product_list([])
No matter where I place the if statement to catch for a blank list, I still receive None as output. I am still learning basics, but this makes no sense to me. I've even tried
from operator import mul
def product_list(a):
if a == None:
return 1
else:
for b in a:
b = reduce(mul, a)
if b == None or a == None:
return 1
return b
print product_list([])
just to see if it would catch the None and return 1. Does reduce() not act the way I think that it does, or is there an obvious mistake in my code that prohibits returning 1 and forces a return of None?
When a is an empty list, your function doesn't return anything, and the default return value is None.
Test for the empty list at the top:
if not a:
return 1
In your second function you only test for if a == None, but an empty list [] is never equal to None. Note that the idiomatic way to test for None is using the is object identity test instead:
if a is None:
By testing for not a instead, you catch both the case where a is an empty list and a being None.
Your code otherwise makes little sense. You loop over a but return and exit the function in the first iteration:
for b in a:
b = reduce(mul, a)
if b == None:
return 1
return b # exit the function here, having only looked at the first element in `a`.
However, I had to fix the indentation in your post and may have misunderstood the indentation of those return statements, in which case you would get a NameError instead when passing in an empty list.
You can pass a third value to reduce, which is used as a starter value.
In [6]: reduce(mul, [], 1)
Out[6]: 1
This is the best way to deal with an empty list. The case None should really be dealt with elsewhere, because it's a different kind of error: it's nothing wrong with the semantics of the program, it's because someone else has given you bad data. You should catch that explicitly, as e.g.
if not isinstance(..., collections.Iterable):
# do something
Of course, reduce will raise an error if you pass it something not iterable, and that may suffice for you.
Note that you're not passing an empty list to reduce as you say. Try it:
>>> reduce(operator.mul, [])
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: reduce() of empty sequence with no initial value
I think that perhaps you don't understand the function reduce. We can't hold it against you though -- it's not used much in python code.
Perhaps you wanted to define a function like this:
from operator import mul
def product_list(a):
try:
return reduce(mul,a)
except TypeError:
return 1
Now you can try it:
print product_list([1,2,3,4]) #24
print product_list([]) #1
if a is None or len(a) == 0:
return 1
Check the empty list condition as above.
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)