What does *= mean in python? [duplicate] - python

This question already has answers here:
What is this operator *= -1
(2 answers)
Closed 9 years ago.
For example in this code:
def product(list):
p =1
for i in list:
p *= i
return p
I found this code, but I need to be able to explain each and every part of it.

It's shorthand for
p = p * i
It's analogous to the more frequently encountered p += i

Taken from the first result in google:
Multiply AND assignment operator, It multiplies right operand with the left operand and assign the result to left operand
*= is the same as saying p = p * i.
This link contains a list of all the operators in their various, wonderful combinations.
Example
A pseudo-code explanation of your code is as follows:
assume list := {4, 6, 5, 3, 5, 1}
p := 1.
for(each number in list)
p = the current value of p * the current number.
// So: 1 * 4, 4 * 6, 24 * 5, 120 * 3...
return p.

Usually p *= i is the same as p = p * i.
Sometimes it can be different, and I think the explanations already posted aren't clear enough for that, so:
It can be different when p is a mutable object. In that case the in-place *= may modify the original object instead of creating a new one. Compare what happens to q in each of these:
>>> p = q = [2]
>>> p *= 5
>>> p
[2, 2, 2, 2, 2]
>>> q
[2, 2, 2, 2, 2]
>>> p = q = [2]
>>> p = p * 5
>>> p
[2, 2, 2, 2, 2]
>>> q
[2]
If can also be different when p is a complex expression with side effects as the in-place version only evaluates sub-expressions once. So for example:
aList[randint(0, 5)] *= 3
is not the same as:
aList[randint(0, 5)] = aList[randint(0, 5)] * 3

It's not exactly the same as p = p * i:
>>> class A(int):
... def __imul__(self, item):
... print '__imul__ is running!'
...
... def __mul__(self, item):
... print '__mul__ is running!'
>>> mynumber = A(10)
>>> mynumber *= 5
__imul__ is running!
>>> mynumber = A(10)
>>> mynumber * 5
__mul__ is running!
However the output is mostly the same, so you should probably treat it so

