Why do these shorthands not work with each other? - python

I was wondering how to take advantage of shorthand notation of if-else and += in Python of this simple expression:
I tried to set brackets everywhere and changed += to *= which didn't change the situation of course.
This works as expected:
a, b = 0, True
for i in range(123):
if b == True:
a = a + 1
Still working as expected, trying shorthand of if-else led me to:
a, b = 0, True
for i in range(123):
a = a + 1 if b == True else a
Finally the attempt to write:
a, b = 0, True
for i in range(123):
a += 1 if b == True else a:
fails and surprisingly I get pretty quickly huge integers for a
Moreover I'd really like something more shorthanded, e.g.:
a, b = 0, True
for i in range(123):
a += 1 if b
The for-loop needs to stay as it is, since in my case there are other operations that affect b.

Since noone seems to be posting, why it goes like this, here is mine - lines:
a = a + 1 if b == True else a
a += 1 if b == True else a
are seen by python as:
a = (a + 1 if b == True else a)
a += (1 if b == True else a)
This is why you get large numbers fast in second version - you will add a to a, when b is False. If you want to keep the if, then go:
a += (1 if b else 0)
Also don't compare b to True (or False), go foif b`, as it's more pythonic (it will prevent some weird mistakes, when other code will start to interact with yours).
EDIT: go for #Tomerikoo answer for even shorter code, but keep in mind, that those waters can be muddy and not everyone knows / easily follows, that adding boolean to int treats first as 1 (or 0 if False).

To closest to your proal is probably:
a, b = 0, True
for i in range(123):
a += b
Since bool is a subtype of int, no conversion is necessary.

You can do:
for i in range(123):
if b:a+=1

You can also do:
for i in range(123):
a = a + 1*b
Because booleans are ints:
>>> isinstance(True, int)
True
>>> True == 1
True

just note that
a += x if condition else y
will resolve to a += x if the condition is True; otherwise it will be a += y. this is why your numbers get big...
apart from that i suggest you use what U10-Forward's answer suggests.

Related

Use cases for "and" in variable assignment

I discovered today that you can use and in variable assignment, similarly to how or is used. I rarely come across using or in this way, but have never even heard of people using and this way. Is this too obscure of a feature to recommend using or are there some concrete use cases where it helps with code clarity or brevity?
a = 1
b = 3
# c is equal to b unless a or b is False; then c is equal to the "False" value. False may be 0, [], False, etc.
c = a and b
print(f'a = {a}, b = {b}, c = {c}')
>>>a = 1, b = 3, c = 3
d = 1
e = 5
# f is equal to d unless d is False; then f is equal to e. Again, "False" may be 0, [], False, etc.
f = d or e
print(f'd = {d}, e = {e}, f = {f}')
>>>d = 1, e = 5, f = 1
There seems to be a weird inconsistency where it's obviously fine to use operators to evaluate a condition and set a variable to the truthiness of that condition (e.g. g = h > i or j = k is l etc).
However, and seems to be an exception. Instead of evaluating the condition right of the assignment, the variable is assigned according to the rule described in the above comment. Why doesn't c = a and b just evaluate to True or False depending on both a and b having truthy values? (The above example would evaluate to True)
Thanks
Short circuiting with and is a convenient way to express your intent with little code (a desirable goal indeed).
Consider this initialization, and what you would have to do if user wasn't known to be non-null.
name = user and user.name
Sure, a ternary would be a similar oneliner
name = user.name if user else None
but is it that readable?
Finally, when chaining multiple getters using the short-circuiting and truly starts to save your sanity.
coords = user and user.location and user.location.coords
Use or to provide a better default instead of None when you know for sure it wouldn't be a problem to override a falsey value.
name = user and user.name or 'Unnamed'
Basically what's going on here as has been stated is Short Circuit Evaluation. When the first value in an and evaluates to True then it returns the second value vice returning the False value. Consider these statements
>>> 1 and 0
0
>>> 1 and 3
3
>>> 0 and 1
0
>>> False and "Text"
False
"Text" and False
False
Your question: Why doesn't c = a and b just evaluate to True or False depending on both a and b having truthy values?
According the the Python manual, the definition of a and b is:
if a is false, then a, else b
So in your particular case when a has no side effects, the above translated into actual Python would be equivalent to:
c = a if not a else b
But it is not generally true that a has no side effects. So the difference between c = a and b and c = a if not a else b is as follows:
With a and b, if a is true then b will never be evaluated and a is evaluated once.
With c = a if not a else b, as before if a is true then b will never be evaluated but a will be evaluated a second time, which could be an issue if a does have side effects.

