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.