python python-2.7 recursion attributes setattr

python - getattr y setattr en objetos anidados?



python-2.7 recursion (3)

Ok, al escribir la pregunta tuve una idea de cómo hacer esto y parece funcionar bien. Aquí es lo que se me ocurrió:

def set_attribute(obj, path_string, new_value): parts = path_string.split(''.'') final_attribute_index = len(parts)-1 current_attribute = obj i = 0 for part in parts: new_attr = getattr(current_attribute, part, None) if current_attribute is None: print ''Error %s not found in %s'' % (part, current_attribute) break if i == final_attribute_index: setattr(current_attribute, part, new_value) current_attribute = new_attr i+=1 def get_attribute(obj, path_string): parts = path_string.split(''.'') final_attribute_index = len(parts)-1 current_attribute = obj i = 0 for part in parts: new_attr = getattr(current_attribute, part, None) if current_attribute is None: print ''Error %s not found in %s'' % (part, current_attribute) return None if i == final_attribute_index: return getattr(current_attribute, part) current_attribute = new_attr i += 1

Supongo que esto resuelve mi pregunta, pero todavía tengo curiosidad de saber si hay una mejor manera de hacerlo.

Siento que esto tiene que ser algo bastante común en OOP y python, así que estoy sorprendido de que gatattr y setattr no lo admitan de forma nativa.

este es probablemente un problema simple, por lo que es fácil para alguien señalar mi error o si esto es posible.

Tengo un objeto que tiene múltiples objetos como propiedades. Quiero ser capaz de establecer dinámicamente las propiedades de estos objetos así:

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__

El resultado es:

{''mascota'': <objeto .Pet principal en 0x10c5ec050>, ''residencia'': <objeto de residencia principal en 0x10c5ec0d0>, ''nombre de mascota'': ''Sparky'', ''residence.type'': ''Apartamento''}

Como puede ver, en lugar de tener el atributo de nombre establecido en el objeto mascota de la persona, se crea un nuevo atributo "pet.name".

No puedo especificar person.pet to setattr porque los diferentes objetos secundarios se establecerán con el mismo método, que es analizar un poco de texto y completar los atributos del objeto si / cuando se encuentra una clave relevante.

¿Hay una forma fácil / construida para lograr esto?

¿O quizás necesito escribir una función recursiva para analizar la cadena y llamar a getattr varias veces hasta que se encuentre el objeto hijo necesario y luego llamar a setattr en ese objeto encontrado?

¡Gracias!


Puedes usar 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 is sentinel: _getattr = getattr else: def _getattr(obj, name): return getattr(obj, name, default) return functools.reduce(_getattr, [obj]+attr.split(''.''))

rgetattr y rsetattr son reemplazos getattr para getattr y setattr , que también pueden manejar cadenas de attr punteadas.

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 is 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 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


Para un padre y un hijo:

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

Esto es más simple que las otras respuestas para este caso de uso particular.