Python 如何在每个对象方法之后获得一个名为(decorator?)的方法
这是一个类似于python的问题 假设我有一个具有一些属性(例如self.db)的爬虫类,它有一个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
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
。