Concat string if condition, else do nothing

I want to concat few strings together, and add the last one only if a boolean condition is True.
Like this (a, b and c are strings):
something = a + b + (c if <condition>)
But Python does not like it. Is there a nice way to do it without the else option?
Thanks! :)
Try something below without using else. It works by indexing empty string when condition False (0) and indexing string c when condition True (1)
something = a + b + ['', c][condition]
I am not sure why you want to avoid using else, otherwise, the code below seems more readable:
something = a + b + (c if condition else '')
This should work for simple scenarios -
something = ''.join([a, b, c if condition else ''])
It is possible, but it's not very Pythonic:
something = a + b + c * condition
This will work because condition * False will return '', while condition * True will return original condition. However, You must be careful here, condition could also be 0 or 1, but any higher number or any literal will break the code.
Is there a nice way to do it without the else option?
Well, yes:
something = ''.join([a, b])
if condition:
something = ''.join([something, c])
But I don't know whether you mean literally without else, or without the whole if statement.
a_list = ['apple', 'banana,orange', 'strawberry']
b_list = []
for i in a_list:
for j in i.split(','):
b_list.append(j)
print(b_list)

Python for loop doesn't have expected results

I got a bit stuck with a for loop - what I can see it is doing appears correct but isn't exactly what I'm trying to accomplish with it. I've come from a C background but any advice here would be beneficial.
def deal(player_num, cards):
a = 0
z = 0
i = 0
b = 0
c = player_num
hand = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16]
for a in range(player_num):
hand[a] = cards[i] + cards[i+b+c]
b == b+1
i == i+1
z == z+1
return hand
So the for a in range(player_num) seems to be working (appends a++) but hand[0], hand[1], etc. gets the same hand. I guess it loops a but not the other variables, so I need to use more than 1 nested loop to get i++, b++ and c++?
b == b+1 is a logical expression (returning False every time), not an assignment. I'm guessing you want something like: b += 1
== is the equality operator in Python. = is the assignment operator.
== checks whether its left operand and its right operand are equal and return True or False accordingly. b and b+1 will never be equal to each other and either way it does not make sense to perform an operation without side-effect (like comparing two values for equality) and then do nothing with its result.
If you want to change the values of your variables, use the assignment operator = instead of ==.

Putting an if-elif-else statement on one line?

