类方法的装饰器

问题描述 投票:0回答:1

我有一个装饰器,可以装饰不以两个下划线开头的类方法。

def log_methods(date_format):
    def decorator(cls):

        for attr_name in dir(cls):
            attr = getattr(cls, attr_name)
            if callable(attr) and not attr_name.startswith('__'):
                def wrapper(*args, **kwargs):
                    start = datetime.now()
                    print(f'Starting {attr.__qualname__} date and time {start.strftime(date_format)}')
                    result = attr(*args, **kwargs)
                    end = datetime.now()
                    print(f'Finishing  {attr.__qualname__}, function operation time: {end.strftime(date_format)}')
                    return result

                setattr(cls, attr_name, wrapper)

        return cls

    return decorator

@log_methods("%b %d %Y - %H:%M:%S")
class A:
    new_class_attr = 2

    def test_func(self):
        print('test_func called')


a = A()
a.test_func()

我决定找出如果我修饰 class 属性会发生什么。为此,我从条件中删除了

callable(attr)
检查。添加了
new_class_attr
属性的装饰器,但是不明白为什么调用的时候会执行
test_func
方法?

def log_methods(date_format):
    def decorator(cls):

        for attr_name in dir(cls):
            attr = getattr(cls, attr_name)
            if not attr_name.startswith('__'):
                def wrapper(*args, **kwargs):
                    start = datetime.now()
                    print(f'Starting {attr.__qualname__} date and time {start.strftime(date_format)}')
                    result = attr(*args, **kwargs)
                    end = datetime.now()
                    print(f'Finishing  {attr.__qualname__}, function operation time: {end.strftime(date_format)}')
                    return result

                setattr(cls, attr_name, wrapper)

        return cls

    return decorator


@log_methods("%b %d %Y - %H:%M:%S")
class A:
    new_class_attr = 2

    def test_func(self):
        print('test_func called')


a = A()
a.new_class_attr() # The test_func method is called
python python-decorators
1个回答
0
投票

正如评论中提到的,你的装饰器(第一个)永远不会正常工作。 让我们用修改后的类来测试它:

from datetime import datetime

# ... 

@log_methods("%b %d %Y - %H:%M:%S") # OP 1st decorator
class A:

    def test_func(self):
        return('test_func called')

    def test_func_X(self):
        return('test_funcX called')


a = A()
print(a.test_func())
#test_funcX called
print(a.test_func_X())
#test_funcX called

所有可调用函数将指向最后一个(按字母顺序)编辑的函数。您可以使用

inspect.source
进行检查,它将函数主体提取为纯文本。

from inspect import getsource

src1 = getsource(a.test_func)
src2 = getsource(a.test_func_X)
print(src1 == src2)
# True

您需要对方法的装饰器进行进一步参数化

def log_methods(date_format):
    def decorator(cls):

        def __wrapper(func):
            def wrapper(*args, **kwargs):
                start = datetime.now()
                print(f'Starting {attr.__qualname__} date and time {start.strftime(date_format)}')
                result = func(*args, **kwargs) # <-
                end = datetime.now()
                print(f'Finishing  {attr.__qualname__}, function operation time: {end.strftime(date_format)}')
                return result
            return wrapper
        
        for attr_name in dir(cls):
            attr = getattr(cls, attr_name)
            if callable(attr) and not attr_name.startswith('__'):
                setattr(cls, attr_name, __wrapper(attr))           # <-

        return cls

    return decorator

对于第二个装饰器,您需要区分可调用和不可调用...否则,

a.new_class_attr()
会得到什么? ...与调用整数相同:
2()

© www.soinside.com 2019 - 2024. All rights reserved.