4

所以有了以下...

    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/>

任何建议都会很棒!

4

3 回答 3

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是修复修饰函数的属性,以便将元数据(例如名称和文档字符串)从参数函数复制到包装函数,以便整个包装过程是透明的。

于 2013-07-19T21:08:20.977 回答
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。因此,“带参数的装饰器”实际上是需要将装饰器作为值返回的装饰器工厂。

于 2013-07-19T21:18:53.240 回答
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
于 2013-07-19T21:09:11.377 回答