如何在每个对象方法之后获得一个名为(decorator?)的方法

Fot*_*tis 1 python metaprogramming decorator python-3.x python-decorators

这是一个类似于如何在每次方法调用后隐式调用方法的问题但是对于python

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

我想以某种方式save_to_db在每次crawl_1, crawl_2, etc.通话后都跑.我已经尝试将其作为"全局"的util装饰器,但我不喜欢结果,因为它涉及self作为参数传递.

lar*_*sks 6

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

import functools

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

    return _
Run Code Online (Sandbox Code Playgroud)

那是一个包装的基本装饰者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)
Run Code Online (Sandbox Code Playgroud)

这将迭代包装类中的方法,查找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'
Run Code Online (Sandbox Code Playgroud)

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

>>> 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
>>> 
Run Code Online (Sandbox Code Playgroud)

您可以看到我们的save_to_database方法在每个crawl_1和之后crawl_2(但不是之后this_is_not_wrapped)被调用.

以上工作在Python 2中.在Python 3中,重新安装:

class Wrapped (object):
    __metaclass__ = Wrapper
Run Code Online (Sandbox Code Playgroud)

附:

class Wrapped (object, metaclass=Wrapper):
Run Code Online (Sandbox Code Playgroud)