[Python-ideas] Python Decorator Improvement Idea

Steven D'Aprano steve at pearwood.info
Sat Jun 16 01:22:10 EDT 2018

On Fri, Jun 15, 2018 at 11:54:42PM -0400, Brian Allen Vanderburg II via Python-ideas wrote:

> An idea I had is that it could be possible for a decorator function to
> declare a parameter which, when the function is called as a decorator,
> the runtime can fill in various information in the parameters for the
> decorator to use.

We can already do this, by writing a decorator factory:

@decorator(any parameters you care to pass)
def spam():

"Explicit is better than implicit" -- it is better to explicitly pass 
the parameters you want, than to hope that "the runtime" (do you mean 
the interpreter?) will guess which parameters you need.

> Some of the information would be available in all
> contexts, while other information may only be available in certain
> contexts.The parameter's value cannot be explicitly specified, defaults
> to Null except when called as a decorator, and can only be specified
> once in the function's parameter list.

Do you mean None?

Why do you think it is a good idea to have the same function, the 
decorator, behave differently when called using decorator syntax and 
standard function call syntax? To me, that sounds like a terrible idea. 
What advantage do you see?

> Rules:
> 1. It is not possible for the parameter's value to be directly
> specified. You can't call fn(info=...)

That sounds like a recipe for confusion to me. How would you explain 
this to a beginner?

Aside from the confusion that something that looks like a parameter 
isn't an actual parameter, but something magical, it is also very 
limiting. It makes it more difficult to use the decorator, since now it 
only works using @ syntax.

> 2. The parameters value is Null except in the cases where it is invoked
> (the callable called a a decorator).  If used in a partial, the
> decorator parameter would be Null. etc.

You keep saying Null. What's Null?

> Information that could be contained in the parameters for all contexts:
> Variable name
> Module object declared in
> Module globals (useful for @export/@public style decorators)
> Etc

The variable name is just the name of the function or class, the first 
parameter received by the decorator. You can get it with func.__name__.

The module globals is already available in globals(). You can either 
pass it directly as an argument to the decorator, or the decorator can 
call it itself. (Assuming the decorator is used in the same module it is 
defined in.)

If the decorator is in the same module as the globals you want to 
access, the decorator can just call globals(). Or use the global 

If the decorator is contained in another module, the caller can pass the 
global namespace as an argument to the decorator:

def func(): ...

Not the neatest solution in the world, but it works now.

> Using the decorator in a class context, pass the class object.

The decorator already receives the class object as the first parameter. 
Why pass it again?

> While the class object hasn't been fully created yet,

What makes you say that?

> this could allow
> accessing attributes of the class (like a registry or such)
> 	def decorator(fn, @info):
> 	    if hasattr(info, "class_obj"):
> 	        registry = info.class_obj.__dict__.setdefault("_registry", [])
> 	    registry.append(fn)
> 	    return fn

Writing "hasattr(info, whatever)" is an anti-pattern.

By the way, the public interface for accessing objects' __dict__ is to 
call the vars() function:


> This could also make it possible to use decorators on assignments.

We already can:

result = decorator(obj)

is equivalent to:

def obj(): ...


class obj: ...

except that we can use the decorator on anything we like, not just a 
function or class.

>     # This will call the decorator passing in 200 as the object, as 
>     # well as info.name as the variable being assigned.
>     @expose

That would require a change to syntax, and would have to be a separate 

If there were a way to get the left hand side of assignments as a 
parameter, that feature would be *far* to useful to waste on just 
decorators. For instance, we could finally do something about:

name = namedtuple("name", fields)

> The two potential benefits I see from this are:
> 1. The runtime can pass certain information to the decorator, some
> information in all contexts, and some information in specific contexts
> such as when decorating a class member, decorating a function defined
> within another function, etc
> 2. It would be possible to decorate values directly, as the runtime can
> pass relevant information such as the variables name

No, that would require a second, independent change.

We could, if desired, allow decorator syntax like this:

value = 1

but it seems pretty pointless since that's the same as:

value = decorator(1)

The reason we have @decorator syntax is not to be a second way to call 
functions, using two lines instead of a single expression, but to avoid 
having to repeat the name of the function three times:

# Repeat the function name three times:
def function():
function = decorate(function)

# Versus only once:
def function():


More information about the Python-ideas mailing list