solid
solid

Reputation: 155

Baffling result (to me) using Decorator in Python 3

I don't even know how to word this question correctly (so I don't know if this topic's been discussed before or how to query it).
The code below returns something mind-boggling to me. It's an exercise on Decorators. This one in particular was made to demonstrate how a Decorator could add HTML tags at the beginning and at the end of a string. If I was to call a function that return a string using the decorator: @AddTags('p','i','b') the return values is say: "p i b *Welcome Joe To my blog!*b i p"

How is it that a for loop returns a single one-line with all the values. And how is it that if we don't use 'reversed' the result is backwards. I'm starting to understand the concept of Decorators but this example is like pure magic...how does this works?

    def AddTags(*tags):
    def decorator(oldFunc):
        def inside(*args, **kwargs):
            code = oldFunc(*args, **kwargs)
            for tag in reversed(tags):
                code = "<{0}>{1}</{0}>".format(tag,code)
            return code
        return inside
    return decorator
@AddTags("p","i","b")
def MyWebWelcome(name):
    return "Welcome "+name+" To my blog!"

Upvotes: 1

Views: 53

Answers (1)

tdelaney
tdelaney

Reputation: 77397

No magic, its just that code = "<{0}>{1}</{0}>".format(tag,code) keeps updating code with expanded versions of itself. By reversing the tags for the loop, you make sure that the rightmost tag is added first, then wrapped by the next, and so on til done. You can see the progression with a couple of print statements:

def AddTags(*tags):
    def decorator(oldFunc):
        def inside(*args, **kwargs):
            code = oldFunc(*args, **kwargs)
            for tag in reversed(tags):
                print('code:', code)
                code = "<{0}>{1}</{0}>".format(tag,code)
            return code
        return inside
    return decorator

@AddTags("p","i","b")
def MyWebWelcome(name):
    return "Welcome "+name+" To my blog!"

print('result:', MyWebWelcome('foo'))

which prints

code: Welcome foo To my blog!
code: <b>Welcome foo To my blog!</b>
code: <i><b>Welcome foo To my blog!</b></i>
result: <p><i><b>Welcome foo To my blog!</b></i></p>

And its nothing magical about decorators either. I could run just the loop itself:

>>> code = "foo"
>>> for tag in ("p", "i", "b"):
...     print(code)
...     code = "<{0}>{1}</{0}>".format(tag,code)
... 
foo
<p>foo</p>
<i><p>foo</p></i>
>>> code
'<b><i><p>foo</p></i></b>'

Upvotes: 1

Related Questions