The idea of having a dunder to introspect the bound variable name has been discussed before. You can find the past discussions in the mailing list archive. If I recall correctly, there were very few use cases beyond namedtuple. With dataclasses available in 3.7, there may be even less interest than before.<div><br><br><div class="gmail_quote"><div dir="ltr">On Sat, Jun 16, 2018, 9:04 AM Brian Allen Vanderburg II via Python-ideas <<a href="mailto:python-ideas@python.org">python-ideas@python.org</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><br>
On 06/16/2018 01:22 AM, Steven D'Aprano wrote:<br>
> Some of the information would be available in all<br>
>> contexts, while other information may only be available in certain<br>
>> contexts.The parameter's value cannot be explicitly specified, defaults<br>
>> to Null except when called as a decorator, and can only be specified<br>
>> once in the function's parameter list.<br>
> Do you mean None?<br>
<br>
Yes, I meant None instead of Null.<br>
<br>
> [...]<br>
>> Rules:<br>
>><br>
>> 1. It is not possible for the parameter's value to be directly<br>
>> specified. You can't call fn(info=...)<br>
> That sounds like a recipe for confusion to me. How would you explain <br>
> this to a beginner?<br>
><br>
> Aside from the confusion that something that looks like a parameter <br>
> isn't an actual parameter, but something magical, it is also very <br>
> limiting. It makes it more difficult to use the decorator, since now it <br>
> only works using @ syntax.<br>
<br>
That was just an initial idea.  However there would be no reason that the<br>
parameter could not be passed directly.  Actually if creating one decorator<br>
that wraps another decorator, being able to pass the parameter on could<br>
be needed.<br>
<br>
Also, the decorator would still work in normal syntax, only with that<br>
parameter<br>
set to None<br>
<br>
>> Information that could be contained in the parameters for all contexts:<br>
>><br>
>> Variable name<br>
>> Module object declared in<br>
>> Module globals (useful for @export/@public style decorators)<br>
>> Etc<br>
> The variable name is just the name of the function or class, the first <br>
> parameter received by the decorator. You can get it with func.__name__.<br>
<br>
This works with functions and classes but not other values that may<br>
not have __name__.<br>
>> Using the decorator in a class context, pass the class object.<br>
> The decorator already receives the class object as the first parameter. <br>
> Why pass it again?<br>
><br>
><br>
>> While the class object hasn't been fully created yet,<br>
> What makes you say that?<br>
<br>
What I mean is used inside the body of a class to decorate a class member:<br>
<br>
    class MyClass(object):<br>
        @decorator<br>
        def method(self):<br>
            pass<br>
<br>
Using the explicit is better than implicit:<br>
<br>
    class MyClass(object):<br>
        @decorator(MyClass, ...)<br>
        def method(self):<br>
            pass<br>
<br>
However right now that does not work as MyClass does not exist when the<br>
decorator is called.  I'm not sure how Python works on this under the hood<br>
as it's been a long time since I've looked through the source code.  If<br>
Python<br>
gather's everything under MyClass first before it even begins to create the<br>
MyClass object, then it may not be possible, but if Python has already<br>
created<br>
a class object, and just not yet assigned it to the MyClass name in the<br>
module,<br>
then perhaps there could be some way to pass that class object to the<br>
decorator.<br>
<br>
I have seen some examples that decorates the class and members to achieve<br>
something similar<br>
<br>
    @outerdecorator<br>
    class MyClass:<br>
        @decorator<br>
        def method(self):<br>
            pass<br>
<br>
><br>
>>     # This will call the decorator passing in 200 as the object, as <br>
>>     # well as <a href="http://info.name" rel="noreferrer" target="_blank">info.name</a> as the variable being assigned.<br>
>>     @expose<br>
>>     SCRIPT_CONSTANT = 200<br>
> That would require a change to syntax, and would have to be a separate <br>
> discussion.<br>
><br>
> If there were a way to get the left hand side of assignments as a <br>
> parameter, that feature would be *far* to useful to waste on just <br>
> decorators. For instance, we could finally do something about:<br>
><br>
> name = namedtuple("name", fields)<br>
<br>
Agreed it would be a change in syntax.  Using the decorator syntax i've<br>
mentioned<br>
the name being assigned would be passed to that extra info parameter. <br>
Python<br>
would treat anything in the form of:<br>
<br>
    @decorator<br>
    NAME = (expression)<br>
<br>
as a decorator as well:<br>
<br>
    _tmp = (expression)<br>
    NAME = decorator(_tmp)<br>
<br>
Right now, there's litlte use as it is just as easy to say directly<br>
<br>
    NAME = decorator(expression)<br>
<br>
With this idea, it could be possible to do something like this:<br>
<br>
    def NamedTuple(obj @info):<br>
        return namedtuple(<a href="http://info.name" rel="noreferrer" target="_blank">info.name</a>, obj)<br>
<br>
    @NamedTuple<br>
    Point3 = ["x", "y", "z"]<br>
