I had an idea for an abstract representation of the feature Nutchanon described. Let's call it aliasing and let's define it intuitively:
a = 1
b alias a
print(b) -> 1
b = 2
print(a) -> 2
The abstract representation would define a new construct _aliased_identifier_ (aid in short), which will behave exactly as the "normal" identifier behaves now. Now the Python identifier is basically a restricted string literal. The new aliased identifier will be a set of current identifiers. I am not saying it needs to be implemented as a Python set (it could be namespace, tuple, list, etc.), but feature-wise it will be a set - without ordering and with only unique members.
Using Python "repr() style", one can write for example an aid with only one identifier called 'a' as aid('a').
Whenever the code uses an identifier, it will be using the aid which contains the corresponding identifier and technically, writing:
a = 1, will mean under the hood
aid('a') = 1.
The important property of the aid is that it behaves the same as an identifier now, so it can bind to only one object.
The other important property is that the regular identifier can be present in only one aid, because the identifier has to identify this aid in a unique way.
Therefore it is not possible to have aid('a', 'b') and aid('b', 'c'), because 'b' does not identify a unique aid.
b alias a, will translate to:
'b' alias aid('a') => aid('a').add('b') = aid('a', 'b').
while preserving the binding aid('a') had before (if it had any).
after that, whenever the code uses 'a' identifier, or 'b' identifier it will mean the same aid('a', 'b').
aids should behave as the identifiers today, except for the aliasing and unaliasing commands (unaliasing done by 'del' in the example below).
For example, what happens if I do:
a = 1 => aid('a') = 1
b alias a => aid('a', 'b') refers to the original object 1
c = 2 => aid('c') = 2
d alias c => aid('c', 'd') refers to the original object 2
Now what to do when:
b alias c
=> aid('c', 'd'),add('b') = aid('b', 'c', 'd') and refer to the object aid('c', 'd') referred before, i.e. 2
but at the same time 'b' has to be removed from aid('a', 'b'), so we need to define:
del b =>
aid('a', 'b').remove('b') => aid('a'), 'b' does not identify anything anymore, aid('a') still holds the reference.
or if 'b' represents only simple aid('b') then
del b => aid('b').remove('b') => 'b' does not exist anymore
Can I alias an identifier to the item of the list, dict, etc.? No, because a or d['key'] are not identifiers. I can make an alias to 'a' or 'd' though.
The same principle can be applied to types, functions, etc. Running 'dir' should return aids though, not plain identifiers, but since each aid is uniquely identifiable by anyone of its member identifiers, dir could return only identifiers with an assumption that each identifier represents an aid to which it belongs.
I expect that this abstraction can be used to build the behavior of the aids as an analogy to how identifiers behave today. It can also point to the problems, for example do we need to make the aids hashable, or how to make the identifiers in them searchable, etc.
The implementation can be an additional attribute on an identifier, which will list the additional aliases (if any was defined). The interpreter will have to check this attribute and eventually search it for matching identifier (or perform some other search to find the matching aid for a given id).