Struggling with the order of operations. Am I overthinking this? - python

I've been given the following, and I'm asked to give the type of value and the expression it returns:
>>> b = 10
>>> c = b > 9
>>> c
I know that in the first part, we're defining b to be 10, but in the second sentence, I'm interpreting this as: Define c to be equal to b>9. Now b>9 as a value, doesn't make sense, so c can't be equal to it, so I put that the answer was error and the type was Nonetype.
The correct answer is apparently True, but why? Why do we take the c=b part first, and then ask whether it's >9? Is there some sort of standard order in which you're supposed to apply these things?
PS: What do the three >>> symbols mean in programming? I'm doing an introductory CS course, so please forgive any misnomers.

Python's order precedence is well documented. b > 9 returns a boolean value that must be evaluated before it can be assigned with c =.
And >>> is part of the interpreter REPL. It doesn't have a specific meaning to all programming languages.
You could run your code in any Python interpreter to see what the output values are. I'm not sure what you mean by getting a Nonetype error as nothing is evaluated to None in those lines

I think you're getting confused between:
the assignment operator (=), which assigns the result of the expression on the right side of the operator to the variable on the left side of the operator and;
the equality operator (==), which tests the expressions on the right and left of the operator for equality and returns a boolean (true/false) value.
The first expression assigns the value 10 to the variable b. The second expression assigns the expression b > 9 (i.e. 10 > 9), which evaluates to true, to c. Therefore, I hope you can see how c ends up being true.
The other issue you might need clarification on is that the = operator is right associative, which means that the expression to the right of the operator will be evaluated first. i.e. in the second line, b > 9 is evaluated first before assigning the result (true) to c.
In answer to the second part of your question. Your code wouldn't actually compile as it stands in a regular C# compiler. I'm not sure what the >>> are. Are you using an online editor or something?
Valid C# code would be:
int b = 10;
bool c = b > 9;
Console.WriteLine(c); //Outputs true

Related

Starred expression in ternary operator python

I wrote a python program to print the ascii value of up to every 3 numbers in a string or "ERROR" if the length is not divisible by three. I was golf the code when I ran into a SyntaxError.
Code:
c=input()
p=len(c)
c=[int(c[i:i+3])for i in range(0,len(c),3)]
print("ERROR"if p%3else*map(chr,c),sep='')#SyntaxError here
But this works:
c=input()
p=len(c)
c=[int(c[i:i+3])for i in range(0,len(c),3)]
print(*map(chr,c),sep='')
Putting a space before the * or after the 3 doesn't work. I could just use ''.join but it's one character longer. My question is why can't I use a starred expression in a ternary operator?
Because the * has to apply to the whole expression that produces the set of arguments, not a part of it, and not conditionally. Internally, CPython uses different bytecodes for calling with unpacking vs. normal calls, so your conditional would require it to change the byte code to call print based on the arguments to print, essentially rewriting what you wrote into pseudocode like:
if p % 3:
call(print, "ERROR", sep='')
else:
call_varargs(print, *map(chr, c), sep='')
which is beyond the compiler's capability.
If you wanted to make this work, you could do the following:
print(*(("ERROR",) if p%3 else map(chr,c)), sep='')
which ensures the whole ternary evaluates to an unpackable sequence and unpacks whatever survives unconditionally, avoiding the confusion.
print(*(["ERROR"] if p%3 else map(chr,c)),sep="!")
keep it outside of the ternary
The * expander transforms a single enumerable variable into individual variables. E.g.
li = [1,2,3]
print(*li)
produces: 1 2 3 instead of [1, 2, 3].
One value vs. multiple values
It appears to remove the brackets and pass a single string to print, but this is only an appearance, it actually replaces the single list variable by 3 variables and is actually equivalent to:
print(li[0], li[1], li[2])
It works because print accepts a variable number of arguments, so in our case it can deal with the single list or with these three integers.
The conditional expression is a one-value operator
However in your code you use the star operator within a conditional expression:
c = '065066067068'
p = len(c)
c = [int(c[i:i+3]) for i in range(0, p, 3)]
print('ERROR' if p%3 else *map(chr, c), sep='!')
print would be able to accept both evaluations of the expression, a single string value ('ERROR') or multiple char values from map.
But the conditional expression prevents returning multiple values according to the condition (p%3). The expression output has to be a single value. So you have no other choice than to return the list from map as an enumerable, and un-nest it only outside of the ternary operator, e.g. in the print statement.
A string is an enumerable, not a scalar value
However this solution now introduces another problem: Un-nesting will also convert the constant string ERROR into single chars, as a string is considered by Python an enumerable of single chars (as you know since you use this property for your input string). When the condition is true, the output would be:
E!R!R!O!R
Therefore the string must be first converted to an enumerable of strings, e.g. a tuple
Final solution
if p%3: s = ('ERROR',)
else: s = map(chr, c)
print(*s, sep='!')
The outputs will be:
A!B!C!D
ERROR

