The generator works strange - python

a = [1, 2, 3]
b = (c for c in a if c in a)
a = [2, 3, 4]
print(list(b))
def d(expr):
for c in expr:
if c in expr:
yield c
a1 = [1,2,3]
t = d(a1)
a1 = [2,3,4]
print(list(t))
output:
[2, 3]
[1, 2, 3]
question:
1) in the first variant, the cycle retains the old value of the list ([1,2,3]), but the conditional expression takes a new list a([2,3,4]).
2) how my own generator give me other result than generative expression?
For example, I replaced a on the real lists of the first example, I hope this will be more clear my first question.
a = [1, 2, 3]
b = (c for c in [1,2,3] if c in [2, 3, 4])
a = [2, 3, 4]
print(list(b))
Output:
[2,3]

As per PEP 289, we can rewrite your generator expression as follows:
# b = (c for c in a if c in a)
def __gen(expr):
for c in expr:
if c in a:
yield c
b = __gen(iter(a))
Here, a reference to [1, 2, 3] for use in the for loop is captured immediately (when you create b), but the if c in a check uses the new global a which is equal to [2, 3, 4].
On the other hand, when you write a generator function, a1 is passed to the function and stored for later use in both for and if x in y expressions. Assigning a1 = [2, 3, 4] has no effect.
To make the function do the same thing as the generator expression, all you need to do is replace if c in expr with if c in a1.

Related

Isn't there a multidimensional set library in Python?

I want to do a multidimensional set compute.
For example:
a = [1, 2, 3, 4]
b = [3, 4, 5, 6]
This one's set.difference is:
c = a - b
c = [1, 2]
But when it's multi-dimensional, I don't know.
How do I do this?
a = [['a',1],['b',2],['c',3]]
b = [['a',2],['c',7],['d',5]]
I want to calculate around a string.
I wish I could get this value.
c = a - b
c = [['b',2]]
You can try
[i for i in a if i[0] in {str(i[0]) for i in a}.difference({str(i[0]) for i in b})]
Output
[['b', 2]]
This code will return the item that is in the list a by the first elements that is not in the b list.

Why doesn't the copy of a list in a function work?

b = [0]
def copyalist(b):
b = [1, 2, 3]
print(b)
copyalist(b)
print(b)
The outputs are below:
[1, 2, 3]
[0]
The first line indicates that in the function, b was set to [1, 2, 3];
However, when you print(b) out of the function,the second output says that b is still [0].
I don't understand that, why the outer b is not changed?
I also tried b = copy.deepcopy([1, 2, 3]), the outputs are the same.
However, the following code works well:
b = [0]
def copyalist(b):
b += [1, 2, 3]
print(b)
copyalist(b)
print(b)
The outputs are below:
[0, 1, 2, 3]
[0, 1, 2, 3]
This code:
def copyalist(b):
b = [1, 2, 3]
print(b)
will only mean to remap the variable name b to a new list, but not modifying the original b. If you mean to modify the original list, you have to explicitly tell Python to replace the actual content. The way to do is:
def copyalist(b):
b[:] = [1, 2, 3]
print(b)
In python, lists are passed as function arguments only by reference, i.e., only the memory address of the first element is given. When defining a new b inside the function, you just change the position in memory to which the inner variable b refers, but the outer b still points to the original position. Vice versa, when you do b += [1, 2, 3], you change the content inside the cell referenced by the inner b and, since inner and outer b point to the same cells, it reflects in a change of the outer b as well.
This is due to a difference in global and local variables. You can add global to your function to get the desired result.
b = [0]
def copyalist():
global b
b = [1, 2, 3]
print(b)
copyalist()
print(b)
OUTPUT
[1, 2, 3]
[1, 2, 3]
A more in depth summary is here
b = copyalist(b)
and return b in the function
def copyalist(b):
b = [1, 2, 3]
return(b)
When you are defining something in a function it is only used inside that function.

Using list comprehension to get a list of entries not in another list, preserving order and quantity

