问题 在方法的开头和结尾做一些事情


是否有一种简单的方法可以在类中的每个函数的开头和结尾处执行某些操作?我调查过了 __getattribute__,但我不认为我可以在这种情况下使用它?

这是我正在尝试做的简化版本:

class Thing():
    def __init__(self):
        self.busy = False

    def func_1(self):
        if self.busy: 
            return None
        self.busy = True
          ...
        self.busy = False

    def func_2(self):
        if self.busy: 
            return None
        self.busy = True
          ...
        self.busy = False
    ...

6763
2018-05-13 01:26


起源

只是想知道,你为什么要那样做? - abccd
@abccd你的意思是他想要使用的原因 self.busy 像那样? - Christian Dean
是的,这就是我的意思 - abccd
我正在制作东西 tkinter,我不希望鼠标或键盘输入中断任何东西 - diligar


答案:


您可以使用装饰器(如果您不了解它们,您可以参考 PEP-318):

def decorator(method):
    def decorated_method(self, *args, **kwargs):
        # before the method call
        if self.busy:
            return None
        self.busy = True

        # the actual method call
        result = method(self, *args, **kwargs)  

        # after the method call
        self.busy = False

        return result

    return decorated_method

class Thing():
    def __init__(self):
        self.busy = False

    @decorator
    def func_1(self):
        ...

    @decorator
    def func_2(self):
        ...

你可能想用 functools.wraps 如果你想让装饰方法“看起来像”原始方法。该 @decorator 只是语法糖,你也可以明确地应用装饰器:

class Thing():
    def __init__(self):
        self.busy = False

    def func_1(self):
        ...

    func_1 = decorator(func_1)  # replace "func_1" with the decorated "func_1"

如果您真的想将它应用于所有方法,您还可以使用类装饰器:

def decorate_all_methods(cls):
    for name, method in cls.__dict__.items():
        if name.startswith('_'):  # don't decorate private functions
            continue 
        setattr(cls, name, decorator(method))
    return cls

@decorate_all_methods
class Thing():
    def __init__(self):
        self.busy = False

    def func_1(self):
        ...

    def func_2(self):
        ...

11
2018-05-13 01:30



射击!,打败我;-) - Christian Dean
这很完美!谢谢!此外,它是否必须被调用 decorator,还是可以称之为什么? - diligar
@diligar你可以随意调用它。只需确保您提供的名称是有意义的。 - Christian Dean
不,它不必被称为 decorator。我只是这样命名,因为我不是很富有想象力。 - MSeifert
我只想补充一点,装饰器语法是语法糖 some_fun = decorator(some_fun) - juanpa.arrivillaga


作为已接受答案的替代方案,如果您希望此装饰仅适用于实例方法,则可以使用 __getattribute__

class Thing(object):
    def __init__(self):
        self.busy = False

    def __getattribute__(self, name):
        attr = object.__getattribute__(self, name)
        if callable(attr) and not name.startswith('_') and attr.__self__ == self:
            attr = decorator(attr)

        return attr

    def func_1(self):
        # instance method will be wrapped by `decorator`
        ...

    @classmethod
    def class_func(cls):
        # class method will not be wrapped by `decorator`
        # when called using `self.`, `cls.` or `Thing.`.
        ...

    @staticmethod
    def static_func():
        # static method will not be wrapped by `decorator`
        # when called using `Thing.`.
        ...
  • 这需要 object 并且不适用于Python 2中的旧式类。
  • callable 已在Python 3.0中删除,但在3.2中返回。或者, isinstance(obj, collections.Callable) 可以使用。

如果您想以不同方式包装类方法和静态方法,则可以从自定义继承 type  metaclass

class Meta(type):
    def __getattribute__(*args):
        print("staticmethod or classmethod invoked")
        return type.__getattribute__(*args)


class Thing(object, metaclass=Meta):
    ...
    def __getattribute__(self, name):
        attr = object.__getattribute__(self, name)
        if callable(attr) and not name.startswith('_'):
            if attr.__self__ == self:
                attr = decorator(attr)
            else:
                attr = Meta.__getattribute__(Thing, name)

        return attr

以上 metaclass=Meta 是Python 3语法。在Python 2中,它必须定义为:

class Thing(object):
    __metaclass__ = Meta

1
2018-05-13 05:24