strange error using AttrDict on Python 2.7 - python

I am getting a weird recurring error using AttrDict 2.0 on Python 2.7. The weird part is that transitive assignment seems to break, but only when using AttrDict.
What's happening is that I want to instantiate a new list on an object if it doesn't exist and then append data to it.
If I use AttrDict, the list somehow gets transformed into a tuple and I get an exception.
from attrdict import AttrDict
class Test(object):
pass
try:
for cls_ in [Test,AttrDict]:
foo = cls_()
print ("\ntesting with class %s" % (cls_))
#this
chk = foo.li = getattr(foo, "li", None) or []
print(" type(chk):%s, id(chk):%s" % (type(chk),id(chk)))
print(" type(foo.li):%s, id(foo.li):%s" % (type(foo.li),id(foo.li)))
foo.li.append(3)
print (" success appending with class %s: foo.li:%s" % (cls_, foo.li))
except (Exception,) as e:
# pdb.set_trace()
raise
Now check out the output, when I use the Test class vs when I use AttrDict.
testing with class <class '__main__.Test'>
type(chk):<type 'list'>, id(chk):4465207704
type(foo.li):<type 'list'>, id(foo.li):4465207704
success appending with class <class '__main__.Test'>: foo.li:[3]
With the custom Test class, as expected, chk and foo.li are both lists and have the same id. append works.
Looking at the pass using AttrDict, id does not match and foo.li is a tuple rather than a list.
testing with class <class 'attrdict.dictionary.AttrDict'>
type(chk):<type 'list'>, id(chk):4465207848
type(foo.li):<type 'tuple'>, id(foo.li):4464595080
Traceback (most recent call last):
File "test_attrdict2.py", line 25, in <module>
test()
File "test_attrdict2.py", line 18, in test
foo.li.append(3)
AttributeError: 'tuple' object has no attribute 'append'
Is attrdict assignment actually returning some kind of property/accessor object that gets changed the 2nd time you access it?
Took #abartnet's suggestion:
from attrdict import AttrDict
a = AttrDict()
a.li = []
print(a.li)
output:
()
OK, but even if that points to some weird behavior on AttrDict's end, how is it the transitive assignment does not assign the tuple as well?
reworked:
from attrdict import AttrDict
a = AttrDict()
b = a.li = []
print("a.li:", a.li)
print("b:",b)
output:
('a.li:', ())
('b:', [])

This is part of the automatic recursiveness of AttrDict. Which is explained better in the inline help (which you can find here in the source) than in the README:
If a values which is accessed as an attribute is a Sequence-type (and is not a string/bytes), it will be converted to a _sequence_type with any mappings within it converted to Attrs.
In other words, in order to auto-convert any dict or other mappings recursively inside your AttrDict to AttrDict values when doing attribute access, it also converts all sequences to (by default) tuple. This is a little weird, but appears to be intentional and somewhat-documented behavior, not a bug.
>>> a = AttrDict()
>>> a._sequence_type
tuple
>>> a.li = []
>>> a.li
()
The more flexible AttrMap type lets you specify the sequence type, and documents that you can disable this recursive remapping stuff by passing None:
>>> a = AttrMap(sequence_type=None)
>>> a.li = []
>>> a.li
[]
But of course AttrMap isn't a dict (although it is a collections.abc.MutableMapping, and more generally it duck-types as a dict-like type).
OK, but even if that points to some weird behavior on AttrDict's end, how is it the transitive assignment does not assign the tuple as well?
Because that's not how chained assignment works. Oversimplifying a bit:
target1 = target2 = value
… is not equivalent to this:
target2 = value
target1 = target2
… but to this:
target2 = value
target1 = value
The best way to understand why that's true: targets aren't expressions, and therefore don't have values. Sure, often the exact same sequence of tokens would be valid as an expression elsewhere in the grammar, but that sequence of tokens never gets evaluated as an expression anywhere in an assignment statement—otherwise, simple things like d['spam'] = 'eggs' would have to raise an exception if d['spam'] didn't exist.
Also, a.li = [] doesn't actually assign tuple([]) anywhere; it actually stores the [] internally, and does the tuple(…) later, when you try to access a.li. You can't really tell that for sure without reading the source, but when you consider that a['li'] gives you [] rather than (), it pretty much has to be true. And, in fact:
>>> li = []
>>> a.li = li
>>> a['li'] is li
True

Related

Python - How to pass an instance variable to method as implicit argument generally and with recursive method

