Effect of "and" on assignment and addition - python

A line of code has tripped me up:
>>> i = 1
>>> j = 1
>>> i += j > 0 and i
>>> print(i)
2
What is the underlying mechanic or system that makes this work? It seems like it's syntactic sugar for i = i + i if j > 0 else i, but that's a lot to unpack. Am I wrong? Is there another system in play I don't know?
Thanks!
EDIT:
For clarity:
>>> i = 3
>>> j = 2
>>> i += j > 1 and i
>>> i
6

Let's break it down:
In [1]: i = 1
In [2]: j = 1
Now, let's look at the expression i += j > 0 and i:
In [3]: j > 0
Out[3]: True
Because j, which is 1 is greater than 0, this evaluates to True.
In [4]: j > 0 and i
Out[4]: 1
Because j > 0 is True, the value of the boolean expression is the value of the right-hand side, namely 1.
Thus, i += j > 0 and i simplifies to i += i or i = i + i:
In [5]: i += i
In [6]: i
Out[6]: 2
Let's also consider your second example:
>>> i = 3
>>> j = 2
>>> i += j > 1 and i
>>> i
6
For the thrid line we have these transforms:
i += j > 1 and i
i = i + (j > 1 and i)
i = 3 + (2 > 1 and 3)
i = 3 + (True and 3)
i = 3 + 3
i = 6

In Python and and or do not return boolean values, but rather return one of the options presented to them which evaluates to the correct boolean value.
For example, and will return either the first False value it encounters, or the last True value:
>>> 1 and 3
3
>>> 1 and 0
0
Similarly, or will return the first True value it encounters, and otherwise return the first False value:
>>> 2 or 3
2
>>> 0 or 2
2
>>> False or 0
0
Basically, you should keep in mind that and and or do not necessarily return True/False, but return one of the elements presented to them which evaluates to True or False.

Related

Python: How to make numeric triangle with recursion

