What I mean is,
I'm looking for really short code that returns the lower value.
for example:
a=[1,2,3,4,5,6,7,8,9,10]
b=[1,2,3,4,5,6,7,8]
len(a) = 10
len(b) = 8
if (fill-this-in):
print(lesser-value)
And I forgot to add that if b is lower than a, I want b returned - not len(b) - the variable b.
print(min(a, b))
You're not hugely clear about what you want, so some alternatives. Given the following two lists:
a = [1,2,3,4,5,6,7,8,9,10]
b = [1,2,3,4,5,6,7,8]
To print the shortest list, you can just do..
>>> print(min(a, b))
[1, 2, 3, 4, 5, 6, 7, 8]
To get the shortest length as an number, you can either min the len() of each list, or do len(min()) (both are identical, choose which ever you find most readable)..
>>> print(min( len(a), len(b) ))
# or..
>>> print(len( min(a, b) ))
8
To print the lowest value in either list, you can supply the list as a single argument to min()
>>> a.extend(b) # Appends b to a
>>> print a
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5, 6, 7, 8]
>>> print(min(a))
1
Finally, another possibility, the list that has the lowest values in total:
>>> max( sum(a), sum(b) )
55
To print the actual list with the highest sum(), you could either use the ternary operator, like..
>>> print a if sum(a) > sum(b) else b
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
..although I never really liked (or use) it, instead using the slight longer, regular if/else statements..
>>> if sum(a) > sum(b):
... print a
... else:
... print b
...
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
If the length of the list is what makes it lower (not its values), then you actually want:
min(a, b, key=len)
which is only incidentally equivalent to
min(a, b)
in the given example.
min() should accomplish what you need
print(min(a,b))
It seems this answer may now be out of date. I just had this same question and found this answer, but wasn't getting the results I expected. Turns out Min doesn't automatically return the shorter of the two lists (in 2.7). To get that you have to use the 'key' argument (introduced in 2.5)(emphasis added):
min(iterable[, key]) min(arg1, arg2, *args[, key]) Return the smallest
item in an iterable or the smallest of two or more arguments.
If one positional argument is provided, iterable must be a non-empty
iterable (such as a non-empty string, tuple or list). The smallest
item in the iterable is returned. If two or more positional arguments
are provided, the smallest of the positional arguments is returned.
The optional key argument specifies a one-argument ordering function
like that used for list.sort(). The key argument, if supplied, must be
in keyword form (for example, min(a,b,c,key=func)).
Changed in version 2.5: Added support for the optional key argument
So in this example, although it seems to work (and still would in 2.7), it only does because the list of integers is the same. However if these were two different non-ordered lists then:
min(a,b)
would return the list with the lowest first integer.
To be sure to get the shorter of two lists, use:
min(a,b, key=len)
heads up, min(a, b, key=len) only works in python 2.5 and up I think.
(it's not working on my macbook with python 2.4, but my linux server with 2.5 is fine)
Is the following what you want?
if len(a) < len(b):
print a
else:
print b
Alternatively, if you want to use the ternary operator like #Andrew G. Johnson:
print a if len(a) < len(b) else b
PS. Remember that Python does not use braces for its blocks, and that its ternary operator is different from C-like languages.
I don't know Python but for something like this I'd use a ternary operator.
print(length(a) < length(b) ? length(a) : length(b))
One thing to note about this that if they are equal it will print length(b)
Related
In a list comprehension with a condition that has a function call in it, does Python (specifically CPython 3.9.4) call the function each time, or does it calculate the value once and then uses it?
For example if you have:
list_1 = [1, 2, 3, 4, 5, 6, 7, 8, 9]
list_2 = [x for x in list_1 if x > np.average(list_1)]
Will Python actually calculate the np.average(list_1) len(list_1) times? So would it be more optimized to write
list_1 = [1, 2, 3, 4, 5, 6, 7, 8, 9]
np_avg = np.average(list_1)
list_2 = [x for x in list_1 if x > np_avg]
instead? Or does Python already "know" to just calculate the average beforehand?
Python has to call the function each time. It cannot optimize that part, because successive calls of the function might return different results (for example because of side effects). There is no easy way for Python’s compiler to be sure that this can’t happen.
Therefore, if you (the programmer) know that the result will always be the same – like in this case – it is probably advisable to calculate the result of the function in advance and use it inside the list comprehension.
Assuming standard CPython - Short answer: Yes. Your second snippet is more efficient.
A function call in the filter part of a list comprehension will be called for each element.
We can test this quite easily with a trivial example:
def f(value):
""" Allow even values only """
print('function called')
return value % 2 == 0
mylist = [x for x in range(5) if f(x)]
# 'function called' will be printed 5 times
The above is somewhat equivalent to doing:
mylist = []
for x in range(5):
if f(x):
mylist.append(x)
Since you're comparing against the same average each time, you can indeed just calculate it beforehand and use the same value as you did in your second code snippet.
I have a dict() called twitter_users which holds TwitterUser objects as values. I want those objects to be sorted by the field mentioned. However, using sorted() does not work as I expect. I provide a lambda function that is supposed to determine if user a or user b was mentioned more often.
srt = sorted(twitter_users.values(),
cmp=(lambda a,b:
True if a.mentioned > b.mentioned else False))
for s in srt:
print s.mentioned
Unfortunately this is not working and the list srt isn't sorted in any way.
How can I make this work?
A cmp function should return an integer, 0 when equal, 1 or higher when a should come after b and -1 or lower if they should come in the opposite order.
You instead return False and True. Because the Python boolean type is a subclass of int, these objects have the values 0 and 1 when interpreted as integers. You never return -1, so you are confusing the sorting algorithm; you tell it the order of a and b is either always 'equal' or a should come before b, always. But the sorting algorithm sometimes asks for a and b swapped, in which case you gave it conflicting information!
Note that your expression is rather verbose; True if a.mentioned > b.mentioned else False could just be simplified to a.mentioned > b.mentioned; the > operator already produces either True or False. Using simple integers you can see that that is not going to produce expected results:
>>> sorted([4, 2, 5, 3, 8], cmp=lambda a, b: a > b)
[4, 2, 5, 3, 8]
while actually returning -1, 0, or 1 does work:
>>> sorted([4, 2, 5, 3, 8], cmp=lambda a, b: 1 if a > b else 0 if a == b else -1)
[2, 3, 4, 5, 8]
or instead of such a verbose expression, just use the built-in cmp() function; for your case you'd use that like this:
srt = sorted(twitter_users.values(), cmp=lambda a, b: cmp(a.mentioned, b.mentioned))
But you shouldn't really use cmp at all; there is a far simpler (and more efficient) option. Just use the key function instead, which simply returns the mentioned attribute:
srt = sorted(twitter_users.values(), key=lambda v: v.mentioned)
The key function produces values by which the actual sort takes place; this function is used to produce a Schwartzian transform. Such a transform is more efficient because it is only called O(n) times, while the cmp function is called O(n log n) times.
Because you are only accessing an attribute, instead of a lambda you can use a operator.attrgetter() object to do the attribute fetching for you:
from operator import attrgetter
srt = sorted(twitter_users.values(), key=attrgetter('mentioned'))
Imagine the following function:
def getMinAndMax(numbers):
return min(numbers), max(numbers)
What will happen if I do this?
num = getMinAndMax([1,2,3,4,5])
Will num assume the value of the first item in the tuple, min, or will something else happen? I know I can just try it, but I'm looking for some defined Python behavior here.
Your function returns the two-element tuple min([1, 2, 3, 4, 5]), max([1, 2, 3, 4, 5]) which evaluates to 1, 5. So the statement
num = getMinAndMax([1,2,3,4,5])
will bind the name num to the tuple (1, 2) and you can access the individual values as num[0] and num[1]. Python does allow you, though, to use what's usually referred to as a unpacking assignment which looks like this:
nmin, nmax = getMinAndMax([1, 2, 3, 4, 5])
That binds each name to a succeeding element of the tuple on the right-hand side and allows you to use the values without indexing. If you need a tuple of the results your formulation is simplest, though of course the expression (nmin, nmax) will re-create it from the second one.
num will be a tuple. The value of num will be equal to (1,5) in your example. Python does not check types by default, so you can safely assign whatever value of whatever type you want to whatever variable.
This question may be very straightforward and obvious to some people, but for whatever reason I have been unable to find the answer online. I did not find my answer by tinkering with IDLE and trying to understand how it worked. How does a for loop work when multiple items are specified?
a = [1,2,3,4,5]
b = [6,7,8,9,0]
for item in a and b:
print 'why does this setup give the list b but nothing from a?'
Followup Questions:
1) What might happen with other operators, such as or and not?
2) Is this proper usage, even? If so, is it messy, unsafe, or frowned upon?
So, you have two lists:
>>> a = [1,2,3,4,5]
>>> b = [6,7,8,9,0]
... and you want to iterate over a and b. So what is a and b, exactly?
>>> a and b
[6, 7, 8, 9, 0]
That might look odd, but it's the result of two facts about Python:
Every object is either True-ish or False-ish. For example:
>>> bool(a)
True
>>> bool(b)
True
In fact, all lists except the empty list [] are True-ish.
Python uses short-circuit evaluation, which means that, for a and b, it:
Checks whether a is True-ish or False-ish
If a is False-ish, evaluates to a
If a is True-ish, evaluates to b
Following those rules, you should be able to see why a and b evaluates to [6, 7, 8, 9, 0] in your case (and following the same rules for combinations of the actual values True and False will show you that short-circuit evaluation does make sense).
If what you want to actually do is iterate trough the items in a and then those in b, you can just use the + operator to concatenate them:
>>> for item in a + b:
... print item,
...
1 2 3 4 5 6 7 8 9 0
As for your followup questions:
What might happen with other operators, such as or and not?
or's rules for short-circuit evaluation are different (you can look them up for yourself or just follow the link above), and in your case a or b evaluates to [1, 2, 3, 4, 5] (in other words, a).
not always returns True for a False-ish value and False for a True-ish value, and since you can't iterate over True or False, you'll get a TypeError.
Is this proper usage, even? If so, is it messy, unsafe, or frowned upon?
Well, there's nothing illegal about it, but as you can see, it doesn't do what you want. There are circumstances where (ab)using short-circuit evaluation to choose an iterable over which to iterate might be helpful, but this isn't one of them.
As you have discovered, for loops don't work when multiple items are specified! What you're getting is an iteration over a and b. a and b returns something True if both items are true; in this case, it's the rightmost operand, since it knows it's true. The correct way to do this is with itertools.chain:
for item in itertools.chain(a, b):
print 'now we get both lists'
The code a and b doesn't join the two lists, it performs the logical and operation. Logical and of two true values, such as lists, evaluates to the value of the second argument. If you use the or operator, it evaluates to the first non-false value. Using not will evaluate to True for a non-true value and False for a true value. Normally, using the value of an and or or as anything other than a Boolean is frowned upon.
There are several ways to accomplish what you're trying to do. The simplest would be to loop over a + b - this gives you a new list. If your lists are large, you might consider using itertools.chain() which will let you loop over all the values without creating a new list object.
This seem to be a situation where Pythons use of English words ends up being confusing. The statement for item in a and b does not mean "iterate over all items in a and then all items in b" but rater "iterate over all items in the list you get when applying the and operation on the lists a and b".
So what you are doing is the latter. You are anding the two lists a and b and then looping over the result. In the interpreter, anding a and b you get
>>> a = [1,2,3,4,5]
>>> b = [6,7,8,9,0]
>>> a and b
[6, 7, 8, 9, 0]
You can of course do this for any operations, but it is still just applied before the for loop.
To loop over both lists one after the other, replace the and operation with a + to put them together, concatenate them. Like this
>>> a + b
[1, 2, 3, 4, 5, 6, 7, 8, 9, 0]
>>> for item in a + b:
... print item,
...
1 2 3 4 5 6 7 8 9 0
a and b will give b if both a and b are not None or empty or 0. Try typing 1 and 2 in Python shell.
Try:
a = [1,2,3,4,5]
b = [6,7,8,9,0]
for item in a + b:
print item
You can concatenate lists with the + operator.
You could also do:
for i in a + b:
print i
This will join the two lists and allow you to iterate through it.
Is there any pre-made optimized tool/library in Python to cut/slice lists for values "less than" something?
Here's the issue: Let's say I have a list like:
a=[1,3,5,7,9]
and I want to delete all the numbers which are <= 6, so the resulting list would be
[7,9]
6 is not in the list, so I can't use the built-in index(6) method of the list. I can do things like:
#!/usr/bin/env python
a = [1, 3, 5, 7, 9]
cut=6
for i in range(len(a)-1, -2, -1):
if a[i] <= cut:
break
b = a[i+1:]
print "Cut list: %s" % b
which would be fairly quick method if the index to cut from is close to the end of the list, but which will be inefficient if the item is close to the beginning of the list (let's say, I want to delete all the items which are >2, there will be a lot of iterations).
I can also implement my own find method using binary search or such, but I was wondering if there's a more... wide-scope built in library to handle this type of things that I could reuse in other cases (for instance, if I need to delete all the number which are >=6).
Thank you in advance.
You can use the bisect module to perform a sorted search:
>>> import bisect
>>> a[bisect.bisect_left(a, 6):]
[7, 9]
bisect.bisect_left is what you are looking for, I guess.
If you just want to filter the list for all elements that fulfil a certain criterion, then the most straightforward way is to use the built-in filter function.
Here is an example:
a_list = [10,2,3,8,1,9]
# filter all elements smaller than 6:
filtered_list = filter(lambda x: x<6, a_list)
the filtered_list will contain:
[2, 3, 1]
Note: This method does not rely on the ordering of the list, so for very large lists it might be that a method optimised for ordered searching (as bisect) performs better in terms of speed.
Bisect left and right helper function
#!/usr/bin/env python3
import bisect
def get_slice(list_, left, right):
return list_[
bisect.bisect_left(list_, left):
bisect.bisect_left(list_, right)
]
assert get_slice([0, 1, 1, 3, 4, 4, 5, 6], 1, 5) == [1, 1, 3, 4, 4]
Tested in Ubuntu 16.04, Python 3.5.2.
Adding to Jon's answer, if you need to actually delete the elements less than 6 and want to keep the same reference to the list, rather than returning a new one.
del a[:bisect.bisect_right(a,6)]
You should note as well that bisect will only work on a sorted list.