#๐ @ problem
129 messages ยท Page 1 of 1 (latest)
@balmy vector
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.
What error are you getting?
TypeError: 'NoneType' object is not callable
i don't understand where it's coming from tho
Oh, you are probably confused about how decorators work
maybe
What do you think is x?
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
if you got (...) in your decorator, then you typically need three levels of functions
x here is your original function, not foo
To write a decorator with parameters, yeah
oh it's doing the
decorator(foo)("value") instead?
no, x is "value" .. foo is the function, and wrapper is the actual decorator, that needs to return a function, but it returns None.
No, it's just decorator(foo)
But in OP's code, the decorator is written in such a way that the function gets assigned to x, and gets completely replaced with wrapper
what does this @ do then
i am kinda confused
i thought it does the
@decorator
def function(): ...
# it's equivalant to function= decorator(function)
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
Wait a sec, maybe i'm truly misunderstanding that
OHHH
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.
that makes senes
nevermind
so I was correct!
Many thanks, i missed the value being passed there
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?!@?!?!?!?
https://gist.github.com/nitori/4dfc60b25bf2e9aa82c39b89202a2e47
I once made this thingy. A decorator to simplify the creation of decorators with additional keyword arguments
is this โ ?
yea, if you use a decorator with (...) it needs additional level
๐งโ๐ณ
why does python do it that way tho?? What's the logic in firstly calling the function and only then using it as decorator?!??!
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.
when you do the @deco(...), the deco is not considered to be the functino at this point, since there's (). Python doesn't just read the code from left to right like humans and does everything on the go. It firstly processes everything, realizes that the deco is not the function but rather something getting called, and so does what it does
it's simplified explanation tho. I am probably wrong at some point
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)
def decorator(): ...
@decorator # notice no ()
def function(): ...```
This uses the decorator directly
```py
def decorator(param): ...
@decorator("value")
def function(): ...```
This runs the function and applies the result as decorator
why would it need to call the function first hten
well you said it just does the "decorator(whatever)". Wouldn't it result in firstly adding the additional (whatever) and then calling everything?
it supports nearly any expression as you said
when working with class decorators, it is very different
!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()
:white_check_mark: Your 3.12 eval job has completed with return code 0.
001 | Hello!
002 | Hello!
003 | Hello!
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)
oh sorry there is indeed no, i mised something
this still remains the same for me
why does it firstly call the thing and only then appends the (whatever)
if you want a decorator with arguments you can make a factory:
def factory(x):
def decorator(foo):
def wrapper():
foo()
print(f"wrapper; {x}")
return wrapper
return decorator
@factory("value")
def foo():
print("foo")
foo()
ik it
but what about my question?
why you code doesnt work?
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!
yes
your code doesnt work because youre using wrapper fiunction as a decorator.
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.
why does it do the :
- call the decorator("value")
- get its return AND USE it as DECORATOR
instead of - add the scopes right away, restulting in
decorator("value")(foo) - execute all of this one by one (return the wrapper, so wrapper(foo))
when you put the parenthesis after the decorator it returns the wrapper function. and because of the @ sign it treats wrapper as a decorator
this part: decorator("value") returns wrapper. Python has to run that part first, before it can apply (foo) on the result
decorator("value")(foo):
- decorator("value") executed, returning the "wrapper"
- wrapper(foo) gets executed. (cause it's the reamining scope after the return)
how doesn't it make sense
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
!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:
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
this results in decorator("value")(foo), right?
in your example, yes
what's the wrong with the order I call things here then?
i am executing that thing from example
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
oh wait quite literally ๐ญ
if you make wrapper return foo, then the foo function stays unchanged
but the foo = decorator("value")(foo)
what about it?
it returns nothing
yes, the entire thing returns None
wrapper (last function call) returns nothing
yes
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
just like this
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.
?!?!?!
!e
def foo():
...
print(foo)
:white_check_mark: Your 3.12 eval job has completed with return code 0.
<function foo at 0x7fe0b043ea20>
!e
def decorator(func):
return None
@decorator
def foo():
...
print(foo)
:white_check_mark: Your 3.12 eval job has completed with return code 0.
None
with a decorator you can change what the function points to. Here in the last example, I replaced that function with the None value
OHHHH
def decorator(func):
return None
def foo():
...
foo = decorator(foo)
print(foo)
a bit complicated looking, but that's what's really happening
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
try too resolve it youself maybe?
threre's something about the function pointing.. im close to the answer
i just did in the comments
and have came to the same result
foo = None
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.
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
OHHH
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
foo() is then the same as wrapper() at the bottom
it literally becomes that wrapper
yup
thank you a lot for your help
apologise for being so inattentive
got the tunnel vision on the other thing
!close
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.