(Newbie; Python) Help understanding why this is

Hi,
I’m a beginner, learning Python.
I’m following a course on EDX Courses and in an exercise, I was surprised my code didn’t work.

Here’s the exercise:

balance = 20.0
price = 19.0
sales_tax_rate = 1.06

#Write some code below that will print True if balance is
#big enough to make a purchase with the given price and sales
#tax rate, False if it is not.
#Add your code here!

So my initial answer was:

price *= sales_tax_rate < balance
print(price)

To my surprise, this output “19”??

So then I used the ‘primitive’ version of *= and wrote out the code in full without the shorthand version " *= " as:

price = price * sales_tax_rate < balance
print(price)

And to my surprise again, that worked and gave a False as expected!?

But why is that? Can someone explain? I mean, the *= is just meant to simplify writing code and make it faster to avoid having to type the ‘full’ version, i.e. price = price *
Why isn’t the result the same then?

Thanks in advance!

Why, oh why, are you changing the meaning of price? That makes your code very confusing.

To your question, you are being sloppy with order of operations.
a *= b
means
a = a * (b)
So
price *= sales_tax_rate < balance
means
price = price * (sales_tax_rate < balance)

In this case, sales_tax_rate < balance is true, as 1.06 < 20. That means that price *= sales_tax_rate < balance is computing price *= 1, as true = 1 and false = 0.

This sort of willy-nilly type conversion that Python does combined with your confusing redefinition of price is the perfect storm which demonstrates why sometimes Python can be a challenging language for beginners to catch mistakes.

What you really want is
sufficient_balance = price * sales_tax_rate < balance
or even more clearly
sufficient_balance = (price * sales_tax_rate) < balance

It’s only an exercise and I’m trying to learn to write as tight and as short code as possible.
So in the context of this very simple and short code, it cannot be confusing that I use ‘price’ as my variable but I of course agree and understand your general point that this wouldn’t be generally seen as good practice. It’s not hurting the code itself (or shouldn’t) so I’m not worried about that right now.

As far as the code itself, I was taught and all the online resources I found tell me that mathematical operators have precedence over logical operators. So in this case my code would normally first execute the *= and then the <.
So…I still don’t understand why it doesn’t work?
I guess it’s an inherent limitation of this *= operator but I just hadn’t seen this explained or documented anywhere before so…I posted here.

Writing tiny code is a very bad habit to get into. It is not tight or simple, its a future maintenance disaster that will cost you time. More development time goes into refactoring and debugging than goes into the original development of the code. Code golf is bad except for a tiny number of niche cases.

As far as assignment operator documentation goes, look at the official documentation here. In section 7.2.1,

An augmented assignment evaluates the target (which, unlike normal assignment statements, cannot be an unpacking) and the expression list, performs the binary operation specific to the type of assignment on the two operands, and assigns the result to the original target. The target is only evaluated once.

This means that
a *= b
is equivalent to
a = a * (b)
as I explained above.

First off, thx for taking the time to help! :wink:

I agree and totally understand your point about writing short code but please just understand that this is purely for my own sake, as I said, to teach myself a skill (shorter code, because til now, my code has often been unnecessarily long). It doesn’t mean that this is how I intend to write code for the rest of my life! :wink:

As for the documentation quote…I barely understood a full sentence! :grin:
But that has almost always been the case for me for me almost all the official doc.

As for

`a *= b`
is equivalent to
`a = a * (b)`

That I of course understood from the start, it’s in my own code as well, when I show my alternative code that gets the intended result.
There where it becomes a problem is when the < comes into play.
I don’t understand why that throws off the code if the order of operation is in fact as I explained it: first *, then <

According to the docs, for
price *= sales_tax_rate < balance
we

  1. Evaluate the target
    price has a value of 19
  2. Evaluate the expression list
    sales_tax_rate < balance has a value of true or 1
  3. Perform the binary operation
    19 * 1 has a value of 19
  4. Assign the result to the original target
    price = 19

This is why you get 19 when you print price.

In other words, you use variable *= expression when you want to multiply a variable by the result of an expression.

Sorry, but…you still haven’t really directly addressed what I’ve mentioned several times already, the order of operations. Why doesn’t it apply here?
In what you just showed, you in fact first evaluated the logical operator < and then peformed the multiplication…why?
I mean, am I not right in saying that math operators are performed before logical operators like <?

I am directly addressing the order of operations!
The order of operations for *= is given in the documentation as the 4 step process above. Those 4 steps are performed in that order.

a *= b
is not the same as
a = a * b

a *= b
has a different order of operations than
a = a * b
as described in the documentation.

Take this example,
a *= b*c < d
This is equivalent to
a = a * (b*c < d)
In this expression, the * in b*c has higher priority than <, but the * in *= always comes last.

Oh…ok. Sorry, I so completely did not understand that quote from the documentation that I didn’t get that.
Ok, I guess that settles it then. Too bad, that seems to unnecessarily complicate things. Oh well, who am I to argue. :smirk:

1 Like

Documentation can be confusing. It takes a lot of practice to be able to read documentation and make sense of it.
It is very necessary for *= to work this way. The augmented assignment operator *= has two benefits

  1. It does make smaller expressions, as
    variable = variable * expression
    reduces to
    variable *= expression
  2. It allows the interpreter to make performance optimizations which can make a big difference in certain cases.

I’m sure you’re right, and I obviously can see the benefit of 1. since that was the whole reason that got me into this problem (but doesn’t explain why the normal order of operations has to be reversed).
As for 2. I’ll just trust you on that :wink:
Thanks! :+1:

1 Like