2013-07-19 96 views
4

因此具有以下...参数为Python装饰

def makeBold(fn): 
     def wrapped(): 
      return '<b>'+fn()+'</b>' 
     return wrapped 

    @makeBold 
    def produceElement(): 
     return 'hello' 

结果是

<b>hello</b> 

我愿做这样的事情......

@makeBold(attrib=val, attrib=val) 
    def produceElement(): 
     return 'hello' 

和有结果就像...

<b attrib=val, attrib=val>hello<b/> 

任何建议将是伟大的!

回答

3

与其他功能总结你的函数:

import functools 
def makeBold(**kwargs): 
    attribstrings = str(kwargs) # figure out what to do with the dict yourself 
    def actualdecorator(fn): 
     @functools.wraps(fn) 
     def wrapped(): 
      return '<b'+attribstrings+'>'+fn()+'</b>' 
     return wrapped 
    return actualdecorator 

我离开工作了如何构建字符串作为练习留给读者。

请注意,装饰器表达式的结构是@ <callable object of one parameter, w> <declaration of callable object, f>。它的效果是f = w(f)。相应地,w(装饰者)必须返回与f相同类型的可召唤物品。

@makebold(foo)def bar(x):pass,表达makebold(foo)是装饰 - 也就是说,装饰的最终效果是bar = makebold(foo)(bar),等等bar结束保持wrapped

functools.wraps的目的是修复装饰函数的属性,以便将元数据(如name和docstring)从参数函数复制到包装函数,以便整个包装过程是透明的。

+0

可能不会伤害推'functools.wraps'那里... –

+0

@JonClements好点。 – Marcin

+0

你能解释一下装饰器的层次,包括functools装饰器吗? – Stephan

1

为了做这样的事情,你需要一个返回装饰函数的函数。因此,在这种情况下(假设你要接受任意属性),你会写

def format_attribs(kwargs): 
    """Properly formats HTML attributes from a dictionary""" 
    return ' '.join('{}="{}"'.format(key, val) for key,val in kwargs.iteritems()) 

def makeBold(**kwargs): 
    attribs = format_attribs(kwargs) 
    def _makeBold(fn): 
     def wrapped(): 
      return '<b ' + attribs + '>' + fn() + '</b>' 
     return wrapped 
    return _makeBold 

为了使这个makeBold功能多一点一般情况下,你要通过传递参数给fn并保持其他信息,如使用functools.wraps函数名称:

import functools 
def makeBold(**kwargs): 
    attribs = format_attribs(kwargs) 
    def _makeBold(fn): 
     @wraps(fn) 
     def wrapped(*args, **kwargs): 
      return '<b ' + attribs + '>' + fn(*args, **kwargs) + '</b>' 
     return wrapped 
    return _makeBold 
2

我也许怀疑这是一个很好的使用案例装饰,但这里:

import string 

SelfClosing = object() 

def escapeAttr(attr): 
    # WARNING: example only, security not guaranteed for any of these functions 
    return attr.replace('"', '\\"') 

def tag(name, content='', **attributes): 
    # prepare attributes 
    for attr,value in attributes.items(): 
     assert all(c.isalnum() for c in attr) # probably want to check xml spec 
    attrString = ' '.join('{}="{}"'.format(k,escapeAttr(v)) for k,v in attributes.items()) 

    if not content==SelfClosing: 
     return '<{name} {attrs}>{content}</{name}>'.format(
      name = name, 
      attrs = attrString, 
      content = content 
     ) 
    else: # self-closing tag 
     return '<{name} {attrs}/>' 

实施例:

def makeBoldWrapper(**attributes): 
    def wrapWithBold(origFunc): 
     def composed(*args, **kw): 
      result = origFunc(*args, **kw) 
      postprocessed = tag('b', content=result, **attributes) 
      return postprocessed 
     return composed 
    return wrapWithBold 

演示:

@makeBoldWrapper(attr1='1', attr2='2') 
def helloWorld(text): 
    return text 

>>> print(helloWorld('Hello, world!')) 
<b attr2="2" attr1="1">Hello, world!</b> 

与装饰常见的误解是,参数(attr1=...)是参数给装饰@myDecorator;事实并非如此。函数调用myDecoratorFactory(attr1=...)的结果计算为someresult,并成为匿名装饰器@someresult。因此'带有参数的装饰器'实际上是装饰器工厂,它需要返回一个装饰器作为一个值。

+0

你应该在你的代码示例中使用'functools.wraps'。这不是必须的,但显然是“最好的”实践”。 – millimoose

+0

@millimoose:我个人与'partial'有爱恨关系,因为我过去曾经遇到过问题。我觉得这是一种克隆和破坏方法签名,虽然咖喱是好的。即使'@ wrapps'存在涉嫌保留函数和文档字符串的名称,如果我想这样做,那么最好使用一个“真正的”装饰器框架来让一切正常。不必要的装饰器也会产生大量的开销,我曾经试图忽略但是失败了。一旦创建了一个框架来重写函数并将其重新编译为“应该”。 – ninjagecko

+0

@millimoose:[接下来]但也许你说得对,我应该使用它,因为没有其他原因可能是向前兼容性的,也许用另一个'from myfuture import wraps'取代它可能会被换掉做别的事情。我仍然不确定是否应该在示例中使用它,因为这可能会增加所需的智力,以找出正在做的事情,因为它本质上什么都不做(从我使用它已经有一段时间了,也许我忽略了* args或* *千方便?)。不过谢谢你的建议。 – ninjagecko