Let L = [1,2,3,4] be our list.
Then 1 in L is True. 2 in L is also True.
Is there a clean way to write (1,2) in L and have it come out true?
That is, given a list L and a test list T and the relation multi-in, if all members of T are in L, then T multi-in L is True, otherwise T multi-in L is False.
Of course I can write a multi-in function, but that seems ugly.
You want to treat (1,2) and L as sets:
set((1,2)).issubset(L)
or, nicer if you understand the notation:
set((1,2)) <= set(L)
all(x in L for x in [1, 2])
Unlike the set-based solutions, this (1) short-curcuits as soon as an element isn't found, (2) works for unhashable types and (3) reads out nice ;)
We can go improve complexity (O(n*m) currently) by going back to sets... although in a different way: Convert L to a set beforehand and you get O(1) membership test back (without needing a second set for the items to check for).
How about:
set((1,2)).issubset(L)
Use sets:
s = set([1,2])
l = set([1,2,3,4])
s.issubset(l)
The .issubset() method will tell you if all the elements in one set exist in another.
Good answers above. Another possibility is:
all(x in L for x in [1,2,3,4])
I'm not dutch, but that's the "single obvious way to do it" to me.
Related
I often see code similar to this:
return [(var, val) for val in self.domains[var]
if self.nconflicts(var, val, assignment) == 0]
and I'm like DAMN that's sexy. But then I try to drop it sometimes and I get syntax errors. Are there any particular rules for this nice form of code writing that reverses the typical placement of for and if statements?
They're called list comprehensions. The basic syntax is (I'm using parens to group my words, not as part of the syntax):
[(an expression involving x) for x in someList if (some condition)]
If the condition evaluates to true, the resulting list includes the (expression involving x). So, for example, the following list comprehension uses this to only include strings in the resulting list.
>>> myList = [1,"hello",5.4,"world"]
>>> [elem for elem in myList if type(elem)==str]
['hello', 'world']
Note that the if part is optional, and the expression involving x can be as simple as just x (often used when you are just filtering out elements from another list).
In fact, the expression involving x doesn't really have to have x in it at all. For example, if for some reason you wanted a list of 0's as long as your name you could do this:
>>> [0 for letter in "Matthew"]
[0, 0, 0, 0, 0, 0, 0]
For when you don't need the list to stick around after you make it, use generator expressions instead. (Generator expressions and list comprehensions have the same syntax.)
See list comprehensions in the Python tutorial documentation. There are quite a number of things you can do with this syntax, including creating lists, sets, and dictionaries.
Although your example code is indeed a list comprehension, you do also occasionally see the reverse if syntax for inline conditionals:
a if b else c
The concept is called list comprehension,
http://www.secnetix.de/olli/Python/list_comprehensions.hawk
Take a look at lambda functions too,
http://www.secnetix.de/olli/Python/lambda_functions.hawk
This question already has answers here:
Strange result when removing item from a list while iterating over it
(8 answers)
Closed 7 years ago.
This is the most common problem I face while trying to learn programming in python. The problem is, when I try to iterate a list using "range()" function to check if given item in list meets given condition and if yes then to delete it, it will always give "IndexError". So, is there a particular way to do this without using any other intermediate list or "while" statement? Below is an example:
l = range(20)
for i in range(0,len(l)):
if l[i] == something:
l.pop(i)
First of all, you never want to iterate over things like that in Python. Iterate over the actual objects, not the indices:
l = range(20)
for i in l:
...
The reason for your error was that you were removing an item, so the later indices cease to exist.
Now, you can't modify a list while you are looping over it, but that isn't a problem. The better solution is to use a list comprehension here, to filter out the extra items.
l = range(20)
new_l = [i for i in l if not i == something]
You can also use the filter() builtin, although that tends to be unclear in most situations (and slower where you need lambda).
Also note that in Python 3.x, range() produces a generator, not a list.
It would also be a good idea to use more descriptive variable names - I'll presume here it's for example, but names like i and l are hard to read and make it easier to introduce bugs.
Edit:
If you wish to update the existing list in place, as pointed out in the comments, you can use the slicing syntax to replace each item of the list in turn (l[:] = new_l). That said, I would argue that that case is pretty bad design. You don't want one segment of code to rely on data being updated from another bit of code in that way.
Edit 2:
If, for any reason, you need the indices as you loop over the items, that's what the enumerate() builtin is for.
You can always do this sort of thing with a list comprehension:
newlist=[i for i in oldlist if not condition ]
As others have said, iterate over the list and create a new list with just the items you want to keep.
Use a slice assignment to update the original list in-place.
l[:] = [item for item in l if item != something]
You should look the problem from the other side: add an element to a list when it is equal with "something". with list comprehension:
l = [i for i in xrange(20) if i != something]
you should not use for i in range(0,len(l)):, use for i, item in enumerate(l): instead if you need the index, for item in l: if not
you should not manipulate a structure you are iterating over. when faced to do so, iterate over a copy instead
don't name a variable l (may be mistaken as 1 or I)
if you want to filter a list, do so explicitly. use filter() or list comprehensions
BTW, in your case, you could also do:
while something in list_: list_.remove(something)
That's not very efficient, though. But depending on context, it might be more readable.
The reason you're getting an IndexError is because you're changing the length of the list as you iterate in the for-loop. Basically, here's the logic...
#-- Build the original list: [0, 1, 2, ..., 19]
l = range(20)
#-- Here, the range function builds ANOTHER list, in this case also [0, 1, 2, ..., 19]
#-- the variable "i" will be bound to each element of this list, so i = 0 (loop), then i = 1 (loop), i = 2, etc.
for i in range(0,len(l)):
if i == something:
#-- So, when i is equivalent to something, you "pop" the list, l.
#-- the length of l is now *19* elements, NOT 20 (you just removed one)
l.pop(i)
#-- So...when the list has been shortened to 19 elements...
#-- we're still iterating, i = 17 (loop), i = 18 (loop), i = 19 *CRASH*
#-- There is no 19th element of l, as l (after you popped out an element) only
#-- has indices 0, ..., 18, now.
NOTE also, that you're making the "pop" decision based on the index of the list, not what's in the indexed cell of the list. This is unusual -- was that your intention? Or did you
mean something more like...
if l[i] == something:
l.pop(i)
Now, in your specific example, (l[i] == i) but this is not a typical pattern.
Rather than iterating over the list, try the filter function. It's a built-in (like a lot of other list processing functions: e.g. map, sort, reverse, zip, etc.)
Try this...
#-- Create a function for testing the elements of the list.
def f(x):
if (x == SOMETHING):
return False
else:
return True
#-- Create the original list.
l = range(20)
#-- Apply the function f to each element of l.
#-- Where f(l[i]) is True, the element l[i] is kept and will be in the new list, m.
#-- Where f(l[i]) is False, the element l[i] is passed over and will NOT appear in m.
m = filter(f, l)
List processing functions go hand-in-hand with "lambda" functions - which, in Python, are brief, anonymous functions. so, we can re-write the above code as...
#-- Create the original list.
l = range(20)
#-- Apply the function f to each element of l.
#-- Where lambda is True, the element l[i] is kept and will be in the new list, m.
#-- Where lambda is False, the element l[i] is passed over and will NOT appear in m.
m = filter(lambda x: (x != SOMETHING), l)
Give it a go and see it how it works!
This code fails:
fCamel = 'F'
bCamel = 'B'
gap = ' '
k = ['F', ' ', 'B', 'F']
def solution(formation):
return ((formation.index(bCamel) > (len(formation) - 1 - (formation.reverse()).index(fCamel))))
solution(k)
I get an exception that says AttributeError: 'NoneType' object has no attribute 'index'.
I know that the problem is that list.reverse() returns None, modifying the list in-place. I want to use .index on the reversed list. Is there a way I can avoid using a separate statement to reverse the list before indexing into it? How?
You can use slicing to return the reversed list:
l[::-1]
You can use reversed(formation) to return a reverse iterator of formation. When you call formation.reverse() it does an in place reversal of the list and returns None.
EDIT:
I see what you are trying to do now, in my opinion it's easier to just do this with a list comprehension:
def solution(formation):
return len([k for k in formation[formation.index(bCamel)+1:] if k == fCamel]) == 0
This basically looks at all the elements after the first bCamel and collects all the elements that have the value fCamel. If that list has a length == 0 you have a solution.
Here's a few examples:
>>> k = ['F','F','B','B','F']
>>> solution(k)
False
>>> k = ['F','F','B','B','B']
>>> solution(k)
True
>>> k = ['F','F','B','F','F','B','B']
>>> solution(k)
False
>>>
To build on GWW's answer, if you want this code to work as is you would just do list(reversed(formation)). If you really want to be able to use formation.reverse() instead, you would have to subclass list:
>>> class ReversableList(list):
... def reverse(self):
... return list(reversed(self))
...
>>> x = ReversableList([1,2,3])
>>> x.reverse()
[3, 2, 1]
Whether or not this is advisable is another question of course.
list.reverse reverses inplace. That is:
>>> l = [1, 2, 3]
>>> l.reverse()
>>> l
[3, 2, 1]
Please consult the Python documentation, things like these are laid out there. You can also try the 'help' built-in:
help(l.reverse) Help on built-in function reverse:
reverse(...)
L.reverse() -- reverse IN PLACE
I just came across this problem and wanted to clarify some things for users new to python coming from a javascript background.
In javascript, a.reverse() reverses in place and also returns the array when called.
Javascript:
var a = [2, 3 ,4]
console.log(a.reverse())
// outputs [4, 3, 2]
console.log(a)
// outputs [4, 3, 2]
In python, a.reverse() reverses in place, but does not return the array. This is what caused confusion for me.
In python:
a = [2, 3, 4]
a.reverse()
print(a)
# outputs [4, 3, 2]
# can't do print(a.reverse())
the following changes will be effective:
NumSet={1,2,3,4,5,6,7,8,9,10}
NumList = list(NumSet)
NumList.reverse()
print(NumList)
avoid using the assignment operator after the initial assignment as lists are mutable types.
Using = operator with a method..(eg NumList = NumSet.reverse()) will cause the method to overwrite list with a blank, thereby effectively clearing the list. That's why list becomes a NoneType.
Methods are functions and doesn't actually have its own value, thus the blank.
There are multiple approaches to the problem shown in the OP. A summary:
Use a different technique that gives you a reversed copy of the list, so that this expression can be used in place. The two main ways to do this are formation[::-1] and list(reversed(formation)) (see How to reverse a list?).
Analogous solutions exist to replace other list functionality, for example:
# mylist.append(1)
# mylist.index(1)
(mylist + [1]).index(1) # note that the value is wrapped in another list
# mylist.extend(anotherlist)
# mylist.index(1)
(mylist + anotherlist).index(1)
# mylist.sort()
# mylist.index(1)
sorted(mylist).index(1)
Bite the bullet and use a separate statement anyway. Simple is better than complex; good Python style often avoids long expressions like ((formation.index(bCamel) > (len(formation) - 1 - (formation.reverse()).index(fCamel)))) because it's hard to follow the logic. Keep in mind that since we are still using an in-place method, the original formation is still modified. This can be useful, but it often causes problems.
(Please do not use this in real code.) We can abuse conditional expressions to make the assignment happen as a side effect:
def solution(formation):
return formation.index(bCamel) > (
len(formation) - 1 - (formation.reverse() or formation).index(fCamel)
)
The idea is that since formation.reverse() will return None, which is falsey, so or is forced not to short-circuit, and will evaluate to formation - after the reversal has occurred as a side effect.
Other expressions can have the same net effect, e.g. [formation, formation.reverse()][0]. The idea is to write something that is an expression that includes the .reverse call, but evaluates to the original list object. We can be arbitrarily creative here - but again, Simple is better than complex. Please don't do these things.
Again, keep in mind that this will still modify the original list, which may impact on future calculations.
Rework the logic to avoid the need to reverse the list. The code tries to reverse the list, search in the reversed list for the index of the first match, and then subtract that result from len(formation) - 1 - the overall effect of this is to search for the index of the last match. Lists don't have a method for that, but strings do; and as it happens, all our elements are single-character strings. We could solve the problem more simply by representing the formation with a string:
def solution(formation):
return formation.index(bCamel) > formation.rindex(fCamel)
solution('F BF')
Alternately, we can conceive of the problem differently: "Is there a fCamel in the part of the list that follows the first bCamel?" The accepted answer shows using a list comprehension to iterate over "the part of the list that follows the first bCamel", making a list of all the fCamels there, and checking whether that list is non-empty. But we can do it much more simply:
# works with either the string or list input
def solution(formation):
return fCamel not in formation[formation.index(bCamel)+1:]
(There are even more clever ways to write this, such as Stefan's answer using iterators.)
Solutions like this are specific to the problem solved by the code, and don't answer the question in general. However, it is often possible to find similar solutions in other contexts.
Not super beautiful, but I did not find the equivalent in the precedent answers. If the speed or memory costs are low (the list is not very long or the operation not repeated a huge amount of times), this is pretty straight forward and even easier to read.
import copy
fCamel = 'F'
bCamel = 'B'
gap = ' '
k = ['F', ' ', 'B', 'F']
def solution(formation):
rev_formation = copy.copy(formation)
rev_formation.reverse()
return ((formation.index(bCamel) > (len(formation) - 1 -
(rev_formation).index(fCamel))))
Cheers
This doesn't provide a solution to the F _ B F pattern problem, but it does address the issue of python not returning a list when you use .reverse().
This is how I got around it:
chars = ['a', '-', 'c']
chars2 = [] + chars
chars2.reverse()
totalChars = chars + chars2
totalChars returns a-cc-a, which is what I wanted, AND chars2 is a list, not a pointer to chars. Hope this helps.
I don't know if this works for you, but this works for me:
list = [1,2,3]
print([list, list.reverse()][0])
The reason list.reverse() returns None is because the function doesn't return anything.
Using your code:
fCamel = 'F'
bCamel = 'B'
gap = ' '
k = ['F', ' ', 'B', 'F']
def solution(formation):
return ((formation.index(bCamel) > (len(formation) - 1 - ([formation, formation.reverse()][0]).index(fCamel))))
print(solution(k))
Hope this works for you!
ps. Honestly, I have no idea why this works. I stumbled on it accidentally.
Title question is already answered, but for what you were really after:
Basically, this function should return true if all the 'F's are on the left hand side of the first 'B'
That's the same as there's no 'B' followed by an 'F'. Nice way to check this is with an iterator:
def solution(formation):
it = iter(formation)
return not (bCamel in it and fCamel in it)
Some advantages:
Unlike every formation.index(...) solution it doesn't crash if the searched value isn't there.
Takes only O(1) extra space (unlike the solutions making a reversed copy of the list).
Touches every element at most once and stops as soon as possible. Even has O(1) time best case (even with millions of elements, if the list starts with ['B', 'F', then it stops right there).
If have a list of dictionary items like so:
L = [{"a":1, "b":0}, {"a":3, "b":1}...]
I would like to split these entries based upon the value of "b", either 0 or 1.
A(b=0) = [{"a":1, "b":1}, ....]
B(b=1) = [{"a":3, "b":2}, .....]
I am comfortable with using simple list comprehensions, and i am currently looping through the list L two times.
A = [d for d in L if d["b"] == 0]
B = [d for d in L if d["b"] != 0]
Clearly this is not the most efficient way.
An else clause does not seem to be available within the list comprehension functionality.
Can I do what I want via list comprehension?
Is there a better way to do this?
I am looking for a good balance between readability and efficiency, leaning towards readability.
Thanks!
update:
thanks everyone for the comments and ideas! the most easiest one for me to read is the one by Thomas. but i will look at Alex' suggestion as well. i had not found any reference to the collections module before.
Don't use a list comprehension. List comprehensions are for when you want a single list result. You obviously don't :) Use a regular for loop:
A = []
B = []
for item in L:
if item['b'] == 0:
target = A
else:
target = B
target.append(item)
You can shorten the snippet by doing, say, (A, B)[item['b'] != 0].append(item), but why bother?
If the b value can be only 0 or 1, #Thomas's simple solution is probably best. For a more general case (in which you want to discriminate among several possible values of b -- your sample "expected results" appear to be completely divorced from and contradictory to your question's text, so it's far from obvious whether you actually need some generality;-):
from collections import defaultdict
separated = defaultdict(list)
for x in L:
separated[x['b']].append(x)
When this code executes, separated ends up with a dict (actually an instance of collections.defaultdict, a dict subclass) whose keys are all values for b that actually occur in dicts in list L, the corresponding values being the separated sublists. So, for example, if b takes only the values 0 and 1, separated[0] would be what (in your question's text as opposed to the example) you want as list A, and separated[1] what you want as list B.
I was looking at the following code in python:
for ob in [ob for ob in context.scene.objects if ob.is_visible()]:
pass
Obviously, it's a for each loop, saying for each object in foo array. However, I'm having a bit of trouble reading the array. If it just had:
[for ob in context.scene.objects if ob.is_visible()]
that would make some sense, granted the use of different objects with the same name ob seems odd to me, but it looks readable enough, saying that the array consists of every element in that loop. However, the use of 'ob', before the for loop makes little sense, is there some syntax that isn't in what I've seen in the documentation?
Thank you
That is the syntax for list comprehension.
The first ob is an expression, take this simple example:
>>>> [ x**2 for x in range(10) if x%2 == 0 ]
[0, 4, 16, 36, 64]
Which reads, create a list from range(10), if x is even then square it. Discard any odd value of x.
It's a list comprehension. You can read it as:
create a list with [some_new_value for the_current_value in source_list_of_values if matches_criteria]
Think of:
[n*2 for n in [1,2,3,4]]
This means, for every item, n, create an entry in a new list with a value of n*2. So, this will yield:
[2,4,6,8]
In your case since the value ob is not modified, it's just filtering the list based on ob.is_visible(). So you're getting a new list of all ob's that are visible.
It might help you see what's going on to rewrite your example code into this equivalent code:
temp_list = [ob for ob in context.scene.objects if ob.is_visible()]
for ob in temp_list:
pass