我仍然没有掌握Python中的装饰器。
我已经开始使用很多闭包来做我编码中的自定义函数和类。
例如
class Node :
def __init__(self,val,children) :
self.val = val
self.children = children
def makeRunner(f) :
def run(node) :
f(node)
for x in node.children :
run(x)
return run
tree=Node(1,[Node(2,[]),Node(3,[Node(4,[]),Node(5,[])])])
def pp(n) : print "%s," % n.val
printTree = makeRunner(pp)
printTree(tree)
据我所知,装饰器只是用于做类似事情的不同语法。
而不是
def pp(n) : print "%s," % n.val
printTree = makeRunner(pp)
我会写:
@makeRunner
def printTree(n) : print "%s," % n.val
这一切都是装饰者吗?或者我错过了一个根本的区别?
答案 0 :(得分:12)
虽然在语法上,装饰器只是“糖”,但这不是考虑它们的最佳方式。
装饰器允许您将功能编织到现有代码中,而无需实际修改它。它们允许您以声明的方式执行此操作。
这允许您使用装饰器来执行面向方面的编程(AOP)。因此,当您想要封装在一个地方的横切关注时,您希望使用装饰器。
典型的示例可能是日志记录,您要记录函数的入口或出口,或两者。使用装饰器相当于将建议(记录此!)应用于连接点(在方法输入或退出期间)。
方法修饰是一种类似OOP或列表推导的概念。正如您所指出的,它并不总是合适的,并且可以被过度使用。但是在正确的地方,它可以使代码更加模块化和分离。
答案 1 :(得分:8)
您的示例是真实代码还是仅仅是示例?
如果它们是真正的代码,我认为你过度使用装饰器,可能是因为你的背景(即你习惯了其他编程语言)
def run(rootnode, func):
def _run(node): # recursive internal function
func(node)
for x in node.children:
_run(x) # recurse
_run(rootnode) # initial run
此run方法废弃了makeRunner。你的例子转向:
def pp(n): print "%s," % n.val
run(tree, pp)
然而,这完全忽略了生成器,所以......
class Node :
def __init__(self,val,children) :
self.val = val
self.children = children
def __iter__(self): # recursive
yield self
for child in self.children:
for item in child: # recurse
yield item
def run(rootnode, func):
for node in rootnode:
func(node)
你的例子仍然是
def pp(n): print "%s," % n.val
run(tree, pp)
请注意,特殊方法__iter__
允许我们使用for node in rootnode:
构造。如果您不喜欢它,只需将__iter__
方法重命名为例如walker
,并将run
循环更改为:for node in rootnode.walker():
显然,run
函数可能是class Node
的一种方法。
如您所见,我建议您直接使用run(tree, func)
而不是将它们绑定到名称printTree
,但您可以在装饰器中使用它们,或者您可以使用{{1}功能:
functools.partial
从那时起,你就会
printTree= functools.partial(run, func=pp)
答案 2 :(得分:0)
继续使用Dutch Master的AOP参考,您会发现当您启动添加参数来修改已修饰函数/方法的行为并在函数定义上方读取时,使用装饰器会变得特别有用太容易了。
在一个项目中,我记得,我们需要监督大量的芹菜任务,因此我们提出了使用装饰器根据需要进行调整和插入的想法,其中包括:
class tracked_with(object):
"""
Method decorator used to track the results of celery tasks.
"""
def __init__(self, model, unique=False, id_attr='results_id',
log_error=False, raise_error=False):
self.model = model
self.unique = unique
self.id_attr = id_attr
self.log_error = log_error
self.raise_error = raise_error
def __call__(self, fn):
def wrapped(*args, **kwargs):
# Unique passed by parameter has priority above the decorator def
unique = kwargs.get('unique', None)
if unique is not None:
self.unique = unique
if self.unique:
caller = args[0]
pending = self.model.objects.filter(
state=self.model.Running,
task_type=caller.__class__.__name__
)
if pending.exists():
raise AssertionError('Another {} task is already running'
''.format(caller.__class__.__name__))
results_id = kwargs.get(self.id_attr)
try:
result = fn(*args, **kwargs)
except Retry:
# Retry must always be raised to retry a task
raise
except Exception as e:
# Error, update stats, log/raise/return depending on values
if results_id:
self.model.update_stats(results_id, error=e)
if self.log_error:
logger.error(e)
if self.raise_error:
raise
else:
return e
else:
# No error, save results in refresh object and return
if results_id:
self.model.update_stats(results_id, **result)
return result
return wrapped
然后我们只使用每个案例所需的参数来装饰任务上的run
方法,例如:
class SomeTask(Task):
@tracked_with(RefreshResults, unique=True, log_error=False)
def run(self, *args, **kwargs)...
然后改变任务的行为(或完全删除跟踪)意味着调整一个参数,或者注释掉装饰线。超级易于实施,但更重要的是,超级易于理解的检查。
答案 3 :(得分:0)
装饰器,在一般意义上,是包围另一个对象,扩展或装饰对象的函数或类。装饰器支持与包装函数或对象相同的接口,因此接收器甚至不知道对象已被装饰。
闭包是一个匿名函数,它引用其参数或其范围之外的其他变量。
基本上,装饰器使用闭包,而不是替换它们。
def increment(x):
return x + 1
def double_increment(func):
def wrapper(x):
print 'decorator executed'
r = func(x) # --> func is saved in __closure__
y = r * 2
return r, y
return wrapper
@double_increment
def increment(x):
return x + 1
>>> increment(2)
decorator executed
(3, 6)
>>> increment.__closure__
(<cell at 0x02C7DC50: function object at 0x02C85DB0>,)
>>> increment.__closure__[0].cell_contents
<function increment at 0x02C85DB0>
所以装饰器使用闭包保存原始功能。
What is the difference between closures and decorators in Python?