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/