python - different behavior of print(*generator, <element>) - python

Question
Please help understand why the two cases act differently although both use a generator (i for i in range(5)).
>>> print(i for i in range(5))
<generator object <genexpr> at 0x7fc409c02900>
>>> print(*(i for i in range(5)))
0 1 2 3 4
>>> print(*(i for i in range(5)), 5)
0 1 2 3 4 5
>>> _r = (i for i in range(5))
>>> print(_r)
<generator object <genexpr> at 0x7fc409c029e0>
>>> print(*_r)
0 1 2 3 4
>>> print(*_r, 5)
5
>>> print(*(_r), 5)
5

When you use the * operator on a generator expression (or any iterator for that matter), it consumes it:
my_iterator = (i for i in range(3))
second_iterator = iter(list(range(3)))
# splat operator consumes the generator expression
print(*my_iterator)
0 1 2
print(*my_iterator, "Empty!")
Empty!
# splat operator also consumes the iterator
print(*second_iterator)
0 1 2
print(*second_iterator, "Also empty!")
Also empty!
You'd need to recreate it to re-use it, which is what you're doing in your first example:
s = (i for i in range(3))
print(*s)
0 1 2
# You create a new generator expression/iterator
s = (i for i in range(3))
print(*s)
0 1 2
# And so it isn't empty
# same with the shorthand
print(*(i for i in range(3))
0 1 2
Note on range
Since I've used range for this example, it's important to note that range doesn't share this behavior, but an iterator over range does:
x = range(3)
print(*x)
0 1 2
print(*x)
0 1 2
# the range object isn't consumed and can be re-used
a = iter(x)
print(*a)
0 1 2
print(*a)
# prints nothing because `a` has been exhausted
More detail can be found in this answer

Related

How do I print every iteration possible between a multiplication of two products in a list?

Given a list with numbers between 0 and 5. I want every possible result of two numbers in this list that returns true for (a ** 2 + b ** 2) < 12.
My code for this is:
from random import choices
from math import factorial
nodes = list(range(0,6))
lis = []
for e in range(0,factorial(5)):
nodesf = choices(nodes, k= 2)
if not nodesf in lis:
if (nodesf[0]**2 + nodesf[1]**2) <= 12:
print(nodesf)
lis.append(nodesf)
lis.append(nodesf[::-1])
print(lis)
else:
lis.append(nodesf)
But, as you can see, this is probably a horrible way to solve this.
If you see a more clear and efficient code to solve this, please, help me.
You can try the combinations_with_replacement() method from the built-in module, itertools:
from itertools import combinations_with_replacement
for a, b in combinations_with_replacement(range(6), 2):
if (a ** 2 + b ** 2) < 12:
print(a, b)
Output:
0 0
0 1
0 2
0 3
1 1
1 2
1 3
2 2
The combinations_with_replacement() method is similar to the combinations() method, except the combinations_with_replacement() method allows one element to be used in the same combination more than once.
Do note that it does not include 1 0 as a solution as there already exists 0 1 in the results. If you want to include such, you can use the product() method:
from itertools import product
for a, b in product(range(6), repeat=2):
if (a ** 2 + b ** 2) < 12:
print(a, b)
Output:
0 0
0 1
0 2
0 3
1 0
1 1
1 2
1 3
2 0
2 1
2 2
3 0
3 1
What's wrong with your code ?
Iterating through range(0, factorial(5)) does not make a lot of sense to me : you're going to do 120 iterations. To solve this, you just need two compare each elements of nodes with the whole list, hence two for loops on your nodes : only 25 iterations.
Why using choices to pick elements from nodes ? It's a stochastic operation, you won't be sure to go through every elements of the list.
Simple solution
If you care about permutation, i.e. you want to get both (0, 1) and (1, 0) for example, a simple list comprehension should do the trick.
[ (a, b) for a in nodes for b in nodes if a**2+b**2<12 ]
Otherwise, just take a look at the perfect Ann Zen's answer using the itertools module :)

Why 10 and 5 returns 5 and 5 and 10 returns 10 in python? Basically it returns the second int after and operator irrespective of value

I was looking into python "and" operator and found out:
>>> 10 and 5
5
>>> 5 and 10
10
Think of statement logic and which operand determines the value of the entire expression:
and:
the first falsy operand makes the expression False regardless of what comes after
if there are only truthy operands, the last one settles it
Examples:
>>> 5 and 0
0
>>> 0 and 5
0
>>> 5 and 0 and 3
0
>>> 10 and 5
5
>>> 5 and 10
10
or:
the first truthy operand makes the expression True regardless of what comes after
if there are only falsy operands, the last one settles it
Examples:
>>> 5 or 0
5
>>> 0 or 5
5
>>> 5 or 0 or 3
5
>>> 10 or 5
10
>>> 5 or 10
5
Shot-Circuit behaviour:
Note that the rest of the expression is not even evaluated, which is relevant if you chain e.g.function calls or other expressions with side effects:
>>> 4/0 or 5
ZeroDivisionError
>>> 5 or 4/0
5
>>> func1() or func2() and func3()
# func2 and func3 might never be called
Right-Left-Associativity
This is kind of directly required for the short-circuit behaviour, but not completely intuitive, especially compared to Python's arithmetic operators:
a or b and c or d == (a or (b and (c or d)))

concise way to unpack twice from returned

This is probably a dumb question, but I couldn't find anything from searches.
I know if I want to unpack a returned tuple into multiple variables, the syntax is A,B = myfunction()
What if my function is returning a tuple of lists, such as ( [1], [2] ), and I want to assign to my variables integer 1 and 2 instead of the lists [1] and [2]? Is there a shorthand one liner for this or do I need to do
A, B = myfunction()
A = A[0]
B = B[0]
Thanks in advance for your help.
Use the same structure:
[A], [B] = myfunction()
Demo:
>>> def myfunction():
return ( [1], [2] )
>>> [A], [B] = myfunction()
>>> A
1
>>> B
2
Regarding DeepSpace's comment that "this creates 2 new lists": It doesn't:
>>> import dis
>>> dis.dis('[A], [B] = myfunction()')
1 0 LOAD_NAME 0 (myfunction)
2 CALL_FUNCTION 0
4 UNPACK_SEQUENCE 2
6 UNPACK_SEQUENCE 1
8 STORE_NAME 1 (A)
10 UNPACK_SEQUENCE 1
12 STORE_NAME 2 (B)
14 LOAD_CONST 0 (None)
16 RETURN_VALUE
This is what creating a list looks like:
>>> dis.dis('[A]')
1 0 LOAD_NAME 0 (A)
2 BUILD_LIST 1
4 RETURN_VALUE
Context matters.
Use arguments so you can control the indexing from outside.
def myfunction(a_index=None, b_index=None):
...
if a_index is None and b_index is None:
return a, b
return a[a_index], b[b_index]
Then
A, B = myfunction(0, 0)
This is a reusable solution. If you ever need other indexes:
A, B = myfunction(2, 1)
This does not support mixing (ie only passing a_index or b_index) but if you need that behavior you can easily add it.
One of methods of achieving this result is flattening your data. I would do it following way:
def myfunction():
return ([1],[2])
A,B = (i[0] for i in myfunction())
print(A) # 1
print(B) # 2
There exist other methods of flattening in Python, but for is totally enough for 2D structure used in your case (tuple of lists).

Python: print without overwriting printed lines

I have
for i in range(0, 11): print i, "\n", i
I'd like my python program to print this way for each for loop
1st loop:
1
1
2nd loop:
1
2
2
1
3rd loop:
1
2
3
3
2
1
I've tried using \r\n or \033[1A but they just overwrite the previous line. Is there a way I can "push" the outputted line down so I don't overwrite it?
One way to do this,
def foo(x, limit):
if x < limit :
print x
foo(x + 1, limit)
print x
foo(1, 11)
It's not possible to do it in 1 for loop as you're currently trying.
As I suggested, you can do it like this by using lists
>>> l1 = []
>>> l2 = []
>>> for i in range(0, 11):
... l1.append(i)
... l2 = [i] + l2
>>> l1.extend(l2)
>>> for i in l1:
... print i
0
1
2
3
4
5
6
7
8
9
10
10
9
8
7
6
5
4
3
2
1
0
Concatenation of two list generators.
>>> ladder = [x for x in range(5)] + [x for x in range(5,-1,-1)]
>>> ladder
[0, 1, 2, 3, 4, 5, 4, 3, 2, 1, 0]
>>> for x in ladder:
... print x
...
0
1
2
3
4
5
4
3
2
1
0
>>>
one of ways to solve this
def two_side_print(start, end):
text = [str(i) for i in range(start,end)]
print '\n'.join(text), '\n', '\n'.join(reversed(text))
two_side_print(1, 11)
Another option is to save the printed values in a list and print that list in reverse order in each loop iteration.
My suggestion:
l = []
for i in range(1, 5):
print 'Loop '+str(i) # This is to ease seeing the printing results
for val in l:
print val
print i
l.insert(len(l),i)
for val in reversed(l):
print val
The output for a loop iterating from 0 to 5:
Loop 1
1
1
Loop 2
1
2
2
1
Loop 3
1
2
3
3
2
1
Loop 4
1
2
3
4
4
3
2
1
I hope this is what you are looking for.
You can use a recursive function to help here:
def print_both_ways(start, end):
print(start)
if start != end:
print_both_ways(start+1, end)
print(start)
Usage:
print_both_ways(1,3) # prints 1,2,3,3,2,1 on separate lines

Is there an easy way to get next and prev values in a for-loop?

So...is there an easy way to get next and previous values while iterating with a for-loop in Python?
I know this can be easily done if you do something like:
a = [3,4,5,6,3,4,5]
for x in range(len(a)):
next = a[x+1]
But what about:
for x in a:
x.next??
Here is a common pattern that I use to iterate over pairs of items in a sequence:
>>> a = range(10)
>>> for i, j in zip(a, a[1:]):
... print i, j
...
0 1
1 2
2 3
3 4
4 5
5 6
6 7
7 8
8 9
If you want three items (prev, item, next) you can do this:
>>> for i, j, k in zip(a, a[1:], a[2:]):
... print i, j, k
...
0 1 2
1 2 3
2 3 4
3 4 5
4 5 6
5 6 7
6 7 8
7 8 9
i is the previous element, j is the current element, k is the next element.
Of course, this "starts" at 1 and "ends" at 8. What should you receive as prev/next at the ends? Perhaps None? Probably easiest to just do this:
>>> a = [None] + a + [None]
>>> for i, j, k in zip(a, a[1:], a[2:]):
... print i, j, k
...
None 0 1
0 1 2
1 2 3
2 3 4
3 4 5
4 5 6
5 6 7
6 7 8
7 8 9
8 9 None
easiest way I know of is
for x,next in zip (a, a[1:]):
# now you have x and next available
You could always convert a into an iterator with iter and then iterate over that. This will allow you to use next inside the loop to advance the iterator that you are iterting over:
>>> a = [3,4,5,6,3,4,5]
>>> it = iter(a)
>>> for i in it:
... j = next(it, None)
... print('i={}, j={}'.format(i, j))
...
i=3, j=4
i=5, j=6
i=3, j=4
i=5, j=None
>>>
Also, the None in there is the default value to return if there is no next item. You can set it to whatever value you want though. Omitting the argument will cause a StopIteration exception to be raised:
>>> a = [1, 2, 3, 4, 5]
>>> it = iter(a)
>>> for i in it:
... j = next(it)
... print('i={}, j={}'.format(i, j))
...
i=1, j=2
i=3, j=4
Traceback (most recent call last):
File "<stdin>", line 2, in <module>
StopIteration
>>>
If you want both the previous and the next element in a circular sequence for each iteration:
a = [3,4,5,6,3,4,5]
l = len(a)
for k, v in enumerate(a):
print a[(k-1)%l], v, a[(k+1)%l] #prints previous, current, next elements
this is easy too:
>>> a = [3,4,5,6,3,4,5]
>>> for i in range(1,len(a)):
... print a[i-1],a[i]
...
3 4
4 5
5 6
6 3
3 4
4 5
Probably overkill but I sometimes use the following more general generator for this, which yields a sequence of 'windows' of any size on a list or other iterable. (The window size must be less than the length of the iterable.)
def sliding_window(iterable, size):
try: # indexed iterable
for i in range(len(iterable) - size + 1):
yield iterable[i:i+size]
except TypeError: # iterator
window = [next(iterable) for _ in range(size)]
yield window
for item in iterable:
window = window[1:] + [item]
yield window
a = [3,4,5,6,3,4,5]
for current, following in sliding_window(a, 2):
print(current, following)

Categories

Resources