>> The two potential benefits I see from this are:<br>
>><br>
>> 1. The runtime can pass certain information to the decorator, some<br>
>> information in all contexts, and some information in specific contexts<br>
>> such as when decorating a class member, decorating a function defined<br>
>> within another function, etc<br>
>><br>
>> 2. It would be possible to decorate values directly, as the runtime can<br>
>> pass relevant information such as the variables name<br>
> No, that would require a second, independent change.<br>
><br>
> We could, if desired, allow decorator syntax like this:<br>
><br>
> @decorate<br>
> value = 1<br>
><br>
> but it seems pretty pointless since that's the same as:<br>
><br>
> value = decorator(1)<br>
><br>
> The reason we have @decorator syntax is not to be a second way to call <br>
> functions, using two lines instead of a single expression, but to avoid <br>
> having to repeat the name of the function three times:<br>
><br>
> # Repeat the function name three times:<br>
> def function():<br>
>    ...<br>
> function = decorate(function)<br>
><br>
> # Versus only once:<br>
> @decorate<br>
> def function():<br>
>     ...<br>
><br>
<br>
The two main use cases I had of this idea were basically assignment<br>
decorators,<br>
pointless as it can just be name = decorator(value), but my idea was to<br>
pass to<br>
the decorator some metadata such as the name being assigned, and as class<br>
member decorators to receive information of the instance of the class object<br>
the member is being declared under.<br>
<br>
A more general idea could be to allow a function call to receive a meta<br>
parameter<br>
that provides some context information of the call.  This parameter is<br>
not part of<br>
a parameter list, but a special __variable__, or perhaps could be<br>
retrieved via a<br>
function call.<br>
<br>
Such contexts could be:<br>
<br>
1) Assignment (includes decorators since they are just sugar for name =<br>
decorator(name))<br>
The meta attribute assignname would contain the name being assigned to<br>
<br>
    def fn(v):<br>
        print(__callinfo__.assignname)<br>
        return v<br>
<br>
    # prints X<br>
    X = fn(12)<br>
<br>
    # prints MyClass<br>
    @fn<br>
    class MyClass:<br>
        pass<br>
<br>
    # Should assignname receive the left-most assignment result or the<br>
rightmost othervar<br>
    # Perhaps assignname could be a tuple of names being assigned to<br>
    result = othervar = fn(12)<br>
<br>
    #assignname would be myothervar in this augmented assignment<br>
    result = [myothervar := fn(12)]<br>
<br>
    # Should expressions be allowed, or would assignname be None?<br>
    result = 1 + fn(12)<br>
<br>
With something like this.<br>
<br>
    name = namedtuple("name", ...)<br>
<br>
could become:<br>
<br>
    def NamedTuple(*args):<br>
        return namedtuple(__callinfo__.assignname, args)<br>
<br>
    Point2 = NamedTuple("x", "y")<br>
    Point3 = NamedTuple("x", "y", "z")<br>
    etc<br>
<br>
2) Class context. The a classobj parameter could contain the class<br>
object it is called under.<br>
This would be a raw object initially as __init__ would not have been<br>
called, but would allow<br>
the decorator to add attributes to a class<br>
<br>
    def fn(v):<br>
        print(__callinfo__.classobj) # classobj is None except when the<br>
function is called in the body of a class declaration<br>
        print(__callinfo__.assignname)<br>
        if __callinfo__.classobj:<br>
            data = vars(__callinfo__.classobj).setdefault("_registry", {})<br>
            data[__callinfo__.assignname] = v<br>
        return v<br>
<br>
    class MyClass:<br>
        # print main.MyClass (probably something else since __init__ not<br>
yet calls, may just be a bare class object at that timie)<br>
        # print X<br>
        # sets MyClass._registry["X"]<br>
        X = fn(12)<br>
<br>
        # print main.MyClass<br>
        # print method<br>
        # sets MyClass._registry["method"]<br>
        @fn<br>
        def method(self):<br>
            pass<br>
   <br>
    # print None<br>
    # print Y<br>
    Y = fn(12)<br>
<br>
In this case it's not longer a decorator idea but more of an idea for a<br>
called function to be able to retrieve certain meta information about<br>
it's call.<br>
In the examples above, I used __callinfo__ with attributes, but direct<br>
names would work the same:<br>
<br>
    def fn(v):<br>
        print(__assignname__) # May be None if no assignment/etc if<br>
otherfunc(fn(value))<br>
        print(__classobj__) # Will be None unless fn is called directly<br>
under a class body<br>
<br>
<br>
There may be other contexts and use cases, and better ways.  Just an idea.<br>
<br>
<br>
_______________________________________________<br>
Python-ideas mailing list<br>
<a href="mailto:Python-ideas@python.org" target="_blank">Python-ideas@python.org</a><br>
<a href="https://mail.python.org/mailman/listinfo/python-ideas" rel="noreferrer" target="_blank">https://mail.python.org/mailman/listinfo/python-ideas</a><br>
Code of Conduct: <a href="http://python.org/psf/codeofconduct/" rel="noreferrer" target="_blank">http://python.org/psf/codeofconduct/</a><br>
</blockquote></div></div>