while I was working on the Python practice, I found a question that I cannot solve by myself.
The question is,
Input one integer(n), and then write the codes that make a triangle using 1 to 'n'. Use the following picture. You should make only one function, and call that function various times to solve the question. The following picture is the result that you should make in the codes.
Receive one integer as an argument, print the number from 1 to the integer received as a factor in a single line, and then print the line break character at the end. Once this function is called, only one line of output should be printed.
So by that question, I found that this is a question that requires the
recursion since I have to call your function only once.
I tried to work on the codes that I made many times, but I couldn't solve it.
global a
a = 1
def printLine(n):
global a
if (n == 0):
return
for i in range(1, a + 1):
print(i, end=" ")
print()
a += 1
for k in range(1, n+1):
print(k, end=" ")
print()
printLine(n - 1)
n = int(input())
printLine(n)
Then I wrote some codes to solve this question, but the ascending and descending part is kept overlapping. :(
What I need to do is to break two ascending and descending parts separately in one function, but I really cannot find how can I do that. So which part should I have to put the recursive function call?
Or is there another way can divide the ascending and descending part in the function?
Any ideas, comments, or solutions are appreciated.
Thx
You can use the below function:
def create_triangle(n, k: int = 1, output: list = []):
if n == 1:
output.append(n)
return output
elif k >= n:
output.append(" ".join([str(i) for i in range(1, n + 1)]))
return create_triangle(n - 1, k)
else:
output.append(" ".join([str(i) for i in range(1, n + 1)[:k]]))
return create_triangle(n, k + 1)
for i in create_triangle(5):
print(i)
Output:
1
1 2
1 2 3
1 2 3 4
1 2 3 4 5
1 2 3 4
1 2 3
1 2
1
# function to print all the numbers from 1 to n with spaces
def printLine(k):
# create a range. if k is 4, will create the range: 1, 2, 3, 4
rng = range(1, k + 1)
# convert each number to string
str_rng = map(lambda x: str(x), rng)
# create one long string with spaces
full_line = " ".join(str_rng)
print(full_line)
# capture input
n = int(input())
# start from 1, and up to n, printing the first half of the triangle
for i in range(1, n):
printLine(i)
# now create the bottom part, by creating a descending range
for i in range(n, 0, -1):
printLine(i)
Using default parameter as a dict, you can manipulate it as your function variables, so in that way, you can have a variable in your function that keeps the current iteration you are at and if your function is ascending or descending.
def triangle_line(n, config={'max':1, 'ascending':True}):
print(*range(1, config['max'] + 1))
if config['ascending']:
config['max'] += 1
else:
config['max'] -= 1
if config['max'] > n:
config['ascending'] = False
config['max'] = n
elif config['max'] == 0:
config['ascending'] = True
config['max'] = 1
Each call you make will return one iteration.
>>> triangle_line(4)
1
>>> triangle_line(4)
1 2
>>> triangle_line(4)
1 2 3
>>> triangle_line(4)
1 2 3 4
>>> triangle_line(4)
1 2 3 4
>>> triangle_line(4)
1 2 3
>>> triangle_line(4)
1 2
>>> triangle_line(4)
1
Or you can run on a loop, two times your input size.
>>> n = 4
>>> for i in range(0,n*2):
... triangle_line(n)
...
1
1 2
1 2 3
1 2 3 4
1 2 3 4
1 2 3
1 2
1

Is there a way for a for loop to not go back to the code on top if the next if statement is true?

a = 0
b = 0
for x in range (100):
a = a + 1
if a == 10:
b = b + 1
print(a)
print(b)
The outcome
99
1
What I want
10
90
IIUC, this should do the trick:
a = 0
b = 0
for x in range (100):
if a < 10:
a = a + 1
else:
b = b + 1
But to simplify further, you can use python's assignment operator, a += 1 syntax, which increments the value of a by 1:
a = 0
b = 0
for x in range (100):
if a < 10:
a += 1
else:
b += 1
Add a conditional check.
a = 0
b = 0
for x in range (100):
if (a % 10 != 0 or a==0):
a = a + 1
else:
b = b + 1
print(a)
print(b)
Just for fun:
a, b = 0, 0
for x in range(100):
add = a % 10 != 0 or a == 0
a += add
b += not add
This uses the fact that a bool is an int although I don’t advise it as it’s not too readable

How to check whether all columns in a row are positive numbers?

I have created a new column by comparing two boolean columns. If both are positive, I assign a 1, otherwise a 0. This is my code below, but is there a way to be more pythonic? I tried list comprehension but failed.
lst = []
for i,k in zip(df['new_customer'],df['y']):
if i == 1 & k == 1:
lst.append(1)
else:
lst.append(0)
df['new_customer_subscription'] = lst
Use np.sign:
m = np.sign(df[['new_customer', 'y']]) >= 0
df['new_customer_subscription'] = m.all(axis=1).astype(int)
If you want to consider only positive non-zero values, change >= 0 to > 0 (since np.sign(0) is 0).
# Sample DataFrame.
df = pd.DataFrame(np.random.randn(5, 2), columns=['A', 'B'])
df
A B
0 0.511684 -0.512633
1 -1.254813 -1.721734
2 0.751830 0.285449
3 -0.934877 1.407998
4 -1.686066 -0.947015
# Get the sign of the numbers.
m = np.sign(df[['A', 'B']]) >= 0
m
A B
0 True False
1 False False
2 True True
3 False True
4 False False
# Find all rows where both columns are `True`.
m.all(axis=1).astype(int)
0 0
1 0
2 1
3 0
4 0
dtype: int64
Another solution if you have to deal with only two columns would be:
df['new_customer_subscription'] = (
df['new_customer'].gt(0) & df['y'].gt(0)).astype(int)
To generalise to multiple columns, use logical_and.reduce:
df['new_customer_subscription'] = np.logical_and.reduce(
df[['new_customer', 'y']] > 0, axis=1).astype(int)
Or,
df['new_customer_subscription'] = (df[['new_customer', 'y']] > 0).all(1).astype(int)
Another way to do this is using the np.where from the numpys module:
df['Indicator'] = np.where((df.A > 0) & (df.B > 0), 1, 0)
Output
A B Indicator
0 -0.464992 0.418243 0
1 -0.902320 0.496530 0
2 0.219111 1.052536 1
3 -1.377076 0.207964 0
4 1.051078 2.041550 1
The np.where method works like this:
np.where(condition, true value, false value)

Parametrized generators?

I need parametrized generator. Such one that will accept parameters on the call to .next(arg).
In this specific case I want the generator to change with +1 when arg is True and -1 on False.
Is this possible in python ?
Using the .send method on a generator instance allows you to inject state into the generator. That makes something like this possible:
>>> def mygen():
... i = 0
... sign = 1
... while True:
... val = yield sign*i
... if val is not None:
... sign = 1 if val else -1
... i += 1
...
>>> g = mygen()
>>> next(g)
0
>>> next(g)
1
>>> next(g)
2
>>> g.send(False)
-3
>>> next(g)
-4
>>> next(g)
-5
>>> g.send(True)
6
>>> next(g)
7
Note that next(g) is equivalent to g.send(None).
Here is my final version :
def flip_flop(low=0, high=10):
i = 0
while i >= low and i <= high :
cond = yield i
if cond : i += 1
else : i -= 1
In [64]: ff = flip_flop()
In [65]: ff.next()
Out[65]: 0
In [66]: ff.send(True)
Out[66]: 1
In [67]: ff.send(True)
Out[67]: 2
In [68]: ff.send(True)
Out[68]: 3
In [69]: ff.send(False)
Out[69]: 2

Is there a way to write a recursive function that looks through all the integers in a list and sees if any two are equal to a negative sum?

So the problem is, you have a list of integers and you have to find if any two in the list sum to a negative.
right now I have this
def negsum(L):
if len(L) <= 2:
if L[0] + L[1] >= -1: #Here is my base case
return False
else:
return True
else:
if L[0] + L[1] <= -1:
return True
else:
return negsum(L[1:]) #Recursive portion
The issue with my code is it only checks the first 2 in the list. So in a list
[-10, 15, 30, -5] you get False when it should be true, because -5 + -10 is a negative sum. My function only checks:
-10 + 15
15 + 30
30 - 5
How can I get it so that it checks -10 + 30, -10 -5 and 15-5 using recursion?
Edit, I forgot to mention, only len() [] and : operators are allowed. No loops. Is this even possible without loops?
Here is one way to do it:
def negsum(L):
if len(L)<2: return False
if len(L) == 2:
if sum(L)<0: return True
else: return False
for i,elem in enumerate(L):
if elem < 0:
return negsum(L[i+1:]+[elem])
Output:
In [39]: negsum([1,2,3,4,5])
Out[39]: False
In [40]: negsum([-1,2,3,4,5])
Out[40]: False
In [41]: negsum([-1,2,3,-4,5])
Out[41]: True
The idea is to divide the list intwo parts: the first element L[0] and the rest L[1:] and apply the function recursively to the L[1:] part:
def negsum(L):
if len(L) == 2: # base case
return True if L[0] + L[1] < 0 else False
elif negsum(L[1:]): # recursion on the L[1:] part
return True
else: # check L[1:] part pairwise against the first element
return any(x + L[0]<0 for x in L[1:])
Here's a solution without loops (implicit or explicit):
def negsum(lst, sub=True):
return len(lst) > 1 \
and ((lst[0] + lst[1]) < 0
or negsum([lst[0]]+lst[2:], False)
or (sub and negsum(lst[1:])))
or, alternatively, the following version separates the process more cleanly into 2 sub-functions and doesn't need the additional parameter 'sub':
def negsum(lst):
def first_with_others(lst): # compare lst[0] with all later values
if len(lst) > 1:
#print("summing", lst[0], "and", lst[1])
return ((lst[0] + lst[1]) < 0) or first_with_others([lst[0]]+lst[2:])
def drop_first(lst): # successively drop first element
if lst:
return first_with_others(lst) or drop_first(lst[1:])
return drop_first(lst) or False # converts None to False
Uncommenting the call to the print function shows which sums are calculated:
>>> negsum([1,2,3,4,5])
summing 1 and 2
summing 1 and 3
summing 1 and 4
summing 1 and 5
summing 2 and 3
summing 2 and 4
summing 2 and 5
summing 3 and 4
summing 3 and 5
summing 4 and 5
False
>>> negsum([-1,2,3,4,5])
summing -1 and 2
summing -1 and 3
summing -1 and 4
summing -1 and 5
summing 2 and 3
summing 2 and 4
summing 2 and 5
summing 3 and 4
summing 3 and 5
summing 4 and 5
False
>>> negsum([-1,2,3,-4,5])
summing -1 and 2
summing -1 and 3
summing -1 and -4
True
>>> negsum([-2,1])
summing -2 and 1
True

Categories

Resources