0

How can you create a variable linking to numpy's assignment operator (=)? For instance, we can assign a equal add like so op = numpy.ndarray.__iadd__ and later on call it using op(initial_var, increment), which would be the same as initial_var += increment. However, I can't seem to figure out how to do the same binding op to the assignment operator =.

Why am I doing this? Simply to slightly optimize, some cases we can avoid the extra __add__ overhead by simply copying the numpy.ndarray item instead of calling __iadd__. Which would be a fancier/uglier way of doing:

if a:
 b = c
else:
 b += c
  • Possible duplicate of [Is it possible to overload Python assignment?](https://stackoverflow.com/questions/11024646/is-it-possible-to-overload-python-assignment) – Mark May 20 '19 at 14:13
  • @MarkMeyer I saw the post in question, however, since I'm not looking to override it but simply call it (bind it to a variable if you will), I thought it would be different. My apologies. – user4218673 May 20 '19 at 14:16

2 Answers2

0

I propose:

import numpy as np
op = lambda x,y: np.ndarray.__eq__(x, y).all()

# Examples

a = np.array([1,2,3])
b = np.array([1,2,3])
print(op(a, b)) # True


c = np.array([1,2,6])
print(op(a, c)) # False

dallonsi
  • 1,299
  • 1
  • 8
  • 29
  • Hi @dallonsi, thanks for your reply! However, I don't think it fits my question - I might be wrong. Check the snippet I added ad the end – user4218673 May 20 '19 at 14:22
0

First, let us work out why what you are trying to achieve is difficult. What would function op_assgn(a, b) that somehow replaces assignment a=b need to do? The main difficulty is that it would need to know the name of the object the caller passed for argument a. In fact, the object bound to a at this point in time is completely irrelevant, yet that is what is available to op_assgn. So, if we were hellbent on getting this to work the function would have to peek up one frame, find the call statement, somehow get hold of what arguments were passed and bind a value to a name outside its (the function's) scope. It probably can be done but not without a considerable amount of black magic.

So maybe it is wiser to not touch the assignment itself and do the packaging just before. Here is a simple implementation of this idea.

def op1(b, c):
    return c

op2 = np.ndarray.__iadd__

c = np.array((1,2))
b = np.array((0,0))

Note that we are assigning to a new variable d merely to be able to see what exactly is going on. Ultimately, you would want to assign to b instead.

# this is straight-forward
d = op1(b, c)
d is c
# True
d is b
# False
d
# array([1, 2])

# this works because .__iadd__ does the inplace op AND
# returns the modified object
d = op2(b, c)
d is c
# False
d is b
# True
d
# array([1, 2])

So, basically this does what you want (once you have replaced d with b) except that it involves a tiny bit more typing and that its equivalent if clause would be something like

if a:
 b = c
else:
 b += c
 b = b

with that slightly ugly redundant assignment in the last line.

Please note that this is mostly an aesthetical problem, since the assignment---being done by reference---is cheap:

def f1():
    global c
    c.__iadd__(b)

def f2():
    global c
    c = c.__iadd__(b)

timeit(f1)
# 1.4087995890295133
timeit(f2)
# 1.4474492500303313
Paul Panzer
  • 51,835
  • 3
  • 54
  • 99
  • I see, clever use indeed, however, it adds even more operations, by generating both assignment and iadd. I guess with Py3.8 we'll be able to do this with lambdas which can call the attribution operation. Thanks @PaulPanzer for your contribution :) – user4218673 May 20 '19 at 15:24
  • @user4218673 yeah, but assignment is about the cheapest thing there is in python, so I wouldn't worry too much about that (I've added timings to the post). Also, I'm not sure the 3.8 lambdas will help since if I'm not mistaken you still have to hardcode the name you are assigning to. – Paul Panzer May 20 '19 at 15:42
  • Commenting on your additions, consider a function `op = numpy.array.__add__ if flag else numpy.array.__assign__` one could simply use `c = op(a, b)' that would be the purpose. I see it is not possible, I should study my Python deeper. – user4218673 May 20 '19 at 16:50