Why does this work
>> x, y = (1, 2)
>> print x, y
1 2
But augmenting results in syntax errors..
>> x, y -= (1, 2)
SyntaxError: illegal expression for augmented assignment
Is there a different way, I was expecting:
>> x, y -= (1, 2)
>> print x, y
0 0
You cannot use an augmented assignment statement on multiple targets, no.
Quoting the augmented assignment documentation:
With the exception of assigning to tuples and multiple targets in a single statement, the assignment done by augmented assignment statements is handled the same way as normal assignments. Similarly, with the exception of the possible in-place behavior, the binary operation performed by augmented assignment is the same as the normal binary operations.
Emphasis mine.
In-place augmented assignment is translated from target -= expression to target = target.__isub__(expression) (with corresponding __i...__ hooks for each operator) and translating that operation to multiple targets is not supported.
Under the hood, augmented assignment is a specialisation of the binary operators (+, *, -, etc), not of assignment. Because the implementation is based on those operators and binary operators only ever have two operands, multiple targets were never included in the original implementation proposal.
You'll have to simply apply the assignments separately:
x -= 1
y -= 2
or, if you really, really wanted to get convoluted, use the operator module and zip() to apply operator.isub to the combinations (via itertools.starmap(), then use tuple assignment:
from operator import sub
from itertools import starmap
x, y = starmap(operator.isub, zip((x, y), (1, 2)))
where isub will ensure that the right hook is called allowing for in-place subtraction for mutable types that support it.
or, if you are manipulating types that don't support in-place manipulation, using a generator expression suffices:
x, y = (val - delta for val, delta in zip((x, y), (1, 2)))
This x, y = (1, 2) is sequence assignment. It relies on the right hand side being an iterable object, and the left hand side being composed of the same number of variables as iterating the left hand side provides.
This x, y -= (1, 2) is attempt to call the method __isub__ on the left hand operand(s). The nature of an in-place ("augmented") assignment is that it must take a variable on its left hand side, whose value receives the operator call, and the variable then receives the result of that call. Python does not allow distribution of an in-place assignment over multiple targets.
Related
Suppose there is a function:
def test(x):
return x**2
When I give a list of ints to the function, an error is raised:
TypeError: unsupported operand type(s) for ** or pow(): 'list' and 'int'
But an array of ints instead, the function returns an array of outputs.
How is this possible?
It's important to understand that operators are functions too:
Writing a**b is like writing pow(a, b)
Functions can't guess what the expected behavior is when you give them different inputs, so behind the scenes, pow(a, b) has different implementations for different inputs (i.e. for two integers, return the first in the power of the second. For an array of integers, return an array where each cell has the corresponding cell in the input array in the power of the second integer)
whoever implemented the numpy array created a ** implementation for it, but an ordinary list doesn't have a ** implementation.
If you want to raise a list to a power, use list comprehension:
[xi ** 2 for xi in x]
You can also write your own class and implement ** for it.
I don't see why it would be impossible. Although at a very high level Python lists and Numpy arrays may appear to be the same (i.e. a sequence of numbers), they are implemented in different ways. Numpy is particularly known for its array operations (where an operation can be applied to each of an array's elements in one go).
Here's another example where you can see their differences in action:
a = [1, 2, 3, 4, 5]
print(np.array(a) * 5)
print(a * 5)
You can use this instead for lists:
x = [k**2 for k in x]
return x
The function you wrote works fine for Numpy array but not for lists.
Use the above line to avoid that error.
Example : 10 ** 4*3 ** 2
Our teacher said python first calculates 3**2 and then 10**4 and then multiplies the answers.
But if both side of the multiplication have same order of precedence and python reads from left to right, doesn’t it do 10**4 first and then 3**2?
(My question is not about the order or precedence of operations as both side have same order of precedence)
Your teacher is wrong. From https://docs.python.org/3/reference/expressions.html#evaluation-order:
6.15. Evaluation order
Python evaluates expressions from left to right. Notice that while
evaluating an assignment, the right-hand side is evaluated before the
left-hand side.
The * has two expressions as its operands, 10**4 and 3**2. The left operand, 10**4 is evaluated first, followed by the right operand, 3**2.
Your teacher may be confused with a chain of exponentiations like 2 ** 3 ** 4. Here the "outer" exponentiation has two arguments 2 and 3 ** 4 (rather than 2**3 and 4, because of right associativity). However, the 2 is still evaluated before 3**4.
For a simple expression like this, it doesn't really matter. But it could matter if one or both operands involved function calls with side effects. Consider something contrived like lambda: print(3) or 3)() + (lambda: print(4) or 4)(). The value of the expression is 7 either way, but the order in which the print functions are called will affect the output. Python, though, guarantees that 3 will be output before 4.
Some languages (notably C, IIRC) don't specify an order of evaluation, so it is an implementation detail whether the left or right is evaluated first. This means you can't predict the value of something like i=3; j=(i++)*i; j could be either 3*3 == 9 or 4*3 == 12, depending on whether i is incremented before or after the right-hand operand is evaluated. (I don't recall if this is considered undefined behavior or implementation-defined behavior, though.)
We can easily test it:
class C:
def __init__(self, v):
self.v = v
def __pow__(self, x):
print(f'eval {self.v} ** {x}')
return self.v ** x
>>> C(1) ** 2 * C(4) ** 5
eval 1 ** 2
eval 4 ** 5
1024
In short, your teacher is wrong, at least for Python 3.7. In any case, regardless of whether it's true, I'd never rely on the order here, since those assumptions make the code less readable.
I would like to write a function that takes integer numbers x, y, L and R as parameters and returns True if x**y lies in the interval (L, R] and False otherwise.
I am considering several ways to write a conditional statement inside this function:
if L < x ** y <= R:
if x ** y > L and x ** y <= R:
if x ** y in range(L + 1, R + 1):
Why is option 1 the most efficient in terms of execution time ?
Both #1 and #3 avoid recalculating x ** y, where #2 must calculate it twice.
On Python 2, #3 will be terrible, because it must compute the whole contents of the range. On Python 3.2+, it doesn't have to (range is smart, and can properly determine mathematically whether an int appears in the range without actually iterating, in constant time), but it's at best equivalent to #1, since creating the range object at all has some overhead.
As tobias_k mentions in the comments, if x ** y produces a float, #3 will be slower (breaks the Python 3.2+ O(1) membership testing optimization, requiring an implicit loop over all values), and will get different results than #1 and #2 if the value is not equal to any int value in the range. That is, testing 3.5 in range(1, 5) returns False, and has to check 3.5 against 1, 2, 3, and 4 individually before it can even tell you that much.
Basically, stick to #1, it's going to be the only one that avoids redundant computations and avoids creating a ton of values for comparison on both Py 2 and Py3. #3 is not going to be much (if at all) slower on Python 3.2+, but it does involve creating a range object that isn't needed here, and won't be quite logically equivalent.
The first one has to evaluate x**y only once, so it should be faster than the second (also, more readable). The third one would have to loop over the iterator (in python 2, so it should be slower than both) or make two comparisons (in python 3, so it is no better than the first one). Keep the first one.
x = [1,2,3,4,5,6,7,8,9,10]
#Random list elements
for i in range(int(len(x)/2)):
value = x[i]
x[i] = x[len(x)-i-1]
x[len(x)-i-1] = value
#Confusion on efficiency
print(x)
This is a uni course for first year. So no python shortcuts are allowed
Not sure what counts as "a shortcut" (reversed and the "Martian Smiley" [::-1] being obvious candidates -- but does either count as "a shortcut"?!), but at least a couple small improvements are easy:
L = len(x)
for i in range(L//2):
mirror = L - i - 1
x[i], x[mirror] = x[mirror], x[i]
This gets len(x) only once -- it's a fast operation but there's no reason to keep repeating it over and over -- also computes mirror but once, does the swap more directly, and halves L (for the range argument) directly with the truncating-division operator rather than using the non-truncating division and then truncating with int. Nanoseconds for each case, but it may be considered slightly clearer as well as microscopically faster.
x = [1,2,3,4,5,6,7,8,9,10]
x = x.__getitem__(slice(None,None,-1))
slice is a python builtin object (like range and len that you used in your example)
__getitem__ is a method belonging to iterable types ( of which x is)
there are absolutely no shortcuts here :) and its effectively one line.
Why am I getting this error, from line 5 of my code, when attempting to solve Project Euler Problem 11?
for x in matrix:
p = 0
for y in x:
if p < 17:
currentProduct = int(y) * int(x[p + 1]) * int(x[p + 2]) * int(x[p + 3])
if currentProduct > highestProduct:
print(currentProduct)
highestProduct = currentProduct
else:
break
p += 1
'generator' object is not subscriptable
Your x value is is a generator object, which is an Iterator: it generates values in order, as they are requested by a for loop or by calling next(x).
You are trying to access it as though it were a list or other Sequence type, which let you access arbitrary elements by index as x[p + 1].
If you want to look up values from your generator's output by index, you may want to convert it to a list:
x = list(x)
This solves your problem, and is suitable in most cases. However, this requires generating and saving all of the values at once, so it can fail if you're dealing with an extremely long or infinite list of values, or the values are extremely large.
If you just needed a single value from the generator, you could instead use itertools.islice(x, p) to discard the first p values, then next(...) to take the one you need. This eliminate the need to hold multiple items in memory or compute values beyond the one you're looking for.
import itertools
result = next(itertools.islice(x, p))
As an extension to Jeremy's answer some thoughts about the design of your code:
Looking at your algorithm, it appears that you do not actually need truly random access to the values produced by the generator: At any point in time you only need to keep four consecutive values (three, with an extra bit of optimization). This is a bit obscured in your code because you mix indexing and iteration: If indexing would work (which it doesn't), your y could be written as x[p + 0].
For such algorithms, you can apply kind of a "sliding window" technique, demonstrated below in a stripped-down version of your code:
import itertools, functools, operator
vs = [int(v) for v in itertools.islice(x, 3)]
for v in x:
vs.append(int(v))
currentProduct = functools.reduce(operator.mul, vs, 1)
print(currentProduct)
vs = vs[1:]