For anyone wondering how to fully type hint a decorator, here it is. import functools from typing import Callable, ParamSpec, TypeVar P = ParamSpec("P") T = TypeVar("T") def time_func(func: Callable[P, T]) -> Callable[P, T]: @functools.wraps(func) def wrapper(*args: P.args, **kwargs: P.kwargs) -> T: # code result = func(*args, **kwargs) # code return result return wrapper What ParamSpec does, is it captures not only the exact signature of the callable, but the names of the args as well.
Making a function async intrinsically requires functions that await it to be async as well. To run an async function without awaiting it, use `asyncio.run(function())`, where "function" is your async function.
Is there a way of telling from function 2 whether function 1 (which calls function 2) is async? If there is, you might be able to make your own where the wrapper could call async.run(function2()) if function 1 is not async, but if function 1 is async, the wrapper can just call function 2 normally. If there isn’t such a thing you might still be able to make a wrapper like `wrapper(is_async: bool, *args, **kwargs)` which then calls function2(*args, **kwargs) or async.run(function2(*args, **kwargs)) based on what is_async is (which function 1 would have to directly supply). I haven’t messed with async/await yet and haven’t successfully found how to check stuff about caller functions from callee functions, though, so I might be misunderstanding how these things work
@@_Blazing_Inferno_This doesn't make sense because you can't nest asyncio.run(), nor should you await arbitrary async functions. In order to make asynchronous execution truly asynchronous, you need asyncio.create_task() to start the task, then await asyncio.gather() to collect the results, handle timeout errors, etc. The entire asynchronous program code is actually much different from the synchronous program.
you can simply wrap the function in `ensure_future` (which would return an awaitable). also check out `.run_in_executor()` method for related functionality.
You should do a pytest video using conftest and show how to mock a wrapped method (I did this recently at work, I'm a senior dev and it certainly wasn't easy just using docs/copilot lol)
The important thing is the first function, in this video "get_time." Notice that instead of taking variables as an input it takes a function. Adding @get_time as a decorator to "calculate" basically says "send this function through the 'get_time' function." Notice that in the "get_time" function, line 12 of code is what actually runs the "calculate" function. So the decorator is telling Python to send the function it's decorating through the decorator function.
@@DrDeuteron Lambda decorators enable IIFEs in python, but as stated the application is less generally applicable and more just a good tool to have in your toolkit. I use it for debugging on occasion
@@celestialowl8865 lambdas have nothing to do with IIFE. You can define an IIFE with a named decorator. And it's not an IIFE anyway because it's not an expression
@@ApprendreSansNecessite I should have specified that they allow for a cleaner standard format*. Obviously anything performed by a lambda can be directly reproduced by a named decorator.
@@celestialowl8865 I totally understand your hype for lambdas but they are half-backed. Most of the use cases I have for them in other languages can't be expressed in (typed) Python
I heard also about decorators with parameters, but the way it's implemented looks dirty to me, so i prefer using functions composition/currying instead of decorators when it becomes more complex.
It is not a macro, it is a higher order function. It is equivalent to writing `def _fn(x, y): ...; fn = decorator(__fn)` or `fn = decorator(lamda x, y: ... )`. The difference is that you pay the price of this abstraction every time you call `fn` whereas a macro would be executed either at compile time or during an initial macro expansion phase. When you use higher order functions, functions are data, but when you use macros, *code* is data
For anyone wondering how to fully type hint a decorator, here it is.
import functools
from typing import Callable, ParamSpec, TypeVar
P = ParamSpec("P")
T = TypeVar("T")
def time_func(func: Callable[P, T]) -> Callable[P, T]:
@functools.wraps(func)
def wrapper(*args: P.args, **kwargs: P.kwargs) -> T:
# code
result = func(*args, **kwargs)
# code
return result
return wrapper
What ParamSpec does, is it captures not only the exact signature of the callable, but the names of the args as well.
Amazing! You even get the parameter *names* and not only their types That's the way to do it
Sorry
Functools wraps solved my biggest problem(I was trying to apply two decorators).
Thanks for this!!
Is there a decorator that makes a functuon async? Without infecting all the functions that call it to be async aswell? Thanks
Making a function async intrinsically requires functions that await it to be async as well. To run an async function without awaiting it, use `asyncio.run(function())`, where "function" is your async function.
@@largewallofbeans9812 thanks! Though still wondering if there's a asyncio decorator that does that automatically
Is there a way of telling from function 2 whether function 1 (which calls function 2) is async? If there is, you might be able to make your own where the wrapper could call async.run(function2()) if function 1 is not async, but if function 1 is async, the wrapper can just call function 2 normally.
If there isn’t such a thing you might still be able to make a wrapper like `wrapper(is_async: bool, *args, **kwargs)` which then calls function2(*args, **kwargs) or async.run(function2(*args, **kwargs)) based on what is_async is (which function 1 would have to directly supply).
I haven’t messed with async/await yet and haven’t successfully found how to check stuff about caller functions from callee functions, though, so I might be misunderstanding how these things work
@@_Blazing_Inferno_This doesn't make sense because you can't nest asyncio.run(), nor should you await arbitrary async functions.
In order to make asynchronous execution truly asynchronous, you need asyncio.create_task() to start the task, then await asyncio.gather() to collect the results, handle timeout errors, etc. The entire asynchronous program code is actually much different from the synchronous program.
you can simply wrap the function in `ensure_future` (which would return an awaitable).
also check out `.run_in_executor()` method for related functionality.
You should do a pytest video using conftest and show how to mock a wrapped method (I did this recently at work, I'm a senior dev and it certainly wasn't easy just using docs/copilot lol)
I always have a confusion with decorators, Thanks for this video❤
I cannot quite understand the super function can please explain it in the next video
Great video, learned a lot
Thank you.
Good video but it'd be helpful if you either briefly summarized what a 'decorator' does or pointed to one of your earlier videos that discussed it.
The important thing is the first function, in this video "get_time." Notice that instead of taking variables as an input it takes a function. Adding @get_time as a decorator to "calculate" basically says "send this function through the 'get_time' function." Notice that in the "get_time" function, line 12 of code is what actually runs the "calculate" function.
So the decorator is telling Python to send the function it's decorating through the decorator function.
awesome!
Thanks for this
And here I was thinking a decorator in Python was a feather boa. ;)
Lambda and nested lambda decorators are novel but neat
the point of a decorator is reuse code...and lambdas don't do that.
@@DrDeuteron Lambda decorators enable IIFEs in python, but as stated the application is less generally applicable and more just a good tool to have in your toolkit. I use it for debugging on occasion
@@celestialowl8865 lambdas have nothing to do with IIFE. You can define an IIFE with a named decorator. And it's not an IIFE anyway because it's not an expression
@@ApprendreSansNecessite I should have specified that they allow for a cleaner standard format*. Obviously anything performed by a lambda can be directly reproduced by a named decorator.
@@celestialowl8865 I totally understand your hype for lambdas but they are half-backed. Most of the use cases I have for them in other languages can't be expressed in (typed) Python
nice
what if func has a return value ?
Good day greetings
what code editor are you using?
pycharm
I heard also about decorators with parameters, but the way it's implemented looks dirty to me, so i prefer using functions composition/currying instead of decorators when it becomes more complex.
May someone tell me that which app is this?
First!
If you do this you totally loose the type signature of your function. It becomes `(...) -> Any`
is there any better way?
Yes, the pinned comment
which IDE is this?
Pycharm
I read a decorator is a macro, but it's written in python.
It is not a macro, it is a higher order function. It is equivalent to writing `def _fn(x, y): ...; fn = decorator(__fn)` or `fn = decorator(lamda x, y: ... )`. The difference is that you pay the price of this abstraction every time you call `fn` whereas a macro would be executed either at compile time or during an initial macro expansion phase.
When you use higher order functions, functions are data, but when you use macros, *code* is data