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

时间:2016-04-21 12:56:07

标签: python python-3.x metaprogramming decorator python-decorators

这个问题与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, etc.运行。我已经尝试将其作为“全局”的util装饰器,但我不喜欢结果,因为它涉及传递self作为参数。

3 个答案:

答案 0 :(得分:6)

如果要在所有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_1crawl_2(但不是this_is_not_wrapped之后)。

上述内容适用于Python 2.在Python 3中,重新使用:

class Wrapped (object):
    __metaclass__ = Wrapper

使用:

class Wrapped (object, metaclass=Wrapper):

答案 1 :(得分:0)

这样的事情:

from functools import wraps

def my_decorator(f):
    @wraps(f)
    def wrapper(*args, **kwargs):
        print 'Calling decorated function'
        res = f(*args, **kwargs)
        obj = args[0] if len(args) > 0 else None
        if obj and hasattr(obj, "bar"):
            obj.bar()

    return wrapper

class MyClass(object):
    @my_decorator
    def foo(self, *args, **kwargs):
        print "Calling foo"

    def bar(self, *args, **kwargs):
        print "Calling bar"

@my_decorator
def example():
    print 'Called example function'

example()

obj = MyClass()
obj.foo()

它会给你以下输出:

Calling decorated function
Called example function
Calling decorated function
Calling foo
Calling bar

答案 2 :(得分:0)

Python中的装饰器看起来像这样,它是一个方法,它将一个方法作为参数并返回另一个应该被调用的包装器方法而不是装饰方法。通常包装器“包装”装饰的方法,即在执行某些其他操作之前/之后调用它。

示例:

# define a decorator method:
def save_db_decorator(fn):

    # The wrapper method which will get called instead of the decorated method:
    def wrapper(self, *args, **kwargs):
        fn(self, *args, **kwargs)           # call the decorated method
        MyTest.save_to_db(self, *args, **kwargs)   # call the additional method

    return wrapper  # return the wrapper method

现在学习如何使用它:

class MyTest:

    # The additional method called by the decorator:

    def save_to_db(self, *args, **kwargs):
        print("Saver")


    # The decorated methods:

    @save_db_decorator
    def crawl_1(self, *args, **kwargs):
        print("Crawler 1")

    @save_db_decorator
    def crawl_2(self, *args, **kwargs):
        print("Crawler 2")


# Calling the decorated methods:

my_test = MyTest()
print("Starting Crawler 1")
my_test.crawl_1()
print("Starting Crawler 1")
my_test.crawl_2()

这将输出以下内容:

Starting Crawler 1
Crawler 1
Saver
Starting Crawler 1
Crawler 2
Saver

See this code running on ideone.com