如何在每个对象方法之后获得一个名为(装饰器?)的方法
How to get a method called (decorator?) after every object method
这是一个类似于 的问题,但 python
假设我有一个爬虫 class,它具有一些属性(例如 self.db),其中一个 crawl_1(self, *args, **kwargs)
和另一个 save_to_db(self, *args, **kwargs)
将爬行结果保存到数据库( self.db)
.
我想以某种方式在每次 crawl_1, crawl_2, etc.
调用后得到 save_to_db
运行。我试过将其作为 "global" util 装饰器,但我不喜欢这个结果,因为它涉及将 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
如果您想在所有 crawl_*
方法之后隐式 运行 一个方法,最简单的解决方案可能是设置一个 metaclass 以编程方式包装方法你。从这个开始,一个简单的包装函数:
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
后。现在,我们设置一个 metaclass
这将以编程方式将其应用于特定方法:
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)
这将遍历包装 class 中的方法,寻找
以 crawl_
开头并用我们的方法包装它们的方法名称
装饰函数。
最后,包装的 class 本身,它将 Wrapper
声明为
元class:
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
之后)。
以上在Python2中有效。在Python3中,替换为:
class Wrapped (object):
__metaclass__ = Wrapper
有:
class Wrapped (object, metaclass=Wrapper):
Python中的装饰器看起来像这样,它是一种方法,它以单个方法作为参数并返回另一个包装器方法,而不是装饰器方法。通常包装器 "wraps" 装饰方法,即调用它 before/after 执行一些其他操作。
示例:
# 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
这是一个类似于
假设我有一个爬虫 class,它具有一些属性(例如 self.db),其中一个 crawl_1(self, *args, **kwargs)
和另一个 save_to_db(self, *args, **kwargs)
将爬行结果保存到数据库( self.db)
.
我想以某种方式在每次 crawl_1, crawl_2, etc.
调用后得到 save_to_db
运行。我试过将其作为 "global" util 装饰器,但我不喜欢这个结果,因为它涉及将 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
如果您想在所有 crawl_*
方法之后隐式 运行 一个方法,最简单的解决方案可能是设置一个 metaclass 以编程方式包装方法你。从这个开始,一个简单的包装函数:
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
后。现在,我们设置一个 metaclass
这将以编程方式将其应用于特定方法:
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)
这将遍历包装 class 中的方法,寻找
以 crawl_
开头并用我们的方法包装它们的方法名称
装饰函数。
最后,包装的 class 本身,它将 Wrapper
声明为
元class:
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
之后)。
以上在Python2中有效。在Python3中,替换为:
class Wrapped (object):
__metaclass__ = Wrapper
有:
class Wrapped (object, metaclass=Wrapper):
Python中的装饰器看起来像这样,它是一种方法,它以单个方法作为参数并返回另一个包装器方法,而不是装饰器方法。通常包装器 "wraps" 装饰方法,即调用它 before/after 执行一些其他操作。
示例:
# 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