Skip to main content
 首页 » 编程设计

python之如何在每个对象方法之后获得一个名为(装饰器)的方法

2025年05月04日16lyj

这是一个类似于How to call a method implicitly after every method call?的问题但是对于 python

假设我有一个带有一些属性(例如 self.db)的爬虫类,其中有一个 crawl_1(self, *args, **kwargs) 和另一个 save_to_db(self, *args , **kwargs) 将爬取结果保存到数据库(self.db)。

我想以某种方式让 save_to_db 在每次 crawl_1、crawl_2 等 调用后运行。我试过将其作为“全局”util 装饰器,但我不喜欢这个结果,因为它涉及将 self 作为参数传递。

请您参考如下方法:

如果您想在所有 crawl_* 方法之后隐式运行一个方法,最简单的解决方案可能是设置一个元类,该元类将以编程方式为您包装这些方法。从这个开始,一个简单的包装函数:

import functools 
 
def wrapit(func): 
    @functools.wraps(func) 
    def _(self, *args, **kwargs): 
        func(self, *args, **kwargs) 
        self.save_to_db() 
 
    return _ 

这是一个包装 func 的基本装饰器,调用 self.save_to_db() 在调用 func 之后。现在,我们建立一个元类 这将以编程方式将其应用于特定方法:

class Wrapper (type): 
    def __new__(mcls, name, bases, nmspc): 
        for attrname, attrval in nmspc.items(): 
            if callable(attrval) and attrname.startswith('crawl_'): 
                nmspc[attrname] = wrapit(attrval) 
 
        return super(Wrapper, mcls).__new__(mcls, name, bases, nmspc) 

这将迭代包装类中的方法,寻找 以 crawl_ 开头并用我们的方法包装它们的方法名称 装饰函数。

最后,包装类本身,它将 Wrapper 声明为 元类:

class Wrapped (object): 
    __metaclass__ = Wrapper 
 
    def crawl_1(self): 
        print 'this is crawl 1' 
 
    def crawl_2(self): 
        print 'this is crawl 2' 
 
    def this_is_not_wrapped(self): 
        print 'this is not wrapped' 
 
    def save_to_db(self): 
        print 'saving to database' 

鉴于上述情况,我们得到以下行为:

>>> W = Wrapped() 
>>> W.crawl_1() 
this is crawl 1 
saving to database 
>>> W.crawl_2() 
this is crawl 2 
saving to database 
>>> W.this_is_not_wrapped() 
this is not wrapped 
>>>  

你可以看到我们的 save_to_database 方法被调用了 crawl_1 和 crawl_2(但不是在 this_is_not_wrapped 之后)。

以上在 Python 2 中有效。在 Python 3 中,替换为:

class Wrapped (object): 
    __metaclass__ = Wrapper 

与:

class Wrapped (object, metaclass=Wrapper):