On Sat, Oct 16, 2021 at 8:46 AM Steven D'Aprano
On Sat, Oct 16, 2021 at 09:19:26AM -0400, Erik Demaine wrote:
To me (a mathematician), the existence of this magic in def, class, import, etc. is a sign that this is indeed useful functionality. As a fan of first-class language features, it definitely makes me wonder whether it could be generalized.
Obviously it is useful, because we have four major uses for it: imports, classes, functions and decorators. And I can think of at least two minor uses: the three argument form of type(), and namedtuple.
The question is, are any additional uses worth the effort and potential ugliness of generalising it?
Not every generalisation is worth it. Sometimes you can overgeneralise.
https://www.joelonsoftware.com/2001/04/21/dont-let-architecture-astronauts-s...
Until we have some compelling use-cases beyond the Big Four, I think this is a case of YAGNI.
https://www.martinfowler.com/bliki/Yagni.html
I've been thinking about use-cases for this feature for literally *years*, I've even got a handful of rough notes for a proto-PEP written down. To my embarrassment, today was the first time I realised that imports are also an example of this feature. So it is possible that there are many great use-cases for this and I just can't see them.
You made me realize what the four built-in methods of accessing target name have in common: they are all building namespaces. Okay so maybe decorators aren't creating a new namespace, not beyond what def already does, and def isn't often thought of as a means to create a new namespace but it certainly does. So not having this access impeeds a pythonista's ability to create novel namespaces. This token alone isn't going to make new namespace types effortless, but it is maybe the last piece not already available at runtime. And yes we already have SimpleNamespace for just creating a bag of things. But a SimpleNamespace is still a class object when broken down, It's just one that does some namespace things better than object. There is actually at least one more first class use of target names: that of async def. This is different than def because it is creating a CoroutineType not simply a FunctionType. Python actually has many different types that can be created using the def keyword, but async shows that sometimes it is not even enough to use a decorator, or a metaclass with a __call__. Having access to the target would allow for new function types as well as new namespace types. Finally, sometimes the name a class instance is assigned to *is* very significant and needs to be taken into account when creating it. These are for instance the classes given here as examples from the typing module, or for a sentinel class that in only ever be used as instances. It is not always possible, or desirable, to subclass and work with class objects rather than instances just to have access to the binding name.
But I'm not sure what the best mechanism is.
The mechanism is probably easy, if built into the compiler. When the compiler sees something that looks like a binding operation:
target = expression
it knows what the target is. If the expression contains some magic token, the compiler can substitute the target as a string for the magic token. (There is at least one other possible solution.)
So the implementation is probably easy. It is the design that is hard, and the justification lacking.
Yes, this is what my toy PoC does. It replaces the node with a string while building the AST. Regards, ~Jeremiah