Dark magics about variable names in python
Installation
pip install python-varname
Features
- Fetching variable names from inside the function/class call
- Fetching variable names directly (added in
v0.1.2
) - A value wrapper to store the variable name that a value is assigned to (added in
v0.1.1
) - Detecting next immediate attribute name (added in
v0.1.4
) - Shortcut for
collections.namedtuple
(added inv0.1.6
) - Injecting
__varname__
to objects (added inv0.1.7
)
Credits
Thanks goes to these awesome people/projects:
@alexmojaki |
executing |
Usage
Retrieving the variable names from inside a function call/class instantiation
-
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 Klass: def __init__(self): self.id = varname() def copy(self): # also able to fetch inside a member call return varname() k = Klass() # k.id == 'k' k2 = k.copy() # k2 == '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' # Since v0.1.3 # We can ask varname to raise exceptions # if it fails to detect the variable name from varname import VarnameRetrievingError def get_name(): try: # if raise_exc is False # "var_<index>" will be returned return varname(raise_exc=True) except VarnameRetrieveingError: return None a.b = get_name() # None
Value wrapper
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}
Getting variable names directly
from varname import varname, nameof
a = 1
aname = nameof(a)
# aname == 'a
b = 2
aname, bname = nameof(a, b)
# aname == 'a', bname == 'b'
def func():
return varname() + '_suffix'
f = func()
# f == 'f_suffix'
fname = nameof(f)
# fname == 'f'
Detecting next immediate attribute name
from varname import will
class AwesomeClass:
def __init__(self):
self.will = None
def permit(self):
self.will = will()
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!'
collections.namedtuple
Shortcut for # instead of
from collections import namedtuple
Name = namedtuple('Name', ['first', 'last'])
# we can do:
from varname import namedtuple
Name = namedtuple(['first', 'last'])
__varname__
Injecting 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
Limitations
- Working in
ipython REPL
but not in standardpython console
- You have to know at which stack the function/class will be called (caller's depth)
- Not working with
reticulate
fromR
since it cuts stacks to the most recent one. (supported innameof
cannot be used in statements inpytest
v0.2.0
)-a = 1 +assert nameof(a) == 'a' -# Retrieving failure. -# The right way: -aname = nameof(a) -assert aname == 'a'