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

Python 如何在每个对象方法之后获得一个名为(decorator?)的方法,python,python-3.x,metaprogramming,decorator,python-decorators,Python,Python 3.x,Metaprogramming,Decorator,Python Decorators,这是一个类似于python的问题 假设我有一个具有一些属性(例如self.db)的爬虫类,它有一个crawl\u 1(self,*args,**kwargs)和另一个save\u to_db(self,*args,**kwargs)将爬虫结果保存到数据库(self.db) 我想在每次调用crawl\u 1、crawl\u 2等之后运行save\u to\u db。我曾尝试将其作为“全局”util decorator,但我不喜欢它的结果,因为它涉及到将self作为参数传递。类似这样的内容: fro

这是一个类似于python的问题

假设我有一个具有一些属性(例如self.db)的爬虫类,它有一个
crawl\u 1(self,*args,**kwargs)
和另一个
save\u to_db(self,*args,**kwargs)
将爬虫结果保存到数据库(
self.db)

我想在每次调用
crawl\u 1、crawl\u 2等之后运行
save\u to\u db
。我曾尝试将其作为“全局”util decorator,但我不喜欢它的结果,因为它涉及到将
self
作为参数传递。

类似这样的内容:

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
大概是这样的:

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

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

import functools

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

    return _
这是一个包装
func
,调用
self.调用
func
后将\u保存到\u db()
。现在,我们建立了一个元类 通过编程将其应用于特定方法:

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)
这将迭代包装类中的方法,查找 以
爬网
开头的方法名称,并用 装饰功能

最后是包装类本身,它将
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\u 1
crawl\u 2
中的每一个(但不在
之后,此\u未包装

以上内容在Python2中起作用。在Python 3中,重新使用以下命令:

class Wrapped (object):
    __metaclass__ = Wrapper
与:


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

import functools

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

    return _
这是一个包装
func
,调用
self.调用
func
后将\u保存到\u db()
。现在,我们建立了一个元类 通过编程将其应用于特定方法:

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)
这将迭代包装类中的方法,查找 以
爬网
开头的方法名称,并用 装饰功能

最后是包装类本身,它将
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\u 1
crawl\u 2
中的每一个(但不在
之后,此\u未包装

以上内容在Python2中起作用。在Python 3中,重新使用以下命令:

class Wrapped (object):
    __metaclass__ = Wrapper
与:


Python中的decorator如下所示,它是一个以单个方法为参数并返回另一个包装器方法的方法,该方法将被调用,而不是被修饰的方法。通常包装器“包装”修饰的方法,即在执行其他操作之前/之后调用它

例如:

# 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

Python中的decorator如下所示,它是一种将单个方法作为参数并返回另一个包装器方法的方法,该方法将被调用,而不是被修饰的方法。通常包装器“包装”修饰的方法,即在执行其他操作之前/之后调用它

例如:

# 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

obj=args[0]如果len(args)>0 else None
应该是
obj=args[0]如果args else None
:在布尔上下文中求值时,空的
元组是“false”,非空的“truthy”。
obj=args[0]如果len(args)>0 else None
应该是
obj=args[0]if args else None
:在布尔上下文中计算时,空的
元组是“false”,非空的“truthy”。如果
crawl\u 1
引发异常,您希望发生什么?如果
crawl\u 1
引发异常,您希望发生什么?是有关元类的有用阅读。是设置
\uuuu name\uuuuuuuu
\uuuuu doc\uuuuuuu
属性的便捷方法。建议不错;我将答案更新为使用
functools.wrapps
。是有关元类的有用阅读。是设置
\uuuu name\uuuuuuuu
\uuuuu doc\uuuuuuu
属性的便捷方法。建议不错;我将答案更新为使用
functools.wrapps