In Python, is there a way for an instance of an object to see the variable name it's assigned to? Take the following for example:
class MyObject(object):
pass
x = MyObject()
Is it possible for MyObject to see it's been assigned to a variable name x at any point? Like in it's __init__ method?
Yes, it is possible*. However, the problem is more difficult than it seems upon first glance:
There may be multiple names assigned to the same object.
There may be no names at all.
The same name(s) may refer to some other object(s) in a different namespace.
Regardless, knowing how to find the names of an object can sometimes be useful for debugging purposes - and here is how to do it:
import gc, inspect
def find_names(obj):
frame = inspect.currentframe()
for frame in iter(lambda: frame.f_back, None):
frame.f_locals
obj_names = []
for referrer in gc.get_referrers(obj):
if isinstance(referrer, dict):
for k, v in referrer.items():
if v is obj:
obj_names.append(k)
return obj_names
If you're ever tempted to base logic around the names of your variables, pause for a moment and consider if redesign/refactor of code could solve the problem. The need to recover an object's name from the object itself usually means that underlying data structures in your program need a rethink.
* at least in Cpython
As many others have said, it can't be done properly. However inspired by jsbueno's, I have an alternative to his solution.
Like his solution, I inspect the callers stack frame, which means it only works properly for Python-implemented callers (see note below). Unlike him, I inspect the bytecode of the caller directly (instead of loading and parsing the source code). Using Python 3.4+'s dis.get_instructions() this can be done with some hope of minimal compatibility. Though this is still some hacky code.
import inspect
import dis
def take1(iterator):
try:
return next(iterator)
except StopIteration:
raise Exception("missing bytecode instruction") from None
def take(iterator, count):
for x in range(count):
yield take1(iterator)
def get_assigned_name(frame):
"""Takes a frame and returns a description of the name(s) to which the
currently executing CALL_FUNCTION instruction's value will be assigned.
fn() => None
a = fn() => "a"
a, b = fn() => ("a", "b")
a.a2.a3, b, c* = fn() => ("a.a2.a3", "b", Ellipsis)
"""
iterator = iter(dis.get_instructions(frame.f_code))
for instr in iterator:
if instr.offset == frame.f_lasti:
break
else:
assert False, "bytecode instruction missing"
assert instr.opname.startswith('CALL_')
instr = take1(iterator)
if instr.opname == 'POP_TOP':
raise ValueError("not assigned to variable")
return instr_dispatch(instr, iterator)
def instr_dispatch(instr, iterator):
opname = instr.opname
if (opname == 'STORE_FAST' # (co_varnames)
or opname == 'STORE_GLOBAL' # (co_names)
or opname == 'STORE_NAME' # (co_names)
or opname == 'STORE_DEREF'): # (co_cellvars++co_freevars)
return instr.argval
if opname == 'UNPACK_SEQUENCE':
return tuple(instr_dispatch(instr, iterator)
for instr in take(iterator, instr.arg))
if opname == 'UNPACK_EX':
return (*tuple(instr_dispatch(instr, iterator)
for instr in take(iterator, instr.arg)),
Ellipsis)
# Note: 'STORE_SUBSCR' and 'STORE_ATTR' should not be possible here.
# `lhs = rhs` in Python will evaluate `lhs` after `rhs`.
# Thus `x.attr = rhs` will first evalute `rhs` then load `a` and finally
# `STORE_ATTR` with `attr` as instruction argument. `a` can be any
# complex expression, so full support for understanding what a
# `STORE_ATTR` will target requires decoding the full range of expression-
# related bytecode instructions. Even figuring out which `STORE_ATTR`
# will use our return value requires non-trivial understanding of all
# expression-related bytecode instructions.
# Thus we limit ourselfs to loading a simply variable (of any kind)
# and a arbitary number of LOAD_ATTR calls before the final STORE_ATTR.
# We will represents simply a string like `my_var.loaded.loaded.assigned`
if opname in {'LOAD_CONST', 'LOAD_DEREF', 'LOAD_FAST',
'LOAD_GLOBAL', 'LOAD_NAME'}:
return instr.argval + "." + ".".join(
instr_dispatch_for_load(instr, iterator))
raise NotImplementedError("assignment could not be parsed: "
"instruction {} not understood"
.format(instr))
def instr_dispatch_for_load(instr, iterator):
instr = take1(iterator)
opname = instr.opname
if opname == 'LOAD_ATTR':
yield instr.argval
yield from instr_dispatch_for_load(instr, iterator)
elif opname == 'STORE_ATTR':
yield instr.argval
else:
raise NotImplementedError("assignment could not be parsed: "
"instruction {} not understood"
.format(instr))
Note: C-implemented functions don't show up as Python stack frames and are thus hidden to this script. This will result in false positives. Consider Python function f() which calls a = g(). g() is C-implemented and calls b = f2(). When f2() tries to lookup up the assigned name, it will get a instead of b because the script is oblivious to C functions. (At least this is how I guess it will work :P )
Usage example:
class MyItem():
def __init__(self):
self.name = get_assigned_name(inspect.currentframe().f_back)
abc = MyItem()
assert abc.name == "abc"
No. Objects and names live in separate dimensions. One object can have many names during its lifetime, and it's impossible to determine which one might be the one you want. Even in here:
class Foo(object):
def __init__(self): pass
x = Foo()
two names denote the same object (self when __init__ runs, x in global scope).
Here is a simple function to achieve what you want, assuming you wish to retrieve the name of the variable where the instance is assigned from a method call :
import inspect
def get_instance_var_name(method_frame, instance):
parent_frame = method_frame.f_back
matches = {k: v for k,v in parent_frame.f_globals.items() if v is instance}
assert len(matches) < 2
return list(matches.keys())[0] if matches else None
Here is an usage example :
class Bar:
def foo(self):
print(get_instance_var_name(inspect.currentframe(), self))
bar = Bar()
bar.foo() # prints 'bar'
def nested():
bar.foo()
nested() # prints 'bar'
Bar().foo() # prints None
It can't be ordinarily done, though this can be achieved by using introspection and facilities meant for debugging a program. The code must run from a ".py" file though, and not from just compiled bytecode, or inside a zipped module - as it relies on the reading of the file source code, from within the method that should find about "where it is running".
The trick is to access the execution frame where the object was initialized from - with inspect.currentframe - the frame object has a "f_lineno" value which states the line number where the call to the object method (in this case, __init__) has been called. The function inspect.filename allows one to retrieve the source code for the file, and fetch the apropriate line number.
A naive parse then peek the part preeceding an "=" sign, and assumes it is the variable that will contain the object.
from inspect import currentframe, getfile
class A(object):
def __init__(self):
f = currentframe(1)
filename = getfile(f)
code_line = open(filename).readlines()[f.f_lineno - 1]
assigned_variable = code_line.split("=")[0].strip()
print assigned_variable
my_name = A()
other_name = A()
That won't work for multiple assignents, expressions composing with the object before the assignemtn is made, objects being appended to lists or added to dictionaries or sets, object instantiation in intialization of for loops, and God knows which more situations --
And have in mind that after the first attribution, the object could be referenced by any other variable as well.
Botton line: it is possible, but as a toy - it can't be used i production code -
just have the varibal name to be passed as a string during object initialization, just as one has to do when creating a collections.namedtuple
The "right way" to do it, if you are needing the name, is to explicitly pass the name to the object initialization, as a string parameter, like in:
class A(object):
def __init__(self, name):
self.name = name
x = A("x")
And still, if absolutely need to type the objects'name only once, there is another way - read on.
Due to Python's syntax, some special assignments, not using the "=" operator do allow an object to know it is assigned name. So, other statemtns that perform assignents in Python are the for, with, def and class keywords - It is possible to abuse this, as specfically a class creation and a function definition are assignment statements that create objects which "know" their names.
Let's focus on the def statement. It ordinarily creates a function. But using a decorator you can use "def" to create any kind of object - and have the name used for the function available to the constructor:
class MyObject(object):
def __new__(cls, func):
# Calls the superclass constructor and actually instantiates the object:
self = object.__new__(cls)
#retrieve the function name:
self.name = func.func_name
#returns an instance of this class, instead of a decorated function:
return self
def __init__(self, func):
print "My name is ", self.name
#and the catch is that you can't use "=" to create this object, you have to do:
#MyObject
def my_name(): pass
(This last way of doing it could be used in production code, unlike the one which resorts to reading the source file)
assuming this:
class MyObject(object):
pass
x = MyObject()
then you can search through the environment by the object's id, returning the key when there is a match.
keys = list(globals().keys()) # list all variable names
target = id(x) # find the id of your object
for k in keys:
value_memory_address = id(globals()[k]) # fetch id of every object
if value_memory_address == target:
print(globals()[k], k) # if there is a variable assigned to that id, then it is a variable that points to your object
I was independently working on this and have the following. It's not as comprehensive as driax's answer, but efficiently covers the case described and doesn't rely on searching for the object's id in global variables or parsing source code...
import sys
import dis
class MyObject:
def __init__(self):
# uses bytecode magic to find the name of the assigned variable
f = sys._getframe(1) # get stack frame of caller (depth=1)
# next op should be STORE_NAME (current op calls the constructor)
opname = dis.opname[f.f_code.co_code[f.f_lasti+2]]
if opname == 'STORE_NAME': # not all objects will be assigned a name
# STORE_NAME argument is the name index
namei = f.f_code.co_code[f.f_lasti+3]
self.name = f.f_code.co_names[namei]
else:
self.name = None
x = MyObject()
x.name == 'x'
Related
In Python, is there a way for an instance of an object to see the variable name it's assigned to? Take the following for example:
class MyObject(object):
pass
x = MyObject()
Is it possible for MyObject to see it's been assigned to a variable name x at any point? Like in it's __init__ method?
Yes, it is possible*. However, the problem is more difficult than it seems upon first glance:
There may be multiple names assigned to the same object.
There may be no names at all.
The same name(s) may refer to some other object(s) in a different namespace.
Regardless, knowing how to find the names of an object can sometimes be useful for debugging purposes - and here is how to do it:
import gc, inspect
def find_names(obj):
frame = inspect.currentframe()
for frame in iter(lambda: frame.f_back, None):
frame.f_locals
obj_names = []
for referrer in gc.get_referrers(obj):
if isinstance(referrer, dict):
for k, v in referrer.items():
if v is obj:
obj_names.append(k)
return obj_names
If you're ever tempted to base logic around the names of your variables, pause for a moment and consider if redesign/refactor of code could solve the problem. The need to recover an object's name from the object itself usually means that underlying data structures in your program need a rethink.
* at least in Cpython
As many others have said, it can't be done properly. However inspired by jsbueno's, I have an alternative to his solution.
Like his solution, I inspect the callers stack frame, which means it only works properly for Python-implemented callers (see note below). Unlike him, I inspect the bytecode of the caller directly (instead of loading and parsing the source code). Using Python 3.4+'s dis.get_instructions() this can be done with some hope of minimal compatibility. Though this is still some hacky code.
import inspect
import dis
def take1(iterator):
try:
return next(iterator)
except StopIteration:
raise Exception("missing bytecode instruction") from None
def take(iterator, count):
for x in range(count):
yield take1(iterator)
def get_assigned_name(frame):
"""Takes a frame and returns a description of the name(s) to which the
currently executing CALL_FUNCTION instruction's value will be assigned.
fn() => None
a = fn() => "a"
a, b = fn() => ("a", "b")
a.a2.a3, b, c* = fn() => ("a.a2.a3", "b", Ellipsis)
"""
iterator = iter(dis.get_instructions(frame.f_code))
for instr in iterator:
if instr.offset == frame.f_lasti:
break
else:
assert False, "bytecode instruction missing"
assert instr.opname.startswith('CALL_')
instr = take1(iterator)
if instr.opname == 'POP_TOP':
raise ValueError("not assigned to variable")
return instr_dispatch(instr, iterator)
def instr_dispatch(instr, iterator):
opname = instr.opname
if (opname == 'STORE_FAST' # (co_varnames)
or opname == 'STORE_GLOBAL' # (co_names)
or opname == 'STORE_NAME' # (co_names)
or opname == 'STORE_DEREF'): # (co_cellvars++co_freevars)
return instr.argval
if opname == 'UNPACK_SEQUENCE':
return tuple(instr_dispatch(instr, iterator)
for instr in take(iterator, instr.arg))
if opname == 'UNPACK_EX':
return (*tuple(instr_dispatch(instr, iterator)
for instr in take(iterator, instr.arg)),
Ellipsis)
# Note: 'STORE_SUBSCR' and 'STORE_ATTR' should not be possible here.
# `lhs = rhs` in Python will evaluate `lhs` after `rhs`.
# Thus `x.attr = rhs` will first evalute `rhs` then load `a` and finally
# `STORE_ATTR` with `attr` as instruction argument. `a` can be any
# complex expression, so full support for understanding what a
# `STORE_ATTR` will target requires decoding the full range of expression-
# related bytecode instructions. Even figuring out which `STORE_ATTR`
# will use our return value requires non-trivial understanding of all
# expression-related bytecode instructions.
# Thus we limit ourselfs to loading a simply variable (of any kind)
# and a arbitary number of LOAD_ATTR calls before the final STORE_ATTR.
# We will represents simply a string like `my_var.loaded.loaded.assigned`
if opname in {'LOAD_CONST', 'LOAD_DEREF', 'LOAD_FAST',
'LOAD_GLOBAL', 'LOAD_NAME'}:
return instr.argval + "." + ".".join(
instr_dispatch_for_load(instr, iterator))
raise NotImplementedError("assignment could not be parsed: "
"instruction {} not understood"
.format(instr))
def instr_dispatch_for_load(instr, iterator):
instr = take1(iterator)
opname = instr.opname
if opname == 'LOAD_ATTR':
yield instr.argval
yield from instr_dispatch_for_load(instr, iterator)
elif opname == 'STORE_ATTR':
yield instr.argval
else:
raise NotImplementedError("assignment could not be parsed: "
"instruction {} not understood"
.format(instr))
Note: C-implemented functions don't show up as Python stack frames and are thus hidden to this script. This will result in false positives. Consider Python function f() which calls a = g(). g() is C-implemented and calls b = f2(). When f2() tries to lookup up the assigned name, it will get a instead of b because the script is oblivious to C functions. (At least this is how I guess it will work :P )
Usage example:
class MyItem():
def __init__(self):
self.name = get_assigned_name(inspect.currentframe().f_back)
abc = MyItem()
assert abc.name == "abc"
No. Objects and names live in separate dimensions. One object can have many names during its lifetime, and it's impossible to determine which one might be the one you want. Even in here:
class Foo(object):
def __init__(self): pass
x = Foo()
two names denote the same object (self when __init__ runs, x in global scope).
Here is a simple function to achieve what you want, assuming you wish to retrieve the name of the variable where the instance is assigned from a method call :
import inspect
def get_instance_var_name(method_frame, instance):
parent_frame = method_frame.f_back
matches = {k: v for k,v in parent_frame.f_globals.items() if v is instance}
assert len(matches) < 2
return list(matches.keys())[0] if matches else None
Here is an usage example :
class Bar:
def foo(self):
print(get_instance_var_name(inspect.currentframe(), self))
bar = Bar()
bar.foo() # prints 'bar'
def nested():
bar.foo()
nested() # prints 'bar'
Bar().foo() # prints None
It can't be ordinarily done, though this can be achieved by using introspection and facilities meant for debugging a program. The code must run from a ".py" file though, and not from just compiled bytecode, or inside a zipped module - as it relies on the reading of the file source code, from within the method that should find about "where it is running".
The trick is to access the execution frame where the object was initialized from - with inspect.currentframe - the frame object has a "f_lineno" value which states the line number where the call to the object method (in this case, __init__) has been called. The function inspect.filename allows one to retrieve the source code for the file, and fetch the apropriate line number.
A naive parse then peek the part preeceding an "=" sign, and assumes it is the variable that will contain the object.
from inspect import currentframe, getfile
class A(object):
def __init__(self):
f = currentframe(1)
filename = getfile(f)
code_line = open(filename).readlines()[f.f_lineno - 1]
assigned_variable = code_line.split("=")[0].strip()
print assigned_variable
my_name = A()
other_name = A()
That won't work for multiple assignents, expressions composing with the object before the assignemtn is made, objects being appended to lists or added to dictionaries or sets, object instantiation in intialization of for loops, and God knows which more situations --
And have in mind that after the first attribution, the object could be referenced by any other variable as well.
Botton line: it is possible, but as a toy - it can't be used i production code -
just have the varibal name to be passed as a string during object initialization, just as one has to do when creating a collections.namedtuple
The "right way" to do it, if you are needing the name, is to explicitly pass the name to the object initialization, as a string parameter, like in:
class A(object):
def __init__(self, name):
self.name = name
x = A("x")
And still, if absolutely need to type the objects'name only once, there is another way - read on.
Due to Python's syntax, some special assignments, not using the "=" operator do allow an object to know it is assigned name. So, other statemtns that perform assignents in Python are the for, with, def and class keywords - It is possible to abuse this, as specfically a class creation and a function definition are assignment statements that create objects which "know" their names.
Let's focus on the def statement. It ordinarily creates a function. But using a decorator you can use "def" to create any kind of object - and have the name used for the function available to the constructor:
class MyObject(object):
def __new__(cls, func):
# Calls the superclass constructor and actually instantiates the object:
self = object.__new__(cls)
#retrieve the function name:
self.name = func.func_name
#returns an instance of this class, instead of a decorated function:
return self
def __init__(self, func):
print "My name is ", self.name
#and the catch is that you can't use "=" to create this object, you have to do:
#MyObject
def my_name(): pass
(This last way of doing it could be used in production code, unlike the one which resorts to reading the source file)
assuming this:
class MyObject(object):
pass
x = MyObject()
then you can search through the environment by the object's id, returning the key when there is a match.
keys = list(globals().keys()) # list all variable names
target = id(x) # find the id of your object
for k in keys:
value_memory_address = id(globals()[k]) # fetch id of every object
if value_memory_address == target:
print(globals()[k], k) # if there is a variable assigned to that id, then it is a variable that points to your object
I was independently working on this and have the following. It's not as comprehensive as driax's answer, but efficiently covers the case described and doesn't rely on searching for the object's id in global variables or parsing source code...
import sys
import dis
class MyObject:
def __init__(self):
# uses bytecode magic to find the name of the assigned variable
f = sys._getframe(1) # get stack frame of caller (depth=1)
# next op should be STORE_NAME (current op calls the constructor)
opname = dis.opname[f.f_code.co_code[f.f_lasti+2]]
if opname == 'STORE_NAME': # not all objects will be assigned a name
# STORE_NAME argument is the name index
namei = f.f_code.co_code[f.f_lasti+3]
self.name = f.f_code.co_names[namei]
else:
self.name = None
x = MyObject()
x.name == 'x'
code = "def foo(): return 'bar'"
def lol(code):
exec code
return foo
a = lol(code)
print a()
This works normally, but the problem starts when we don't know what the function in the string is called. If I can guarantee that the code will be small, with a single function, how can I return that function?
One solution I thought of was just requiring the function be called 'foo' etc, so I can just return that, but it feels ugly.
Thoughts?
You could do it by explicitly specifying dictionaries exec should use for the global and local execution context. Afterwards the one used for locals should have a single entry for the function object, which can be returned without knowing its name since it should be the only item defined in the dictionary:
from textwrap import dedent
import types
def lol(code):
globals_ = {"__builtins__": None} # no built-ins for safety
locals_ = {}
exec(code, globals_, locals_)
if len(locals_) != 1:
raise ValueError("code didn't define exactly one item")
value = locals_.popitem()[1] # get value of the one item defined
if type(value) != types.FunctionType:
raise ValueError("code didn't define a function")
return value # return function object that was defined
my_code = dedent("""
def foo():
return 'bar'
""")
a = lol(my_code)
print(a())
In Python, is there a way for an instance of an object to see the variable name it's assigned to? Take the following for example:
class MyObject(object):
pass
x = MyObject()
Is it possible for MyObject to see it's been assigned to a variable name x at any point? Like in it's __init__ method?
Yes, it is possible*. However, the problem is more difficult than it seems upon first glance:
There may be multiple names assigned to the same object.
There may be no names at all.
The same name(s) may refer to some other object(s) in a different namespace.
Regardless, knowing how to find the names of an object can sometimes be useful for debugging purposes - and here is how to do it:
import gc, inspect
def find_names(obj):
frame = inspect.currentframe()
for frame in iter(lambda: frame.f_back, None):
frame.f_locals
obj_names = []
for referrer in gc.get_referrers(obj):
if isinstance(referrer, dict):
for k, v in referrer.items():
if v is obj:
obj_names.append(k)
return obj_names
If you're ever tempted to base logic around the names of your variables, pause for a moment and consider if redesign/refactor of code could solve the problem. The need to recover an object's name from the object itself usually means that underlying data structures in your program need a rethink.
* at least in Cpython
As many others have said, it can't be done properly. However inspired by jsbueno's, I have an alternative to his solution.
Like his solution, I inspect the callers stack frame, which means it only works properly for Python-implemented callers (see note below). Unlike him, I inspect the bytecode of the caller directly (instead of loading and parsing the source code). Using Python 3.4+'s dis.get_instructions() this can be done with some hope of minimal compatibility. Though this is still some hacky code.
import inspect
import dis
def take1(iterator):
try:
return next(iterator)
except StopIteration:
raise Exception("missing bytecode instruction") from None
def take(iterator, count):
for x in range(count):
yield take1(iterator)
def get_assigned_name(frame):
"""Takes a frame and returns a description of the name(s) to which the
currently executing CALL_FUNCTION instruction's value will be assigned.
fn() => None
a = fn() => "a"
a, b = fn() => ("a", "b")
a.a2.a3, b, c* = fn() => ("a.a2.a3", "b", Ellipsis)
"""
iterator = iter(dis.get_instructions(frame.f_code))
for instr in iterator:
if instr.offset == frame.f_lasti:
break
else:
assert False, "bytecode instruction missing"
assert instr.opname.startswith('CALL_')
instr = take1(iterator)
if instr.opname == 'POP_TOP':
raise ValueError("not assigned to variable")
return instr_dispatch(instr, iterator)
def instr_dispatch(instr, iterator):
opname = instr.opname
if (opname == 'STORE_FAST' # (co_varnames)
or opname == 'STORE_GLOBAL' # (co_names)
or opname == 'STORE_NAME' # (co_names)
or opname == 'STORE_DEREF'): # (co_cellvars++co_freevars)
return instr.argval
if opname == 'UNPACK_SEQUENCE':
return tuple(instr_dispatch(instr, iterator)
for instr in take(iterator, instr.arg))
if opname == 'UNPACK_EX':
return (*tuple(instr_dispatch(instr, iterator)
for instr in take(iterator, instr.arg)),
Ellipsis)
# Note: 'STORE_SUBSCR' and 'STORE_ATTR' should not be possible here.
# `lhs = rhs` in Python will evaluate `lhs` after `rhs`.
# Thus `x.attr = rhs` will first evalute `rhs` then load `a` and finally
# `STORE_ATTR` with `attr` as instruction argument. `a` can be any
# complex expression, so full support for understanding what a
# `STORE_ATTR` will target requires decoding the full range of expression-
# related bytecode instructions. Even figuring out which `STORE_ATTR`
# will use our return value requires non-trivial understanding of all
# expression-related bytecode instructions.
# Thus we limit ourselfs to loading a simply variable (of any kind)
# and a arbitary number of LOAD_ATTR calls before the final STORE_ATTR.
# We will represents simply a string like `my_var.loaded.loaded.assigned`
if opname in {'LOAD_CONST', 'LOAD_DEREF', 'LOAD_FAST',
'LOAD_GLOBAL', 'LOAD_NAME'}:
return instr.argval + "." + ".".join(
instr_dispatch_for_load(instr, iterator))
raise NotImplementedError("assignment could not be parsed: "
"instruction {} not understood"
.format(instr))
def instr_dispatch_for_load(instr, iterator):
instr = take1(iterator)
opname = instr.opname
if opname == 'LOAD_ATTR':
yield instr.argval
yield from instr_dispatch_for_load(instr, iterator)
elif opname == 'STORE_ATTR':
yield instr.argval
else:
raise NotImplementedError("assignment could not be parsed: "
"instruction {} not understood"
.format(instr))
Note: C-implemented functions don't show up as Python stack frames and are thus hidden to this script. This will result in false positives. Consider Python function f() which calls a = g(). g() is C-implemented and calls b = f2(). When f2() tries to lookup up the assigned name, it will get a instead of b because the script is oblivious to C functions. (At least this is how I guess it will work :P )
Usage example:
class MyItem():
def __init__(self):
self.name = get_assigned_name(inspect.currentframe().f_back)
abc = MyItem()
assert abc.name == "abc"
No. Objects and names live in separate dimensions. One object can have many names during its lifetime, and it's impossible to determine which one might be the one you want. Even in here:
class Foo(object):
def __init__(self): pass
x = Foo()
two names denote the same object (self when __init__ runs, x in global scope).
Here is a simple function to achieve what you want, assuming you wish to retrieve the name of the variable where the instance is assigned from a method call :
import inspect
def get_instance_var_name(method_frame, instance):
parent_frame = method_frame.f_back
matches = {k: v for k,v in parent_frame.f_globals.items() if v is instance}
assert len(matches) < 2
return list(matches.keys())[0] if matches else None
Here is an usage example :
class Bar:
def foo(self):
print(get_instance_var_name(inspect.currentframe(), self))
bar = Bar()
bar.foo() # prints 'bar'
def nested():
bar.foo()
nested() # prints 'bar'
Bar().foo() # prints None
It can't be ordinarily done, though this can be achieved by using introspection and facilities meant for debugging a program. The code must run from a ".py" file though, and not from just compiled bytecode, or inside a zipped module - as it relies on the reading of the file source code, from within the method that should find about "where it is running".
The trick is to access the execution frame where the object was initialized from - with inspect.currentframe - the frame object has a "f_lineno" value which states the line number where the call to the object method (in this case, __init__) has been called. The function inspect.filename allows one to retrieve the source code for the file, and fetch the apropriate line number.
A naive parse then peek the part preeceding an "=" sign, and assumes it is the variable that will contain the object.
from inspect import currentframe, getfile
class A(object):
def __init__(self):
f = currentframe(1)
filename = getfile(f)
code_line = open(filename).readlines()[f.f_lineno - 1]
assigned_variable = code_line.split("=")[0].strip()
print assigned_variable
my_name = A()
other_name = A()
That won't work for multiple assignents, expressions composing with the object before the assignemtn is made, objects being appended to lists or added to dictionaries or sets, object instantiation in intialization of for loops, and God knows which more situations --
And have in mind that after the first attribution, the object could be referenced by any other variable as well.
Botton line: it is possible, but as a toy - it can't be used i production code -
just have the varibal name to be passed as a string during object initialization, just as one has to do when creating a collections.namedtuple
The "right way" to do it, if you are needing the name, is to explicitly pass the name to the object initialization, as a string parameter, like in:
class A(object):
def __init__(self, name):
self.name = name
x = A("x")
And still, if absolutely need to type the objects'name only once, there is another way - read on.
Due to Python's syntax, some special assignments, not using the "=" operator do allow an object to know it is assigned name. So, other statemtns that perform assignents in Python are the for, with, def and class keywords - It is possible to abuse this, as specfically a class creation and a function definition are assignment statements that create objects which "know" their names.
Let's focus on the def statement. It ordinarily creates a function. But using a decorator you can use "def" to create any kind of object - and have the name used for the function available to the constructor:
class MyObject(object):
def __new__(cls, func):
# Calls the superclass constructor and actually instantiates the object:
self = object.__new__(cls)
#retrieve the function name:
self.name = func.func_name
#returns an instance of this class, instead of a decorated function:
return self
def __init__(self, func):
print "My name is ", self.name
#and the catch is that you can't use "=" to create this object, you have to do:
#MyObject
def my_name(): pass
(This last way of doing it could be used in production code, unlike the one which resorts to reading the source file)
assuming this:
class MyObject(object):
pass
x = MyObject()
then you can search through the environment by the object's id, returning the key when there is a match.
keys = list(globals().keys()) # list all variable names
target = id(x) # find the id of your object
for k in keys:
value_memory_address = id(globals()[k]) # fetch id of every object
if value_memory_address == target:
print(globals()[k], k) # if there is a variable assigned to that id, then it is a variable that points to your object
I was independently working on this and have the following. It's not as comprehensive as driax's answer, but efficiently covers the case described and doesn't rely on searching for the object's id in global variables or parsing source code...
import sys
import dis
class MyObject:
def __init__(self):
# uses bytecode magic to find the name of the assigned variable
f = sys._getframe(1) # get stack frame of caller (depth=1)
# next op should be STORE_NAME (current op calls the constructor)
opname = dis.opname[f.f_code.co_code[f.f_lasti+2]]
if opname == 'STORE_NAME': # not all objects will be assigned a name
# STORE_NAME argument is the name index
namei = f.f_code.co_code[f.f_lasti+3]
self.name = f.f_code.co_names[namei]
else:
self.name = None
x = MyObject()
x.name == 'x'
I'm developing a documentation testing framework -- basically unit tests for PDFs. Tests are (decorated) methods of instances of classes defined by the framework, and these are located and instantiated at runtime and the methods are invoked to execute the tests.
My goal is to cut down on the amount of quirky Python syntax that the people who will write tests need to be concerned about, as these people may or may not be Python programmers, or even very much programmers at all. So I would like them to be able to write "def foo():" instead of "def foo(self):" for methods, but still be able to use "self" to access members.
In an ordinary program I would consider this a horrible idea, but in a domain-specific-languagey kind of program like this one, it seems worth a try.
I have successfully eliminated the self from the method signature by using a decorator (actually, since I am using a decorator already for the test cases, I would just roll it into that), but "self" does not then refer to anything in the test case method.
I have considered using a global for self, and even come up with an implementation that more or less works, but I'd rather pollute the smallest namespace possible, which is why I would prefer to inject the variable directly into the test case method's local namespace. Any thoughts?
My accepted answer to this question was pretty dumb but I was just starting out. Here's a much better way. This is only scantily tested but it's good for a demonstration of the proper way to do this thing which is improper to do. It works on 2.6.5 for sure. I haven't tested any other versions but no opcodes are hardcoded into it so it should be about as portable as most other 2.x code.
add_self can be applied as a decorator but that would defeat the purpose (why not just type 'self'?) It would be easy to adapt the metaclass from my other answer to apply this function instead.
import opcode
import types
def instructions(code):
"""Iterates over a code string yielding integer [op, arg] pairs
If the opcode does not take an argument, just put None in the second part
"""
code = map(ord, code)
i, L = 0, len(code)
extended_arg = 0
while i < L:
op = code[i]
i+= 1
if op < opcode.HAVE_ARGUMENT:
yield [op, None]
continue
oparg = code[i] + (code[i+1] << 8) + extended_arg
extended_arg = 0
i += 2
if op == opcode.EXTENDED_ARG:
extended_arg = oparg << 16
continue
yield [op, oparg]
def write_instruction(inst):
"""Takes an integer [op, arg] pair and returns a list of character bytecodes"""
op, oparg = inst
if oparg is None:
return [chr(op)]
elif oparg <= 65536L:
return [chr(op), chr(oparg & 255), chr((oparg >> 8) & 255)]
elif oparg <= 4294967296L:
# The argument is large enough to need 4 bytes and the EXTENDED_ARG opcode
return [chr(opcode.EXTENDED_ARG),
chr((oparg >> 16) & 255),
chr((oparg >> 24) & 255),
chr(op),
chr(oparg & 255),
chr((oparg >> 8) & 255)]
else:
raise ValueError("Invalid oparg: {0} is too large".format(oparg))
def add_self(f):
"""Add self to a method
Creates a new function by prepending the name 'self' to co_varnames, and
incrementing co_argcount and co_nlocals. Increase the index of all other locals
by 1 to compensate. Also removes 'self' from co_names and decrease the index of
all names that occur after it by 1. Finally, replace all occurrences of
`LOAD_GLOBAL i,j` that make reference to the old 'self' with 'LOAD_FAST 0,0'.
Essentially, just create a code object that is exactly the same but has one more
argument.
"""
code_obj = f.func_code
try:
self_index = code_obj.co_names.index('self')
except ValueError:
raise NotImplementedError("self is not a global")
# The arguments are just the first co_argcount co_varnames
varnames = ('self', ) + code_obj.co_varnames
names = tuple(name for name in code_obj.co_names if name != 'self')
code = []
for inst in instructions(code_obj.co_code):
op = inst[0]
if op in opcode.haslocal:
# The index is now one greater because we added 'self' at the head of
# the tuple
inst[1] += 1
elif op in opcode.hasname:
arg = inst[1]
if arg == self_index:
# This refers to the old global 'self'
if op == opcode.opmap['LOAD_GLOBAL']:
inst[0] = opcode.opmap['LOAD_FAST']
inst[1] = 0
else:
# If `self` is used as an attribute, real global, module
# name, module attribute, or gets looked at funny, bail out.
raise NotImplementedError("Abnormal use of self")
elif arg > self_index:
# This rewrites the index to account for the old global 'self'
# having been removed.
inst[1] -= 1
code += write_instruction(inst)
code = ''.join(code)
# type help(types.CodeType) at the interpreter prompt for this one
new_code_obj = types.CodeType(code_obj.co_argcount + 1,
code_obj.co_nlocals + 1,
code_obj.co_stacksize,
code_obj.co_flags,
code,
code_obj.co_consts,
names,
varnames,
'<OpcodeCity>',
code_obj.co_name,
code_obj.co_firstlineno,
code_obj.co_lnotab,
code_obj.co_freevars,
code_obj.co_cellvars)
# help(types.FunctionType)
return types.FunctionType(new_code_obj, f.func_globals)
class Test(object):
msg = 'Foo'
#add_self
def show(msg):
print self.msg + msg
t = Test()
t.show('Bar')
little upgrade for aaronasterling's solution( i haven't enough reputation to comment it ):
def wrap(f):
#functools.wraps(f)
def wrapper(self,*arg,**kw):
f.func_globals['self'] = self
return f(*arg,**kw)
return wrapper
but both this solutions will work unpredictable if f function will be called recursively for different instance, so you have to clone it like this:
import types
class wrap(object):
def __init__(self,func):
self.func = func
def __get__(self,obj,type):
new_globals = self.func.func_globals.copy()
new_globals['self'] = obj
return types.FunctionType(self.func.func_code,new_globals)
class C(object):
def __init__(self,word):
self.greeting = word
#wrap
def greet(name):
print(self.greeting+' , ' + name+ '!')
C('Hello').greet('kindall')
Here's a one line method decorator that seems to do the job without modifying any Special attributes of Callable types* marked Read-only:
# method decorator -- makes undeclared 'self' argument available to method
injectself = lambda f: lambda self: eval(f.func_code, dict(self=self))
class TestClass:
def __init__(self, thing):
self.attr = thing
#injectself
def method():
print 'in TestClass::method(): self.attr = %r' % self.attr
return 42
test = TestClass("attribute's value")
ret = test.method()
print 'return value:', ret
# output:
# in TestClass::method(): self.attr = "attribute's value"
# return value: 42
Note that unless you take precautions to prevent it, a side-effect of the eval() function may be it adding a few entries -- such as a reference to the __builtin__ module under the key __builtins__ -- automatically to the dict passed to it.
#kendall: Per your comment about how you're using this with methods being in container classes (but ignoring the injection of additional variables for the moment) -- is the following something like what you're doing? It's difficult for me to understand how things are split up between the framework and what the users write. It sounds like an interesting design pattern to me.
# method decorator -- makes undeclared 'self' argument available to method
injectself = lambda f: lambda self: eval(f.func_code, dict(self=self))
class methodclass:
def __call__():
print 'in methodclass::__call__(): self.attr = %r' % self.attr
return 42
class TestClass:
def __init__(self, thing):
self.attr = thing
method = injectself(methodclass.__call__)
test = TestClass("attribute's value")
ret = test.method()
print 'return value:', ret
# output
# in methodclass::__call__(): self.attr = "attribute's value"
# return value: 42
The trick is to add 'self' to f.func_globals. This works in python2.6. I really should get around to installing other versions to test stuff like this on. Sorry for the wall of code but I cover two cases: doing it with a metaclass and doing it with a decorator. For your usecase, I think the metaclass is better since the whole point of this exercise is to shield users from syntax.
import new, functools
class TestMeta(type):
def __new__(meta, classname, bases, classdict):
for item in classdict:
if hasattr(classdict[item], '__call__'):
classdict[item] = wrap(classdict[item])
return type.__new__(meta, classname, bases, classdict)
def wrap(f):
#functools.wraps(f)
def wrapper(self):
f.func_globals['self'] = self
return f()
return wrapper
def testdec(f):
#functools.wraps(f)
def wrapper():
return f()
return wrapper
class Test(object):
__metaclass__ = TestMeta
message = 'You can do anything in python'
def test():
print self.message
#testdec
def test2():
print self.message + ' but the wrapper funcion can\'t take a self argument either or you get a TypeError'
class Test2(object):
message = 'It also works as a decorator but (to me at least) feels better as a metaclass'
#wrap
def test():
print self.message
t = Test()
t2 = Test2()
t.test()
t.test2()
t2.test()
This might be a use case for decorators - you give them a small set of lego bricks to build functions with, and the complicated framework stuff is piped in via #testcase or somesuch.
Edit: You didn't post any code, so this is going to be sketchy, but they don't need to write methods. They can write ordinary functions without "self", and you could use decorators like in this example from the article I linked:
class myDecorator(object):
def __init__(self, f):
print "inside myDecorator.__init__()"
f() # Prove that function definition has completed
def __call__(self):
print "inside myDecorator.__call__()"
#myDecorator
def aFunction():
print "inside aFunction()"
This question already has answers here:
Getting the name of a variable as a string
(32 answers)
Closed 3 years ago.
While building a new class object in python, I want to be able to create a default value based on the instance name of the class without passing in an extra argument. How can I accomplish this? Here's the basic pseudo-code I'm trying for:
class SomeObject():
defined_name = u""
def __init__(self, def_name=None):
if def_name == None:
def_name = u"%s" % (<INSTANCE NAME>)
self.defined_name = def_name
ThisObject = SomeObject()
print ThisObject.defined_name # Should print "ThisObject"
Well, there is almost a way to do it:
#!/usr/bin/env python
import traceback
class SomeObject():
def __init__(self, def_name=None):
if def_name == None:
(filename,line_number,function_name,text)=traceback.extract_stack()[-2]
def_name = text[:text.find('=')].strip()
self.defined_name = def_name
ThisObject = SomeObject()
print ThisObject.defined_name
# ThisObject
The traceback module allows you to peek at the code used to call SomeObject().
With a little string wrangling, text[:text.find('=')].strip() you can
guess what the def_name should be.
However, this hack is brittle. For example, this doesn't work so well:
ThisObject,ThatObject = SomeObject(),SomeObject()
print ThisObject.defined_name
# ThisObject,ThatObject
print ThatObject.defined_name
# ThisObject,ThatObject
So if you were to use this hack, you have to bear in mind that you must call SomeObject()
using simple python statement:
ThisObject = SomeObject()
By the way, as a further example of using traceback, if you define
def pv(var):
# stack is a list of 4-tuples: (filename, line number, function name, text)
# see http://docs.python.org/library/traceback.html#module-traceback
#
(filename,line_number,function_name,text)=traceback.extract_stack()[-2]
# ('x_traceback.py', 18, 'f', 'print_var(y)')
print('%s: %s'%(text[text.find('(')+1:-1],var))
then you can call
x=3.14
pv(x)
# x: 3.14
to print both the variable name and its value.
Instances don't have names. By the time the global name ThisObject gets bound to the instance created by evaluating the SomeObject constructor, the constructor has finished running.
If you want an object to have a name, just pass the name along in the constructor.
def __init__(self, name):
self.name = name
You can create a method inside your class that check all variables in the current frame and use hash() to look for the self variable.
The solution proposed here will return all the variables pointing to the instance object.
In the class below, isinstance() is used to avoid problems when applying hash(), since some objects like a numpy.array or a list, for example, are unhashable.
import inspect
class A(object):
def get_my_name(self):
ans = []
frame = inspect.currentframe().f_back
tmp = dict(frame.f_globals.items() + frame.f_locals.items())
for k, var in tmp.items():
if isinstance(var, self.__class__):
if hash(self) == hash(var):
ans.append(k)
return ans
The following test has been done:
def test():
a = A()
b = a
c = b
print c.get_my_name()
The result is:
test()
#['a', 'c', 'b']
This cannot work, just imagine this: a = b = TheMagicObjet(). Names have no effect on Values, they just point to them.
One horrible, horrible way to accomplish this is to reverse the responsibilities:
class SomeObject():
def __init__(self, def_name):
self.defined_name = def_name
globals()[def_name] = self
SomeObject("ThisObject")
print ThisObject.defined_name
If you wanted to support something other than global scope, you'd have to do something even more awful.
In Python, all data is stored in objects. Additionally, a name can be bound with an object, after which that name can be used to look up that object.
It makes no difference to the object what names, if any, it might be bound to. It might be bound to dozens of different names, or none. Also, Python does not have any "back links" that point from an object to a name.
Consider this example:
foo = 1
bar = foo
baz = foo
Now, suppose you have the integer object with value 1, and you want to work backwards and find its name. What would you print? Three different names have that object bound to them, and all are equally valid.
print(bar is foo) # prints True
print(baz is foo) # prints True
In Python, a name is a way to access an object, so there is no way to work with names directly. You could search through various name spaces until you find a name that is bound with the object of interest, but I don't recommend this.
How do I get the string representation of a variable in python?
There is a famous presentation called "Code Like a Pythonista" that summarizes this situation as "Other languages have 'variables'" and "Python has 'names'"
http://python.net/~goodger/projects/pycon/2007/idiomatic/handout.html#other-languages-have-variables
If you want an unique instance name for a class, try __repr__() or id(self)
class Some:
def __init__(self):
print(self.__repr__()) # = hex(id(self))
print(id(self))
It will print the memory address of the instance, which is unique.
Inspired by the answers of unutbu and Saullo Castro, I have created a more sophisticated class that can even be subclassed. It solves what was asked for in the question.
"create a default value based on the instance name of the class
without passing in an extra argument."
Here's what it does, when an instance of this class or a subclass is created:
Go up in the frame stack until the first frame which does not belong to a method of the current instance.
Inspect this frame to get the attributes self.creation_(name/file/module/function/line/text).
Perform an an additional check whether an object with name self.creation_name was actually defined in the frame's locals() namespace to make 100% sure the found creation_name is correct or raise an error otherwise.
The Code:
import traceback, threading, time
class InstanceCreationError(Exception):
pass
class RememberInstanceCreationInfo:
def __init__(self):
for frame, line in traceback.walk_stack(None):
varnames = frame.f_code.co_varnames
if varnames is ():
break
if frame.f_locals[varnames[0]] not in (self, self.__class__):
break
# if the frame is inside a method of this instance,
# the first argument usually contains either the instance or
# its class
# we want to find the first frame, where this is not the case
else:
raise InstanceCreationError("No suitable outer frame found.")
self._outer_frame = frame
self.creation_module = frame.f_globals["__name__"]
self.creation_file, self.creation_line, self.creation_function, \
self.creation_text = \
traceback.extract_stack(frame, 1)[0]
self.creation_name = self.creation_text.split("=")[0].strip()
super().__init__()
threading.Thread(target=self._check_existence_after_creation).start()
def _check_existence_after_creation(self):
while self._outer_frame.f_lineno == self.creation_line:
time.sleep(0.01)
# this is executed as soon as the line number changes
# now we can be sure the instance was actually created
error = InstanceCreationError(
"\nCreation name not found in creation frame.\ncreation_file: "
"%s \ncreation_line: %s \ncreation_text: %s\ncreation_name ("
"might be wrong): %s" % (
self.creation_file, self.creation_line, self.creation_text,
self.creation_name))
nameparts = self.creation_name.split(".")
try:
var = self._outer_frame.f_locals[nameparts[0]]
except KeyError:
raise error
finally:
del self._outer_frame
# make sure we have no permament inter frame reference
# which could hinder garbage collection
try:
for name in nameparts[1:]: var = getattr(var, name)
except AttributeError:
raise error
if var is not self: raise error
def __repr__(self):
return super().__repr__()[
:-1] + " with creation_name '%s'>" % self.creation_name
A simple example:
class MySubclass(RememberInstanceCreationInfo):
def __init__(self):
super().__init__()
def print_creation_info(self):
print(self.creation_name, self.creation_module, self.creation_function,
self.creation_line, self.creation_text, sep=", ")
instance = MySubclass()
instance.print_creation_info()
#out: instance, __main__, <module>, 68, instance = MySubclass()
If the creation name cannot be determined properly an error is raised:
variable, another_instance = 2, MySubclass()
# InstanceCreationError:
# Creation name not found in creation frame.
# creation_file: /.../myfile.py
# creation_line: 71
# creation_text: variable, another_instance = 2, MySubclass()
# creation_name (might be wrong): variable, another_instance
I think that names matters if they are the pointers to any object..
no matters if:
foo = 1
bar = foo
I know that foo points to 1 and bar points to the same value 1 into the same memory space.
but supose that I want to create a class with a function that adds a object to it.
Class Bag(object):
def __init__(self):
some code here...
def addItem(self,item):
self.__dict__[somewaytogetItemName] = item
So, when I instantiate the class bag like below:
newObj1 = Bag()
newObj2 = Bag()
newObj1.addItem(newObj2)I can do this to get an attribute of newObj1:
newObj1.newObj2
The best way is really to pass the name to the constructor as in the chosen answer. However, if you REALLY want to avoid asking the user to pass the name to the constructor, you can do the following hack:
If you are creating the instance with 'ThisObject = SomeObject()' from the command line, you can get the object name from the command string in command history:
import readline
import re
class SomeObject():
def __init__(self):
cmd = readline.get_history_item(readline.get_current_history_length())
self.name = re.split('=| ',cmd)[0]
If you are creating the instance using 'exec' command, you can handle this with:
if cmd[0:4] == 'exec': self.name = re.split('\'|=| ',cmd)[1] # if command performed using 'exec'
else: self.name = re.split('=| ',cmd)[0]