Is it possible to use the `a = b or c` construct if b and c are arrays/DataFrames in python?

This is about the following type of statement:
b = np.array([1,2,3,4])
def func(a=None):
if a is None:
a = b
which I would like to replace the if statement with:
def func(a=None):
a = a or b
so firstly two things: I can't do the above as an argument default because I need the default value to be evaluated at runtime (i.e if the default value is actually an object attribute that is expected to change). Secondly, I heard that maybe the way I want to do it is not always recommended by convention, but I'm just asking from a more hypothetical perspective how to make the above work. So far I've always been writing it the first way, but in my honest opinion the second feels better.
a = a or b above fails because in this case, a is expected to be an array or pandas.DataFrame, so it gives me the standard truth value of an array is ambiguous error. It would work if a and b were floats for example.
So is there some way to write the above similarly to how I'm doing (and concisely), but to use the fact that arrays/lists of non-zero length are evaluated as true in python (i.e it would work if a was a list for example?
You can use the tertiary operator and compute the truth value yourself. Assuming a is an array for example:
a = a if a is not None and all(a) else b
where you can replace the truth logic if you like.

"Nonetype" error while being assigned to variables

Just started my Programming 1 class, and we were assigned to create a quiz, and tally up the final score at the end. As a result I created this simple statement to play after every question.
if answer_1 == correct_1:
a1 = 1 and print("You are correct!")
else:
a1 = 0 and print("You are incorrect.")
However, at the end it is telling me I cannot add an integer and a NoneType. The wrong answers (zeroes) are being applied fine, it's when the answer is right that it gets stored as a NoneType (which I know means nothing). I'm aware I'm doing some syntax wrong, but do not quite have the know how to fix it.
You are stuck with couple of things here:
you got precedence wrong since here and will be evaluated first, then =
print returns None, and 1 and None = None, while 0 and None = 0.
Here is a solution:
if answer_1 == correct_1:
a1 = 1
print("You are correct!")
else:
a1 = 0
print("You are incorrect.")
Edit:
Just wanted to point out why it stands that 1 and None = None and 0 and None = 0.
It is because Python evaluates boolean conditions lazily.
Here is what docs say about it:
The expression x and y first evaluates x; if x is false, its value is
returned; otherwise, y is evaluated and the resulting value is
returned.
The expression x or y first evaluates x; if x is true, its value is
returned; otherwise, y is evaluated and the resulting value is
returned.
Just for science
Having lazy evaluation of boolean conditions and = precedence you can mimic conditional (ternary) operator, construct you might find in other languages.
Definition of ternary operator: condition ? expr1 : expr2.
Explanation: If condition is true, the operator returns the value of
expr1; otherwise, it returns the value of expr2.
In Python, you can mimic this with: condition and expr1 or expr2.
Same applies as with definition of ternary operator. If contition evaluates to True, expr1 is returned. Otherwise, expr2 is returned.
Why I said just for science? Because although this looks great, it's not used often and may affect readability of your code.
Python’s and is a logical operator. It takes two values (either side) and returns True or False.
Since it's homework, I'll give you a strong hint. and doesn't work the way you're thinking here. You can have more than one line within an if statement.

Is 'or' used on the right-hand-side of an assignment pythonic?