I am having an issue passing an instance variable of an object to an instance method.
I have searched for this elsewhere, but all I keep finding is information on how the object is passed to the method using self, which I already know, or just tutorials on general differences between class and instance methods that don't specifically answer my question. The answer to my question definitely exists somewhere, I think I just don't know what to actually ask for.
In my code, I have this class:
class SongData:
def __init__(self, datapoint):
self.artist = datapoint['artist']
self.track = datapoint['name']
def xtradata_rm(self, regex, string=None):
if string is None:
string = self
srchrslts = re.search(regex, string)
if srchrslts is not None:
if regex == 'f.*?t':
self = self.replace(string,'')
self.xtradata_rm('\((.*?)\)')
else:
self.xtradata_rm('f.*?t', srchrslts)
def example_method(self):
#This one isn't actually in the code, included for ease of explanation.
print(self)
#some more methods irrelevant to question down here.
Imagine we instantiate an object by doing song = SongData(datapoint). The method xtradata_rm is supposed to search either the song.artist or song.track string for a section in brackets, then if the section found contains any form of the word "featuring" remove that from the string and then try again until no more bracketed expressions with brackets containing "featuring" are found.
I am aware now this is probably 100% the wrong usage of self, but I don't know what to put in its place to achieve the behaviour I want. So then in my script I try to do:
file_list = glob.glob("*procData.json")
for datafname in file_list:
datafile = json.load(open(datafname))
for i, datapoint in enumerate(datafile['EnvDict']):
song = SongData(datapoint)
song.track.xtradata_rm('\((.*?)\)')
song.releasefetch(lfmapi)
song.dcsearcher(dcapi)
datapoint.update({"album": song.release, "year": song.year})
with open("upd" + datafname, 'w') as output:
json.dump(datafile, output)
but then I get this error:
Traceback (most recent call last):
song.track.xtradata_rm('\((.*?)\)')
AttributeError: 'str' object has no attribute 'xtradata_rm'
If I comment out that line, the code runs.
So my first question is, in general, what do I have to do so I can go song.track.example_method() or song.artist.example_method() and get track_name or artist_name printed in the console as expected respectively.
My second question is, how can I do the same with xtradata_rm (i.e.
be able to do song.track.xtradata_rm('\((.*?)\)') and essentially insert song.track in place of self within the method), and how does xtradata_rm being recursive and trying to pass the instance variable implicitly to itself within itself change things?
Looks like you want to add method xtradata_rm to str objects self.artist and self.track.
One thing that you misunderstand about Python is that can't change your object by assigning something to the variable self (or any other variable). self = 123 doesn't change the object, that is behind the name self to 123, it makes the name self point to object 123 (and do it only inside current scope).
To really get this distinction you should watch the talk Facts and Myths about Python names and values by Ned Batchelder.
The other thing is that str objects are immutable, so even if names worked as you expected, you simply could not modify str. For example, bytearray is mutable, and str is not, see the difference:
In [1]: b = bytearray(b'My example string')
In [2]: id(b)
Out[2]: 4584776792
In [3]: b[3:10] = b'modified'
In [4]: b
Out[4]: bytearray(b'My modified string')
In [5]: id(b) # same object
Out[5]: 4584776792
In [6]: s = 'My example string'
In [7]: s[3:10] = 'modified'
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-7-22fe89ae82a3> in <module>()
----> 1 s[3:10] = 'modified'
TypeError: 'str' object does not support item assignment
In [8]: new_s = s.replace('example', 'modified')
In [9]: id(new_s) # different object
Out[9]: 4584725936
In [10]: id(s)
Out[10]: 4584762296
In [11]: s # original string unmodified
Out[11]: 'My example string'
So to implement your method we need to create wrapper for str object that looks like str and acts like str, but also implements your method. This can be rather hard, for many complicated reasons proxying objects in python is a really involved ordeal.
But fear not! In the dephs of standard library lives a class (144 lines of boring code) just for you: collections.UserString.
All we need to do is to subclass it and implement your method on it:
class SongAttribute(collections.UserString):
def example_mutate(self):
"""Works UNLIKE other string methods, mutates SongAttribute object,
but I think this is how you want your code to work. Jugging just a bit ;)
Note: actual str object still is immutable and wasn't mutated,
self.data now just references another immutable str object.
P.S.: self.data is the object being proxied by UserString class
"""
self.data = self.data.replace(' ', '_')
return self
def example_return_new(self):
"""Works like all other string metods, returns new string"""
return self.replace(' ', '_')
song = SongAttribute('My Song Name') # creating new song attribute (artist or track)
print(song, type(song)) # it looks like str, but isn't
print(song.upper(), type(song.upper())) # it has all of the str methods, but they return SongAttribute objects, not str objects.
# Return new
print()
new_song = song.example_return_new()
print(new_song, type(new_song)) # we got underscored SongAttribute
# Mutate
print()
print(song, type(song))
print(song.example_mutate(), type(song.example_mutate())) # this method changed song object internally
print(song, type(song)) # and now we still see the changes
Output:
My Song Name <class '__main__.SongAttribute'>
MY SONG NAME <class '__main__.SongAttribute'>
My_Song_Name <class '__main__.SongAttribute'>
My Song Name <class '__main__.SongAttribute'>
My_Song_Name <class '__main__.SongAttribute'>
My_Song_Name <class '__main__.SongAttribute'>
Now you can implement your method on SongAttribute, and change SongData constructor to:
def __init__(self, datapoint):
self.artist = SongAttribute(datapoint['artist'])
self.track = SongAttribute(datapoint['name'])

How do I detect variables in a python eval expression

