1

Caution: This question is deceptively simple in its wording, but it might require an extra intellectual effort to answer it. Trying to take an approach as in How can I get the name of an object in Python? is not a valid answer (you may want to read it first). That approach via an object is, by definition, ambiguous, since any object could be referenced by several names or even none. The approach recommended requires to reach back to the step of parsing, where objects didn't have any existence yet.

Background: I am exploring how to create constructs in Python that allow us to do away with some redundancies, e.g. when doing unit tests:

y = 5
print("%s: %s" % ('y', y))

It would be better to write:

y = 5    
print(info(y)) => "Value of 'y' is 5"

Note: This could be solved the other way round, by passing a string and then converting it to an id, e.g. print(info('y') using something like globals()['y']. But here we want to go from the id to the string, because there are cases when it it is simpler to do it this way, e.g. when a test routine is passed a list of variables to inspect.

Question: Given an identifier called x in source code, is it possible to write a symbol function that will return the string corresponding to x?

Semantically, if I write

x = [1, 2, 3, 4] # whatever object

It is obvious from the source code that x is the string 'x'.

In other words, is it possible (in standard Python) to write a symbol function (just to coin a name), which given x, considers it as symbol at the exact place where the function is called, and returns the string 'x'?

x = [1, 2, 3, 4] # whatever object
y = x
symbol(x) = 'x'
symbol(y) = 'y'

The answer is unambiguous in this way: x and y were used as symbols in the source code when the function was called, so the result of symbol must be the string equivalent to the symbol ('x' and 'y' respectively).

I realize that this might be a good piece of 2nd degree introspection work (to find the place in source code (AST?) where the function was called and then to reason with it). Yet could it be done with Python as it exists today?

So if the proposed symbol function works, one should be able to write (typically for a unit test):

   x = 5
   y = x
   print("The value of %s is %s", symbol(x), x) => 5
   print("The value of %s is %s", symbol(y), y) => 5

Then the info function could be written as:

def info(y):
    return("%s: %s" % (symbol(y), y))

Note 1: for the sake of generality, the solution should work in standard Python, but answers for other implementations are also acceptable.

Note 2: If you feel that 'symbol' is not the right name for this function or concept, you are welcome to point this out.

Note 3: The is a precise question: given that symbol Y is used in code, write a symbol function, which gives symbol(Y) = 'Y', regardless of object on which the symbol is pointing (class, object, etc.).

10
  • It is a duplicate. The answer to your question lies in the other thread: "The objects itself do not know what names you are using for it". Commented Nov 3, 2017 at 15:29
  • 1
    I'm voting to reopen this question because it's not asking about functions like the selected duplicate, and it has a clear definition of what the expected output is (unlike other "what's the name of this value" questions I've seen). The question is essentially "how can I read the source code that was used to call my function", and that is clearly a different question than "how can I get an object's name". Commented Nov 3, 2017 at 20:43
  • @Rawing Even if it's a different question, it strikes me as pretty broad. Commented Nov 3, 2017 at 23:00
  • 1
    In order to recover this question please show an example of how you intend to use it. I can't come up with a definition for what this does that isn't subject to several reasoning flaws that make the answer useless. Commented Nov 4, 2017 at 2:07
  • 1
    @bfontaine : I reworded my question to make it crystal clear that it is not about the name of an object. Commented Nov 4, 2017 at 7:45

2 Answers 2

2

This can be done by reading the source code that calls the symbol function:

import inspect
import re

def symbol(arg):
    frame = inspect.currentframe().f_back
    frameinfo = inspect.getouterframes(frame)[0]
    calling_code = frameinfo[4][frameinfo[5]].strip()
    del frame

    # this adds support for multiple calls in the same line
    matches = re.findall(r'\bsymbol\((\w+)\)', calling_code)
    match = matches[symbol.call_count]
    symbol.call_count += 1
    if symbol.call_count == len(matches):
        symbol.call_count = 0
    return match

symbol.call_count = 0
x = 3
print(symbol(x)) # output: x

Caveats:

  • Doesn't work in an interactive session or in any other scenario where the source code cannot be read
  • Can get tripped up by strings:

    fake = "symbol(fake)"; print(symbol(5))
    # output: fake
    
Sign up to request clarification or add additional context in comments.

4 Comments

Thanks a lot, this was fast and impressive! I guess there migh be a way to improve on the parsing of the calling code to avoid your second caveat?
@fralau I think there are only hackish ways to work around the 2nd caveat. AFAIK there's no way to find the calling code any more precisely than "it's somewhere in this line". The only workaround I can think of is to maintain a counter how often symbol has been called - when it's called the first time, return "symbol(1)". When it's called the 2nd time, return "symbol(2)" and reset the counter.
Nice! sys._getframe and inspect were my next things to investigate.
@Rawing: thanks for your help: it seems I have been spending unncessary time explaining my question, while you understood it at once!
0

Not sure if I got the question right, but here's an example that searches for symbols in [Python]: globals():

import sys

x = [1, 2, 33]


def dummy():
    pass


def symbol(obj):
    globals_dict = globals()
    for name in globals_dict:
        if obj is globals_dict[name]:
            return name


def main():
    for obj in [sys, x, dummy, symbol, main]:
        obj_name = symbol(obj)
        print(obj_name, type(obj_name))


if __name__ == "__main__":
    main()

Output:

E:\Work\Dev\StackOverflow\q47091183>"E:\WinOBT\1.0.0.0\OPSWpython\2.7.10__00\x86\python.exe" a.py
('sys', <type 'str'>)
('x', <type 'str'>)
('dummy', <type 'str'>)
('symbol', <type 'str'>)
('main', <type 'str'>)

3 Comments

Thanks a lot and this is getting the thing started. The problem I wish to solve is that if both x and y refer to the same string, the function can only determine which one I really want by looking at the source code (since that's where I said that I wanted x => 'x').
I imagined that it shouldn't be that simple. So what would symbol(y) from your code return? Also, how do you tell the function which object you really want?
I understand. The answer is to be found in the source code: symbol(y) => 'y'. The content of the object is not relevant.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.