简体   繁体   English

在Python 2.7中定义一个类似“属性”的装饰器

[英]Defining a “property”-like decorator in Python 2.7

I'm trying to write a decorator that works like @property, but running into some problems. 我正在尝试编写一个像@property一样工作的装饰器,但遇到了一些问题。

class Dec(object):
  def __init__(self, fn):
    self._fn = fn
    self._before = None
  @property
  def before(self)
    return self._before
  @before.setter
  def before(self, fn):
    self._before = fn
  def __call__(self, *args, **kwargs):
    self._before(*args, **kwargs)
    self._fn(*args, **kwargs)

def withbefore(fn):
  return Dec(fn)

Its a simple chaining decorator. 它是一个简单的链式装饰器。 The @property/@.setter syntax is exactly what I'm trying to clone. @property / @。setter语法正是我想要克隆的。

This works: 这有效:

@withbefore
def foo():
   ...
@foo.before
def beforefoo():
  ...

But on a class it doesn't: 但在课堂上却没有:

class Weee(object):
    @withbefore
    def do_stuff(self):
      pass
    @do_stuff.before
    def before_do_stuff(self):
      pass

It raises an import error. 它引发了导入错误。

TypeError: 'NoneType' object is not callable

How can i correctly emulate @property/.{setter,getter,deleter} ? 我怎样才能正确模仿@property /。{setter,getter,deleter}?

Actually, it raises a TypeError. 实际上,它引发了一个TypeError。

Anyway, I got the same error when running your decorator with functions, too. 无论如何,我在使用函数运行装饰器时也遇到了同样的错误。 It happens because, when you decorate a function with @foo.before , it will call the self._before function with the decorated function as parameter. 之所以发生这种情况,是因为当你使用@foo.before修饰一个函数时,它将使用修饰函数作为参数调用self._before函数。 Since self._before is None , it will raise the error. 由于self._beforeNone ,因此会引发错误。

There are various solutions for it. 它有各种解决方案。 My favorite is to set a different default value to self._before - a function which will set the self._before value: 我最喜欢的是为self._before设置一个不同的默认值 - 一个设置self._before值的函数:

class Dec(object):
  def __init__(self, fn):
    self._fn = fn
    def setbefore(b):
        self._before = b
    self._before = self.default_before = setbefore

Of course, this function should not be called when the Dec object is called so we change the __call__ method: 当然, 调用Dec对象时不应调用此函数,因此我们更改了__call__方法:

  def __call__(self, *args, **kwargs):
      if self._before != self.default_before:
         self._before(*args, **kwargs)
      self._fn(*args, **kwargs)

Sincerely, I think you'd be better off with: 真诚地,我认为你会更好:

from functools import wraps

def withbefore(fn):
    def dec(bef):
        fn._before_fn = bef
        return bef

    @wraps(fn)
    def _wrapper(*args, **kwargs):
        fn._before_fn(*args, **kwargs)
        return fn(*args, **kwargs)

    _wrapper.before = dec
    return _wrapper

It is more compact, more Pythonic and should work OK for all cases. 它更紧凑,更Pythonic,应该适用于所有情况。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM