I want to call the function bar() from the instance foo, just like this:
foo.bar()
But both instance and function names come from a given string. I tried getattr but it just let me use a string for the function, not the instance:
strbar = 'bar'
getattr(foo, strbar)()
What I want to do is something like:
strfoo = 'foo'
strbar = 'bar'
getattr(strfoo, strbar)()
But it gives me:
AttributeError: 'str' object has no attribute 'bar'
I know a dictionary could be an option, but that makes me write a really long dictionary.
The best way would be to have a dictionary or a similar structure.
However you could use eval which is evil. Or you can get your instance from the locals() dictionary:
getattr(locals()[instance_name], attribute_name)
I would think about redesigning your code. There must be a better solution as eval or locals ... like a dictionary ...
Related
Construction getattr(obj, 'attr1.attr2', None) does not work.
What are the best practices to replace this construction?
Divide that into two getattr statements?
You can use operator.attrgetter() in order to get multiple attributes at once:
from operator import attrgetter
my_attrs = attrgetter(attr1, attr2)(obj)
As stated in this answer, the most straightforward solution would be to use operator.attrgetter (more info in this python docs page).
If for some reason, this solution doesn't make you happy, you could use this code snippet:
def multi_getattr(obj, attr, default = None):
"""
Get a named attribute from an object; multi_getattr(x, 'a.b.c.d') is
equivalent to x.a.b.c.d. When a default argument is given, it is
returned when any attribute in the chain doesn't exist; without
it, an exception is raised when a missing attribute is encountered.
"""
attributes = attr.split(".")
for i in attributes:
try:
obj = getattr(obj, i)
except AttributeError:
if default:
return default
else:
raise
return obj
# Example usage
obj = [1,2,3]
attr = "append.__doc__.capitalize.__doc__"
multi_getattr(obj, attr) #Will return the docstring for the
#capitalize method of the builtin string
#object
from this page, which does work. I tested and used it.
I would suggest using something like this:
from operator import attrgetter
attrgetter('attr0.attr1.attr2.attr3')(obj)
If you have the attribute names you want to get in a list, you can do the following:
my_attrs = [getattr(obj, attr) for attr in attr_list]
A simple, but not very eloquent way, to get multiple attr would be to use tuples with or without brackets something like
aval, bval = getattr(myObj,"a"), getattr(myObj,"b")
but I think you might be wanting instead to get atrribute of a contained object with the way you are using dot notation. In which case it would be something like
getattr(myObj.contained, "c")
where contained is an object cotained within myObj object and c is an attribute of contained. Let me know if this is not what you want.
I am trying to log with syslog. I have a string with the facility in it. In this case "LOG_LOCAL0". I want to use this string to access syslog.LOG_LOCAL0, which is an int of value 128. If this was a function I would use getattr() but I do not know how do do it for something that is not callable like an int, how do I?
Basically I want to call syslog.LOG_LOCAL0 but have the "LOG_LOCAL0" as a string so I can't.
If you're trying to set a value you want setattr(object, name, value) (note there's no underscore btw). So you could do:
setattr(syslog, "LOG_LOCAL0", "The log value")
If you want to use the value then getattr is still appropriate
getattr(syslog, "LOG_LOCAL0") # syslog.LOG_LOCAL0 for all intents and purposes
You can use __dict__ it returns a dictionary of all functions and variables in the class , and then use the variable you want to get as key, to get its value.
Example -
>>> class CA:
... i = 15
... def hello(self):
... print("Hello")
...
>>> CA.__dict__
mappingproxy({'__dict__': <attribute '__dict__' of 'CA' objects>, '
>>> CA.__dict__['i']
15
In your case maybe -
syslog.__dict__['LOG_LOCAL0']
Though a footnote from here -
Except for one thing. Module objects have a secret read-only attribute called dict which returns the dictionary used to implement the module’s namespace; the name dict is an attribute but not a global name. Obviously, using this violates the abstraction of namespace implementation, and should be restricted to things like post-mortem debuggers.
How would I go about converting a string into a function call for a function that is within the same class? I used this question to help a bit but I think it has something to do with "self.".
ran_test_opt = choice(test_options)
ran_test_func = globals()[ran_test_opt]
ran_test_func()
where test_options is a list of the names of the functions available in string format. With the above code I get the error
KeyError: 'random_aoi'
Don't use globals() (the functions are not in the global symbol table), just use getattr:
ran_test_func = getattr(self, ran_test_opt)
globals() is a function that you should use very, very rarely, it smells of mixing code and data. Calling an instance method by a name found in a string is similar, but slightly less hacky. Use getattr:
ran_test_func = getattr(self, ran_test_opt)
ran_test_func()
I have 5 objects, mac5_le(), mac4_le and so on. I am trying to extract some value from each of the objects as follows,
for i in range(5,-1,-1):
m = locals()['self.mac'+str(i)+'_le.text()']
print m
I am getting the error as KeyError: 'self.mac5_le.text()'.
Any Idea?
What the what?
m = getattr(self, 'mac%d_le' % i).text()
Not sure why you would want to munge objects around that way, but you've definitely got your syntax wrong:
locals()['self.mac'+str(i)+'_le'].text()
should "work".
I see a few things wrong with what you're attempting. First, self.name variables are not local scope. They're either part of the instance, or part of the class. Locals are variables that are accessible from your current function scope, but not the global scope. For instance, in the code below, you would see foo and bar, but not baz:
baz = 1
def silly():
# These two variables are local
foo = 2
bar = 3
print locals()
Calling silly():
>>> silly()
{'foo': 2, 'bar': 3}
Secondly, the locals() and globals() dictionaries don't resolve the dot operator, nor will they call functions.
What you want to do is use something like getattr, or create an api that works better for you (the code you're trying to write isn't very idiomatic Python). Here's what is might look like with getattr:
for i in range(5,-1,-1):
m = getattr(self, 'mac'+str(i)+'_le').text()
print m
getattr is will do all the right lookups underneath the hood to find macN_le. Once you have a reference to the object, then you can call .text() on it.
Hope that helps!
I want a dict or tuple I can sort based on attributes of the objects I'm using as arguments for *arg. The way I've been trying to do it just gives me AttributeErrors, which leads me to believe I'm doing it weird.
def function(*arg):
items = {}
for thing in arg:
items.update({thing.name:thing})
while True:
for thing in items:
## lots of other code here, basically just a game loop.
## Problem is that the 'turn order' is based on whatever
## Python decides the order of arguments is inside "items".
## I'd like to be able to sort the dict based on each object's
## attributes (ie, highest 'thing.speed' goes first in the while loop)
The problem is when I try to sort "items" based on an attribute of the objects I put into function(), it gives me "AttributeError: 'str' object has no attribute 'attribute'". Which leads me to believe I'm either unpacking *arg in a lousy way, or I'm trying to do something the wrong way.
while True:
for thing in sorted(items, key=attrgetter('attribute')):
...doesn't work either, keeps telling me I'm trying to manipulate a 'str' object. What am I not doing here?
arg already is a tuple you can sort by an attribute of each item:
def function(*args):
for thing in sorted(args, key=attrgetter('attribute')):
When you iterate over a dict, as sorted is doing, you just get the keys, not the values. So, if you want to use a dict, you need to do:
def function(*args):
# or use a dict comprehension on 2.7+
items = dict((thing.name, thing) for thing in args)
# or just items.values on 3+
for thing in sorted(items.itervalues(), key=attrgetter('attribute')):
to actually sort the args by an attribute. If you want the keys of the dict available as well (not necessary here because the key is also an attribute of the item), use something like:
for name, thing in sorted(items.iteritems(), key=lambda item: item[1].attribute):
Your items is a dict, you can't properly sort a dict. When you try to use it as an iterable, it silently returns its keys list, which is a list of strings. And you don't use your arg after creating a dict.
If you don't need dict lookup, as you just iterate through it, you can replace dict with list of 2-tuples (thing.name, thing), sort it by any attribute and iterate through it. You can also use collections.OrderedDict from Python 2.7 (it exists as a separate ordereddict package for earlier versions) if you really want both dict lookup and ordering.
{edit} Thanks to agf, I understood the problem. So, what I wrote below is a good answer in itself, but not when related to the question above... I let it here for the trace.
Looking to the answers, I may have not understood the question. But here's my understanding: as args is a tuple of arguments you give to your function, it's likely that none of these arguments is an object with a name attribute. But, looking to the errors you report, you're giving string arguments.
Maybe some illustration will help my description:
>>> # defining a function using name attribute
>>> def f(*args):
... for arg in args:
... print arg.name
>>> # defining an object with a name attribute
>>> class o(object):
... def __init__(self, name):
... self.name = name
>>> # now applying the function on the previous object, and on a string
>>> f( o('arg 1'), 'arg 2' )
arg 1
Traceback (most recent call last):
File "<pyshell#9>", line 1, in <module>
f(o('arg 1'), 'ets')
File "<pyshell#3>", line 3, in f
print arg.name
AttributeError: 'str' object has no attribute 'name'
This is failing as strings have no such attribute.
For me, in your code, there is a mistake: you're trying to use attribute name on your inputs, without ever verifying that they have such an attribute. Maybe you should test with hasattr first:
>>> if hasattr(arg, 'name'):
... print arg.name
... else:
... print arg
or with some inspection on the input, to verify if it's an instance of a given class, known to have the requested attribute.