嵌套子对象/链接属性上的getattr和setattr?

发布于 2021-01-29 15:10:13

我有一个对象(Person),其中有多个子对象(Pet, Residence)作为属性。我希望能够像这样动态设置这些子对象的属性:

class Person(object):
    def __init__(self):
        self.pet = Pet()
        self.residence = Residence()

class Pet(object):
    def __init__(self,name='Fido',species='Dog'):
        self.name = name
        self.species = species

class Residence(object):
    def __init__(self,type='House',sqft=None):
        self.type = type
        self.sqft=sqft


if __name__=='__main__':
    p=Person()
    setattr(p,'pet.name','Sparky')
    setattr(p,'residence.type','Apartment')
    print p.__dict__

目前我得到了错误的输出: {'pet': <__main__.Pet object at 0x10c5ec050>, 'residence': <__main__.Residence object at 0x10c5ec0d0>, 'pet.name': 'Sparky', 'residence.type': 'Apartment'}

如您所见,不是name在的Pet子对象上设置属性,而是在上创建Person了新属性。pet.name``Person

  • 我无法指定person.petsetattr()因为将通过相同的方法设置不同的子对象,该方法将解析一些文本并在/找到相关键时填充对象属性。

  • 是否有简单/内置的方法来完成此任务?

  • 还是我需要编写一个递归函数来解析字符串并getattr()多次调用,直到找到必要的子对象,然后再调用setattr()找到的子对象?

关注者
0
被浏览
47
1 个回答
  • 面试哥
    面试哥 2021-01-29
    为面试而生,有面试问题,就找面试哥。

    您可以使用functools.reduce

    import functools
    
    def rsetattr(obj, attr, val):
        pre, _, post = attr.rpartition('.')
        return setattr(rgetattr(obj, pre) if pre else obj, post, val)
    
    # using wonder's beautiful simplification: https://stackoverflow.com/questions/31174295/getattr-and-setattr-on-nested-objects/31174427?noredirect=1#comment86638618_31174427
    
    def rgetattr(obj, attr, *args):
        def _getattr(obj, attr):
            return getattr(obj, attr, *args)
        return functools.reduce(_getattr, [obj] + attr.split('.'))
    

    rgetattr并且rsetattr是插入式替代getattrsetattr,这也可以处理点attr串。


    import functools
    
    class Person(object):
        def __init__(self):
            self.pet = Pet()
            self.residence = Residence()
    
    class Pet(object):
        def __init__(self,name='Fido',species='Dog'):
            self.name = name
            self.species = species
    
    class Residence(object):
        def __init__(self,type='House',sqft=None):
            self.type = type
            self.sqft=sqft
    
    def rsetattr(obj, attr, val):
        pre, _, post = attr.rpartition('.')
        return setattr(rgetattr(obj, pre) if pre else obj, post, val)
    
    def rgetattr(obj, attr, *args):
        def _getattr(obj, attr):
            return getattr(obj, attr, *args)
        return functools.reduce(_getattr, [obj] + attr.split('.'))
    

    if __name__=='__main__':
        p = Person()
        print(rgetattr(p, 'pet.favorite.color', 'calico'))
        # 'calico'
    
        try:
            # Without a default argument, `rgetattr`, like `getattr`, raises
            # AttributeError when the dotted attribute is missing
            print(rgetattr(p, 'pet.favorite.color'))
        except AttributeError as err:
            print(err)
            # 'Pet' object has no attribute 'favorite'
    
        rsetattr(p, 'pet.name', 'Sparky')
        rsetattr(p, 'residence.type', 'Apartment')
        print(p.__dict__)
        print(p.pet.name)
        # Sparky
        print(p.residence.type)
        # Apartment
    


知识点
面圈网VIP题库

面圈网VIP题库全新上线,海量真题题库资源。 90大类考试,超10万份考试真题开放下载啦

去下载看看