Nombrado como la clase

Me encuentro escribiendo esta clase a menudo en mi código de Python cuando necesito una clase de uso único rápido.

class Struct(object): def __init__( self, **kwargs ): for k in kwargs: setattr(self,k,kwargs[k]) 

La idea básica es que puedo hacer cosas rápidas como esta:

 foo = Struct( bar='one', baz=1 ) print foo.bar foo.baz += 1 foo.novo = 42 # I don't do this as often. 

Por supuesto, esto no se adapta bien y agregar métodos es una locura, pero aún así tengo suficientes clases de desecho que solo sigo usando.

Esto es lo que pensé que se llamaría timbre . Pero la syntax de la pareja nombrada es grande y difícil de manejar.

¿Hay algo en la biblioteca estándar que no haya encontrado todavía que haga esto también o mejor?

¿Es este mal estilo malo? ¿O tiene algún defecto oculto?

actualizar

Dos ejemplos concretos para mostrar por qué no solo uso un dict. Ambos ejemplos podrían hacerse con un dictado, pero obviamente no es idiomático.

 #I know an order preserving dict would be better but they don't exist in 2.6. closure = Struct(count=0) def mk_Foo( name, path ): closure.count += 1 return (name, Foo( name, path, closure.count )) d = dict([ mk_Foo( 'a', 'abc' ), mk_Foo( 'b', 'def' ), # 20 or so more ] ) @contextmanager def deleter( path ): control = Struct(delete=True,path=path) try: yield control finally: if control.delete: shutil.rmtree(path) with deleter( tempfile.mkdtemp() ) as tmp: # do stuff with tmp.path # most contexts don't modify the delete member # but occasionally it's needed if keep_tmp_dir: tmp.delete = False 

Hay una receta de Python para esto (solo actualiza el dictado de la instancia en lugar de llamar a setattr) Receta 52308

 class Bunch(object): def __init__(self, **kwds): self.__dict__.update(kwds) 

Desde Python 3.3 y más adelante, puedes usar tipos.SimpleNamespace :

 >>> import types >>> foo = types.SimpleNamespace(bar='one', baz=1) >>> print(foo.bar) one >>> foo.baz += 1 >>> foo.novo = 42 

El tipo incorporado es aproximadamente equivalente al siguiente código:

 class SimpleNamespace: def __init__(self, **kwargs): self.__dict__.update(kwargs) def __repr__(self): keys = sorted(self.__dict__) items = ("{}={!r}".format(k, self.__dict__[k]) for k in keys) return "{}({})".format(type(self).__name__, ", ".join(items)) def __eq__(self, other): return self.__dict__ == other.__dict__ 

actualizar

A partir de Python 3.7, puede usar el módulo de clase de datos :

 from dataclasses import dataclass, field @dataclass class Struct: bar: str = field(default='one') baz: int = field(default=1) 

Puedes usar esto de la siguiente manera:

 foo = Struct( bar='one', baz=1 ) print(foo.bar) foo.baz += 1 foo.novo = 42 

Por defecto, incorpora pruebas de igualdad y una reproducción agradable:

 >>> foo == Struct(bar='one', baz=2) True >>> foo Struct(bar='one', baz=2) 
 class t(dict): def __init__(self, **kwargs): for key, value in kwargs.items(): dict.__setitem__(self, key, value) def __getattr__(self, key): return dict.__getitem__(self, key) def __setattr__(self, key, value): raise StandardError("Cannot set attributes of tuple") def __setitem__(self, key, value): raise StandardError("Cannot set attributes of tuple") def __delitem__(self, key): raise StandardError("Cannot delete attributes of tuple") point = t(x=10, y=500, z=-50) print point.x # 10 print point.y # 500 print point['z'] # -50 print point # {'z': -50, 'y': 500, 'x': 10} point.x = 100 # StandardError: cannot set attributes of tuple point.y += 5 # StandardError: cannot set attributes of tuple point.z = -1 # StandardError: cannot set attributes of tuple def hypo(x, y, z): return (x**2 + y**2 + z**2)**0.5 print hypo(point) # TypeError: unsupported operand type(s) print hypo(**point) # 502.593274925 for k in point.items(): print k # ('y', 500) # ('x', 10) # ('z', -50) for k in point.keys(): print k # x # y # z for k in point.values(): print k # 500 # 10 # -50 print len(point) # 3 print dict(point) # {'y': 500, 'x': 10, 'z': -50} 

Esta es mi solución a este problema. Hermosa syntax, inmutable (al menos sin recurrir a algún objeto desagradable. Setattr () gimnasia), ligera y bastante imprimible. Aunque no hay nada que puedas hacer con esto que no puedas hacer con un dict,

 point = t(x=10, y=20, z=30) d = point.x ** 2 + point.y ** 2 + point.z ** 2 

tiene una simetría muy agradable con

 point = (10, 20, 30) d = point[0] ** 2 + point[1] ** 2 + point[2] ** 2 

y en general es mucho más limpio que

 point = {'x': 10, 'y': 20, 'z': 30} d = point['x'] ** 2 + point['y'] ** 2 + point['z'] ** 2 

Lo que tienes es un prototipo perfectamente razonable, pero tienes razón en que no se puede escalar.

Si te gusta usarlos, pero quieres tener una ruta para un mejor código más adelante, esto es lo que sugeriría:

  • Cada vez que haces eso, estructura de subclase:

    class Control(Structure): pass

  • más adelante, cuando desee una clase “real”, reemplace la superclase con algo como strongbox.Strongbox ( ejemplo de uso ) que proporciona el mismo constructor y la misma interfaz de atributos, pero restringe las ranuras que puede completar.

Una disciplina como esta solo le cuesta una línea adicional por adelantado, y no romperá su código si quiere más poder más adelante.

Es posible que desee ver los registros de George Sakkis . Ha funcionado bien para mí como una “tupla llamada mutable”.