Assignment of Boolean within a ternary operator

Asked

Viewed 462 times

0

I would like to know why this syntax is invalid and how to do the equivalent with a ternary operator

seventh_state = False

state_machine_var = 1

for x in range(0, 10):
  state_machine_var += 1
  (seventh_state = False, seventh_state = True)[state_machine_var == 7]

The fifth line is considered an invalid syntax, but I used the template

(if_test_is_false, if_test_is_true)[test]

and that works.

Change that to a if normal also solves the problem

  • 1

    We are at [pt.so], so please ask your question in our language. Take the opportunity to do the [tour], read the [Ask] guide and access [help].

  • 1

    What do you want to do? This code makes no sense. Use and translate to Portuguese.

  • 1

    Do you want to seventh_state be it False when state_machine_var is different from 7 and True when it’s the same, then why don’t you just seventh_state = (state_machine_var == 7)? Much simpler, easier to read and understand and has valid syntax.

  • This code is a simplification of the original code, it is normal not to make much sense out of context. I thank @Andersoncarloswoss ! But it doesn’t answer my question

  • Incidentally, in the previous line, loop x in range(0, 10), that loop does not exist in Python, maybe the syntax error starts there. Possibly it would be for correct by adding the two dots to the end of the line.

  • I’ve fixed @Andersoncarloswoss but the error was passing here, the loop is correct

Show 1 more comment

2 answers

5

TL;DR: The ternary operator in Python is written in the form:

resultado_pro_caso_verdadeito if condicao else resultado_pro_caso_falso

With the keywords if...else separating the 3 terms.


In Python, things are different from language that inherits direct syntax from C, such as Java, Javascript and others.

First, until Python 3.7 (the most current version), assignment actions are exclusively commands (statments), and cannot be used as part of an expression. (From Python 3.8 this will be possible with the use of the operator :=)

In addition, the ternary operator that exists in C-like languages: expr? res_true: res_false item a different syntax in Python - it uses a if...else online as in res_true if expr else res_false.

Then his expression

(seventh_state = False, seventh_state = True)[state_machine_var == 7]

should be written, if necessary, as:

seventh_state = True if state_machine_var == 7 else False

Only in this case you don’t even need it - since the expression itself state_machine_var == 7 already has boolean result True or False, then you just really need to:

seventh_state = state_machinne_var == 7

Historical and other forms

The syntax for ternary operators expr1 if condition else expr2 was only implemented in version 2.5 of Python, which is from 2006. Before that people used recipes like the one in the question to try to have the same functionality. The official recommendation, however, was to create a set of if/else with even separate blocks.

The first recipe for ternary operators in old Python, which can still be found in many tutorials and books Python 2 took advantage of a characteristic of operators and and or python - and would suggest using the following form: cond and expr1 or expr2

The idea in that way is as follows:

  • An expression with and only has "True" value if your two operands are positive. Then the language shortcuts: if you have expr3 and expr4 and expr3 is false, the code of expr4 is never executed. In addition to the shortcut, there is a feature - the whole value of expr3 and expr4 if expr3 is true, it is expr4 - the and does not test expr4 - no matter if its value is a "true" or "false" object, as it was only executed respecting the and its value is the final value of the and.

  • Already the or is the opposite - the language shortcut is: if in expr5 or expr6, expr5 has a true value, its output is used as the whole expression value and expr6 is never executed.

  • The precedence of and is greater than the precedence of or, then cond and expr1 or expr2 is the same as (cond and expr1) or expr2

  • thus, in cond and expr1 or expr2 the operator and will always perform the expr1 only if the expression given in cond is true. And then you will get as a result, instead of "True/False" the result of expr1. The operator or, in turn, if this first working of it is true, it assumes the value of that working, and if it is false, the second working - expr2 is evaluated and used as a result of the whole expression.

The big problem this way is that if the value of expr1 has a false boolean value in Python, expr2 will be used anyway - even if the condition is true. And in Python, None objects, any number with value 0 (int, float, Complex, Fraction, Decimal or other type of custom number), any sequence, map or empty container ("", [] set(), {}) are objects with false boolean value.

The second fear is like the one that appears in the question here: create a sequence with the desired result expressions, and use the condition as the index of this sequence - using the fact that in Python, the booleans True and False also have numerical values 1 and 0, respectively: [expr2, expr1][cond]

It has two disadvantages: one that the order of the desired expressions is reversed - that is, when cond is True, Python takes the value at index 1 of the sequence, which is the second. The other is that no matter the condition, the expr2 and expr1 are always executed - if they involve a function call that uses resources, gets bad, or worse, or accounts with values that are not available if cond is false, nor is it possible to write the expression in this way.

Future

As of Python 3.8 (currently in Beta, scheduled for the end of October 2019), the assignments in expressions with the operator := will become valid, and this greatly increases the utility of the ternary operator - since it is possible to re-use a value calculated during the condition.

So if you need a "face" function call and then run code depending on the result of that call, but using the return value, until today (Python 3.7), the only way to do that is:

valor_intermediario = funcao_cara()

if valor_intermediario > 5:
    resultado = coeficiente_grande * valor_intermediario
else:
    resultado = coeficiente_pequeno * valor intermediaio

With the new type of assignment this sequence can be written like this:

resultado = coeficiente_grande * interm if (interm:= funcao_cara()) > 5 else coeficiente_pequeno * interm
  • 1

    As a curiosity, I’ve seen some publications saying about doing (if_test_is_false, if_test_is_true)[test], or better, result = (if_test_is_false, if_test_is_true)[test], that in the context of the question seventh_state = (False, True)[state_machinne_var == 7]; but anyway it’s unjustifiable to do that.

  • 3

    This way of using Boolean as an index for a tuple was sometimes used before the syntax of ternary operators - which unless mistaken is from Python 2.5 . The two big problem in this way are, primeori all expressions are executed - not only the expression "chosen" and second that gets a somewhat strange construction, which takes some time for those reading understand the real intentions (including why the values for "True" and "False" are reversed -the expression for the true case has to be in second place, in index 1)

2

The syntax error you cite is due to the fact that you are doing assignments within the boot of a tuple. This is not possible, not until version 3.7, as jsbueno has already commented in its reply.

To better understand, let’s analyze the template used:

(if_test_is_false, if_test_is_true)[test]

There are two expressions, if_test_is_false and if_test_is_true that are assessed by the interpreter, regardless of the value of test, to build a tuple with the results. The only possible indexes of this tuple will be 0 or 1, and given the implementation of Python, it is allowed to be accessible through boolean values, since False is equivalent to the value int(0) and True at value int(1), what justifies the positions of the values in the tuple, being the first the expression for when the test fails and the second for when it is successful.

Attribution, as it did, is not a valid expression to initialize a tuple as it did:

(seventh_state = False, seventh_state = True)[state_machine_var == 7]

Just analyze which tuple would be built in this context. You could even change to

seventh_state = (False, True)[state_machine_var == 7]

Which has a valid syntax and produces the expected result, but there is no justification for doing so. Impairs the readability of your code, creates a overhead unnecessary both to define the tuple and to access operations on it. Briefly, this expression has only negative points when compared to the expression with the ternary operator:

seventh_state = True if state_machine_var == 7 else False

And yet that expression is redundant, because you’re explicitly saying that a variable should be True when the condition is True or False otherwise. So why not already assign the result of the condition?

seventh_state = (state_machine_var == 7)

Your code becomes much simpler and readable.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.