问题
I'm trying to define an attribute in a nested class and then access it later using a string or maybe a list of strings. Here's code for what I'm trying to do
class MyNestedClass(object):
def __init__(self):
self.att1 = 5.
class MyClass(object):
def __init__(self):
self.my_nested_inst = MyNestedClass()
my_inst = MyClass()
I want to change the value of my_inst.my_nested_inst.att1
when all I have is a list like this: my_list = ['my_inst','my_nested_inst','att1']
.
If I use this:
vars(vars(vars()[my_list[0]])[my_list[1]])[my_list[2]]
This works, but the problem is that I need to extend it to an arbitrary depth of nested instances. I can't figure out a good way to make this work with a for loop. Any help is greatly appreciated.
Also, note that converting a string to a variable name in the global namespace has been well addressed, but none of the answers seem to apply here.
EDIT1: I'll try to explain why I'm doing this, and let me know if I do a poor job of explaining. I am using scipy.optimize.fmin, and I have been using only 4 parameters for optimzation. However, I now want to expand my optimization code to handle an arbitrary number of parameters, some of which are nested attributes several layers into the class/instance hierarchy. I want to be able to create a list or dictionary at the top level to tell fmin how to unpack the parameter array for setting the nested attributes.
回答1:
You can use operator.attrgetter to get nested attributes by specifying an attribute name containing dots (requires Python 2.6+):
After
f = attrgetter('date.month')
, the callf(b)
returnsb.date.month
.
For convenience, you can create a pair of helper functions:
def get_nested_attr(vars_dict, attrs):
inst = vars_dict[attrs[0]]
return operator.attrgetter('.'.join(attrs[1:]))(inst)
def set_nested_attr(vars_dict, attrs, value):
setattr(get_nested_attr(vars_dict, attrs[0:-1]), attrs[-1], value)
Here's a complete example (tested with Python 2.7.2):
import operator
class MyNestedClass(object):
def __init__(self):
self.att1 = 5.
class MyClass(object):
def __init__(self):
self.my_nested_inst = MyNestedClass()
def get_nested_attr(vars_dict, attrs):
inst = vars_dict[attrs[0]]
return operator.attrgetter('.'.join(attrs[1:]))(inst)
def set_nested_attr(vars_dict, attrs, value):
setattr(get_nested_attr(vars_dict, attrs[0:-1]), attrs[-1], value)
my_inst = MyClass()
my_list = ['my_inst','my_nested_inst','att1']
assert(my_inst.my_nested_inst.att1 == 5.)
set_nested_attr(vars(), my_list, 10.)
assert(my_inst.my_nested_inst.att1 == 10.)
回答2:
Assuming the last )
in your vars...
was a typo, you have various choices for implementing a function like:
def namespace(names):
"""
returns vars(vars(..vars(locals())[names[0]])..[names[-2]])[names[-1]]
"""
- Use
reduce
(as someone wrote in an answer which he then deleted...) - A recursive function
- A function which explicitly uses a stack and a while-loop
Perhaps the easiest to understand is the recursive implementation:
def namespace(myList):
if len(myList)==0:
return locals()
else:
oneLevelUp = namespace(myList[:-1])
return vars(oneLevelUp[myList[-1]])
The reduce implementation equally elegant. Here's how reduce
works:
>>> functools.reduce(lambda a,b:[a,b], range(4), 'x')
[[[['x', 0], 1], 2], 3]
The implementation:
def namespace(myList):
return reduce(lambda ns,item:vars(ns[item]), myList, locals())
回答3:
Edit: As pointed out in the comments, attrgetter
doesn't work the way I used it. You have to
use reduce or a loop.
来源:https://stackoverflow.com/questions/9523934/use-a-dict-to-access-nested-instances-of-classes-in-python