¿Cómo agregar propiedad a una clase dinámicamente?

215

El objetivo es crear una clase simulada que se comporte como un conjunto de resultados db.

Entonces, por ejemplo, si regresa una consulta de base de datos, usando una expresión dict {'ab':100, 'cd':200}, entonces me gustaría ver:

>>> dummy.ab
100

Al principio pensé que tal vez podría hacerlo de esta manera:

ks = ['ab', 'cd']
vs = [12, 34]
class C(dict):
    def __init__(self, ks, vs):
        for i, k in enumerate(ks):
            self[k] = vs[i]
            setattr(self, k, property(lambda x: vs[i], self.fn_readyonly))

    def fn_readonly(self, v)
        raise "It is ready only"

if __name__ == "__main__":
    c = C(ks, vs)
    print c.ab

pero c.abdevuelve un objeto de propiedad en su lugar.

Reemplazar la setattrlínea con k = property(lambda x: vs[i])no sirve para nada.

Entonces, ¿cuál es la forma correcta de crear una propiedad de instancia en tiempo de ejecución?

PD: Conozco una alternativa presentada en ¿Cómo se __getattribute__usa el método?

Anthony Kong
fuente
2
Hay algunos errores tipográficos en su código: la definición de fn_readonly necesita una :y __init__referencias self.fn_readyonly.
mhawke
Tienes razón. Agregué esa función setter en el último minuto para subrayar la razón de crear una propiedad en tiempo de ejecución.
Anthony Kong
El problema principal que tuve con la creación de propiedades en la inicialización fue que, en algunos casos, si llamaba a los ayudantes rápidamente después, o si había un problema, recibía un error de que no existían a pesar del hecho de que sí. En mi solución a continuación, creo 2 clases. Uno como Base / Parent (que estoy tratando de encontrar una solución para evitar), y el objeto principal, que extiende la Base / Parent. Luego, en el objeto principal, sin inicializar, llamo a mi creador AccessorFunc que crea las propiedades, funciones auxiliares y más.
Acecool
es decir: clase ExampleBase: pass; clase Ejemplo (ExampleBase): __x = Accessor (ExampleBase, 'x', 'X', 123); --- que crearía una propiedad debajo de x y funciones con nombre usando X, por lo que GetX, SetX y más ... y .x, ._x y .__ x para la propiedad. Entonces .x es la propiedad en sí misma para que los datos pasen (obteniendo / configurando a través de self.x = 123; o self.x a la salida). Utilicé self._x para los datos RAW almacenados para que pudiera ser fácilmente accesible, ya que también permití asignar valores predeterminados, sin establecerlos en los datos almacenados. entonces _x podría ser None y .x podría devolver 123. y .__ x vinculado al
Accesor
Aquí hay un enlace a la versión básica que crea propiedades dinámicas y funciones dinámicas: el archivo tiene un montón de enlaces a otras versiones. Uno es el sistema AccessorFunc que usa una función para crear ayudantes (uno para funciones, uno para propiedades, uno para ambos como elementos individuales, por lo que no usa acortamiento de código en nada en ese archivo). Si falta algo, uno de los otros archivos lo tienen: dropbox.com/s/phnnuavssmzeqrr/dynamic_properties_simple.py?dl=0
Acecool

Respuestas:

333

Supongo que debería ampliar esta respuesta, ahora que soy mayor y más sabio y sé lo que está sucediendo. Mejor tarde que nunca.

Usted puede agregar una propiedad a una clase dinámicamente. Pero ese es el truco: tienes que agregarlo a la clase .

>>> class Foo(object):
...     pass
... 
>>> foo = Foo()
>>> foo.a = 3
>>> Foo.b = property(lambda self: self.a + 1)
>>> foo.b
4

A propertyes en realidad una implementación simple de una cosa llamada descriptor . Es un objeto que proporciona un manejo personalizado para un atributo dado, en una clase dada . Algo así como una forma de factorizar un ifárbol enorme __getattribute__.

Cuando pido foo.ben el ejemplo anterior, Python ve que el bdefinido en los implementos de clase el protocolo descriptor -que simplemente significa que es un objeto con una __get__, __set__o __delete__método. El descriptor se responsabiliza de manejar ese atributo, por lo que Python llama Foo.b.__get__(foo, Foo), y el valor de retorno se le devuelve como el valor del atributo. En el caso de propertycada uno de estos métodos sólo llama a la fget, fseto fdelse pasan al propertyconstructor.

Los descriptores son realmente la forma en que Python expone la plomería de toda su implementación OO. De hecho, hay otro tipo de descriptor aún más común que property.

>>> class Foo(object):
...     def bar(self):
...         pass
... 
>>> Foo().bar
<bound method Foo.bar of <__main__.Foo object at 0x7f2a439d5dd0>>
>>> Foo().bar.__get__
<method-wrapper '__get__' of instancemethod object at 0x7f2a43a8a5a0>

El método humilde es solo otro tipo de descriptor. Su __get__tachuelas en la instancia de llamada como primer argumento; en efecto, hace esto:

def __get__(self, instance, owner):
    return functools.partial(self.function, instance)

De todos modos, sospecho que esta es la razón por la cual los descriptores solo funcionan en las clases: en primer lugar, son una formalización de las cosas que potencian las clases. Incluso son la excepción a la regla: ¡obviamente puedes asignar descriptores a una clase, y las clases son en sí mismas instancias type! De hecho, tratar de leer Foo.baún llamadas property.__get__; es idiomático que los descriptores regresen a sí mismos cuando se accede a ellos como atributos de clase.

Creo que es genial que prácticamente todo el sistema OO de Python se pueda expresar en Python. :)

Ah, y escribí una publicación de blog sobre descriptores hace un tiempo, si estás interesado.

Eevee
fuente
35
No es necesario agregar el método add_property. setattr (Foo, 'name', property (func))
Courtney D
8
Tu "Pero esa es la trampa ..." me ahorró varias horas de trabajo. Gracias.
Matt Howell
2
Si desea definir una propiedad en una sola instancia, puede crear una clase en tiempo de ejecución y modificar __class__ .
Wilfred Hughes
1
¿Qué pasa con @ myproperty.setter? ¿Cómo agregarlo dinámicamente?
LRMAAX
No necesita agregar una propiedad a un objeto inicializado. Hacerlo puede significar que se adhiere solo a la instancia, pero tendría que verificarlo dos veces. Sé que me encontré con un problema similar en el que mis propiedades dinámicas eran solo de instancia, también terminé con una configuración estática y la que quería para el objeto para que las futuras inicializaciones las utilizaran. Mi publicación está debajo y crea funciones de ayuda y formas fáciles de acceder fácilmente a todo. .x para la propiedad, ._x para los datos sin procesar que usa el captador / definidor (que puede ser Ninguno) y .__ x para el objeto de acceso.
Acecool
57

El objetivo es crear una clase simulada que se comporte como un conjunto de resultados db.

Entonces, ¿qué quieres es un diccionario donde puedas deletrear a ['b'] como ab?

Eso es fácil:

class atdict(dict):
    __getattr__= dict.__getitem__
    __setattr__= dict.__setitem__
    __delattr__= dict.__delitem__