Situation
(Note: The following situation is just exemplary. This question applys to anything that can evaluate to bool)
A default list should be used if the user does not provide a custom list:
default_list = ...
custom_list = ...
if custom_list:
list = custom_list
else:
list = default_list
You can shorten this to:
default_list = ...
custom_list = ...
list = custom_list if custom_list else default_list
Now, as per https://docs.python.org/2.7/reference/expressions.html#or ...
The expression x or y first evaluates x; if x is true, its value is returned; otherwise, y is evaluated and the resulting value is returned.
..., or does not return a boolean, but rather the first value whose boolean conversion is not false. Therefore, the following would be valid code:
list = custom_list or default_list
This is similar to the C# Null Coalescing Operator, except it should be recoined in Python as False Coalescing Operator, which returns the first non-false argument.
Question
The last example seems to be easier to read, but is it considered pythonic?
Neither pep8 (the program) nor pylint do complain.
That is perfectly valid and you can use that. Even the documentation of or has an example for the same.
Note that neither and nor or restrict the value and type they return to False and True, but rather return the last evaluated argument. This is sometimes useful, e.g., if s is a string that should be replaced by a default value if it is empty, the expression s or 'foo' yields the desired value.
However, the or method has a limitation. If you want to purposefully allow a Non-Truthy value, then it is not possible with that.
Let us say you want to allow an empty list
my_list = [] or default_list
will always give default_list. For example,
print [] or [1, 2, 3]
# [1, 2, 3]
But with conditional expression we can handle it like this
custom_list if isinstance(custom_list, list) else default_list
Dusting off the older documents, quoting the BDFL's FAQ,
4.16. Q. Is there an equivalent of C's "?:" ternary operator?
A. Not directly. In many cases you can mimic a?b:c with a and b or
c, but there's a flaw: if b is zero (or empty, or None -- anything
that tests false) then c will be selected instead. In many cases you
can prove by looking at the code that this can't happen (e.g. because
b is a constant or has a type that can never be false), but in general
this can be a problem.
Steve Majewski (or was it Tim Peters?) suggested the following
solution: (a and [b] or [c])[0]. Because [b] is a singleton list it
is never false, so the wrong path is never taken; then applying [0] to
the whole thing gets the b or c that you really wanted. Ugly, but it
gets you there in the rare cases where it is really inconvenient to
rewrite your code using 'if'.
Yes, using or for it's short-circuiting properties was the norm, before the conditional expression construct was added to the language.
It was, and still is, perfectly pythonic to use:
foo = bar or baz
to fall back to a default if bar is false-y (evaluates to false in a boolean context). The fact that it short-circuits also lets you do things like:
foo = bar or expensive_calculation(baz) # only if bar is false-y
and not have expensive_calculation() execute if bar is truth-y (evaluates to true in a boolean context). Similarly, you can use and to ensure that preconditions are met:
foo = bar and bar(baz) # call `bar()` only if it is truth-y
You should use a conditional expression for:
foo = bar and spam or eggs
however. That's what the conditional expression aims to replace. The idea was that if bar is truth-y, then spam is picked, else eggs is picked. But that breaks down if spam is false-y! This was a common source of errors, while
foo = spam if bar else eggs
always picks spam if bar is truth-y.

Dive into python and-or fail

I am stuck at this particular example from dive into python
Example 4.18. When the and−or Trick Fails
>>>>a = ""
>>>>b = "second"
>>>1 and a or b
>>>>'second'
Since a is an empty string, which Python considers false in a boolean context, 1 and '' evalutes to '', and
then '' or 'second' evalutes to 'second'. Oops! That's not what you wanted.
The and−or trick, bool and a or b, will not work like the C expression bool ? a : b when a is false in a
boolean context.
Why does it says it isn't what the user wants, I mean 1 and "" would evaluate to False, while "" or b will evaluate to "second", that's perfectly what should happen, I don't understand why is it wrong?am I missing something?
No, you're not missing something.
The expr if cond else expr inline conditional was introduced to Python 2.5 in PEP 308; before that, conditionals had to be of the full form
if cond:
expr
else:
expr
However, people noticed that expr and cond or expr sort-of worked as an inline conditional, as long as the first expressions happened to evaluate to boolean true. As most things in Python are true, this syntax generally tended to work -- and then, of course, sometimes fail. This was in fact the motivation for adding the "proper" inline conditional.
The "and-or trick" was used to emulate ternary conditional operator, condition ? if-true : if-false or similar. Since 1 is equivalent to True in boolean context, the expression is expected to yield a, not b. So, the lesson is, don't use and-or trick. Python has ternary conditional since 2.5:
true if condition else false
Dive into python is not a good tutorial and should not be followed
The x and y or z expression was used before python had a proper ternary expression (y if x else z). This section is saying why it can surprise you if you expect it to behave like C's x ? y : z.
There's a list of tutorials on the python wiki. that might give you a more up to date resource.
Why does it says it isn't what the user wants, I mean 1 and "" would
evaluate to False, while "" or b will evaluate to "second", that's
perfectly what should happen, I don't understand why is it wrong?am I
missing something?
I feel this part of the question is being ignored. condition and if-true or if-false is a trick used in versions >2.5, as explained in other answers, to mimic other languages' condition ? if-true : if-false or later versions' if-true if condition else if-false.
The problem with it is that if if-true is an object that evaluates to false ("", [], (), False or 0), then this would fail, as expected when you look at the evaluation, but which might creep in as a bug if if-true is dynamically set and can evaluate to false.
The naively expected result of condition and a or b would have been the contents of a ("", empty string), as the condition (1) evaluates to true, but as 1 and a evaluates to false so you get the contents of b.
The solution to this problem is to either use the Python 2.5+ syntax (if-true if condition else if-false) or make sure that if-true is never false. One way to accomplish the second is to pack if-true and if-false in a tuple or list, as a nonempty iterable is always true:
>>> a = ""
>>> b = "second"
>>> condition = 1
>>> condition and a or b
'second'
>>> condition and [a] or [b]
['']
>>> (condition and [a] or [b])[0]
''
It's a kind of ugly solution, but it's there if you need to target old versions of Python.

Categories