On 06/16/2018 01:22 AM, Steven D'Aprano wrote: > 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?
Yes, I meant None instead of Null. > [...] >> 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. That was just an initial idea. However there would be no reason that the parameter could not be passed directly. Actually if creating one decorator that wraps another decorator, being able to pass the parameter on could be needed. Also, the decorator would still work in normal syntax, only with that parameter set to None >> 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__. This works with functions and classes but not other values that may not have __name__. >> 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? What I mean is used inside the body of a class to decorate a class member: class MyClass(object): @decorator def method(self): pass Using the explicit is better than implicit: class MyClass(object): @decorator(MyClass, ...) def method(self): pass However right now that does not work as MyClass does not exist when the decorator is called. I'm not sure how Python works on this under the hood as it's been a long time since I've looked through the source code. If Python gather's everything under MyClass first before it even begins to create the MyClass object, then it may not be possible, but if Python has already created a class object, and just not yet assigned it to the MyClass name in the module, then perhaps there could be some way to pass that class object to the decorator. I have seen some examples that decorates the class and members to achieve something similar @outerdecorator class MyClass: @decorator def method(self): 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 > That would require a change to syntax, and would have to be a separate > discussion. > > 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) Agreed it would be a change in syntax. Using the decorator syntax i've mentioned the name being assigned would be passed to that extra info parameter. Python would treat anything in the form of: @decorator NAME = (expression) as a decorator as well: _tmp = (expression) NAME = decorator(_tmp) Right now, there's litlte use as it is just as easy to say directly NAME = decorator(expression) With this idea, it could be possible to do something like this: def NamedTuple(obj @info): return namedtuple(info.name, obj) @NamedTuple Point3 = ["x", "y", "z"] >> 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: > > @decorate > 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: > @decorate > def function(): > ... > The two main use cases I had of this idea were basically assignment decorators, pointless as it can just be name = decorator(value), but my idea was to pass to the decorator some metadata such as the name being assigned, and as class member decorators to receive information of the instance of the class object the member is being declared under. A more general idea could be to allow a function call to receive a meta parameter that provides some context information of the call. This parameter is not part of a parameter list, but a special __variable__, or perhaps could be retrieved via a function call. Such contexts could be: 1) Assignment (includes decorators since they are just sugar for name = decorator(name)) The meta attribute assignname would contain the name being assigned to def fn(v): print(__callinfo__.assignname) return v # prints X X = fn(12) # prints MyClass @fn class MyClass: pass # Should assignname receive the left-most assignment result or the rightmost othervar # Perhaps assignname could be a tuple of names being assigned to result = othervar = fn(12) #assignname would be myothervar in this augmented assignment result = [myothervar := fn(12)] # Should expressions be allowed, or would assignname be None? result = 1 + fn(12) With something like this. name = namedtuple("name", ...) could become: def NamedTuple(*args): return namedtuple(__callinfo__.assignname, args) Point2 = NamedTuple("x", "y") Point3 = NamedTuple("x", "y", "z") etc 2) Class context. The a classobj parameter could contain the class object it is called under. This would be a raw object initially as __init__ would not have been called, but would allow the decorator to add attributes to a class def fn(v): print(__callinfo__.classobj) # classobj is None except when the function is called in the body of a class declaration print(__callinfo__.assignname) if __callinfo__.classobj: data = vars(__callinfo__.classobj).setdefault("_registry", {}) data[__callinfo__.assignname] = v return v class MyClass: # print main.MyClass (probably something else since __init__ not yet calls, may just be a bare class object at that timie) # print X # sets MyClass._registry["X"] X = fn(12) # print main.MyClass # print method # sets MyClass._registry["method"] @fn def method(self): pass # print None # print Y Y = fn(12) In this case it's not longer a decorator idea but more of an idea for a called function to be able to retrieve certain meta information about it's call. In the examples above, I used __callinfo__ with attributes, but direct names would work the same: def fn(v): print(__assignname__) # May be None if no assignment/etc if otherfunc(fn(value)) print(__classobj__) # Will be None unless fn is called directly under a class body There may be other contexts and use cases, and better ways. Just an idea.
signature.asc
Description: OpenPGP digital signature
_______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/