bobince
fuente
1
En una configuración más general, esto tiene un propósito limitado. si el dict tiene una jerarquía multinivel, como d = {'a1': {'b': 'c'}, 'a2': ...}, entonces mientras puede hacer d.a1 o d.a2, puede ' t do d.a1.b
Shreyas
1
Una cosa a tener en cuenta es que esto permite el establecimiento de valores de atributo para atributos con el mismo nombre que dict métodos o atributos, pero no permite recuperar los valores de la misma manera: d.items = 1, d.itemsretornos <built-in method items of atdict object at ...>. Todavía podría hacer d["items"]o usar en __getattribute__lugar de __getattr__, pero esto evita el uso de la mayoría de los métodos del dict.
Marcono1234
¡Solo usa la biblioteca munch ! (tenedor del grupo)
Brian Peterson
38

Parece que podría resolver este problema mucho más simplemente con un namedtuple, ya que conoce la lista completa de campos con anticipación.

from collections import namedtuple

Foo = namedtuple('Foo', ['bar', 'quux'])

foo = Foo(bar=13, quux=74)
print foo.bar, foo.quux

foo2 = Foo()  # error

Si absolutamente necesita escribir su propio setter, tendrá que hacer la metaprogramación a nivel de clase; property()no funciona en instancias.

Eevee
fuente
Gran idea. Lamentablemente, estoy atascado con Python 2.4 en este momento.
Anthony Kong
2
El tipo que escribió namedtuplemerece un premio por hacer suave y elegante ser fieles a los principios orientados a objetos.
Keith Pinson
44
Lo sentimos, en el mejor de los casos, esta respuesta solo es aplicable al caso especial en el que una clase de wanteda que consta de solo atributos de solo lectura, todos conocen de antemano. En otras palabras, no creo que aborde la cuestión más amplia de cómo agregar propiedades generales, no solo de solo lectura, a una clase en tiempo de ejecución (ni la versión actual del otro "complemento" también responde publicado por el autor).
Martineau
@martineau así que ... pasa más argumentos a property()? no hay nada en ninguna de las respuestas que sea específico de las propiedades de solo lectura.
Eevee
32

No necesita usar una propiedad para eso. Simplemente anule __setattr__para que sean de solo lectura.

class C(object):
    def __init__(self, keys, values):
        for (key, value) in zip(keys, values):
            self.__dict__[key] = value

    def __setattr__(self, name, value):
        raise Exception("It is read only!")

Tada

>>> c = C('abc', [1,2,3])
>>> c.a
1
>>> c.b
2
>>> c.c
3
>>> c.d
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'C' object has no attribute 'd'
>>> c.d = 42
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 6, in __setattr__
Exception: It is read only!
>>> c.a = 'blah'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 6, in __setattr__
Exception: It is read only!
Ryan
fuente
9

¿Cómo agregar propiedad a una clase de Python dinámicamente?

Supongamos que tiene un objeto al que desea agregar una propiedad. Por lo general, quiero usar propiedades cuando necesito comenzar a administrar el acceso a un atributo en el código que tiene un uso posterior, para poder mantener una API consistente. Ahora normalmente los agregaré al código fuente donde se define el objeto, pero supongamos que no tiene ese acceso, o que realmente necesita elegir dinámicamente sus funciones mediante programación.

Crear una clase

Usando un ejemplo basado en la documentación paraproperty , creemos una clase de objeto con un atributo "oculto" y creemos una instancia del mismo:

class C(object):
    '''basic class'''
    _x = None

o = C()

En Python, esperamos que haya una forma obvia de hacer las cosas. Sin embargo, en este caso, voy a mostrar dos formas: con notación de decorador y sin ella. Primero, sin notación de decorador. Esto puede ser más útil para la asignación dinámica de captadores, establecedores o eliminadores.

Dinámico (también conocido como Monkey Patching)

Creemos algunos para nuestra clase:

def getx(self):
    return self._x

def setx(self, value):
    self._x = value

def delx(self):
    del self._x

Y ahora los asignamos a la propiedad. Tenga en cuenta que podríamos elegir nuestras funciones mediante programación aquí, respondiendo la pregunta dinámica:

C.x = property(getx, setx, delx, "I'm the 'x' property.")

Y uso:

>>> o.x = 'foo'
>>> o.x
'foo'
>>> del o.x
>>> print(o.x)
None
>>> help(C.x)
Help on property:

    I'm the 'x' property.

Decoradores

Podríamos hacer lo mismo que hicimos anteriormente con la notación de decorador, pero en este caso, debemos nombrar todos los métodos con el mismo nombre (y recomendaría mantenerlo igual que el atributo), por lo que la asignación programática no es tan trivial como está utilizando el método anterior:

@property
def x(self):
    '''I'm the 'x' property.'''
    return self._x

@x.setter
def x(self, value):
    self._x = value

@x.deleter
def x(self):
    del self._x

Y asigne el objeto de propiedad con sus establecedores y eliminadores aprovisionados a la clase:

C.x = x

Y uso:

>>> help(C.x)
Help on property:

    I'm the 'x' property.

>>> o.x
>>> o.x = 'foo'
>>> o.x
'foo'
>>> del o.x
>>> print(o.x)
None
Aaron Hall
fuente
5

Hice una pregunta similar en esta publicación de Stack Overflow para crear una fábrica de clases que creara tipos simples. El resultado fue esta respuesta que tenía una versión funcional de la fábrica de la clase. Aquí hay un fragmento de la respuesta:

def Struct(*args, **kwargs):
    def init(self, *iargs, **ikwargs):
        for k,v in kwargs.items():
            setattr(self, k, v)
        for i in range(len(iargs)):
            setattr(self, args[i], iargs[i])
        for k,v in ikwargs.items():
            setattr(self, k, v)

    name = kwargs.pop("name", "MyStruct")
    kwargs.update(dict((k, None) for k in args))
    return type(name, (object,), {'__init__': init, '__slots__': kwargs.keys()})

>>> Person = Struct('fname', 'age')
>>> person1 = Person('Kevin', 25)
>>> person2 = Person(age=42, fname='Terry')
>>> person1.age += 10
>>> person2.age -= 10
>>> person1.fname, person1.age, person2.fname, person2.age
('Kevin', 35, 'Terry', 32)
>>>

Podría usar alguna variación de esto para crear valores predeterminados, que es su objetivo (también hay una respuesta en esa pregunta que trata de esto).

kjfletch
fuente
4

No estoy seguro si entiendo completamente la pregunta, pero puede modificar las propiedades de la instancia en tiempo de ejecución con el incorporado en __dict__su clase:

class C(object):
    def __init__(self, ks, vs):
        self.__dict__ = dict(zip(ks, vs))


if __name__ == "__main__":
    ks = ['ab', 'cd']
    vs = [12, 34]
    c = C(ks, vs)
    print(c.ab) # 12
Creciente Fresco
fuente
En esencia, mi pregunta es averiguar si es posible crear una nueva propiedad en tiempo de ejecución. El consenso parece ser negativo. Su sugerencia es ciertamente simple y práctica. (Lo mismo que otras respuestas que usan dict )
Anthony Kong
Una respuesta simple también sería:self.__dict__[key] = value
Allan Karlson
4

Para aquellos que provienen de motores de búsqueda, estas son las dos cosas que estaba buscando al hablar de propiedades dinámicas :

