Skip to content

Instantly share code, notes, and snippets.

@darvil82
Last active July 25, 2022 15:27
Show Gist options
  • Save darvil82/04ed4d691c8b7c622ed074352e34bc8e to your computer and use it in GitHub Desktop.
Save darvil82/04ed4d691c8b7c622ed074352e34bc8e to your computer and use it in GitHub Desktop.
A very simple explanation about how python decorators work.

Small explanation about how decorators work.

Lets say decorator has this definition:

def decorator(func: Callable[[], Any]) -> Callable[[], Any]:
  def wrapper():
    ...
    func()
    ...
  return wrapper

When doing @decorator with, say a function like:

@decorator
def thing():
  pass

This pretty much gets converted to

def thing():
  pass
thing = decorator(thing)

When calling decorator, this will define a function that wraps the function we just passed (the decorated one), and then it immediately returns that function definition that was defined. Now thing gets redefined to a completely different function:

def thing():
  ...
  func() # original thing()
  ...

Now a bit of a more complex decorator:

def decorator2(name: str):
    def outer_wrapper(func: Callable[[str], Any]) -> Callable[[], None]:
        def wrapper() -> None:
            func(name)
        return wrapper
    return outer_wrapper

Which may be used like this:

@decorator2("juan")
def thing(name: str):
    print(f"name: {name}")

This is what actually happens when doing this:

def thing(name: str):
    print(f"name: {name}")
thing = decorator2("juan")(thing)

First, the decorator gets called with the parameter that was passed into it (name). On this call, decorator2 returns the outer_wrapper function. Which wraps another function (that's it entire purpose). As seen in the definition, outer_wrapper takes a function as a parameter, which is the function that is being decorated.

#                > decorator2(name)
#                         > outer_wrapper(func)
thing = decorator2("juan")(thing)

At this point, thing becomes:

def thing():
    ...
    func("juan") # original thing(name)
    ...

Important to note that calling thing now will always return None, because the wrapper decorator doesn't return the result of func().

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment