#๐Ÿ”’ @ problem

129 messages ยท Page 1 of 1 (latest)

balmy vector
#
def decorator(x):
    def wrapper(foo):
        foo()
        print(f"wrapper; {x}")

    return wrapper


@decorator("value")  
def foo():
    print("foo")


foo()

why doesn't it work?

hoary ivyBOT
#

@balmy vector

Python help channel opened

Remember to:

  • Ask your Python question, not if you can ask or if there's an expert who can help.
  • Show a code sample as text (rather than a screenshot) and the error message, if you've got one.
  • Explain what you expect to happen and what actually happens.

:warning: Do not pip install anything that isn't related to your question, especially if asked to over DMs.

drowsy thistle
#

What error are you getting?

balmy vector
#

TypeError: 'NoneType' object is not callable

#

i don't understand where it's coming from tho

drowsy thistle
#

Oh, you are probably confused about how decorators work

balmy vector
#

maybe

drowsy thistle
#

What do you think is x?

balmy vector
#

it wraps the function below, by making the interpreter call itself with the function as argument

#

soo

#

decorator("value")(foo)

#

(foo) function is appended at the end to call it from decorator

worthy whale
#

if you got (...) in your decorator, then you typically need three levels of functions

drowsy thistle
#

x here is your original function, not foo

drowsy thistle
balmy vector
#

oh it's doing the
decorator(foo)("value") instead?

worthy whale
#

no, x is "value" .. foo is the function, and wrapper is the actual decorator, that needs to return a function, but it returns None.

drowsy thistle
#

No, it's just decorator(foo)

drowsy thistle
balmy vector
#

what does this @ do then

#

i am kinda confused

#

i thought it does the

@decorator
def function(): ...
# it's equivalant to function= decorator(function)
worthy whale
#

nope. x is the string "value". and decorataor("value") returns wrapper ... and the wrapper is applied to the function as decorator.
The NoneType error comes from the line at the very bottom, because wrapper returned None

drowsy thistle
#

Wait a sec, maybe i'm truly misunderstanding that

balmy vector
#

OHHH

worthy whale
#

the @ syntax is syntactic sugar:

@decorator("value")  
def foo():
    print("foo")
``` is the same as: ```py
def foo():
    print("foo")
foo = decorator("value")(foo)
``` and `decorator("value")` returns `wrapper`, so it's basically:
```py
foo = wrapper(foo)  # wrapper from inside decorator.
balmy vector
drowsy thistle
balmy vector
#

oohhh

#

i was wrong because I assumed that it would execute the code with the (foo), but instead, as @worthy whale said, it turns the wrapper into the decorator??!?

#

so the syntax of ```python
@decorator(blabla)

firstly executes the `decorator(blabla)` and then uses it as decorator?!@?!?!?!?
worthy whale
worthy whale
#

yea, if you use a decorator with (...) it needs additional level

balmy vector
#

why does python do it that way tho?? What's the logic in firstly calling the function and only then using it as decorator?!??!

worthy whale
#

Normal decorator (without extra arguments)

def decorator(func):
    def wrapper(*args, **kwargs):
        return func(*args, **kwargs)
    return wrapper

@decorator
def foobar()
    ...

decorator with arguments:

def decorator(value):
    def real_decorator(func):
        def wrapper(*args, **kwargs):
            return func(*args, **kwargs)
        return wrapper
    return real_decorator

@decorator("foobar")
def foobar()
    ...
#

it's not really "python doing things" ... it's just that you can nest however deep you want.

hallow valley
hallow valley
worthy whale
#

the @ supports nearly any expression. so the general rule is really just:

@WHATERVER_IS_HERE
def somefunc():
    ...
``` being the same as: ```py
def somefunc():
    ...
somefunc = WHATERVER_IS_HERE(somefunc)
drowsy thistle
balmy vector
worthy whale
#

to pass in arguments?

#

those arguemnts will be available inside the decotrator

balmy vector
#

well you said it just does the "decorator(whatever)". Wouldn't it result in firstly adding the additional (whatever) and then calling everything?

balmy vector
#

when working with class decorators, it is very different

worthy whale
#

!e

def repeat(num):
    def deco(func):
        def wrapper(*args, **kwargs):
            for _ in range(num):
                last = func(*args, **kwargs)
            return last
        return wrapper
    return deco

@repeat(3)
def foobar():
    print('Hello!')

foobar()
hoary ivyBOT
worthy whale
#

there is no real difference between decorators on functions and on classes

#
@deco
class MyClass:
    ...
``` is also the same as: ```py
class MyClass:
    ...
MyClass = deco(MyClass)
balmy vector
#

oh sorry there is indeed no, i mised something

balmy vector
#

why does it firstly call the thing and only then appends the (whatever)

azure solstice
balmy vector
#

ik it

balmy vector
azure solstice
#

why you code doesnt work?

worthy whale
#

not sure I understand your question

#
def decorator(x):
    def wrapper(foo):
        foo()
        print(f"wrapper; {x}")
    return wrapper

@decorator("value")
def foo():
    print("foo")

foo()  # Error is here!
balmy vector
#

yes

azure solstice
#

your code doesnt work because youre using wrapper fiunction as a decorator.

worthy whale
#
def decorator(x):
    def wrapper(foo):
        foo()
        print(f"wrapper; {x}")
    return wrapper

def foo():
    print("foo")
foo = decorator("value")(foo)

foo()  # Error is here!
``` this is the same. Note: it executes from left to right. first: `decorator("value")` ... which puts x to "value" and returns wrapper.
balmy vector
azure solstice
#