class Foo:
    def __init__(self):
        # we can dynamically have access to the properties dict using __dict__
        self.__dict__['foo'] = 'bar'

assert Foo().foo == 'bar'


# or we can use __getattr__ and __setattr__ to execute code on set/get
class Bar:
    def __init__(self):
        self._data = {}
    def __getattr__(self, key):
        return self._data[key]
    def __setattr__(self, key, value):
        self._data[key] = value

bar = Bar()
bar.foo = 'bar'
assert bar.foo == 'bar'

__dict__es bueno si desea poner propiedades creadas dinámicamente. __getattr__es bueno hacer algo solo cuando se necesita el valor, como consultar una base de datos. El combo set / get es bueno para simplificar el acceso a los datos almacenados en la clase (como en el ejemplo anterior).

Si solo desea una propiedad dinámica, eche un vistazo a la función incorporada property () .

tleb
fuente
4

No puede agregar una nueva property()a una instancia en tiempo de ejecución, porque las propiedades son descriptores de datos. En su lugar, debe crear dinámicamente una nueva clase o sobrecarga __getattribute__para procesar descriptores de datos en instancias.

Alex Gaynor
fuente
Esto está mal. Puede agregar la propiedad a la clase y luego acceder desde el método.
Ahmed
2

La mejor manera de lograrlo es definiendo __slots__. De esa manera, sus instancias no pueden tener nuevos atributos.

ks = ['ab', 'cd']
vs = [12, 34]

class C(dict):
    __slots__ = []
    def __init__(self, ks, vs): self.update(zip(ks, vs))
    def __getattr__(self, key): return self[key]

if __name__ == "__main__":
    c = C(ks, vs)
    print c.ab

Que imprime 12

    c.ab = 33

Eso da: AttributeError: 'C' object has no attribute 'ab'

nosklo
fuente
2

Solo otro ejemplo de cómo lograr el efecto deseado

class Foo(object):

    _bar = None

    @property
    def bar(self):
        return self._bar

    @bar.setter
    def bar(self, value):
        self._bar = value

    def __init__(self, dyn_property_name):
        setattr(Foo, dyn_property_name, Foo.bar)

Entonces ahora podemos hacer cosas como:

>>> foo = Foo('baz')
>>> foo.baz = 5
>>> foo.bar
5
>>> foo.baz
5
lehins
fuente
2

Aquí hay una solución que:

  • Permite especificar nombres de propiedad como cadenas , por lo que pueden provenir de alguna fuente de datos externa en lugar de estar todos listados en su programa.
  • Agrega las propiedades cuando se define la clase , en lugar de cada vez que se crea un objeto.

Una vez que se ha definido la clase, simplemente haga esto para agregarle una propiedad dinámicamente:

setattr(SomeClass, 'propertyName', property(getter, setter))

Aquí hay un ejemplo completo, probado en Python 3:

#!/usr/bin/env python3

class Foo():
  pass

def get_x(self):
  return 3

def set_x(self, value):
  print("set x on %s to %d" % (self, value))

setattr(Foo, 'x', property(get_x, set_x))

foo1 = Foo()
foo1.x = 12
print(foo1.x)
David Grayson
fuente
1

Puede usar el siguiente código para actualizar los atributos de clase utilizando un objeto de diccionario:

class ExampleClass():
    def __init__(self, argv):
        for key, val in argv.items():
            self.__dict__[key] = val

if __name__ == '__main__':
    argv = {'intro': 'Hello World!'}
    instance = ExampleClass(argv)
    print instance.intro
Anthony Holloman
fuente
1

Esto es un poco diferente de lo que OP quería, pero me sacudí el cerebro hasta que encontré una solución que funcionara, así que estoy aquí para el próximo chico / chica

Necesitaba una manera de especificar setters y getters dinámicos.

class X:
    def __init__(self, a=0, b=0, c=0):
        self.a = a
        self.b = b
        self.c = c

    @classmethod
    def _make_properties(cls, field_name, inc):
        _inc = inc

        def _get_properties(self):
            if not hasattr(self, '_%s_inc' % field_name):
                setattr(self, '_%s_inc' % field_name, _inc)
                inc = _inc
            else:
                inc = getattr(self, '_%s_inc' % field_name)

            return getattr(self, field_name) + inc

        def _set_properties(self, value):
            setattr(self, '_%s_inc' % field_name, value)

        return property(_get_properties, _set_properties)

Conozco mis campos con anticipación, así que voy a crear mis propiedades. NOTA: ¡no puede hacer esta instancia PER, estas propiedades existirán en la clase!

for inc, field in enumerate(['a', 'b', 'c']):
    setattr(X, '%s_summed' % field, X._make_properties(field, inc))

Probemos todo ahora ...

x = X()
assert x.a == 0
assert x.b == 0
assert x.c == 0

assert x.a_summed == 0  # enumerate() set inc to 0 + 0 = 0
assert x.b_summed == 1  # enumerate() set inc to 1 + 0 = 1
assert x.c_summed == 2  # enumerate() set inc to 2 + 0 = 2

# we set the variables to something
x.a = 1
x.b = 2
x.c = 3

assert x.a_summed == 1  # enumerate() set inc to 0 + 1 = 1
assert x.b_summed == 3  # enumerate() set inc to 1 + 2 = 3
assert x.c_summed == 5  # enumerate() set inc to 2 + 3 = 5

# we're changing the inc now
x.a_summed = 1 
x.b_summed = 3 
x.c_summed = 5

assert x.a_summed == 2  # we set inc to 1 + the property was 1 = 2
assert x.b_summed == 5  # we set inc to 3 + the property was 2 = 5
assert x.c_summed == 8  # we set inc to 5 + the property was 3 = 8

¿Es confuso? Sí, lo siento, no pude encontrar ningún ejemplo significativo del mundo real. Además, esto no es para los alegres.

Javier Buzzi
fuente
Si recuerdo correctamente, encontré una manera durante todas mis pruebas para crear una propiedad de tipo ESTÁTICO / g / setter agregado dinámicamente. Tendría que pasar por todo lo anterior, pero definitivamente es posible poder agregar algo que se comparte entre todas las instancias. En cuanto a crear en un proceso por instancia ... Estoy bastante seguro de que puede hacerlo para que una instancia tenga algo que otra no tenga. Tendría que verificar, pero también me encontré con algo como esto (en mis primeros intentos cometí un error que causó la creación de las funciones, pero no todas las instancias las tenían debido a un defecto)
Acecool
Además, todas las soluciones posibles son bienvenidas ya que este es un repositorio de conocimiento. También es emocionante ver las diferentes formas en que diferentes personas crean soluciones a un problema. Mi solución hace MUCHO, redujiste esto a algo más simple para compartir. También hice una variante más pequeña de la mía, debería estar en algún lugar de este tema, y ​​me di cuenta de que no es la que
publiqué
0

Esto parece funcionar (pero ver más abajo):

class data(dict,object):
    def __init__(self,*args,**argd):
        dict.__init__(self,*args,**argd)
        self.__dict__.update(self)
    def __setattr__(self,name,value):
        raise AttributeError,"Attribute '%s' of '%s' object cannot be set"%(name,self.__class__.__name__)
    def __delattr__(self,name):
        raise AttributeError,"Attribute '%s' of '%s' object cannot be deleted"%(name,self.__class__.__name__)

