Question

I understand a bit about python function decorators. I think the answer to my question is no, but I want to make sure. With a decorator and a numpy array of x = np.array([1,2,3]) I can override x.sqrt() and change the behavior. Is there some way I can override np.sqrt(x) in Python?

Use case: working on the quantities package. Would like to be able to take square root of uncertain quantities without changing code base that currently uses np.sqrt().

Edit:

I'd like to modify np.sqrt in the quantities package so that the following code works (all three should print identical results, note the 0 uncertainty when using np.sqrt()). I hope to not require end-users to modify their code, but in the quantities package properly wrap/decorate np.sqrt(). Currently many numpy functions are decorated (see https://github.com/python-quantities/python-quantities/blob/ca87253a5529c0a6bee37a9f7d576f1b693c0ddd/quantities/quantity.py), but seem to only work when x.func() is called, not numpy.func(x).

import numpy as np
import quantities as pq
x = pq.UncertainQuantity(2, pq.m, 2)
print x.sqrt()
>>> 1.41421356237 m**0.5 +/- 0.707106781187 m**0.5 (1 sigma)
print x**0.5
>>> 1.41421356237 m**0.5 +/- 0.707106781187 m**0.5 (1 sigma)
print np.sqrt(x)
>>> 1.41421356237 m**0.5 +/- 0.0 dimensionless (1 sigma)
Was it helpful?

Solution

Monkeypatching

If I understand your situation correctly, your use case is not really about decoration (modifying a function you write, in a standard manner) but rather about monkey patching: Modifying a function somebody else wrote without actually changing that function's definition's source code.

The idiom for what you then need is something like

import numpy as np  # provide local access to the numpy module object

original_np_sqrt = np.sqrt

def my_improved_np_sqrt(x):
  # do whatever you please, including:
  # - contemplating the UncertainQuantity-ness of x and
  # - calling original_np_sqrt as needed

np.sqrt = my_improved_np_sqrt

Of course, this can change only the future meaning of numpy.sqrt, not the past one. So if anybody has imported numpy before the above and has already used numpy.sqrt in a way you would have liked to influence, you lose. (And the name to which they map numpy does not matter.)

But after the above code was executed, the meaning of numpy.sqrt in all modules (whether they imported numpy before it or after it) will be that of my_improved_np_sqrt, whether the creators of those modules like it or not (and of course unless some more monkeypatching of numpy.sqrt is going on elsewhere).

Note that

  1. When you do weird things, Python can become a weird platform!
  2. When you do weird things, Python can become a weird platform!
  3. When you do weird things, Python can become a weird platform!

This is why monkey patching is not normally considered good design style. So if you take that route, make sure you announce it very prominently in all relevant documentation.

Oh, and if you do not want to modify other code than that which is directly or indirectly executed from your own methods, you could introduce a decorator that performs monkeypatching before the call and un-monkeypatching (reassigning original_np_sqrt) after the call and apply that decorator to all your functions in question. Make sure you handle exceptions in that decorator then, so that the un-monkeypatching is really executed in all cases.

OTHER TIPS

Maybe, as BrenBarn stated,

np.sqrt = decorator(np.sqrt)

because a decorator is just a callable that takes an object and returns a modified object.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top