python 自动修饰类中的每个示例方法

6ljaweal  于 2023-02-15  发布在  Python
关注(0)|答案(3)|浏览(158)

我想对给定类中的每个方法应用相同的装饰器,除了那些以__开头和结尾的方法。
在我看来,使用类装饰器应该是可行的。有什么陷阱需要注意吗?
理想情况下,我还希望能够:
1.通过用特殊的装饰器标记某些方法来禁用该机制
1.也为子类启用此机制
1.即使对于在运行时添加到此类的方法,也启用此机制
[Note:我使用的是Python 3.2,所以如果这依赖于最近添加的特性,我很好。]
下面是我的尝试:

_methods_to_skip = {}

def apply(decorator):
  def apply_decorator(cls):
    for method_name, method in get_all_instance_methods(cls):
      if (cls, method) in _methods_to_skip:
        continue
      if method_name[:2] == `__` and method_name[-2:] == `__`:
        continue
      cls.method_name = decorator(method)
  return apply_decorator

def dont_decorate(method):
  _methods_to_skip.add((get_class_from_method(method), method))
  return method

以下是我遇到的问题:

  • 如何实现get_all_instance_methods函数
  • 不确定我的cls.method_name = decorator(method)行是否正确
  • 如何对在运行时添加到类中的任何方法执行相同的操作
  • 如何将其应用于子类
  • 如何实现get_class_from_method
bq3bfh9z

bq3bfh9z1#

我认为这最好用元类来完成,以便处理运行时和子类方法装饰。我没有看到用类装饰器自动处理子类的优雅方法。

from types import FunctionType

# check if an object should be decorated
def do_decorate(attr, value):
    return ('__' not in attr and
            isinstance(value, FunctionType) and
            getattr(value, 'decorate', True))

# decorate all instance methods (unless excluded) with the same decorator
def decorate_all(decorator):
    class DecorateAll(type):
        def __new__(cls, name, bases, dct):
            for attr, value in dct.iteritems():
                if do_decorate(attr, value):
                    dct[attr] = decorator(value)
            return super(DecorateAll, cls).__new__(cls, name, bases, dct)
        def __setattr__(self, attr, value):
            if do_decorate(attr, value):
                value = decorator(value)
            super(DecorateAll, self).__setattr__(attr, value)
    return DecorateAll

# decorator to exclude methods
def dont_decorate(f):
    f.decorate = False
    return f

下面是一个使用它的例子(Python 2,但在Python 3中做了一些小的修改):

def printer(f):
    print f
    return f

class Foo(object):
    __metaclass__ = decorate_all(printer)
    def bar(self):
        pass
    @dont_decorate
    def baz(self):
        pass
    @classmethod
    def test(self):
        pass
# prints
# <function bar at 0x04EB59B0>

class AnotherName(Foo):
    def blah(self):
        pass
# prints
# <function blah at 0x04EB5930>

Foo.qux = lambda: 1
# prints
# <function <lambda> at 0x04EB57F0>
9vw9lbht

9vw9lbht2#

你可以这样做(虽然不确定这是否是最优雅的方式):

def get_all_instance_methods(x):
    return filter(callable, map(lambda d: getattr(x, d), dir(x)))

至于cls.method_name,您将不得不使用getattr(cls, method_name)

kmpatx3s

kmpatx3s3#

这是@agf的答案的更新版本,它使用python 3,添加了类型提示(传递mypy),添加了docstring,并做了一个非常小的重构,装饰器在方法执行之前和之后都做了一些事情。

from functools import wraps
from types import FunctionType
from typing import Any, Callable, Dict, Tuple

def dont_decorate(f: Callable) -> Callable:
    """Decorator to exclude methods from autodecoration."""
    f._decorate = False  # type: ignore[attr-defined]
    return f

def my_decorator(f: Callable) -> Callable:
    """Dummy decorator which prints before and after the function it decorates."""

    @wraps(f)
    def wrapper(*args: Any, **kwargs: Any) -> Any:
        """Wraps provided function and prints before and after."""
        print(f"Calling decorated function {f.__name__}")
        return_val = f(*args, **kwargs)
        print(f"Called decorated function {f.__name__}")
        return return_val

    return wrapper

def decorate_all(decorator: Callable) -> type:
    """Decorate all instance methods (unless excluded) with the same decorator."""

    class DecorateAll(type):
        """Decorate all instance methods (unless excluded) with the same decorator."""

        @classmethod
        def do_decorate(cls, attr: str, value: Any) -> bool:
            """Checks if an object should be decorated."""
            return (
                "__" not in attr
                and isinstance(value, FunctionType)
                and getattr(value, "_decorate", True)
            )

        def __new__(
            cls, name: str, bases: Tuple[type, ...], dct: Dict[str, Any]
        ) -> type:
            for attr, value in dct.items():
                if cls.do_decorate(attr, value):
                    dct[attr] = decorator(value)
            return super().__new__(cls, name, bases, dct)

        def __setattr__(self, attr: str, value: Any) -> None:
            if self.do_decorate(attr, value):
                value = decorator(value)
            super().__setattr__(attr, value)

    return DecorateAll

和它的使用示例:

# Mypy does not support dynamically computed metaclasses.
class Foo(metaclass=decorate_all(decorator=my_decorator)):  # type: ignore[misc]
    """Dummy class."""

    def foo(self) -> None:
        """Should be decorated."""
        print("foo")

    @dont_decorate
    def test(self) -> None:
        """Should not be decorated."""
        print("test")

class Bar(Foo):
    """Dummy class."""

    def bar(self) -> None:
        """Should be decorated."""
        print("bar")


foo = Foo()
foo.foo()
# Calling decorated function foo
# foo
# Called decorated function foo
foo.test()
# test

bar = Bar()
bar.foo()
# Calling decorated function foo
# foo
# Called decorated function foo
bar.bar()
# Calling decorated function bar
# bar
# Called decorated function bar
bar.test()
# test

相关问题