Si necesita un comportamiento más complejo, no dude en editar su respuesta.

editar

Lo siguiente probablemente sería más eficiente en la memoria para grandes conjuntos de datos:

class data(dict,object):
    def __init__(self,*args,**argd):
        dict.__init__(self,*args,**argd)
    def __getattr__(self,name):
        return self[name]
    def __setattr__(self,name,value):
        raise AttributeError,"Attribute '%s' of '%s' object cannot be set"%(name,self.__class__.__name__)
    def __delattr__(self,name):
        raise AttributeError,"Attribute '%s' of '%s' object cannot be deleted"%(name,self.__class__.__name__)
David X
fuente
0

Para responder al objetivo principal de su pregunta, desea un atributo de solo lectura de un dict como fuente de datos inmutable:

El objetivo es crear una clase simulada que se comporte como un conjunto de resultados db.

Entonces, por ejemplo, si regresa una consulta de base de datos, usando una expresión dict {'ab':100, 'cd':200}, entonces vería

>>> dummy.ab
100

Demostraré cómo usar un namedtupledel collectionsmódulo para lograr esto:

import collections

data = {'ab':100, 'cd':200}

def maketuple(d):
    '''given a dict, return a namedtuple'''
    Tup = collections.namedtuple('TupName', d.keys()) # iterkeys in Python2
    return Tup(**d)

dummy = maketuple(data)
dummy.ab

devoluciones 100

Aaron Hall
fuente
0
class atdict(dict):
  def __init__(self, value, **kwargs):
    super().__init__(**kwargs)
    self.__dict = value

  def __getattr__(self, name):
    for key in self.__dict:
      if type(self.__dict[key]) is list:
        for idx, item in enumerate(self.__dict[key]):
          if type(item) is dict:
            self.__dict[key][idx] = atdict(item)
      if type(self.__dict[key]) is dict:
        self.__dict[key] = atdict(self.__dict[key])
    return self.__dict[name]



d1 = atdict({'a' : {'b': [{'c': 1}, 2]}})

print(d1.a.b[0].c)

Y la salida es:

>> 1
Serhii Khachko
fuente
0

Extendiendo la idea desde kjfletch

# This is my humble contribution, extending the idea to serialize
# data from and to tuples, comparison operations and allowing functions
# as default values.

def Struct(*args, **kwargs):
    FUNCTIONS = (types.BuiltinFunctionType, types.BuiltinMethodType, \
                 types.FunctionType, types.MethodType)
    def init(self, *iargs, **ikwargs):
        """Asume that unamed args are placed in the same order than
        astuple() yields (currently alphabetic order)
        """
        kw = list(self.__slots__)

        # set the unnamed args
        for i in range(len(iargs)):
            k = kw.pop(0)
            setattr(self, k, iargs[i])

        # set the named args
        for k, v in ikwargs.items():
            setattr(self, k, v)
            kw.remove(k)

        # set default values
        for k in kw:
            v = kwargs[k]
            if isinstance(v, FUNCTIONS):
                v = v()
            setattr(self, k, v)

    def astuple(self):
        return tuple([getattr(self, k) for k in self.__slots__])

    def __str__(self):
        data = ['{}={}'.format(k, getattr(self, k)) for k in self.__slots__]
        return '<{}: {}>'.format(self.__class__.__name__, ', '.join(data))

    def __repr__(self):
        return str(self)

    def __eq__(self, other):
        return self.astuple() == other.astuple()

    name = kwargs.pop("__name__", "MyStruct")
    slots = list(args)
    slots.extend(kwargs.keys())
    # set non-specific default values to None
    kwargs.update(dict((k, None) for k in args))

    return type(name, (object,), {
        '__init__': init,
        '__slots__': tuple(slots),
        'astuple': astuple,
        '__str__': __str__,
        '__repr__': __repr__,
        '__eq__': __eq__,
    })


Event = Struct('user', 'cmd', \
               'arg1', 'arg2',  \
               date=time.time, \
               __name__='Event')

aa = Event('pepe', 77)
print(aa)
raw = aa.astuple()

bb = Event(*raw)
print(bb)

if aa == bb:
    print('Are equals')

cc = Event(cmd='foo')
print(cc)

Salida:

<Event: user=pepe, cmd=77, arg1=None, arg2=None, date=1550051398.3651814>
<Event: user=pepe, cmd=77, arg1=None, arg2=None, date=1550051398.3651814>
Are equals
<Event: user=None, cmd=foo, arg1=None, arg2=None, date=1550051403.7938335>
asterio gonzalez
fuente
0

Aunque se dan muchas respuestas, no pude encontrar una con la que estoy contento. Descubrí mi propia solución que hace que el propertytrabajo sea dinámico. La fuente para responder la pregunta original:

#!/usr/local/bin/python3

INITS = { 'ab': 100, 'cd': 200 }

class DP(dict):
  def __init__(self):
    super().__init__()
    for k,v in INITS.items():
        self[k] = v 

def _dict_set(dp, key, value):
  dp[key] = value

for item in INITS.keys():
  setattr(
    DP,
    item,
    lambda key: property(
      lambda self: self[key], lambda self, value: _dict_set(self, key, value)
    )(item)
  )

a = DP()
print(a)  # {'ab': 100, 'cd': 200}
a.ab = 'ab100'
a.cd = False
print(a.ab, a.cd) # ab100 False
Rob L
fuente
0

Algo que funciona para mí es esto:

class C:
    def __init__(self):
        self._x=None

    def g(self):
        return self._x

    def s(self, x):
        self._x = x

    def d(self):
        del self._x

    def s2(self,x):
        self._x=x+x

    x=property(g,s,d)


c = C()
c.x="a"
print(c.x)

C.x=property(C.g, C.s2)
C.x=C.x.deleter(C.d)
c2 = C()
c2.x="a"
print(c2.x)

Salida

a
aa
prosti
fuente
-1

Recientemente me encontré con un problema similar, la solución que se me ocurrió utiliza __getattr__y __setattr__para las propiedades que quiero que maneje, todo lo demás pasa a los originales.

class C(object):
    def __init__(self, properties):
        self.existing = "Still Here"
        self.properties = properties

    def __getattr__(self, name):
        if "properties" in self.__dict__ and name in self.properties:
            return self.properties[name] # Or call a function, etc
        return self.__dict__[name]

    def __setattr__(self, name, value):
        if "properties" in self.__dict__ and name in self.properties:
            self.properties[name] = value
        else:
            self.__dict__[name] = value

if __name__ == "__main__":
    my_properties = {'a':1, 'b':2, 'c':3}
    c = C(my_properties)
    assert c.a == 1
    assert c.existing == "Still Here"
    c.b = 10
    assert c.properties['b'] == 10