The idea behind the operator a *= b is to mean the same as a = a * b. In most cases (as in yours) it will do exactly this, so it multiplies a variable with a value and stores the result in the variable again.
The notation using *= might be faster (depending on the classes involved), and it is, in any case, the clearer version, so it should be favored. Its main advantage shows if the variable a is itself already a complex expression like myGiantValueField[f(42)].getValue()[3]:
myGiantValueField[f(42)].getValue()[3] = myGiantValueField[f(42)].getValue()[3] * 3
is certainly less readable and due to code doubling more prone to fixing-errors than
myGiantValueField[f(42)].getValue()[3] *= 3
In general, though, the operator *= calls the method __imul__() of the variable a and hands over the argument b, so it means exactly the same as a.__imul__(b) (which isn't as intuitive).
Why could there be a difference between a = a * b and a *= b? Three reasons come two mind at once, but there might be more.
A class could implement only the *= operator (so a * b could be undefined although a *= b exists) due to performance aspects. Multiplying a very large value (e. g. a giant matrix) with a number is sometimes better done in-place to avoid having to allocate memory for the result (which might at once after computation be copied into the original variable by the assignment). That a = a * b is internally sth like tmp = a * b and a = tmp.
A class might find its use unintuitive using the direct multiplication, hence it could leave the * operator unimplemented. An example might be a class representing the volume of the computer speaker. Doubling the volume might make sense (volumeKnob *= 2) whereas computing it without using (assigning) it is not recommended (x = volumeKnob * 2? ← makes no sense as it does nothing).
If the type of the result of the multiplication differs from the type of a, I would not expect or recommend implementing the *= operator as it would be misleading. An example might be if a and b are vectors whose multiplication result would be a matrix (or a number). The name __imul__ already suggests that it is meant for being applied iteratively, i. e. more than once. But if a's type changed, this would be problematic.

Related

Avoid Mean of Floating Point Error

When I calculate the mean of a list of floats the following way
def mean(x):
sum(x) / len(x)
then I usually do not care about tiny errors in floating point operations. Though, I am currently facing an issue where I want to get all elements in a list that are equal or above the list's average.
Again, this is usually no issue but when I face cases where all elements in the list are equal floating point numbers than the mean value calculated by the function above actually returns a value above all the elements. That, in my case, obviously is an issue.
I need a workaround to that involving no reliability on python3.x libraries (like e.g. statistics).
Edit:
It has been suggested in the comments to use rounding. This interestingly resulted in errors being rarer, but they still occur, as e.g. in this case:
[0.024484987, 0.024484987, 0.024484987, 0.024484987, ...] # x
0.024485 # mean
[] # numbers above mean
I believe you should be using math.fsum() instead of sum. For example:
>>> a = [0.024484987, 0.024484987, 0.024484987, 0.024484987] * 1360001
>>> math.fsum(a) / len(a)
0.024484987
This is, I believe, the answer you are looking for. It produces more consistent results, irrespective of the length of a, than the equivalent using sum().
>>> sum(a) / len(a)
0.024484987003073517
One neat solution is to use compensated summation, combined with double-double tricks to perform the division accurately:
def mean_kbn(X):
# 1. Kahan-Babuska-Neumaier summation
s = c = 0.0
n = 0
for x in X:
t = s + x
if abs(s) >= abs(x):
c -= ((s-t) + x)
else:
c -= ((x-t) + s)
s = t
n += 1
# sum is now s - c
# 2. double-double division from Dekker (1971)
# https://link.springer.com/article/10.1007%2FBF01397083
u = s / n # first guess of division
# Python doesn't have an fma function, so do mul2 via Veltkamp splitting
v = 1.34217729e8 # 0x1p27 + 1
uv = u*v
u_hi = (u - uv) + uv
u_lo = u - u_hi
nv = n*v
n_hi = (n - nv) + nv
n_lo = n - n_hi
# r = s - u*n exactly
r = (((s - u_hi*n_hi) - u_hi*n_lo) - u_lo*n_hi) - u_lo*n_lo
# add correction
return u + (r-c)/n
Here's a sample case I found, comparing with the sum, math.fsum and numpy.mean:
>>> mean_kbn([0.2,0.2,0.2])
0.2
>>> sum([0.2,0.2,0.2])/3
0.20000000000000004
>>> import math
>>> math.fsum([0.2,0.2,0.2])/3
0.20000000000000004
>>> import numpy
>>> numpy.mean([0.2,0.2,0.2])
0.20000000000000004
How about not using the mean but just multiplying each element by the length of the list and comparing it directly to the sum of the original list?
I think this should do what you want without relying on division

Python sympy - simplify nonzero factors from an equation

I am using the sympy library for python3, and I am handling equations, such as the following one:
a, b = symbols('a b', positive = True)
my_equation = Eq((2 * a + b) * (a - b) / 2, 0)
my_equations gets printed exactly as I have defined it ((2 * a + b) * (a - b) / 2 == 0, that is), and I am unable to reduce it even using simplify or similar functions.
What I am trying to achieve is simplifying the nonzero factors from the equation (2 * a + b and 1 / 2); ideally, I'd want to be able to simplify a - b as well, if I am sure that a != b.
Is there any way I can reach this goal?
The point is that simplify() is not capable (yet) of complex reasoning about assumptions. I tested it on Wolfram Mathematica's simplify, and it works. It looks like it's a missing feature in SymPy.
Anyway, I propose a function to do what you're looking for.
Define this function:
def simplify_eq_with_assumptions(eq):
assert eq.rhs == 0 # assert that right-hand side is zero
assert type(eq.lhs) == Mul # assert that left-hand side is a multipl.
newargs = [] # define a list of new multiplication factors.
for arg in eq.lhs.args:
if arg.is_positive:
continue # arg is positive, let's skip it.
newargs.append(arg)
# rebuild the equality with the new arguments:
return Eq(eq.lhs.func(*newargs), 0)
Now you can call:
In [5]: simplify_eq_with_assumptions(my_equation)
Out[5]: a - b = 0
You can easily adapt this function to your needs. Hopefully, in some future version of SymPy it will be sufficient to call simplify.

Evaluating first-order arithmetic formulas

Is there a python package for evaluating bounded first-order arithmetic formulas?
For example, it gets a bounded first-order arithmetic expression
>>> exp = 'forall x < z exists y < x ((2 * y + 1 = x) or (2 * y = x))'
and a value for the free variable z
>>> tau = [(z,20)]
and returns its value
>>> eval(exp, tau)
False
Maybe what you are looking for is something called "quantifier elimination". If so, take a look at QEPCAD. [1] It may be easier to create a Python interface for QEPCAD than to find a Python implementation.
[1] http://www.usna.edu/CS/~qepcad/B/QEPCAD.html

What is this operator *= -1

I'm going through some Python activities and was given example code with this operator: y *= -1
I had a look through the relevant Python docs, to no avail.
I know y += 1, for example, is short for y = y + 1. So is this y = y * -1 y equals y times -1 maybe?
Closest thing in Python docs I could find is this: x * y: product of x and y
Is this it?
In the vast majority of the cases
y *= <expr>
is the same as
y = y * <expr>
but in the general case, it is interpreted as:
y = imul(y, <expr>)
which is then equivalent to:
y = y.__imul__(<expr>)
if y's type overrides __imul__.
This means that if y's type overrides the inplace multiplication operator, y*=<expr> is performed inplace, while y=y*<expr> is not.
EDIT
It might not be immediately clear why the assignment is needed, i.e. why it is intrepreted as y = imul(y, <expr>), and not just imul(y, <expr>).
The reason is that it makes a lot of sense for the following two scenarios to give the same result:
c = a * b
and
c = a
c *= b
Now, this of course works if a and b are of the same type (e.g. floats, numpy arrays, etc.), but if they aren't, it is possible for the result of the operation to have the type of b, in which case the operation cannot be an inplace operation of a, thus the result needs to be assigned to a, in order to achieve the correct behavior.
For example, this works, thanks to the assignment:
from numpy import arange
a = 2
a *= arange(3)
a
=> array([0, 2, 4])
Whereas if the assignment is dropped, a remains unchanged:
a = 2
imul(a, arange(3))
=> array([0, 2, 4])
a
=> 2
Yes that's correct. It just means multiply the left-hand value by negative the right-hand value. They're both arithmetic operators that differ simply by operation and expression binding, so I believe +/* are parallel everywhere else in overloads.
y = y * -1

Python variable allocation and `id` keyword [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Python “is” operator behaves unexpectedly with integers
Why (0-6) is -6 = False?
So, while playing with a bit with id (python 2.6.5), I noticed the following (shell session):
>>> a = 1
>>> id(a)
140524904
>>> b = 1
>>> id(b)
140524904
Of course, as soon as I modify one of the variables it gets assigned to a new memory address, i.e.
>>> b += 1
>>> id(b)
140524892
Is it the normal behavior to initially assign both variables that have identical values to the same memory location or just an optimization of i.e. CPython?
P.s. I spent a little time browsing around the code in parser, but couldn't find where and how variables are allocated.
As mentioned by glglgl, this is an implementation detail of CPython. If you look at Objects/longobject.c in the source code for CPython (e.g. version 3.3.0), you'll find the answer to what's happening:
#if NSMALLNEGINTS + NSMALLPOSINTS > 0
/* Small integers are preallocated in this array so that they
can be shared.
The integers that are preallocated are those in the range
-NSMALLNEGINTS (inclusive) to NSMALLPOSINTS (not inclusive).
*/
static PyLongObject small_ints[NSMALLNEGINTS + NSMALLPOSINTS];
This explains why, after a = 1; b = 1, a is b will be True, even when you say a += 2; b +=2; a -= 2; b -= 2. Whenever a number is calculated to have a value that fits in this array, the resulting object is simply picked from this array instead, saving a bit of memory.
You can figure out the bounds of this small_ints array using a function like this:
def binary_search(predicate, lo, hi):
while lo + 1 < hi:
mid = (lo + hi) / 2
if predicate(mid):
lo = mid
else:
hi = mid
return lo
def is_small_int(n):
p = n + 1
q = n + 1
return (p - 1) is (q - 1)
def min_neg_small_int():
p, q = -1, -1
if p is not q:
return 0
while p is q:
p += p
q += q
return binary_search(is_small_int, p / 2, p) - 1
def max_pos_small_int():
p, q = 1, 1
if p is not q:
return 0
while p is q:
p += p
q += q
return binary_search(is_small_int, p / 2, p)
def small_int_bounds():
return (min_neg_small_int(), max_pos_small_int())
For my build (Python 2.7, 64-bit Windows build), small_int_bounds() == (-5, 256). This means that numbers between -5 and 256 (inclusive) are shared through the small_ints array in Objects/longobject.c.
-edit- I see elssar noted that there is a similar answer about interning of some literals. This fact is also mentioned in the documentation for PyInt_FromLong, as mentioned by this answer.
In python all variables are pointers to some objects. Even number.
Number is immutable object. So, CPython not need to create a new object with the same value.
This does not mean that CPython will always use the same objects.
In your first example variables a and b point to the same object.
When your make b += 1 you "create" new object 2.
Here the term "variables" must be precised: there are objects at one hand, and names which are bound to objects at the other hand.
If you do a = b = 1, both a and b are bound to the same object representing 1.
If you do a = 1; b = 1, I think it is a CPython detail that it is the same. Generally, an implementation could choose to have two objects both representing 1 and using them both here. But as that would be a waste of memory, it is generally not done in this way.
a and b both refer to the same object in memory (1), with the ID 140524904. Once you do b += 1 you have 2, which is located elsewhere.

Categories

Resources