我有一个对象(Person),其中有多个子对象(Pet, Residence)作为属性。我希望能够像这样动态设置这些子对象的属性:
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'}
{'pet': <__main__.Pet object at 0x10c5ec050>, 'residence': <__main__.Residence object at 0x10c5ec0d0>, 'pet.name': 'Sparky', 'residence.type': 'Apartment'}
如您所见,不是name在的Pet子对象上设置属性,而是在上创建Person了新属性。pet.name``Person
name
Pet
pet.name``Person
我无法指定person.pet,setattr()因为将通过相同的方法设置不同的子对象,该方法将解析一些文本并在/找到相关键时填充对象属性。
person.pet
setattr()
是否有简单/内置的方法来完成此任务?
还是我需要编写一个递归函数来解析字符串并getattr()多次调用,直到找到必要的子对象,然后再调用setattr()找到的子对象?
getattr()
您可以使用functools.reduce:
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是插入式替代getattr和setattr,这也可以处理点attr串。
rgetattr
rsetattr
getattr
setattr
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