On 6/16/2018 8:22 PM, Michael Selik wrote:
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.

One such thread is here:
https://mail.python.org/pipermail/python-ideas/2011-March/009250.html

Eric



On Sat, Jun 16, 2018, 9:04 AM Brian Allen Vanderburg II via Python-ideas <python-ideas@python.org <mailto:python-ideas@python.org>> wrote:


    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 <http://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 <http://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.


    _______________________________________________
    Python-ideas mailing list
    Python-ideas@python.org <mailto:Python-ideas@python.org>
    https://mail.python.org/mailman/listinfo/python-ideas
    Code of Conduct: http://python.org/psf/codeofconduct/



_______________________________________________
Python-ideas mailing list
Python-ideas@python.org
https://mail.python.org/mailman/listinfo/python-ideas
Code of Conduct: http://python.org/psf/codeofconduct/

_______________________________________________
Python-ideas mailing list
Python-ideas@python.org
https://mail.python.org/mailman/listinfo/python-ideas
Code of Conduct: http://python.org/psf/codeofconduct/

Reply via email to