Let's say i get a string to eval
temperature*x
And I have two sets of variables - the easy one:
easy_ns = {'x':3, 'y':4}
And a harder one:
harder = ['temperature', 'sumofall']
Each of which will take significant time to calculate and I don't want to calculate them unless they are part of the user supplied expression
E.g. I don't want to start the detection of "temperature" unless I know it is required
I may have some variables in my namespace that are "inexpensive" but others I would like to postpone calculating as much as possible
How do I get a list of variables from my eval string before it is evaluated
I know I can try: eval() except: and I will get a:
NameError: name 'temperature' is not defined
Is there a pythonic way of extracting the exact variable name?
Is there a nice way to build your namespace for lazy evaluation?
Something like
namespace = {'x':3, 'y':4, 'temperature':lazy_temperature_function}
So that only when my expression is evaluated
res=eval('temperature*x')
is my lazy temperature function called
And yes of course - I absolutely do have to use 'eval' - that is why I have posted these questions
The scenario is that I get an input file with set of keys and values and then the user can supply an expression he wants me to calculate from a combination of those values and some generated variables that I do not want to calculate unless the user includes them in his/her expression
You could, if you really really have to, parse the code using the ast module. The ast.parse helper will give you an AST tree representation of the code:
import ast
code = "temperature*x"
st = ast.parse(code)
for node in ast.walk(st):
if type(node) is ast.Name:
print(node.id)
This will print:
temperature
x
So this only extracts the variable names, like you said. It seems like a first step, but I'm not sure what you are trying to do so maybe a different approach is better.
Edit: If I understand your problem correctly, you want some values to be calculated only if they appear in an expression? I tried something like this:
>>> import ast
>>> code = "temperature*x"
>>> x = 5
>>> def lazy_temperature():
return 78
...
>>> names = [node.id for node in ast.walk(ast.parse(code))
if type(node) is ast.Name]
>>> ns = {name: (globals()['lazy_%s' % name])()
if ('lazy_%s' % name) in globals()
else globals()[name]
for name in names}
>>> ns
{'x': 5, 'temperature': 78}
>>> eval(code, ns)
390
This snippet will load the value out of the current scope, unless there's a function called lazy_<name>. This function will be called in case the <name> part appears in the expression.
You could make it a lambda function, and simply execute it whenever you need to, as such:
a = lambda : 5*temperature
a()
> Traceback (most recent call last):
> File "<stdin>", line 1, in <module>
> File "<stdin>", line 1, in <lambda>
> NameError: global name 'temperature' is not defined
temperature = 100
a()
> 500
In this way, you don't look for execute unless you consciously want to.
However, you could also make sure to only enter the lambda function if temperature exists. You can do that by assigning temperature to None in the beginning of your file, and only enter the lambda if you need to:
temperature = None
if temperature:
a()
# do something else
If you don't want to use the parens `fn() - You could also build a class to do this with a property.
class a(object):
#property
def temp_calc(self):
return self.temp*5
In this way, you can do the following:
temp_obj = a()
temp_obj.temp_calc
This will return an error since you don't have a "temp" attribute. But you can assign it if you need to:
temp_obj.temp = 5
temp_obj.temp_calc
> 25
There are lots of options here, i hope these few help.

Python: finding all classes with a particular attribute