teeks99
fuente
Investigué esto, sin embargo, técnicamente estás revisando una lista en tus ayudantes getter y setter. Debido a esto, cada llamada sería más lenta porque la está buscando desde una lista, primero, en lugar de acceder a ella directamente. A menos que Python lo asigne automáticamente; puede, pero aún no lo he evaluado para saber con certeza, pero es una preocupación para mí antes de intentarlo. En segundo lugar, al hacer esto, debe definir los ayudantes de otra manera. Tampoco puede bloquear tipos de datos y / o valores sin terminar con un diccionario grande o muchas líneas adicionales.
Acecool
es decir: tendría que crear una clase base en la que extienda a todos mis hijos que usan el sistema, O tengo que agregar funciones mágicas s / getattr a todo y duplicar el sistema cada vez. La declaración de propiedades también significa que debe configurarlas de una manera, y si desea algo de soporte adicional, como he enumerado, como la protección de tipo de datos o valor para permitir o evitar que se asignen datos y otros ayudantes , entonces tienes que codificarlos. De acuerdo, podrías hacer que el sistema tenga un comportamiento similar, pero termina donde declaras algo diferente y voluminoso.
Acecool
-1

Aquí está el ejemplo simple para crear objetos de propiedad mediante programación.

#!/usr/bin/python3

class Counter:
    def __init__(self):
        cls = self.__class__
        self._count = 0
        cls.count = self.count_ref()

    def count_get(self):
        print(f'count_get: {self._count}')
        return self._count

    def count_set(self, value):
        self._count = value
        print(f'count_set: {self._count}')

    def count_del(self):
        print(f'count_del: {self._count}')

    def count_ref(self):
        cls = self.__class__
        return property(fget=cls.count_get, fset=cls.count_set, fdel=cls.count_del)

counter = Counter()

counter.count
for i in range(5):
    counter.count = i
del counter.count

'''
output
======
count_get: 0
count_set: 0
count_set: 1
count_set: 2
count_set: 3
count_set: 4
count_del: 4
'''
Suhas Siddu
fuente
-2

La única forma de adjuntar dinámicamente una propiedad es crear una nueva clase y su instancia con su nueva propiedad.

class Holder: p = property(lambda x: vs[i], self.fn_readonly)
setattr(self, k, Holder().p)
M. Utku ALTINKAYA
fuente
1
Esto no parece funcionar. asignará el resultado de la propiedad, no la propiedad en sí.
mjallday
Esto es incorrecto. Adjunto propiedades dinámicas con mi sistema sin tener que inicializar la clase. Inicialice siendo x = Ejemplo (), luego agregue la propiedad a x.
Acecool
Si miras mi código, verás que uso la clase ExampleBase: pass, luego la clase Example (ExampleBase): ... luego adjunto las propiedades a ExampleBase, porque el nombre existe, y dado que Example se extiende desde él, Tiene acceso a todo. Utilizo __ var para que el ayudante de acceso pueda tener acceso directo a los objetos de acceso, uso _ para los datos almacenados (en bruto) que pueden ser Ninguno y no subrayan la propiedad real que pasa por el captador. Puedo llamar a la función getter usando una función agregada dinámicamente, o usar la propiedad. Todo sin tener primero iniciado.
Acecool
Nota: Lo mencioné, pero mi definición de definición significa que la referencia existe en el espacio de nombres, es decir: clase Ejemplo (Objeto): pasar ... Existe, pero no se ha inicializado. Inicialización significa blah = Ejemplo (); ahora el objeto ha sido 'duplicado' y construido, luego almacenado como referencia en bla. --- Si hace esto, entonces las funciones / propiedades agregadas dinámicamente solo deberían estar en la instancia: el problema que tuve con esto fue incluso si las funciones existían, hubo casos en los que recibí un error que decía que no. El error de bloqueo detuvo la creación o la ejecución asincrónica.
Acecool
Algunas otras notas: es posible crear propiedades dinámicamente y funciona de una manera que solo existe por instancia. También puede hacerlos para que existan para el objeto (que es lo que desearía en la mayoría de los casos). Y hay casos en los que los elementos agregados son 'estáticos', es decir, las mismas referencias y los valores devueltos se comparten en todas las instancias: si actualiza en un área, todos obtienen lo mismo ...
Acecool
-6

Muchas de las respuestas proporcionadas requieren tantas líneas por propiedad, es decir, / y / o lo que yo consideraría una implementación fea o tediosa debido a la repetitividad requerida para múltiples propiedades, etc. Prefiero mantener las cosas en ebullición / simplificarlas hasta que ya no se puede simplificar o hasta que no tenga mucho propósito hacerlo.

En resumen: en trabajos completos, si repito 2 líneas de código, normalmente lo convierto en una función auxiliar de una sola línea, y así sucesivamente ... Simplifico argumentos matemáticos o extraños como (start_x, start_y, end_x, end_y) a (x, y, w, h) es decir, x, y, x + w, y + h (a veces requiere min / max o si w / h es negativo y la implementación no le gusta, restaré de x / y y abs w / h. etc.).

Anular los getters / setters internos es una buena manera de hacerlo, pero el problema es que debes hacer eso para cada clase, o poner a la clase en esa base ... Esto no funciona para mí, ya que preferiría ser libre de elegir los hijos / padres para herencia, nodos hijos, etc.

He creado una solución que responde a la pregunta sin utilizar un tipo de datos Dict para proporcionar los datos, ya que considero que es tedioso ingresar los datos, etc.

Mi solución requiere que agregue 2 líneas adicionales sobre su clase para crear una clase base para la clase a la que desea agregar las propiedades, luego 1 línea por y tiene la opción de agregar devoluciones de llamada para controlar los datos, informarle cuando los datos cambien , restrinja los datos que se pueden establecer en función del valor y / o tipo de datos, y mucho más.

También tiene la opción de usar _object.x, _object.x = value, _object.GetX (), _object.SetX (value) y se manejan de manera equivalente.

Además, los valores son los únicos datos no estáticos que se asignan a la instancia de clase, pero la propiedad real se asigna a la clase, lo que significa que las cosas que no desea repetir no necesitan repetirse ... Usted puede asignar un valor predeterminado para que el getter no lo necesite cada vez, aunque hay una opción para anular el valor predeterminado predeterminado, y hay otra opción para que el getter devuelva el valor almacenado en bruto anulando los retornos predeterminados (nota: este método significa que el valor bruto solo se asigna cuando se asigna un valor; de lo contrario, es Ninguno; cuando el valor es Restablecer, no se asigna Ninguno, etc.)

También hay muchas funciones auxiliares: la primera propiedad que se agrega agrega 2 o más ayudantes a la clase para hacer referencia a los valores de instancia ... Son varargs ResetAccessors (_key, ..) repetidos (todos pueden repetirse usando los primeros args nombrados ) y SetAccessors (_key, _value) con la opción de agregar más a la clase principal para ayudar en la eficiencia: los planificados son: una forma de agrupar los accesores, por lo que si tiende a restablecer algunos a la vez, cada vez , puede asignarlos a un grupo y restablecer el grupo en lugar de repetir las teclas con nombre cada vez, y más.

La instancia / valor almacenado sin procesar se almacena en clase. , la clase. hace referencia a la clase Accessor que contiene vars / valores / funciones estáticos para la propiedad. _clase. es la propiedad en sí que se llama cuando se accede a través de la clase de instancia durante la configuración / obtención, etc.

Accessor _class .__ apunta a la clase, pero debido a que es interna, debe asignarse en la clase, por eso opté por usar __Name = AccessorFunc (...) para asignarla, una sola línea por propiedad con muchas opciones opcionales. argumentos para usar (usando varargs con clave porque son más fáciles y más eficientes de identificar y mantener) ...

