6

I'll explain with an example:

list_1 = [1, 2, 3]
list_2 = list_3 = list_1 # reference copy

print(magic_method(list_1))
# Should print ['list_1', 'list_2', 'list_3']

set_1 = {'a', 'b'}
print(magic_method(set_1))
# Should print ['set_1']

The requirement: return names of all variables pointing to the same reference. Is this at all possible with python?

I'm thinking something along the lines of iterating over globals() and locals() and equating ids. Is there anything better?

cs95
  • 379,657
  • 97
  • 704
  • 746
  • Are closures of interest as well? – donkopotamus Aug 14 '17 at 10:40
  • @donkopotamus I don't think it needs to be _too_ complicated. I'm considering just global scope for now. – cs95 Aug 14 '17 at 10:41
  • For global scope `globals()` is fine, no need to complicate it. And no need to compare ids when we have `is`. – Ashwini Chaudhary Aug 14 '17 at 10:48
  • Although you are not explicitly writing it, I understand that what you have is `def magic_method(obj): return [name for name, val in globals().items() if val is obj]`, and you'd like something more direct? – jdehesa Aug 14 '17 at 10:52
  • @jdehesa I could not effectively think of a solution, but that seems right. Will you write an answer? I am just looking at any possibilities to solve this. – cs95 Aug 14 '17 at 10:55
  • 1
    What about the references under other containers, classes etc? `list_4 = [list_1, 0]; class A: foo = list_1`. – Ashwini Chaudhary Aug 14 '17 at 10:57
  • @AshwiniChaudhary I was not considering such scenarios, but if you feel that taking those into consideration would improve the quality of this question, I am all for it. However, if it results in a compromise of simplicity, it may not be worth it. What do you say? – cs95 Aug 14 '17 at 10:59
  • If question is only about simple global references then the title should be changed. Also a dupe: [How can I get a list of references of an object in Python?](https://stackoverflow.com/q/4341942/846892) – Ashwini Chaudhary Aug 14 '17 at 11:03
  • @AshwiniChaudhary I'm also curious to know how an extension to the current solutions would be formulated for the container situation you mentioned. Have any ideas? – cs95 Aug 14 '17 at 11:06
  • 1
    A recursive solution based on `gc.get_referrers`? But it is going to be very very tricky and most probably impossible. For example check this: http://ideone.com/rH1keq. `[[1, 2, 3], 0]` and `'list_4': [[1, 2, 3], 0]` are two separate entries, but are actually same references. There `'foo': [1, 2, 3]`, but who contains `foo`? Lastly for slots it simply says: `<__main__.A object at 0x2ae6efcf3ca8>`. – Ashwini Chaudhary Aug 14 '17 at 11:45
  • Check this lib for a basic implementation: https://github.com/mgedmin/objgraph/blob/master/objgraph.py#L417 But its output wasn't what I was expecting for the Ideone code. – Ashwini Chaudhary Aug 14 '17 at 11:47

2 Answers2

5

For global variables you can do:

def magic_method(obj):
    return [name for name, val in globals().items() if val is obj]

If you want local names too, you can use the inspect module:

def magic_method(obj):
    import inspect
    frame = inspect.currentframe()
    try:
        names = [name for name, val in frame.f_back.f_locals.items() if val is obj]
        names += [name for name, val in frame.f_back.f_globals.items()
                  if val is obj and name not in names]
        return names
    finally:
        del frame

And then:

list_1 = [1, 2, 3]
list_2 = list_1

def my_fun():
    list_3 = list_1
    list_2 = list_1
    print(magic_method(list_1))

my_fun()
>>> ['list_3', 'list_1', 'list_2']
jdehesa
  • 58,456
  • 7
  • 77
  • 121
  • @cᴏʟᴅsᴘᴇᴇᴅ I've updated the answer to include local variables too. – jdehesa Aug 14 '17 at 11:12
  • 1
    Note that this only works with local variables from the function that calls `magic_method`. If there are any more functions on the callstack they're simply ignored. You could loop through the callstack until `frame.f_back` returns `None`. – Aran-Fey Aug 14 '17 at 11:16
  • @Rawing Yes, you're right, I assumed only the locals from the direct caller would be of interest, but you could also inspect the whole stack like that. – jdehesa Aug 14 '17 at 11:28
  • if you call the function as magic_method(), will it return all the global methods of the environment? – anitasp Sep 09 '19 at 17:25
  • @anitasp You mean like `magic_method()` without arguments? That wouldn't work, the function requires one argument as it is written. What do you mean by "all the global methods in the environment"? – jdehesa Sep 09 '19 at 17:30
-2

This works:

def magic_method(var):
     names = filter(lambda x: globals()[x] is var, globals().keys())
     return names

is carries out a reference comparison. Add list(...) to the resultant expression if you are using Python3.

cs95
  • 379,657
  • 97
  • 704
  • 746
Radek Hofman
  • 517
  • 3
  • 12