I saw a nearly identical post for C#, but couldn't find anything relevant for Python.
Because I'm just starting to learn Python, I have a feeling I'll think of an attribute I've used before like .count() and forget which objects have the attribute.
I want to write a function that, given the directory list and a certain attribute, will return a set of the object classes that have the attribute. (Ideally I could search all classes objects imported at the time of the running of this function, not just what's in dir())
Here is what I have so far:
def whereattr(directory_list, attrib):
haveit = set()
for obj in directory_list:
try:
haveit.add(type(eval(obj)) if hasattr(eval(obj), attrib) else '')
except:
pass
return haveit
I am using try/except because I found I'm unable to evaluate all objects.
Example:
In [244]: whos
Variable Type Data/Info
-------------------------------------
DataFrame type <class 'pandas.core.frame.DataFrame'>
Series type <class 'pandas.core.series.Series'>
count int 5
counts dict n=97
first_letter function <function <lambda> at 0x1039daf50>
frame DataFrame _heartbeat_ <...>n[3560 rows x 18 columns]
get_counts function <function get_counts at 0x10770ac08>
haveit list n=0
itertools module <module 'itertools' from <...>ib-dynload/itertools.so'>
json module <module 'json' from '/Use<...>on2.7/json/__init__.pyc'>
letter str A
line str { "a": "Mozilla\/4.0 (com<...>.935799, -77.162102 ] }\n
names _grouper <itertools._grouper object at 0x103b51b90>
path str pydata-book/ch02/usagov_b<...>2012-03-16-1331923249.txt
rec dict n=16
records list n=3560
results Series 0 Mozilla/<...>ngth: 3440, dtype: object
test set set([])
testfun function <function testfun at 0x107b2d938>
tz unicode Asia/Seoul
tzs list n=3440
vkp list n=97
whereattr function <function whereattr at 0x107b2d9b0>
x unicode Mozilla/4.0 (compatible; <...>T4.0E; .NET CLR 1.1.4322)
In [247]: whereattr(dir(), "count")
Out[247]: {'', str}
Would someone please tell me what I'm doing wrong? Clearly, list, Series, etc. have a count attribute and should be included.
In addition, if you want to scold me for any other problems with my code, I'm open to (nice) criticism.
dir is a bit useless here, I would rather recommend locals or globals as that gives you the references so you won't need to use eval.
[label for label, ref in locals().items() if hasattr(ref, 'count')]
Obviously you could put that into a function like
whereattr = lambda scope, attr: [label for label, ref in scope.items() if hasattr(ref, attr)]
and you use it like this
whereattr(locals(), 'count')
or
whereattr(vars(__builtin__), 'count')
You should change your except clause to except Exception, e: print str(e). It's telling you what you are doing wrong. If you lose the eval, it seems to work.
def whereattr(directory_list, attrib):
haveit = set()
for obj in directory_list:
try:
haveit.add(type(obj) if hasattr(obj, attrib) else '')
except Exception, e:
print str(e)
return haveit
>>> whereattr([list], "count")
set([<type 'type'>])
>>>
You should not be using eval() to look up the objects. Try something like this instead:
def whereattr(directory_list, attrib):
haveit = set()
scope = globals()
for obj_name in directory_list:
if obj_name in scope:
obj = scope[obj_name]
if hasattr(obj, attrib):
haveit.add(type(obj))
return haveit
This will find all objects in the global scope having the given attribute.
Edit: Even though using eval() is bad practice, your original code should have worked. So I'm actually not sure what the underlying problem is. (See my comment on the original question.)

How to use dot notation for dict in python?

I'm very new to python and I wish I could do . notation to access values of a dict.
Lets say I have test like this:
>>> test = dict()
>>> test['name'] = 'value'
>>> print(test['name'])
value
But I wish I could do test.name to get value. Infact I did it by overriding the __getattr__ method in my class like this:
class JuspayObject:
def __init__(self,response):
self.__dict__['_response'] = response
def __getattr__(self,key):
try:
return self._response[key]
except KeyError,err:
sys.stderr.write('Sorry no key matches')
and this works! when I do:
test.name // I get value.
But the problem is when I just print test alone I get the error as:
'Sorry no key matches'
Why is this happening?
This functionality already exists in the standard libraries, so I recommend you just use their class.
>>> from types import SimpleNamespace
>>> d = {'key1': 'value1', 'key2': 'value2'}
>>> n = SimpleNamespace(**d)
>>> print(n)
namespace(key1='value1', key2='value2')
>>> n.key2
'value2'
Adding, modifying and removing values is achieved with regular attribute access, i.e. you can use statements like n.key = val and del n.key.
To go back to a dict again:
>>> vars(n)
{'key1': 'value1', 'key2': 'value2'}
The keys in your dict should be string identifiers for attribute access to work properly.
Simple namespace was added in Python 3.3. For older versions of the language, argparse.Namespace has similar behaviour.
I assume that you are comfortable in Javascript and want to borrow that kind of syntax... I can tell you by personal experience that this is not a great idea.
It sure does look less verbose and neat; but in the long run it is just obscure. Dicts are dicts, and trying to make them behave like objects with attributes will probably lead to (bad) surprises.
If you need to manipulate the fields of an object as if they were a dictionary, you can always resort to use the internal __dict__ attribute when you need it, and then it is explicitly clear what you are doing. Or use getattr(obj, 'key') to have into account the inheritance structure and class attributes too.
But by reading your example it seems that you are trying something different... As the dot operator will already look in the __dict__ attribute without any extra code.
In addition to this answer, one can add support for nested dicts as well:
from types import SimpleNamespace
class NestedNamespace(SimpleNamespace):
def __init__(self, dictionary, **kwargs):
super().__init__(**kwargs)
for key, value in dictionary.items():
if isinstance(value, dict):
self.__setattr__(key, NestedNamespace(value))
else:
self.__setattr__(key, value)
nested_namespace = NestedNamespace({
'parent': {
'child': {
'grandchild': 'value'
}
},
'normal_key': 'normal value',
})
print(nested_namespace.parent.child.grandchild) # value
print(nested_namespace.normal_key) # normal value
Note that this does not support dot notation for dicts that are somewhere inside e.g. lists.
Could you use a named tuple?
from collections import namedtuple
Test = namedtuple('Test', 'name foo bar')
my_test = Test('value', 'foo_val', 'bar_val')
print(my_test)
print(my_test.name)
__getattr__ is used as a fallback when all other attribute lookup rules have failed. When you try to "print" your object, Python look for a __repr__ method, and since you don't implement it in your class it ends up calling __getattr__ (yes, in Python methods are attributes too). You shouldn't assume which key getattr will be called with, and, most important, __getattr__ must raise an AttributeError if it cannot resolve key.
As a side note: don't use self.__dict__ for ordinary attribute access, just use the plain attribute notation:
class JuspayObject:
def __init__(self,response):
# don't use self.__dict__ here
self._response = response
def __getattr__(self,key):
try:
return self._response[key]
except KeyError,err:
raise AttributeError(key)
Now if your class has no other responsability (and your Python version is >= 2.6 and you don't need to support older versions), you may just use a namedtuple : http://docs.python.org/2/library/collections.html#collections.namedtuple
You can use the built-in method argparse.Namespace():
import argparse
args = argparse.Namespace()
args.name = 'value'
print(args.name)
# 'value'
You can also get the original dict via vars(args).
class convert_to_dot_notation(dict):
"""
Access dictionary attributes via dot notation
"""
__getattr__ = dict.get
__setattr__ = dict.__setitem__
__delattr__ = dict.__delitem__
test = {"name": "value"}
data = convert_to_dot_notation(test)
print(data.name)
You have to be careful when using __getattr__, because it's used for a lot of builtin Python functionality.
Try something like this...
class JuspayObject:
def __init__(self,response):
self.__dict__['_response'] = response
def __getattr__(self, key):
# First, try to return from _response
try:
return self.__dict__['_response'][key]
except KeyError:
pass
# If that fails, return default behavior so we don't break Python
try:
return self.__dict__[key]
except KeyError:
raise AttributeError, key
>>> j = JuspayObject({'foo': 'bar'})
>>> j.foo
'bar'
>>> j
<__main__.JuspayObject instance at 0x7fbdd55965f0>
Here is a simple, handy dot notation helper example that is working with nested items:
def dict_get(data:dict, path:str, default = None):
pathList = re.split(r'\.', path, flags=re.IGNORECASE)
result = data
for key in pathList:
try:
key = int(key) if key.isnumeric() else key
result = result[key]
except:
result = default
break
return result
Usage example:
my_dict = {"test1": "str1", "nested_dict": {"test2": "str2"}, "nested_list": ["str3", {"test4": "str4"}]}
print(dict_get(my_dict, "test1"))
# str1
print(dict_get(my_dict, "nested_dict.test2"))
# str2
print(dict_get(my_dict, "nested_list.1.test4"))
# str4
With a small addition to this answer you can support lists as well:
class NestedNamespace(SimpleNamespace):
def __init__(self, dictionary, **kwargs):
super().__init__(**kwargs)
for key, value in dictionary.items():
if isinstance(value, dict):
self.__setattr__(key, NestedNamespace(value))
elif isinstance(value, list):
self.__setattr__(key, map(NestedNamespace, value))
else:
self.__setattr__(key, value)
2022 answer: I've created the dotwiz package -- this is a fast, tiny library that seems to perform really well in most cases.
>>> from dotwiz import DotWiz
>>> test = DotWiz(hello='world')
>>> test.works = True
>>> test
✫(hello='world', works=True)
>>> test.hello
'world'
>>> assert test.works
This feature is baked into OmegaConf:
from omegaconf import OmegaConf
your_dict = {"k" : "v", "list" : [1, {"a": "1", "b": "2", 3: "c"}]}
adot_dict = OmegaConf.create(your_dict)
print(adot_dict.k)
print(adot_dict.list)
Installation is:
pip install omegaconf
This lib comes in handy for configurations, which it is actually made for:
from omegaconf import OmegaConf
cfg = OmegaConf.load('config.yml')
print(cfg.data_path)
I use the dotted_dict package:
>>> from dotted_dict import DottedDict
>>> test = DottedDict()
>>> test.name = 'value'
>>> print(test.name)
value
Advantages over SimpleNamespace
(See #win's answer.) DottedDict is an actual dict:
>>> isinstance(test, dict)
True
This allows, for example, checking for membership:
>>> 'name' in test
True
whereas for SimpleNamespace you need something much less readable like hasattr(test, 'name').
Don't use DotMap
I found this out the hard way. If you reference a non-member it adds it rather than throwing an error. This can lead to hard to find bugs in code:
>>> from dotmap import DotMap
>>> dm = DotMap()
>>> 'a' in dm
False
>>> x = dm.a
>>> 'a' in dm
True
#!/usr/bin/env python3
import json
from sklearn.utils import Bunch
from collections.abc import MutableMapping
def dotted(inpt: MutableMapping,
*args,
**kwargs
) -> Bunch:
"""
Enables recursive dot notation for ``dict``.
"""
return json.loads(json.dumps(inpt),
object_hook=lambda x:
Bunch(**{**Bunch(), **x}))
You can make hacks adding dot notation to Dicts mostly work, but there are always namespace problems. As in, what does this do?
x = DotDict()
x["values"] = 1989
print(x. values)
I use pydash, which is a Python port of JS's lodash, to do these things a different way when the nesting gets too ugly.
Add a __repr__() method to the class so that you can customize the text to be shown on
print text
Learn more here: https://web.archive.org/web/20121022015531/http://diveintopython.net/object_oriented_framework/special_class_methods2.html

Convert Variable Name to String?

I would like to convert a python variable name into the string equivalent as shown. Any ideas how?
var = {}
print ??? # Would like to see 'var'
something_else = 3
print ??? # Would print 'something_else'
TL;DR: Not possible. See 'conclusion' at the end.
There is an usage scenario where you might need this. I'm not implying there are not better ways or achieving the same functionality.
This would be useful in order to 'dump' an arbitrary list of dictionaries in case of error, in debug modes and other similar situations.
What would be needed, is the reverse of the eval() function:
get_indentifier_name_missing_function()
which would take an identifier name ('variable','dictionary',etc) as an argument, and return a
string containing the identifier’s name.
Consider the following current state of affairs:
random_function(argument_data)
If one is passing an identifier name ('function','variable','dictionary',etc) argument_data to a random_function() (another identifier name), one actually passes an identifier (e.g.: <argument_data object at 0xb1ce10>) to another identifier (e.g.: <function random_function at 0xafff78>):
<function random_function at 0xafff78>(<argument_data object at 0xb1ce10>)
From my understanding, only the memory address is passed to the function:
<function at 0xafff78>(<object at 0xb1ce10>)
Therefore, one would need to pass a string as an argument to random_function() in order for that function to have the argument's identifier name:
random_function('argument_data')
Inside the random_function()
def random_function(first_argument):
, one would use the already supplied string 'argument_data' to:
serve as an 'identifier name' (to display, log, string split/concat, whatever)
feed the eval() function in order to get a reference to the actual identifier, and therefore, a reference to the real data:
print("Currently working on", first_argument)
some_internal_var = eval(first_argument)
print("here comes the data: " + str(some_internal_var))
Unfortunately, this doesn't work in all cases. It only works if the random_function() can resolve the 'argument_data' string to an actual identifier. I.e. If argument_data identifier name is available in the random_function()'s namespace.
This isn't always the case:
# main1.py
import some_module1
argument_data = 'my data'
some_module1.random_function('argument_data')
# some_module1.py
def random_function(first_argument):
print("Currently working on", first_argument)
some_internal_var = eval(first_argument)
print("here comes the data: " + str(some_internal_var))
######
Expected results would be:
Currently working on: argument_data
here comes the data: my data
Because argument_data identifier name is not available in the random_function()'s namespace, this would yield instead:
Currently working on argument_data
Traceback (most recent call last):
File "~/main1.py", line 6, in <module>
some_module1.random_function('argument_data')
File "~/some_module1.py", line 4, in random_function
some_internal_var = eval(first_argument)
File "<string>", line 1, in <module>
NameError: name 'argument_data' is not defined
Now, consider the hypotetical usage of a get_indentifier_name_missing_function() which would behave as described above.
Here's a dummy Python 3.0 code: .
# main2.py
import some_module2
some_dictionary_1 = { 'definition_1':'text_1',
'definition_2':'text_2',
'etc':'etc.' }
some_other_dictionary_2 = { 'key_3':'value_3',
'key_4':'value_4',
'etc':'etc.' }
#
# more such stuff
#
some_other_dictionary_n = { 'random_n':'random_n',
'etc':'etc.' }
for each_one_of_my_dictionaries in ( some_dictionary_1,
some_other_dictionary_2,
...,
some_other_dictionary_n ):
some_module2.some_function(each_one_of_my_dictionaries)
# some_module2.py
def some_function(a_dictionary_object):
for _key, _value in a_dictionary_object.items():
print( get_indentifier_name_missing_function(a_dictionary_object) +
" " +
str(_key) +
" = " +
str(_value) )
######
Expected results would be:
some_dictionary_1 definition_1 = text_1
some_dictionary_1 definition_2 = text_2
some_dictionary_1 etc = etc.
some_other_dictionary_2 key_3 = value_3
some_other_dictionary_2 key_4 = value_4
some_other_dictionary_2 etc = etc.
......
......
......
some_other_dictionary_n random_n = random_n
some_other_dictionary_n etc = etc.
Unfortunately, get_indentifier_name_missing_function() would not see the 'original' identifier names (some_dictionary_,some_other_dictionary_2,some_other_dictionary_n). It would only see the a_dictionary_object identifier name.
Therefore the real result would rather be:
a_dictionary_object definition_1 = text_1
a_dictionary_object definition_2 = text_2
a_dictionary_object etc = etc.
a_dictionary_object key_3 = value_3
a_dictionary_object key_4 = value_4
a_dictionary_object etc = etc.
......
......
......
a_dictionary_object random_n = random_n
a_dictionary_object etc = etc.
So, the reverse of the eval() function won't be that useful in this case.
Currently, one would need to do this:
# main2.py same as above, except:
for each_one_of_my_dictionaries_names in ( 'some_dictionary_1',
'some_other_dictionary_2',
'...',
'some_other_dictionary_n' ):
some_module2.some_function( { each_one_of_my_dictionaries_names :
eval(each_one_of_my_dictionaries_names) } )
# some_module2.py
def some_function(a_dictionary_name_object_container):
for _dictionary_name, _dictionary_object in a_dictionary_name_object_container.items():
for _key, _value in _dictionary_object.items():
print( str(_dictionary_name) +
" " +
str(_key) +
" = " +
str(_value) )
######
In conclusion:
Python passes only memory addresses as arguments to functions.
Strings representing the name of an identifier, can only be referenced back to the actual identifier by the eval() function if the name identifier is available in the current namespace.
A hypothetical reverse of the eval() function, would not be useful in cases where the identifier name is not 'seen' directly by the calling code. E.g. inside any called function.
Currently one needs to pass to a function:
the string representing the identifier name
the actual identifier (memory address)
This can be achieved by passing both the 'string' and eval('string') to the called function at the same time. I think this is the most 'general' way of solving this egg-chicken problem across arbitrary functions, modules, namespaces, without using corner-case solutions. The only downside is the use of the eval() function which may easily lead to unsecured code. Care must be taken to not feed the eval() function with just about anything, especially unfiltered external-input data.
Totally possible with the python-varname package (python3):
from varname import nameof
s = 'Hey!'
print (nameof(s))
Output:
s
Install:
pip3 install varname
Or get the package here:
https://github.com/pwwang/python-varname
I searched for this question because I wanted a Python program to print assignment statements for some of the variables in the program. For example, it might print "foo = 3, bar = 21, baz = 432". The print function would need the variable names in string form. I could have provided my code with the strings "foo","bar", and "baz", but that felt like repeating myself. After reading the previous answers, I developed the solution below.
The globals() function behaves like a dict with variable names (in the form of strings) as keys. I wanted to retrieve from globals() the key corresponding to the value of each variable. The method globals().items() returns a list of tuples; in each tuple the first item is the variable name (as a string) and the second is the variable value. My variablename() function searches through that list to find the variable name(s) that corresponds to the value of the variable whose name I need in string form.
The function itertools.ifilter() does the search by testing each tuple in the globals().items() list with the function lambda x: var is globals()[x[0]]. In that function x is the tuple being tested; x[0] is the variable name (as a string) and x[1] is the value. The lambda function tests whether the value of the tested variable is the same as the value of the variable passed to variablename(). In fact, by using the is operator, the lambda function tests whether the name of the tested variable is bound to the exact same object as the variable passed to variablename(). If so, the tuple passes the test and is returned by ifilter().
The itertools.ifilter() function actually returns an iterator which doesn't return any results until it is called properly. To get it called properly, I put it inside a list comprehension [tpl[0] for tpl ... globals().items())]. The list comprehension saves only the variable name tpl[0], ignoring the variable value. The list that is created contains one or more names (as strings) that are bound to the value of the variable passed to variablename().
In the uses of variablename() shown below, the desired string is returned as an element in a list. In many cases, it will be the only item in the list. If another variable name is assigned the same value, however, the list will be longer.
>>> def variablename(var):
... import itertools
... return [tpl[0] for tpl in
... itertools.ifilter(lambda x: var is x[1], globals().items())]
...
>>> var = {}
>>> variablename(var)
['var']
>>> something_else = 3
>>> variablename(something_else)
['something_else']
>>> yet_another = 3
>>> variablename(something_else)
['yet_another', 'something_else']
as long as it's a variable and not a second class, this here works for me:
def print_var_name(variable):
for name in globals():
if eval(name) == variable:
print name
foo = 123
print_var_name(foo)
>>>foo
this happens for class members:
class xyz:
def __init__(self):
pass
member = xyz()
print_var_name(member)
>>>member
ans this for classes (as example):
abc = xyz
print_var_name(abc)
>>>abc
>>>xyz
So for classes it gives you the name AND the properteries
This is not possible.
In Python, there really isn't any such thing as a "variable". What Python really has are "names" which can have objects bound to them. 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.
Consider this example:
foo = 1
bar = 1
baz = 1
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.
In Python, a name is a way to access an object, so there is no way to work with names directly. There might be some clever way to hack the Python bytecodes or something to get the value of the name, but that is at best a parlor trick.
If you know you want print foo to print "foo", you might as well just execute print "foo" in the first place.
EDIT: I have changed the wording slightly to make this more clear. Also, here is an even better example:
foo = 1
bar = foo
baz = foo
In practice, Python reuses the same object for integers with common values like 0 or 1, so the first example should bind the same object to all three names. But this example is crystal clear: the same object is bound to foo, bar, and baz.
Technically the information is available to you, but as others have asked, how would you make use of it in a sensible way?
>>> x = 52
>>> globals()
{'__builtins__': <module '__builtin__' (built-in)>, '__name__': '__main__',
'x': 52, '__doc__': None, '__package__': None}
This shows that the variable name is present as a string in the globals() dictionary.
>>> globals().keys()[2]
'x'
In this case it happens to be the third key, but there's no reliable way to know where a given variable name will end up
>>> for k in globals().keys():
... if not k.startswith("_"):
... print k
...
x
>>>
You could filter out system variables like this, but you're still going to get all of your own items. Just running that code above created another variable "k" that changed the position of "x" in the dict.
But maybe this is a useful start for you. If you tell us what you want this capability for, more helpful information could possibly be given.
By using the the unpacking operator:
>>> def tostr(**kwargs):
return kwargs
>>> var = {}
>>> something_else = 3
>>> tostr(var = var,something_else=something_else)
{'var' = {},'something_else'=3}
You somehow have to refer to the variable you want to print the name of. So it would look like:
print varname(something_else)
There is no such function, but if there were it would be kind of pointless. You have to type out something_else, so you can as well just type quotes to the left and right of it to print the name as a string:
print "something_else"
What are you trying to achieve? There is absolutely no reason to ever do what you describe, and there is likely a much better solution to the problem you're trying to solve..
The most obvious alternative to what you request is a dictionary. For example:
>>> my_data = {'var': 'something'}
>>> my_data['something_else'] = 'something'
>>> print my_data.keys()
['var', 'something_else']
>>> print my_data['var']
something
Mostly as a.. challenge, I implemented your desired output. Do not use this code, please!
#!/usr/bin/env python2.6
class NewLocals:
"""Please don't ever use this code.."""
def __init__(self, initial_locals):
self.prev_locals = list(initial_locals.keys())
def show_new(self, new_locals):
output = ", ".join(list(set(new_locals) - set(self.prev_locals)))
self.prev_locals = list(new_locals.keys())
return output
# Set up
eww = None
eww = NewLocals(locals())
# "Working" requested code
var = {}
print eww.show_new(locals()) # Outputs: var
something_else = 3
print eww.show_new(locals()) # Outputs: something_else
# Further testing
another_variable = 4
and_a_final_one = 5
print eww.show_new(locals()) # Outputs: another_variable, and_a_final_one
Does Django not do this when generating field names?
http://docs.djangoproject.com/en/dev//topics/db/models/#verbose-field-names
Seems reasonable to me.
I think this is a cool solution and I suppose the best you can get. But do you see any way to handle the ambigious results, your function may return?
As "is" operator behaves unexpectedly with integers shows, low integers and strings of the same value get cached by python so that your variablename-function might priovide ambigous results with a high probability.
In my case, I would like to create a decorator, that adds a new variable to a class by the varialbename i pass it:
def inject(klass, dependency):
klass.__dict__["__"+variablename(dependency)]=dependency
But if your method returns ambigous results, how can I know the name of the variable I added?
var any_var="myvarcontent"
var myvar="myvarcontent"
#inject(myvar)
class myclasss():
def myclass_method(self):
print self.__myvar #I can not be sure, that this variable will be set...
Maybe if I will also check the local list I could at least remove the "dependency"-Variable from the list, but this will not be a reliable result.
Here is a succinct variation that lets you specify any directory.
The issue with using directories to find anything is that multiple variables can have the same value. So this code returns a list of possible variables.
def varname( var, dir=locals()):
return [ key for key, val in dir.items() if id( val) == id( var)]
I don't know it's right or not, but it worked for me
def varname(variable):
for name in list(globals().keys()):
expression = f'id({name})'
if id(variable) == eval(expression):
return name
it is possible to a limited extent. the answer is similar to the solution by #tamtam .
The given example assumes the following assumptions -
You are searching for a variable by its value
The variable has a distinct value
The value is in the global namespace
Example:
testVar = "unique value"
varNameAsString = [k for k,v in globals().items() if v == "unique value"]
#
# the variable "varNameAsString" will contain all the variable name that matches
# the value "unique value"
# for this example, it will be a list of a single entry "testVar"
#
print(varNameAsString)
Output : ['testVar']
You can extend this example for any other variable/data type
I'd like to point out a use case for this that is not an anti-pattern, and there is no better way to do it.
This seems to be a missing feature in python.
There are a number of functions, like patch.object, that take the name of a method or property to be patched or accessed.
Consider this:
patch.object(obj, "method_name", new_reg)
This can potentially start "false succeeding" when you change the name of a method. IE: you can ship a bug, you thought you were testing.... simply because of a bad method name refactor.
Now consider: varname. This could be an efficient, built-in function. But for now it can work by iterating an object or the caller's frame:
Now your call can be:
patch.member(obj, obj.method_name, new_reg)
And the patch function can call:
varname(var, obj=obj)
This would: assert that the var is bound to the obj and return the name of the member. Or if the obj is not specified, use the callers stack frame to derive it, etc.
Could be made an efficient built in at some point, but here's a definition that works. I deliberately didn't support builtins, easy to add tho:
Feel free to stick this in a package called varname.py, and use it in your patch.object calls:
patch.object(obj, varname(obj, obj.method_name), new_reg)
Note: this was written for python 3.
import inspect
def _varname_dict(var, dct):
key_name = None
for key, val in dct.items():
if val is var:
if key_name is not None:
raise NotImplementedError("Duplicate names not supported %s, %s" % (key_name, key))
key_name = key
return key_name
def _varname_obj(var, obj):
key_name = None
for key in dir(obj):
val = getattr(obj, key)
equal = val is var
if equal:
if key_name is not None:
raise NotImplementedError("Duplicate names not supported %s, %s" % (key_name, key))
key_name = key
return key_name
def varname(var, obj=None):
if obj is None:
if hasattr(var, "__self__"):
return var.__name__
caller_frame = inspect.currentframe().f_back
try:
ret = _varname_dict(var, caller_frame.f_locals)
except NameError:
ret = _varname_dict(var, caller_frame.f_globals)
else:
ret = _varname_obj(var, obj)
if ret is None:
raise NameError("Name not found. (Note: builtins not supported)")
return ret
This will work for simnple data types (str, int, float, list etc.)
>>> def my_print(var_str) :
print var_str+':', globals()[var_str]
>>> a = 5
>>> b = ['hello', ',world!']
>>> my_print('a')
a: 5
>>> my_print('b')
b: ['hello', ',world!']
It's not very Pythonesque but I was curious and found this solution. You need to duplicate the globals dictionary since its size will change as soon as you define a new variable.
def var_to_name(var):
# noinspection PyTypeChecker
dict_vars = dict(globals().items())
var_string = None
for name in dict_vars.keys():
if dict_vars[name] is var:
var_string = name
break
return var_string
if __name__ == "__main__":
test = 3
print(f"test = {test}")
print(f"variable name: {var_to_name(test)}")
which returns:
test = 3
variable name: test
To get the variable name of var as a string:
var = 1000
var_name = [k for k,v in locals().items() if v == var][0]
print(var_name) # ---> outputs 'var'
Thanks #restrepo, this was exactly what I needed to create a standard save_df_to_file() function. For this, I made some small changes to your tostr() function. Hope this will help someone else:
def variabletostr(**df):
variablename = list(df.keys())[0]
return variablename
variabletostr(df=0)
The original question is pretty old, but I found an almost solution with Python 3. (I say almost because I think you can get close to a solution but I do not believe there is a solution concrete enough to satisfy the exact request).
First, you might want to consider the following:
objects are a core concept in Python, and they may be assigned a variable, but the variable itself is a bound name (think pointer or reference) not the object itself
var is just a variable name bound to an object and that object could have more than one reference (in your example it does not seem to)
in this case, var appears to be in the global namespace so you can use the global builtin conveniently named global
different name references to the same object will all share the same id which can be checked by running the id builtin id like so: id(var)
This function grabs the global variables and filters out the ones matching the content of your variable.
def get_bound_names(target_variable):
'''Returns a list of bound object names.'''
return [k for k, v in globals().items() if v is target_variable]
The real challenge here is that you are not guaranteed to get back the variable name by itself. It will be a list, but that list will contain the variable name you are looking for. If your target variable (bound to an object) is really the only bound name, you could access it this way:
bound_names = get_variable_names(target_variable)
var_string = bound_names[0]
Possible for Python >= 3.8 (with f'{var=}' string )
Not sure if this could be used in production code, but in Python 3.8(and up) you can use f' string debugging specifier. Add = at the end of an expression, and it will print both the expression and its value:
my_salary_variable = 5000
print(f'{my_salary_variable = }')
Output:
my_salary_variable = 5000
To uncover this magic here is another example:
param_list = f'{my_salary_variable=}'.split('=')
print(param_list)
Output:
['my_salary_variable', '5000']
Explanation: when you put '=' after your var in f'string, it returns a string with variable name, '=' and its value. Split it with .split('=') and get a List of 2 strings, [0] - your_variable_name, and [1] - actual object of variable.
Pick up [0] element of the list if you need variable name only.
my_salary_variable = 5000
param_list = f'{my_salary_variable=}'.split('=')
print(param_list[0])
Output:
my_salary_variable
or, in one line
my_salary_variable = 5000
print(f'{my_salary_variable=}'.split('=')[0])
Output:
my_salary_variable
Works with functions too:
def my_super_calc_foo(number):
return number**3
print(f'{my_super_calc_foo(5) = }')
print(f'{my_super_calc_foo(5)=}'.split('='))
Output:
my_super_calc_foo(5) = 125
['my_super_calc_foo(5)', '125']
Process finished with exit code 0
This module works for converting variables names to a string:
https://pypi.org/project/varname/
Use it like this:
from varname import nameof
variable=0
name=nameof(variable)
print(name)
//output: variable
Install it by:
pip install varname
print "var"
print "something_else"
Or did you mean something_else?

Categories

Resources