Arguments for Python Decorators

So, with the following ...

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

Result

  <b>hello</b> 

I would like to do something like this ...

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

and the result will be like ...

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

Any advice would be great!

+4
source share
3 answers

Wrap your function with another function:

 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 

I leave the question of how to build a string as an exercise for the reader.

Note that the decorator expression structure is @ <callable object of one parameter, w> <declaration of callable object, f> . The effect f = w(f) . Accordingly, w (the decorator) should return a call of the same type as f .

In @makebold(foo)def bar(x):pass expression makebold(foo) is a decorator, that is, the final effect of the decorator is bar = makebold(foo)(bar) , so bar ends by holding wrapped .

The purpose of functools.wraps is to fix the properties of a decorated function to copy metadata (such as name and docstring) from an argument function to a wrapped function so that the entire packaging process is transparent.

+3
source

Perhaps I doubt this is a good option for decorators, but here:

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

Example:

 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 

Demo:

 @makeBoldWrapper(attr1='1', attr2='2') def helloWorld(text): return text >>> print( helloWorld('Hello, world!') ) <b attr2="2" attr1="1">Hello, world!</b> 

A common misconception with decorators is that the parameters (attr1=...) are the parameters of the @myDecorator decorator; This is not relevant. Rather, the result of calling the myDecoratorFactory(attr1=...) function is evaluated as someresult and becomes an anonymous @someresult decorator. Therefore, โ€œdecorators with argumentsโ€ are actually decorator factories that should return a decorator as a value.

+2
source

To do something like this, you will need a function that returns a decorator function. So in this case (if you want to accept arbitrary attributes), you have to write

 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 

To make this makeBold function more general, you want to pass arguments to fn and store other information, such as the name of the function, using 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 
+1
source

Source: https://habr.com/ru/post/1492395/


All Articles