Dark magics about variable names in python
pip install varname
- Fetching variable names from inside the function/class call using
varname
- Fetching variable names directly using
nameof
- A value wrapper to store the variable name that a value is assigned to using
Wrapper
- Detecting next immediate attribute name using
will
- Injecting
__varname__
to objects - A
debug
function to print variables with their names and values.
Thanks goes to these awesome people/projects:
@alexmojaki |
executing |
Special thanks to @HanyuuLu to give up the name varname
in pypi for this project.
-
From insdie a function call
from varname import varname def function(): return varname() func = function() # func == 'func'
-
varname
calls being buried deeplydef function(): # I know that at which stack this will be called return varname(caller=3) def function1(): return function() def function2(): return function1() func = function2() # func == 'func'
-
Retrieving instance name of a class
class Foo: def __init__(self): self.id = varname() def copy(self): # also able to fetch inside a method call copied = Foo() # copied.id == 'copied' copied.id = varname() # assign id to whatever variable name return copied k = Foo() # k.id == 'k' k2 = k.copy() # k2.id == 'k2'
-
Some unusual use
func = [function()] # func == ['func'] func = [function(), function()] # func == ['func', 'func'] func = function(), function() # func = ('func', 'func') func = func1 = function() # func == func1 == 'func' # a warning will be printed # since you may not want func1 to be 'func' x = func(y = func()) # x == 'x' # get part of the name func_abc = function()[-3:] # func_abc == 'abc' # function alias supported now function2 = function func = function2() # func == 'func' a = lambda: 0 a.b = function() # a.b == 'b' # Since v0.1.3 # We can ask varname to raise exceptions # if it fails to detect the variable name def get_name(raise_exc): return varname(raise_exc=raise_exc) a = {} a['b'] = get_name(True) # VarnameRetrievingError a['b'] = get_name(False) # None
from varname import Wrapper
foo = Wrapper(True)
# foo.name == 'foo'
# foo.value == True
bar = Wrapper(False)
# bar.name == 'bar'
# bar.value == False
def values_to_dict(*args):
return {val.name: val.value for val in args}
mydict = values_to_dict(foo, bar)
# {'foo': True, 'bar': False}
from varname import varname, nameof
a = 1
nameof(a) # 'a'
b = 2
nameof(a, b) # ('a', 'b')
def func():
return varname() + '_suffix'
f = func() # f == 'f_suffix'
nameof(f) # 'f'
# get full names of (chained) attribute calls
func.a = func
nameof(func.a, full=True) # 'func.a'
func.a.b = 1
nameof(func.a.b, full=True) # 'func.a.b'
from varname import will
class AwesomeClass:
def __init__(self):
self.will = None
def permit(self):
self.will = will(raise_exc=False)
if self.will == 'do':
# let self handle do
return self
raise AttributeError('Should do something with AwesomeClass object')
def do(self):
if self.will != 'do':
raise AttributeError("You don't have permission to do")
return 'I am doing!'
awesome = AwesomeClass()
awesome.do() # AttributeError: You don't have permission to do
awesome.permit() # AttributeError: Should do something with AwesomeClass object
awesome.permit().do() == 'I am doing!'
from varname import inject
class MyList(list):
pass
a = inject(MyList())
b = inject(MyList())
a.__varname__ == 'a'
b.__varname__ == 'b'
a == b
# other methods not affected
a.append(1)
b.append(1)
a == b
a = 'value'
b = object()
debug(a) # DEBUG: a='value'
debug(b) # DEBUG: b=<object object at 0x2b70580e5f20>
debug(a, b)
# DEBUG: a='value'
# DEBUG: b=<object object at 0x2b70580e5f20>
debug(a, b, merge=True)
# DEBUG: a='value', b=<object object at 0x2b70580e5f20>
debug(a, repr=False, prefix='') # a=value
varname
is all depending on executing
package to look for the node.
The node executing
detects is ensured to be the correct one (see this).
It partially works with environments where other AST magics apply, including
pytest
, ipython
, macropy
, birdseye
, reticulate
with R
, etc. Neither
executing
nor varname
is 100% working with those environments. Use
it at your own risk.
For example:
-
This will not work with
pytest
:a = 1 assert nameof(a) == 'a' # do this instead name_a = nameof(a) assert name_a == 'a'
-
R
withreticulate
.