when you put the parenthesis after the decorator it returns the wrapper function. and because of the @ sign it treats wrapper as a decorator

worthy whale
#

this part: decorator("value") returns wrapper. Python has to run that part first, before it can apply (foo) on the result

balmy vector
#

how doesn't it make sense

azure solstice
#

look at @worthy whale code example. decorator("value") returns wrapper function, and then you call the wrapper function and store what it returns (nothing) as foo. and at the last line you call the result which is None because wrapper doesnt return anything

hearty mirage
#

!decorators

hoary ivyBOT
#
Decorators

A decorator is a function that modifies another function.

Consider the following example of a timer decorator:

>>> import time
>>> def timer(f):
...     def inner(*args, **kwargs):
...         start = time.time()
...         result = f(*args, **kwargs)
...         print('Time elapsed:', time.time() - start)
...         return result
...     return inner
...
>>> @timer
... def slow(delay=1):
...     time.sleep(delay)
...     return 'Finished!'
...
>>> print(slow())
Time elapsed: 1.0011568069458008
Finished!
>>> print(slow(3))
Time elapsed: 3.000307321548462
Finished!

More information:

worthy whale
#

the @ syntax is literally just syntactic sugar for an assignment and call.
"decorating" means: use this expression as a function and call it with the function name, and reassign the result to the same name

balmy vector
worthy whale
#

in your example, yes

balmy vector
#

i am executing that thing from example

worthy whale
#
foo = decorator("value")(foo)
foo = wrapper(foo)  # wrapper returned by decorator("value")
foo = None  # wrapper itself returns nothing, so None
#

your error is the last line.

#

and only that line. because by then foo has been assigned that value None, which was returned from wrapper

balmy vector
#

oh wait quite literally ๐Ÿ˜ญ

worthy whale
#

if you make wrapper return foo, then the foo function stays unchanged

balmy vector
#

but the foo = decorator("value")(foo)

worthy whale
#

what about it?

balmy vector
#

it returns nothing

worthy whale
#

yes, the entire thing returns None

balmy vector
#

wrapper (last function call) returns nothing

balmy vector
#

how do normal functions work without returning anything then?

def a():
  print("a")
a()```
why doesn't this restul in the similar error, if it also returns nothing
balmy vector
worthy whale
#

a is still a function

#

foo does not point to a function (in the global scope!) anymore, because a decorator replaces the "foo" name with whatever the decorator returns.

worthy whale
#

!e

def foo():
    ...

print(foo)
hoary ivyBOT
worthy whale
#

!e

def decorator(func):
    return None

@decorator
def foo():
    ...

print(foo)
hoary ivyBOT
worthy whale
#

with a decorator you can change what the function points to. Here in the last example, I replaced that function with the None value

balmy vector
#

OHHHH

worthy whale
#
def decorator(func):
    return None

def foo():
    ...
foo = decorator(foo)

print(foo)
#

a bit complicated looking, but that's what's really happening

balmy vector
#

why does this by @azure solstice work then :

def factory(x):
    def decorator(foo):
        def wrapper():
            foo()
            print(f"wrapper; {x}")

        return wrapper

    return decorator


@factory("value")
def foo():
    print("foo")


foo()
# foo = factory("value")(foo)
# foo = decorator(foo)
# foo = wrapper(foo)
# foo = None
worthy whale
#

try too resolve it youself maybe?

balmy vector
#

threre's something about the function pointing.. im close to the answer

balmy vector
#

and have came to the same result

#

foo = None

worthy whale
#

well. yea, but at what point do you need to stop?

#

foo = factory("value")(foo)
this is the decorator expression that needs to be evaluated.

azure solstice
#

you HAD a function called foo but then when you ran @decorator("value") it replaces the function with the result of wrapper
so it WAS a function initially but afterwards it wasnt anymore. so you cant call foo because its no longer a function

balmy vector
#

OHHH

worthy whale
#
foo = factory("value")(foo)   # factory("value") returns decorator
foo = decorator(foo)  # decorator(foo) returns wrapper
foo = wrapper
#

your mistake was to add another call to wrapper. but there is no more call

balmy vector
#

yessss

#

that third # foo = wrapper(foo) was wrong

worthy whale
#

foo() is then the same as wrapper() at the bottom

balmy vector
worthy whale
#

yup

balmy vector
#

thank you a lot for your help

#

apologise for being so inattentive

#

got the tunnel vision on the other thing

#

!close

hoary ivyBOT
#
Python help channel closed

This help channel has been closed and it's no longer possible to send messages here. If your question wasn't answered, feel free to create a new post in #1035199133436354600. To maximize your chances of getting a response, check out this guide on asking good questions.