Use a dict to access nested instances of classes in Python

狂风中的少年 提交于 2019-12-11 03:47:10

问题


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 call f(b) returns b.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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!