开发者

Decorate a whole library in Python

开发者 https://www.devze.com 2023-01-06 17:46 出处:网络
I\'m new to the ideas of decorators (and still trying to wrap my head around them), but I think I\'ve come across a problem that would be well suited for them. I\'d like to have class that is decorate

I'm new to the ideas of decorators (and still trying to wrap my head around them), but I think I've come across a problem that would be well suited for them. I'd like to have class that is decorated across all of the functions in the math library. More specifically my class has two members, x and flag. When flag is true, I'd like the original math function to be called. When flag is false, I'd like to return None.

As a framework for what I'm asking here is the class:

import math

class num(object):
  def __init__(self, x, flag):
    self.x = x
    self.flag = flag

  def __float__(self):
    return float(self.x)

As a result, this works fine:

a = num(3, True)
print math.sqrt(a)

However this should (in my perfect world), return None:

b = num(4, False)
print math.sqrt(b)

Any suggestions or tips on ho开发者_如何学Pythonw this would be possible to apply over a whole library of functions?


Here's the general idea...:

>>> class num(object):
...   def __init__(self, x, flag):
...     self.x = x
...     self.flag = flag
...   def __float__(self):
...     return float(self.x)
...   from functools import wraps
>>> def wrapper(f):
...   @wraps(f)
...   def wrapped(*a):
...     if not all(getattr(x, 'flag', True) for x in a):
...       return None
...     return f(*(getattr(x, 'x', x) for x in a))
...   return wrapped
... 
>>> import inspect
>>> import math
>>> for n, v in inspect.getmembers(math, inspect.isroutine):
...   setattr(math, n, wrapper(v))
... 

>>> a = num(3, True)
>>> print math.sqrt(a)
1.73205080757
>>> b = num(4, False)
>>> print math.sqrt(b)
None

Note that this wrapper also covers non-unary functions in math (returning None if any argument has a False .flag) and allows mixed calls thereof (with some args being instances of num and others being actual floats).

The key part, applicable to any "wrap all functions in a certain module" tasks, is using module inspect to get all the names and values of functions (built-in or not) in module math, and an explicit call to the wrapper (same semantics as the decorator syntax) to set that name to the wrapped value in the math module.


You can use decorators for this, although you won't need the @decorator syntax.

The following code imports each function you list from the math module into the current module's namespace, wrapping it in the defined decorator. It should give you the basic idea.

from functools import wraps
def check_flag(func):
    @wraps(func)
    def _exec(x, *args, **kw):
        if getattr(x, 'flag', False):
            return None

        return func(x, *args, **kw)

    return _exec

import sys, math
_module = sys.modules[__name__]
for func in ('exp', 'log', 'sqrt'):
    setattr(_module, func, check_flag(getattr(math, func)))

You could automate the listing of functions defined in the math module, as Alex demonstrates, but I think explicitly wrapping just the functions you're interested in using is a better way to go.

0

精彩评论

暂无评论...
验证码 换一张
取 消