So I have a list, let's say
a = [1, 2, 3, 4, 2, 1, 2, 4]
1 appears twice, 2 appears three times, 4 appears twice. Now I define
b = [4, 2, 2]
Now I want a new list, c, that has the entries of a that are not in b. I tried using list comprehension:
c = [x for x in a if x not in b]
However, this omits the entry if it is in b, rather than seeing how many of each entry are in b and removing that many from a.
c == [1, 3, 1]
I would want it to be
c == [1, 3, 1, 2, 4]
Can anyone provide some help?
You can loop through list b and remove each element from list a:
for i in b:
a.remove(i)
a
# [1, 3, 1, 2, 4]

Create a list of repeated elements from 2 lists in python

Being new to python, here is my problem. Given two lists:
a = [3, 4]
b = [5, 2]
I want to create a new list which consists of items of first list repeated n number of times, where n is the corresponding element in second list. So I would like an output like:
c = [3,3,3,3,3,4,4]
Where 3 is repeated 5 times, 4 is repeated 2 times and so on.
My current code looks like this
for item,no in zip(a,b):
print(str(item)*no)
which gets me:
33333
44
I am trying to figure out how to get from my current stage to the output that I want. Any help would be appreciated.
I was also thinking that maybe this could be done with list comprehension but I could not come up with the python code for it.
A simple list comprehension would work:
>>> a = [3, 4]
>>> b = [5, 2]
>>> [c for n, m in zip(a, b) for c in [n]*m]
[3, 3, 3, 3, 3, 4, 4]
If you are concerned on the memory efficiency (e.g. for large m) then you can use itertools.repeat() to avoid the intermediate lists:
>>> import itertools as it
>>> [c for n, m in zip(a, b) for c in it.repeat(n, m)]
[3, 3, 3, 3, 3, 4, 4]
You can use list comprehension like this:
[i for i,j in zip(a, b) for k in range(j)]
The above line is equivalent to the following nested for loops:
c = []
for i,j in zip(a, b):
for k in range(j):
c.append(i)
Output:
>>> a = [3, 4]
>>> b = [5, 2]
>>> c = [i for i,j in zip(a, b) for k in range(j)]
>>> c
[3, 3, 3, 3, 3, 4, 4]
c=[]
for item, count in zip(a,b):
c.extend([item]*count)
print(c)
should do it. Add to the list one element at a time, and then print all at once at the end. [item]*count is a count-element list([]) of item, and extend adds the contents of the list you provide to the list you call extend on.
You just need to use extend
c = []
for item, no in zip(a, b):
c.extend([item] * no)
c = []
for i in range(len(b)):
for j in range(b[i]):
c.append(a[i])
print c
Something a bit different
c = [a[b.index(j)] for i,j in enumerate(b) for i in range(j)]
You said you were new to Python, so here's a simple way to do it, just using basic loops:
a = [3 , 4]
b = [5 , 2]
c = []
index = 0
for i in a:
for i in range(0 , b[index]):
c.append(i)
index += 1

How can I switch contents of lists?

I have two lists, a and b.
I want to reassign the content of a into b and the content of b into a.
or in other words, after running a few operations with two lists, I want to switch the names of the lists. so that the list that was named a would now be named b, and the list that was named b would now be named a.
this way is obviously wrong:
a=b
b=a
(because they are both b now)
so, is there a right way to do this?
python lets you do that as follows:
a, b = b, a
Example
a = [1,2,3]
b = [4,5,6]
a, b = b, a
>>> print a
[4,5,6]
>>> print b
[1,2,3]
This is because the expression on the right hand side is evaluated first and then assigned to variables on the left.
This might be not necessary for you, but good to remember that:
a = [1, 2, 3]
b = [3, 4, 5]
c = a # now 'c' points to the same list as 'a'
a[0] = 0 # let's change the first element
print a
# [0, 2, 3]
print c
# now let's use multiple assigment:
a, b = b, a
print a
# [3, 4, 5]
print c
# [0, 2, 3]
# list didn't change, but 'a' now point do different list
# but 'c' still point to the same, unchanged list, the one
# that is now referenced by 'b'
Above solution is fast and usually the best. But if you have the same list (or other mutable data structure) referenced by other names, you may swap contents of list instead:
a = [1, 2, 3]
b = [3, 4, 5]
c = a
a[0] = 0
a[:], b[:] = b[:], a[:] # now we actually swap contents
# 'a' and 'c' still point to the same list,
# but values are from 'b' list
print a
# [3, 4, 5]
print b
# [0, 2, 3]
print c
# [3, 4, 5]

Categories