Backtics to allow any name

This idea is simply that anywhere an identifier is expected in Python, a programmer could instead supply a string between two backtics. The only characters that could not be used between these backticks are other backtics, or any line breaks.

`Monty`  # valid, but not necessary
`Monty Python`  # valid
`twenty.two`  # valid as one name
`1234`  # perhaps very confusing, but allowed
`this\`  # valid as backtics can't be contained, and so can't be escaped

`
not this
`

Python is very often a glue language, and this would allow it to fit even more bits together. It would allow more seamless interaction with namespaces where kebab case is prevalent; not only other languages but also the filesystem, shell options, docker registries, toml, etc. It would also allow the use of symbols used in other languages, such as Ruby’s success? or JavaScript’s $item, without any lossy conversion.

Perhaps importantly, this change would be a change semantically by allowing a new way to write identifiers but does not require changes to how identifiers are stored or retrieved. The dict behind each python namespace is already capable of holding these and still more that still can’t be represented by this new syntax.

Some further examples, including how the same could be achieved with the current implementation of python:

import `#hidden` as hidden
# hidden = __import__("#hidden")

order = menu.`spam&eggs`
# order = gettattr(menu, "spam&eggs")

`1 answer` = 42
# globlas()["1 answer"] = 42

post(`user-data`=my_data)
# post(**{"user-data": my_data})
4 Likes

I like this idea. I’ve been running into this problem repeatedly across projects and the only “fix” was with MacroPy. Since that’s not maintained anymore, I had to go back to “backpatching” the names.

3 Likes

When I had the need, I was told to use dicts, and when shown the task, there was always a way to use dicts.

Thinking about your back ticks suggestion, I am concerned that you state that this form wouldn’t be able to specify any string as a name - what if you want backticks in a name? Or to use hex chars in a name?

I also think there could be issues with having keywords in backticks, and in generating code.

In general, there could be a lot to resolve when fleshed out.

1 Like

Approval is unlikely as some core developers really hate backticks and were delighted when they were removed from Python syntax. The currently methods listed above are considered to be the proper solution. The Steering Council has decided that these string options should be considered part of Python rather than just CPython implementation accidents. See https://discuss.python.org/t/non-identifier-names-of-kwargs-attributes-variables-etc/19293/3

2 Likes

Predictably, backticks are not going to be accepted because they are almost invisible. Also, how would you quote your code in Markdown :slight_smile: ?

The idea of a syntax (allowing any name expressed as a string) is not unreasonable. The argument would have to be accepted that a spelling more succinct than getattr(o, "Monty Python") (etc.) is feasible and useful to enough people to be worth the complexity. I’m not one of those people.

I am wondering how many languages allow to specially quote (almost) arbitrary strings to be used as identifiers?

Currently I know only about SQL:

SELECT * FROM "weirdly named table"

Though the idea is interesting I do not think it is worth the added complexity and the new special character.

Look for quoted identifiers in statically typed languages that don’t have equivalents of getattr and setattr. C# has it, verbatim identifiers prefixed with @. Java apparently doesn’t.

Thanks for the link. I knew about the Ideas thread but not the Steering Counsel pronouncement.

The methods I listed above work, but I would argue the way of getting or setting a global variable is not obvious. But the real problem with the current functions is that there is no way to set a local. There is of course locals() but as noted in its own docstring

NOTE: Whether or not updates to this dictionary will affect name lookups in
the local scope and vice-versa is implementation dependent and not
covered by any backwards compatibility guarantees.

and indeed a statement like locals()["foo"] = 42 in CPython will sometimes alter the variable named foo (in module scope), sometimes not (in function scope).

And if what you actually need to access or edit is a nonlocal variable you are left with basically no functions to help you out.

A list of other languages that have this feature would be good. So far I know of these:

  • Rust has raw identifiers r#a_name which do not allow for arbitrary strings but do allow usage of language reserved word
  • Swift has a similar mechanism to allow reserved words as identifiers and also chooses backticks: `class`
  • for a dynamic example I believe Perl allows arbitrary strings as symbolic references using ${"This is my name"}
  • R allows seemingly any string as an identifier if it is quoted in backticks. Their operators are even functions named as such: `+`

If anyone knows of other languages, it would be great to get some examples.

Maybe this is a knee-jerk reaction, but I don’t think we need this feature, we can use getattr() and friends for those cases where it’s necessary. Also, I would like to reserve backticks for a much more important feature.

11 Likes

Indeed. Backticks were removed from Python 3.0 syntax with this comment (PEP 3099):

Backticks will no longer be used as shorthand for repr – but that doesn’t mean they are available for other uses. Even ignoring the backwards compatibility confusion, the character itself causes too many problems (in some fonts, on some keyboards, when typesetting a book, etc).

Thread: “new operators via backquoting”,

The rise of Markdown added another reason against backticks, so I think we can rule that particular syntax out. Rust- or Perl-style quoting might be still worth discussing here on Ideas, but IMO it’s getting too close to the verbosity of getattr(obj, "...") etc. to be a win.

(Speaking as myself here – I haven’t consulted with the Steering Council on this)

Jeremiah’s reply that there is no real alternate to local identifier is the only good reason I can see for needing the backtick syntax. I’ve been using Python since 1.5.2, and I’ve only ever needed something like this once in all that time. It wasn’t a local variable, but a class one, and I was able to solve it by using self.dict[“class identifier”]. Assigning to such a variable outside a function is a piece of cake:
assuming a function called ‘ugh’, then “ugh.dict[‘weird variable’]=12345” works fine, but inside the function, it’s different, as locals() is the only way to access the function’s dict inside itself:

>>> def bar():
...  locals()['weird variable']=22
...  print(locals()['weird variable'])
...
>>> bar()
22
>>>

So there’s a use case for backticks, but they’re ugly. There’s another way, of course, and that would be to use the name of the function inside itself. However, that fails:

>>> def blurgh():
...  blurgh.__dict__['weird variable']-'w'
...  print(blurgh.__dict__["weird variable"])
...
>>> blurgh()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in blurgh
KeyError: 'weird variable'
>>>

Even if that worked, it’s also ugly, as well as a pain in the butt.

Looking at the use cases I’d be hard pressed to find any way to apply any of them in my code.

Like this:

the Markdown code

Some code `` order = menu.`spam&eggs` `` between text.

produces the text:

Some code order = menu.`spam&eggs` between text.

Here is the spec.

You have some guts participating in a discussion after the major troll you posted yesterday.

6 Likes

You actually had a typo there, you did subtraction not assignment which is why the lookup failed. It would otherwise print w. But this does not change the locals in the function, it changes the attributes of the function object.

>>> def blurgh():
...     blurgh.__dict__['weird variable']='w'
...     print(blurgh.__dict__["weird variable"])
...     print(locals())
...
>>> getattr(blurgh, 'weird variable', None)
>>> blurgh()
w
{}
>>> getattr(blurgh, 'weird variable', None)
'w'

The other thing that is not possible right now, not even with dunders, is capturing a nonlocal in a closure. Whether explicitly with the keyword or implicitly by being referenced in the function body, there is no real way to find which outer namespace an identifier belongs to, or that it will still exist when you need it.