Python Decorator Improvement Idea
Just a small idea that could possibly be useful for python decorators. 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. 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. Called as a decorator implies: @decorator def decorated(): pass These are not called as a decorator. The first item's return value would be called as a decorator, thus if it has a decorator parameter, it would be used. @decorator(...) def decorated(): pass def decorated(): pass decorated = decorator(decorated) Any declared callable (function, class instance with __call__, etc) can have an explicitly declared parameter called a decorator information parameter. The syntax could be as follows. "info" is just a name I've chosen and could be any legal name. def decorator(..., @info): pass def wrapper(..., @info): def decorator(obj, @info): ... return obj if info: called directly as decorator, do something else: return decorator Rules: 1. It is not possible for the parameter's value to be directly specified. You can't call fn(info=...) 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. 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 Using the decorator in a class context, pass the class object. While the class object hasn't been fully created yet, 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 class MyClass(Base): # Add "method" to the MyClass._registry list @decorator def method(...): pass def call_all(cls): for method in self._registry: method(self) This could also make it possible to use decorators on assignments. Information could be passed to the decorator by the runtime: script_vars = {} def expose(obj, @info): script_vars[info.name] = obj return obj def load(filename): # load script and compile exec(code, script_vars) @expose def script_function(...): pass # This will call the decorator passing in 200 as the object, as well as info.name as the variable being assigned. @expose SCRIPT_CONSTANT = 200 # If stacked, only the first would be used as info.name, in this case SCRIPT_CONSTANT2 @expose SCRIPT_CONSTANT2 = A_VAR = 300 def default(obj, @info): if info.name in info.ctx.vars: return info.ctx.vars[info.name] if info.name in info.globals: return info.globals[info.name] return obj @default X = 12 @default X = 34 print(X) # print 12 since during the second call, X existed and it's value was returned instead of 34, and was assigned to X 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 This was just an idea I had that may have some use. Thanks, Brian Vanderburg II
participants (4)
-
Brian Allen Vanderburg II
-
Eric V. Smith
-
Michael Selik
-
Steven D'Aprano