También creo muchas funciones, como se mencionó, algunas de las cuales usan información de función de acceso para que no sea necesario llamarla (ya que es un poco incómodo en este momento, ahora necesita usar _class. .FunctionName (_class_instance , args) - Utilicé la pila / traza para tomar la referencia de instancia para tomar el valor agregando las funciones que ejecutan este maratón de bits o agregando los accesores al objeto y usando self (llamado esto para señalar que son para la instancia y para conservar el acceso a uno mismo, la referencia de clase AccessorFunc y otra información desde dentro de las definiciones de funciones).

No está hecho del todo, pero es un apoyo fantástico para los pies. Nota: Si no usa __Name = AccessorFunc (...) para crear las propiedades, no tendrá acceso a la tecla __ aunque la defina dentro de la función init. Si lo hace, entonces no hay problemas.

Además: tenga en cuenta que Nombre y Clave son diferentes ... El nombre es 'formal', se usa en la Creación de nombres de funciones, y la clave es para el almacenamiento y acceso de datos. es decir, _class.x donde x minúscula es la clave, el nombre sería X mayúscula para que GetX () sea la función en lugar de Getx (), lo que parece un poco extraño. esto permite que self.x funcione y se vea apropiado, pero también permite que GetX () se vea apropiado.

Tengo una clase de ejemplo configurada con clave / nombre idéntico y diferente para mostrar. muchas funciones auxiliares creadas para generar los datos (Nota: no todo esto está completo) para que pueda ver lo que está sucediendo.

La lista actual de funciones que usa la tecla: x, nombre: X se muestra como:

De ninguna manera se trata de una lista exhaustiva: hay algunas que no lo han hecho al momento de publicar ...

_instance.SetAccessors( _key, _value [ , _key, _value ] .. )                   Instance Class Helper Function: Allows assigning many keys / values on a single line - useful for initial setup, or to minimize lines.    In short: Calls this.Set<Name>( _value ) for each _key / _value pairing.
_instance.ResetAccessors( _key [ , _key ] .. )                                 Instance Class Helper Function: Allows resetting many key stored values to None on a single line.                                           In short: Calls this.Reset<Name>() for each name provided.


Note: Functions below may list self.Get / Set / Name( _args ) - self is meant as the class instance reference in the cases below - coded as this in AccessorFuncBase Class.

this.GetX( _default_override = None, _ignore_defaults = False )                 GET:            Returns    IF ISSET: STORED_VALUE .. IF IGNORE_DEFAULTS: None  .. IF PROVIDED: DEFAULT_OVERRIDE ELSE: DEFAULT_VALUE       100
this.GetXRaw( )                                                                 RAW:            Returns    STORED_VALUE                                                                                                     100
this.IsXSet( )                                                                  ISSET:          Returns    ( STORED_VALUE != None )                                                                                         True

this.GetXToString( )                                                            GETSTR:         Returns    str( GET )                                                                                                       100
this.GetXLen( _default_override = None, _ignore_defaults = False )              LEN:            Returns    len( GET )                                                                                                       3
this.GetXLenToString( _default_override = None, _ignore_defaults = False )      LENSTR:         Returns    str( len( GET ) )                                                                                                3
this.GetXDefaultValue( )                                                        DEFAULT:        Returns    DEFAULT_VALUE                                                                                                    1111

this.GetXAccessor( )                                                            ACCESSOR:       Returns    ACCESSOR_REF ( self.__<key> )                                                                                    [ AccessorFuncBase ] Key: x : Class ID: 2231452344344 : self ID: 2231448283848        Default: 1111       Allowed Types: {"<class 'int'>": "<class 'type'>", "<class 'float'>": "<class 'type'>"}     Allowed Values: None
this.GetXAllowedTypes( )                                                        ALLOWED_TYPES:  Returns    Allowed Data-Types                                                                                               {"<class 'int'>": "<class 'type'>", "<class 'float'>": "<class 'type'>"}
this.GetXAllowedValues( )                                                       ALLOWED_VALUES: Returns    Allowed Values                                                                                                   None

this.GetXHelpers( )                                                             HELPERS:        Returns    Helper Functions String List - ie what you're reading now...                                                     THESE ROWS OF TEXT
this.GetXKeyOutput( )                                                           Returns information about this Name / Key                                                                                                   ROWS OF TEXT
this.GetXGetterOutput( )                                                        Returns information about this Name / Key                                                                                                   ROWS OF TEXT

this.SetX( _value )                                                             SET:            STORED_VALUE Setter - ie Redirect to __<Key>.Set                                                                            N / A
this.ResetX( )                                                                  RESET:          Resets STORED_VALUE to None                                                                                                 N / A

this.HasXGetterPrefix( )                                                        Returns Whether or Not this key has a Getter Prefix...                                                                                      True
this.GetXGetterPrefix( )                                                        Returns Getter Prefix...                                                                                                                    Get

this.GetXName( )                                                                Returns Accessor Name - Typically Formal / Title-Case                                                                                       X
this.GetXKey( )                                                                 Returns Accessor Property Key - Typically Lower-Case                                                                                        x
this.GetXAccessorKey( )                                                         Returns Accessor Key - This is to access internal functions, and static data...                                                             __x
this.GetXDataKey( )                                                             Returns Accessor Data-Storage Key - This is the location where the class instance value is stored..                                         _x

Algunos de los datos que se emiten son:

Esto es para una nueva clase creada usando la clase Demo sin ningún dato asignado que no sea el nombre (por lo que se puede generar) que es _foo, el nombre de la variable que utilicé ...

_foo         --- MyClass: ---- id( this.__class__ ): 2231452349064 :::: id( this ): 2231448475016

    Key       Getter Value        | Raw Key   Raw / Stored Value       | Get Default Value             Default Value            | Get Allowed Types             Allowed Types                                                              | Get Allowed Values            Allowed Values                                                                                                                                                                                                                   |

    Name:     _foo                | _Name:    _foo                     | __Name.DefaultValue( ):       AccessorFuncDemoClass    | __Name.GetAllowedTypes( )     <class 'str'>                                                              | __Name.GetAllowedValues( )    Saved Value Restrictions Levied by Data-Type                                                                                                                                                                                     |
    x:        1111                | _x:       None                     | __x.DefaultValue( ):          1111                     | __x.GetAllowedTypes( )        (<class 'int'>, <class 'float'>)                                           | __x.GetAllowedValues( )       Saved Value Restrictions Levied by Data-Type                                                                                                                                                                                     |
    y:        2222                | _y:       None                     | __y.DefaultValue( ):          2222                     | __y.GetAllowedTypes( )        (<class 'int'>, <class 'float'>)                                           | __y.GetAllowedValues( )       Saved Value Restrictions Levied by Data-Type                                                                                                                                                                                     |
    z:        3333                | _z:       None                     | __z.DefaultValue( ):          3333                     | __z.GetAllowedTypes( )        (<class 'int'>, <class 'float'>)                                           | __z.GetAllowedValues( )       Saved Value Restrictions Levied by Data-Type                                                                                                                                                                                     |
    Blah:     <class 'int'>       | _Blah:    None                     | __Blah.DefaultValue( ):       <class 'int'>            | __Blah.GetAllowedTypes( )     <class 'str'>                                                              | __Blah.GetAllowedValues( )    Saved Value Restrictions Levied by Data-Type                                                                                                                                                                                     |
    Width:    1                   | _Width:   None                     | __Width.DefaultValue( ):      1                        | __Width.GetAllowedTypes( )    (<class 'int'>, <class 'bool'>)                                            | __Width.GetAllowedValues( )   Saved Value Restrictions Levied by Data-Type                                                                                                                                                                                     |
    Height:   0                   | _Height:  None                     | __Height.DefaultValue( ):     0                        | __Height.GetAllowedTypes( )   <class 'int'>                                                              | __Height.GetAllowedValues( )  (0, 1, 2, 3, 4, 5, 6, 7, 8, 9)                                                                                                                                                                                                   |
    Depth:    2                   | _Depth:   None                     | __Depth.DefaultValue( ):      2                        | __Depth.GetAllowedTypes( )    Saved Value Restricted to Authorized Values ONLY                           | __Depth.GetAllowedValues( )   (0, 1, 2, 3, 4, 5, 6, 7, 8, 9)                                                                                                                                                                                                   |


this.IsNameSet( ):    True      this.GetName( ):     _foo                     this.GetNameRaw( ):    _foo                     this.GetNameDefaultValue( ):    AccessorFuncDemoClass    this.GetNameLen( ):    4    this.HasNameGetterPrefix( ):    <class 'str'>                                this.GetNameGetterPrefix( ):    None
this.IsXSet( ):       False     this.GetX( ):        1111                     this.GetXRaw( ):       None                     this.GetXDefaultValue( ):       1111                     this.GetXLen( ):       4    this.HasXGetterPrefix( ):       (<class 'int'>, <class 'float'>)             this.GetXGetterPrefix( ):       None
this.IsYSet( ):       False     this.GetY( ):        2222                     this.GetYRaw( ):       None                     this.GetYDefaultValue( ):       2222                     this.GetYLen( ):       4    this.HasYGetterPrefix( ):       (<class 'int'>, <class 'float'>)             this.GetYGetterPrefix( ):       None
this.IsZSet( ):       False     this.GetZ( ):        3333                     this.GetZRaw( ):       None                     this.GetZDefaultValue( ):       3333                     this.GetZLen( ):       4    this.HasZGetterPrefix( ):       (<class 'int'>, <class 'float'>)             this.GetZGetterPrefix( ):       None
this.IsBlahSet( ):    False     this.GetBlah( ):     <class 'int'>            this.GetBlahRaw( ):    None                     this.GetBlahDefaultValue( ):    <class 'int'>            this.GetBlahLen( ):    13   this.HasBlahGetterPrefix( ):    <class 'str'>                                this.GetBlahGetterPrefix( ):    None
this.IsWidthSet( ):   False     this.GetWidth( ):    1                        this.GetWidthRaw( ):   None                     this.GetWidthDefaultValue( ):   1                        this.GetWidthLen( ):   1    this.HasWidthGetterPrefix( ):   (<class 'int'>, <class 'bool'>)              this.GetWidthGetterPrefix( ):   None
this.IsDepthSet( ):   False     this.GetDepth( ):    2                        this.GetDepthRaw( ):   None                     this.GetDepthDefaultValue( ):   2                        this.GetDepthLen( ):   1    this.HasDepthGetterPrefix( ):   None                                         this.GetDepthGetterPrefix( ):   (0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
this.IsHeightSet( ):  False     this.GetHeight( ):   0                        this.GetHeightRaw( ):  None                     this.GetHeightDefaultValue( ):  0                        this.GetHeightLen( ):  1    this.HasHeightGetterPrefix( ):  <class 'int'>                                this.GetHeightGetterPrefix( ):  (0, 1, 2, 3, 4, 5, 6, 7, 8, 9)

Y esto es después de asignar todas las propiedades _foo (excepto el nombre) los siguientes valores en el mismo orden: 'cadena', 1.0, Verdadero, 9, 10, Falso

this.IsNameSet( ):    True      this.GetName( ):     _foo                     this.GetNameRaw( ):    _foo                     this.GetNameDefaultValue( ):    AccessorFuncDemoClass    this.GetNameLen( ):    4    this.HasNameGetterPrefix( ):    <class 'str'>                                this.GetNameGetterPrefix( ):    None
this.IsXSet( ):       True      this.GetX( ):        10                       this.GetXRaw( ):       10                       this.GetXDefaultValue( ):       1111                     this.GetXLen( ):       2    this.HasXGetterPrefix( ):       (<class 'int'>, <class 'float'>)             this.GetXGetterPrefix( ):       None
this.IsYSet( ):       True      this.GetY( ):        10                       this.GetYRaw( ):       10                       this.GetYDefaultValue( ):       2222                     this.GetYLen( ):       2    this.HasYGetterPrefix( ):       (<class 'int'>, <class 'float'>)             this.GetYGetterPrefix( ):       None
this.IsZSet( ):       True      this.GetZ( ):        10                       this.GetZRaw( ):       10                       this.GetZDefaultValue( ):       3333                     this.GetZLen( ):       2    this.HasZGetterPrefix( ):       (<class 'int'>, <class 'float'>)             this.GetZGetterPrefix( ):       None
this.IsBlahSet( ):    True      this.GetBlah( ):     string Blah              this.GetBlahRaw( ):    string Blah              this.GetBlahDefaultValue( ):    <class 'int'>            this.GetBlahLen( ):    11   this.HasBlahGetterPrefix( ):    <class 'str'>                                this.GetBlahGetterPrefix( ):    None
this.IsWidthSet( ):   True      this.GetWidth( ):    False                    this.GetWidthRaw( ):   False                    this.GetWidthDefaultValue( ):   1                        this.GetWidthLen( ):   5    this.HasWidthGetterPrefix( ):   (<class 'int'>, <class 'bool'>)              this.GetWidthGetterPrefix( ):   None
this.IsDepthSet( ):   True      this.GetDepth( ):    9                        this.GetDepthRaw( ):   9                        this.GetDepthDefaultValue( ):   2                        this.GetDepthLen( ):   1    this.HasDepthGetterPrefix( ):   None                                         this.GetDepthGetterPrefix( ):   (0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
this.IsHeightSet( ):  True      this.GetHeight( ):   9                        this.GetHeightRaw( ):  9                        this.GetHeightDefaultValue( ):  0                        this.GetHeightLen( ):  1    this.HasHeightGetterPrefix( ):  <class 'int'>                                this.GetHeightGetterPrefix( ):  (0, 1, 2, 3, 4, 5, 6, 7, 8, 9)

_foo         --- MyClass: ---- id( this.__class__ ): 2231452349064 :::: id( this ): 2231448475016

    Key       Getter Value        | Raw Key   Raw / Stored Value       | Get Default Value             Default Value            | Get Allowed Types             Allowed Types                                                              | Get Allowed Values            Allowed Values                                                                                                                                                                                                                   |

    Name:     _foo                | _Name:    _foo                     | __Name.DefaultValue( ):       AccessorFuncDemoClass    | __Name.GetAllowedTypes( )     <class 'str'>                                                              | __Name.GetAllowedValues( )    Saved Value Restrictions Levied by Data-Type                                                                                                                                                                                     |
    x:        10                  | _x:       10                       | __x.DefaultValue( ):          1111                     | __x.GetAllowedTypes( )        (<class 'int'>, <class 'float'>)                                           | __x.GetAllowedValues( )       Saved Value Restrictions Levied by Data-Type                                                                                                                                                                                     |
    y:        10                  | _y:       10                       | __y.DefaultValue( ):          2222                     | __y.GetAllowedTypes( )        (<class 'int'>, <class 'float'>)                                           | __y.GetAllowedValues( )       Saved Value Restrictions Levied by Data-Type                                                                                                                                                                                     |
    z:        10                  | _z:       10                       | __z.DefaultValue( ):          3333                     | __z.GetAllowedTypes( )        (<class 'int'>, <class 'float'>)                                           | __z.GetAllowedValues( )       Saved Value Restrictions Levied by Data-Type                                                                                                                                                                                     |
    Blah:     string Blah         | _Blah:    string Blah              | __Blah.DefaultValue( ):       <class 'int'>            | __Blah.GetAllowedTypes( )     <class 'str'>                                                              | __Blah.GetAllowedValues( )    Saved Value Restrictions Levied by Data-Type                                                                                                                                                                                     |
    Width:    False               | _Width:   False                    | __Width.DefaultValue( ):      1                        | __Width.GetAllowedTypes( )    (<class 'int'>, <class 'bool'>)                                            | __Width.GetAllowedValues( )   Saved Value Restrictions Levied by Data-Type                                                                                                                                                                                     |
    Height:   9                   | _Height:  9                        | __Height.DefaultValue( ):     0                        | __Height.GetAllowedTypes( )   <class 'int'>                                                              | __Height.GetAllowedValues( )  (0, 1, 2, 3, 4, 5, 6, 7, 8, 9)                                                                                                                                                                                                   |
    Depth:    9                   | _Depth:   9                        | __Depth.DefaultValue( ):      2                        | __Depth.GetAllowedTypes( )    Saved Value Restricted to Authorized Values ONLY                           | __Depth.GetAllowedValues( )   (0, 1, 2, 3, 4, 5, 6, 7, 8, 9)                                                                                                                                                                                                   |

Tenga en cuenta que debido a tipos de datos restringidos o restricciones de valor, algunos datos no fueron asignados, esto es por diseño. El configurador prohíbe que se asignen valores o tipos de datos incorrectos, incluso si se asignan como un valor predeterminado (a menos que anule el comportamiento de protección del valor predeterminado)

El código no se ha publicado aquí porque no tuve espacio después de los ejemplos y explicaciones ... También porque va a cambiar.

Tenga en cuenta: al momento de esta publicación, el archivo está desordenado, esto cambiará. Pero, si lo ejecuta en Sublime Text y lo compila, o lo ejecuta desde Python, compilará y escupirá una gran cantidad de información: la parte de AccessorDB no se realiza (que se utilizará para actualizar el Asistente de impresión Getters y GetKeyOutput funciones junto con el cambio a una función de instancia, probablemente puesta en una sola función y renombrada - búscala ..)

A continuación: no se requiere todo para que se ejecute; muchas de las cosas comentadas en la parte inferior son para obtener más información utilizada para la depuración; puede que no esté allí cuando la descargue. Si es así, debería poder descomentar y volver a compilar para obtener más información.

Estoy buscando una solución alternativa para necesitar MyClassBase: pass, MyClass (MyClassBase): ... - si conoce una solución, publíquela.

Lo único necesario en la clase son las líneas __: el str es para la depuración como el init ; se pueden eliminar de la Clase de demostración, pero deberá comentar o eliminar algunas de las líneas a continuación (_foo / 2/3 ) ..

Las clases String, Dict y Util en la parte superior son parte de mi biblioteca Python, no están completas. Copié algunas cosas que necesitaba de la biblioteca, y creé algunas nuevas. El código completo se vinculará a la biblioteca completa y lo incluirá junto con el suministro de llamadas actualizadas y la eliminación del código (en realidad, el único código restante será la Clase Demo y las declaraciones impresas; el sistema AccessorFunc se moverá a la biblioteca). ..

Parte del archivo:

##
## MyClass Test AccessorFunc Implementation for Dynamic 1-line Parameters
##
class AccessorFuncDemoClassBase( ):
    pass
class AccessorFuncDemoClass( AccessorFuncDemoClassBase ):
    __Name      = AccessorFuncBase( parent = AccessorFuncDemoClassBase, name = 'Name',      default = 'AccessorFuncDemoClass',  allowed_types = ( TYPE_STRING ),                    allowed_values = VALUE_ANY,                 documentation = 'Name Docs',        getter_prefix = 'Get',  key = 'Name',       allow_erroneous_default = False,    options = { } )
    __x         = AccessorFuncBase( parent = AccessorFuncDemoClassBase, name = 'X',         default = 1111,                     allowed_types = ( TYPE_INTEGER, TYPE_FLOAT ),       allowed_values = VALUE_ANY,                 documentation = 'X Docs',           getter_prefix = 'Get',  key = 'x',          allow_erroneous_default = False,    options = { } )
    __Height    = AccessorFuncBase( parent = AccessorFuncDemoClassBase, name = 'Height',    default = 0,                        allowed_types = TYPE_INTEGER,                       allowed_values = VALUE_SINGLE_DIGITS,       documentation = 'Height Docs',      getter_prefix = 'Get',  key = 'Height',     allow_erroneous_default = False,    options = { } )

Esta belleza hace que sea increíblemente fácil crear nuevas clases con propiedades agregadas dinámicamente con AccessorFuncs / callbacks / data-type / valueforcement, etc.

Por ahora, el enlace está en (Este enlace debe reflejar los cambios en el documento): https://www.dropbox.com/s/6gzi44i7dh58v61/dynamic_properties_accessorfuncs_and_more.py?dl=0

Además: si no usa Sublime Text, lo recomiendo sobre Notepad ++, Atom, Visual Code y otros debido a las implementaciones de subprocesos adecuadas que lo hacen mucho, mucho más rápido de usar ... También estoy trabajando en un código similar a IDE sistema de mapeo para ello: eche un vistazo a: https://bitbucket.org/Acecool/acecoolcodemappingsystem/src/master/ (Primero agregue Repo en el Administrador de paquetes, luego instale el complemento; cuando la versión 1.0.0 esté lista, agregaré a la lista principal de complementos ...)

Espero que esta solución ayude ... y, como siempre:

Solo porque funciona, no lo hace bien - Josh 'Acecool' Moser

Acecool
fuente
Quería agregar una visualización rápida del aspecto de la clase para que no sea necesario abrir el archivo de código, pero los comentarios no parecen
admitirlo
Aparentemente esto está recibiendo mucho odio, lo cual es confuso. Hace exactamente lo que pide el OP: agregar dinámicamente propiedades a un objeto. También agrega funciones de ayuda, que no tienen que incluirse, tal vez por eso es que se está odiando, y también se asegura de que el desarrollador tenga una manera fácil de acceder a la propiedad (.x) que se procesa a través del getter, el valor bruto almacenado (._x) que puede ser Ninguno cuando .x devuelve el valor predeterminado u otra cosa, y una forma de acceder al
descriptor de