Here’s a concrete proposal.
tl;dr: `nameof x == "x"`, and `nameof x.y == "y"`.
Rationale:
Even though as far as Python is concerned `nameof x.y` is identical to `"y"`,
it can make a difference to human readers—and to at least two classes of
automated tools.
When refactoring code to rename something, there is no way to tell whether a
string—or a substring in a larger string—that matches the name should be
changed. But a nameof expression is unambiguously a reference to the name, and
therefore the tool knows that it needs to change.
For example:
class Spam:
cheese = 10
def __repr__(self)
return f"<{type(self).__name__} at {id(self)} {nameof self.cheese}:
{self.cheese}>"
If I ask my IDE to rename cheese to beans, that `nameof self.cheese` is
unambiguous: it clearly should be changed to `nameof self.beans`. If I had just
included the substring “cheese” within the string, it would need some kind of
heuristics or trained AI or something to figure that out.
Similarly for this example:
rpc.register(nameof spam, spam)
Again, if I just used the string "spam", renaming the function spam to eggs
would be ambiguous.
And if I used spam.__name__, this would be unambiguous, but potentially
incorrect. For example:
spam = SpamHandler.handle
rpc.register(spam.__name__, spam) # oops
(spam could be the name of a lambda, or a function defined as bar and copied
to spam, or a bound classmethod SpamHandler.spam
This is different from registering "spam” because if I rename the function the
refactoring tool will automatically change the string. And it’s different from
registering spam.__name__ because it works even if spam is a name for a lambda,
or another name for a function defined as eggs.
Grammar:
atom ::= identifier | literal | enclosure | nameof_atom
nameof_atom ::= "nameof" nameof_name
nameof_name ::= identifier | attributeref
Semantics:
The value of nameof_atom is a string: the name of the identifier itself for the
first case, or of the identifier after the dot for the second case. The string
value is semantically identical to a literal for the same string, so `nameof
foo.bar` and `"bar"` should be compiled to identical code. There is no
difference to Python, only to human readers—and at least two classes of
automated tools.
A refactoring tool should assume that `nameof self.spam` needs to change when
`spam` is renamed.
A static type checker may be able to
A static type checker may perform additional checks. If it can tell that the
identifier, or the primary of the attributeref, does not match any accessible
local or global (or bulltin) variable, or if the primary of the attributeref
does match a variable with a type that specifies its attributes and the
identifier of the attributeref does not match any of those attributes, an error
should be reported. If the type checker cannot tell the type, or the attributes
of that type, the checker may report an error or a warning or nothing. If the
type is explicitly Any, or if it’s determined to be some type intended to have
dynamic attributes (like SimpleNamespace, or a bridge or proxy), the checker
should not report an error.
_______________________________________________
Python-ideas mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at
https://mail.python.org/archives/list/[email protected]/message/4GWYKCEVW3XUU52JAGXXGC3DEFYDODJM/
Code of Conduct: http://python.org/psf/codeofconduct/