python - getattr and setattr on nested objects? -
this simple problem hopefuly easy point out mistake or if possible.
i have object has multiple objects properties. want able dynamically set properties of these objects so:
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__
the output is:
{'pet': <main.pet object @ 0x10c5ec050>, 'residence': <main.residence object @ 0x10c5ec0d0>, 'pet.name': 'sparky', 'residence.type': 'apartment'}
as can see, rather having name attribute set on pet object of person, new attribute "pet.name" created.
i cannot specify person.pet setattr because different child-objects set same method, parsing text , filling in object attributes if/when relevant key found.
is there easy/built in way accomplish this?
or perhaps need write recursive function parse string , call getattr multiple times until necessary child-object found , call setattr on found object?
thank you!
you use functools.reduce
:
import functools def rsetattr(obj, attr, val): pre, _, post = attr.rpartition('.') return setattr(rgetattr(obj, pre) if pre else obj, post, val) sentinel = object() def rgetattr(obj, attr, default=sentinel): if default sentinel: _getattr = getattr else: def _getattr(obj, name): return getattr(obj, name, default) return functools.reduce(_getattr, [obj]+attr.split('.'))
rgetattr
, rsetattr
drop-in replacements getattr
, setattr
, can handle dotted attr
strings.
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) sentinel = object() def rgetattr(obj, attr, default=sentinel): if default sentinel: _getattr = getattr else: def _getattr(obj, name): return getattr(obj, name, default) return functools.reduce(_getattr, [obj]+attr.split('.'))
if __name__=='__main__': p = person() print(rgetattr(p, 'pet.favorite.color', 'calico')) # 'calico' try: # without default argument, `rgetattr`, `getattr`, raises # attributeerror when dotted attribute missing print(rgetattr(p, 'pet.favorite.color')) except attributeerror 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
Comments
Post a Comment