I have read the links below, but it doesn't address my question.
Does Python have a ternary conditional operator? (the question is about condensing if-else statement to one line)
Is there an easier way of writing an if-elif-else statement so it fits on one line?
For example,
if expression1:
statement1
elif expression2:
statement2
else:
statement3
Or a real-world example:
if i > 100:
x = 2
elif i < 100:
x = 1
else:
x = 0
I just feel if the example above could be written the following way, it could look like more concise.
x = 2 if i>100 elif i<100 1 else 0 # [WRONG]
No, it's not possible (at least not with arbitrary statements), nor is it desirable. Fitting everything on one line would most likely violate PEP-8 where it is mandated that lines should not exceed 80 characters in length.
It's also against the Zen of Python: "Readability counts". (Type import this at the Python prompt to read the whole thing).
You can use a ternary expression in Python, but only for expressions, not for statements:
>>> a = "Hello" if foo() else "Goodbye"
Edit:
Your revised question now shows that the three statements are identical except for the value being assigned. In that case, a chained ternary operator does work, but I still think that it's less readable:
>>> i=100
>>> a = 1 if i<100 else 2 if i>100 else 0
>>> a
0
>>> i=101
>>> a = 1 if i<100 else 2 if i>100 else 0
>>> a
2
>>> i=99
>>> a = 1 if i<100 else 2 if i>100 else 0
>>> a
1
If you only need different expressions for different cases then this may work for you:
expr1 if condition1 else expr2 if condition2 else expr
For example:
a = "neg" if b<0 else "pos" if b>0 else "zero"
Despite some other answers: YES it IS possible:
if expression1:
statement1
elif expression2:
statement2
else:
statement3
translates to the following one liner:
statement1 if expression1 else (statement2 if expression2 else statement3)
in fact you can nest those till infinity. Enjoy ;)
Just nest another if clause in the else statement. But that doesn't make it look any prettier.
>>> x=5
>>> x if x>0 else ("zero" if x==0 else "invalid value")
5
>>> x = 0
>>> x if x>0 else ("zero" if x==0 else "invalid value")
'zero'
>>> x = -1
>>> x if x>0 else ("zero" if x==0 else "invalid value")
'invalid value'
There's an alternative that's quite unreadable in my opinion but I'll share anyway just as a curiosity:
x = (i>100 and 2) or (i<100 and 1) or 0
More info here: https://docs.python.org/3/library/stdtypes.html#boolean-operations-and-or-not
You can optionally actually use the get method of a dict:
x = {i<100: -1, -10<=i<=10: 0, i>100: 1}.get(True, 2)
You don't need the get method if one of the keys is guaranteed to evaluate to True:
x = {i<0: -1, i==0: 0, i>0: 1}[True]
At most one of the keys should ideally evaluate to True. If more than one key evaluates to True, the results could seem unpredictable.
yes you can by doing this :
i = int(input('type your num here : '))
x = 2 if i > 100 else ( 1 if i < 100 else 0)
print (x)
if i > 100:
x = 2
elif i < 100:
x = 1
else:
x = 0
If you want to use the above-mentioned code in one line, you can use the following:
x = 2 if i > 100 else 1 if i < 100 else 0
On doing so, x will be assigned 2 if i > 100, 1 if i < 100 and 0 if i = 100
The ternary operator is the best way to a concise expression. The syntax is variable = value_1 if condition else value_2. So, for your example, you must apply the ternary operator twice:
i = 23 # set any value for i
x = 2 if i > 100 else 1 if i < 100 else 0
Nested ternary operator is the best solution --
Example case -
4 = 1
3 = 2
2 = 3
1 = 4
a = 4
prio = 4 if a == 1 else (3 if a == 2 else (2 if a == 3 else 1))
People have already mentioned ternary expressions. Sometimes with a simple conditional assignment as your example, it is possible to use a mathematical expression to perform the conditional assignment. This may not make your code very readable, but it does get it on one fairly short line. Your example could be written like this:
x = 2*(i>100) | 1*(i<100)
The comparisons would be True or False, and when multiplying with numbers would then be either 1 or 0. One could use a + instead of an | in the middle.
It also depends on the nature of your expressions. The general advice on the other answers of "not doing it" is quite valid for generic statements and generic expressions.
But if all you need is a "dispatch" table, like, calling a different function depending on the value of a given option, you can put the functions to call inside a dictionary.
Something like:
def save():
...
def edit():
...
options = {"save": save, "edit": edit, "remove": lambda : "Not Implemented"}
option = get_input()
result = options[option]()
Instead of an if-else:
if option=="save":
save()
...
You can use nested ternary if statements.
# if-else ternary construct
country_code = 'USA'
is_USA = True if country_code == 'USA' else False
print('is_USA:', is_USA)
# if-elif-else ternary construct
# Create function to avoid repeating code.
def get_age_category_name(age):
age_category_name = 'Young' if age <= 40 else ('Middle Aged' if age > 40 and age <= 65 else 'Senior')
return age_category_name
print(get_age_category_name(25))
print(get_age_category_name(50))
print(get_age_category_name(75))
MESSAGELENGHT = 39
"A normal function call using if elif and else."
if MESSAGELENGHT == 16:
Datapacket = "word"
elif MESSAGELENGHT == 8:
Datapacket = 'byte'
else:
Datapacket = 'bit'
#similarly for a oneliner expresion:
Datapacket = "word" if MESSAGELENGHT == 16 else 'byte' if MESSAGELENGHT == 8 else 'bit'
print(Datapacket)
Thanks

python Home work

define a string
define string's len
print true if the string len is even and the right half identical to the left half, else - print false.
I need to do it only in 3 lines and it has to work for every string len without change the condition in (3).
We didn't learn or loop yet. It supposed to be with slice.
please help.
i tried to do it:
some_string = "bb"
str_len = len (some_string),str(a)
if str_len%2==0 and some_string[0:a/2]==some_string[a/2:0]: print "True"
else: print "False"
but something went wrong
Your code is almost fine, just a little improvement needed. You don't actually need to check for even length using modulus(%) operator, as the second condition will automatically take care of it.
So, Here's how your code would look: -
>>> def check_str(value):
mid = len(value) / 2
return True if value[:mid] == value[mid:] else False
>>> check_str("abab")
True
>>> check_str("abb")
False
>>> check_str("aaba")
False
use a ternary operator:
In [38]: def ans(x):
strs=x # line 1
length=len(strs) # line 2
print True if length%2==0 and strs[0:length/2]==strs[length/2:] else False ## line 3
....:
....:
In [42]: ans("bb")
True
In [43]: ans("bbb")
False
In [44]: ans("abcabc")
True
In [45]: ans("abcdabc")
False

Categories

Resources