Cómo añadir una propiedad a una clase dinámicamente?
-
19-09-2019 - |
Pregunta
El objetivo es crear una clase simulada que se comporta como un conjunto de resultados db.
Así, por ejemplo, si una consulta de base de datos devuelve, utilizando 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
c.ab
pero devuelve un objeto de propiedad en lugar.
Sustitución de la línea setattr
con k = property(lambda x: vs[i])
es de ninguna utilidad en absoluto.
Entonces, ¿cuál es el camino correcto para crear una propiedad de instancia en tiempo de ejecución?
P.S. Soy consciente de una alternativa que se presenta en ¿Cómo se utiliza el método __getattribute__
?
Solución
supongo que debería ampliar esta respuesta, ahora que soy más viejo y más sabio y sé lo que está pasando. Mejor tarde que nunca.
puede añadir una propiedad a una clase dinámicamente. Pero eso está la trampa: hay que añadirlo a la class
.>>> class Foo(object):
... pass
...
>>> foo = Foo()
>>> foo.a = 3
>>> Foo.b = property(lambda self: self.a + 1)
>>> foo.b
4
A property
es en realidad una aplicación simple de una cosa que se llama un descriptor . Es un objeto que proporciona la costumbre de manipulación para un atributo dado, en una clase dada . Un poco como una manera de factorizar un enorme árbol if
de __getattribute__
.
Cuando pido foo.b
en el ejemplo anterior, Python ve que el b
definido en la clase implementa la protocolo descriptor -que simplemente significa que es un objeto con un __get__
, __set__
, o método __delete__
. El descriptor se atribuye la responsabilidad para el manejo de ese atributo, lo que Python llama Foo.b.__get__(foo, Foo)
, y el valor de retorno se pasa de nuevo a usted como el valor del atributo. En el caso de property
, cada uno de estos métodos sólo llama a la fget
, fset
o fdel
se pasa al constructor property
.
Los descriptores son realmente manera de exponer las cañerías de la totalidad de su aplicación OO de Python. De hecho, hay otro tipo de descriptor incluso 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 más que otro tipo de descriptor. Sus tachuelas __get__
en la instancia llamando como primer argumento; en efecto, se hace lo siguiente:
def __get__(self, instance, owner):
return functools.partial(self.function, instance)
De todos modos, sospecho que esta es la razón por descriptores sólo funcionan en las clases: son una formalización de las cosas que las clases de poderes en el primer lugar. Son aún la excepción a la regla: es obvio que puede asignar descriptores a una clase, y las clases son a su vez los casos de type
! De hecho, tratando de leer Foo.b
todavía llama property.__get__
; es sólo idiomática para los descriptores para volver a sí mismos cuando se accede como atributos de clase.
Creo que es muy bueno que prácticamente todos los sistemas OO de Python se puede expresar en Python. :)
Ah, y escribí un blog prolijo acerca de los descriptores hace un tiempo si está interesado.
Otros consejos
El objetivo es crear una clase simulada que se comporta como un conjunto de resultados db.
Así que lo que quiere es un diccionario donde se puede deletrear una [ 'b'] como a.b?
Eso es fácil:
class atdict(dict):
__getattr__= dict.__getitem__
__setattr__= dict.__setitem__
__delattr__= dict.__delitem__
Parece que podría resolver este problema mucho más simple con un namedtuple
, ya que usted sabe toda la lista de campos antes de tiempo.
from collections import namedtuple
Foo = namedtuple('Foo', ['bar', 'quux'])
foo = Foo(bar=13, quux=74)
print foo.bar, foo.quux
foo2 = Foo() # error
Si es absolutamente necesario escribir su propio organismo, que tendrá que hacer el metaprogramming a nivel de clase; property()
no funciona en los casos.
No es necesario utilizar una propiedad para eso. Sólo anular __setattr__
para que sean de sólo 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!
Cómo añadir una propiedad a una clase dinámicamente pitón?
Digamos que tienes un objeto que desea agregar una propiedad a. Por lo general, quiero usar propiedades cuando necesito para comenzar a gestionar el acceso a un atributo de código que tiene el uso de aguas abajo, de modo que pueda mantener una API consistente. Ahora voy a típicamente añadirlas al código fuente donde se define el objeto, pero vamos a suponer que usted no tiene que el acceso, o tiene que elegir verdaderamente dinámicamente sus funciones mediante programación.
Crea una clase
El uso de un ejemplo basado en la documentación rel="noreferrer"> property , vamos a crear una clase de objeto con un atributo "oculto" y crear una instancia del mismo:
class C(object):
'''basic class'''
_x = None
o = C()
En Python, esperamos que haya una manera obvia de hacer las cosas. Sin embargo, en este caso, voy a mostrar dos maneras: con la notación decorador, y desde fuera. En primer lugar, sin notación decorador. Esto puede ser más útil para la asignación dinámica de getters, organismos, o eliminadores.
Dinámica (también conocido como mono de parches)
Vamos a crear 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 asignamos estos a la propiedad. Tenga en cuenta que podríamos elegir nuestras funciones mediante programación aquí, responder a 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 lo hicimos anteriormente con la notación decorador, pero en este caso, que el nombre de los métodos, todos ellos del mismo nombre (y recomiendo mantenerlo lo mismo que el atributo ), misiones de manera programática no es tan trivial, ya que 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 asignar el objeto de propiedad con su setter aprovisionados y eliminadores 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
Le pregunté a una pregunta similary en este desbordamiento de pila publicación para crear un generador de clases que creó tipos simples. El resultado fue esta respuesta la que tenía una versión de trabajo de la fábrica de 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)
>>>
Usted podría utilizar alguna variación de esto para crear valores por defecto, que es su objetivo (también hay una respuesta en esa pregunta que se ocupa de esto).
No se puede agregar un nuevo property()
a una instancia en tiempo de ejecución, ya que las propiedades son descriptores de datos. En su lugar debe crear dinámicamente una nueva clase, o __getattribute__
sobrecarga con el fin de procesar descriptores de datos sobre los casos.
No estoy seguro si entiendo muy bien la pregunta, pero se puede modificar propiedades de la instancia en tiempo de ejecución con la incorporada en el __dict__
de la 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
Para los que vienen de los motores de búsqueda, aquí son las dos cosas que estaba buscando cuando se habla de dinámicas propiedades:
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 usted quiere poner propiedades creadas dinámicamente. __getattr__
es bueno sólo para hacer algo cuando se necesita el valor, al igual que consulta una base de datos. El set / get combo es buena para simplificar el acceso a los datos almacenados en la clase (como en el ejemplo anterior).
Si sólo desea una propiedad dinámica, echar un vistazo a la href="https://docs.python.org/3/library/functions.html#property" rel="nofollow noreferrer"> propiedad función incorporada.
La mejor manera de lograr es mediante la definición de __slots__
. De esa manera sus casos no puede 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'
Sólo 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)
Así que ahora podemos hacer cosas como:
>>> foo = Foo('baz')
>>> foo.baz = 5
>>> foo.bar
5
>>> foo.baz
5
Puede utilizar 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
Esto parece funcionar (pero véase 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
La siguiente probablemente sería más eficaz 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__)
Para responder a la idea central de su pregunta, usted quiere un atributo de sólo lectura de un diccionario como una fuente de datos inmutables:
El objetivo es crear una clase simulada que se comporta como un conjunto de resultados db.
Así, por ejemplo, si una consulta de base de datos devuelve, utilizando una expresión dict,
{'ab':100, 'cd':200}
, entonces yo sería para ver>>> dummy.ab 100
Voy a demostrar cómo utilizar un namedtuple
del módulo collections
a lograr precisamente 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
retornos 100
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
La extensión de la idea de 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>
A pesar de que se dan muchas respuestas, no pude encontrar uno que estoy feliz. Me di cuenta de mi propia solución que hace que el trabajo property
para el caso dinámico. La fuente para responder a 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
Algo que funciona para mí es la siguiente:
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
Esto es un poco diferente de lo que quería OP, pero me sacudió mi cerebro hasta que llegué a una solución de trabajo, por lo que estoy poniendo aquí para el siguiente tipo / gal
que necesitaba una manera de especificar los emisores y los captadores 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)
Sé que mis campos antes de tiempo tan im va a crear mis propiedades. NOTA: no se puede hacer esto por instancia, 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))
Vamos a probar 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 del mundo real significativas. Además, esto no es para la luz de corazón.
Sólo manera de unir dinámicamente una propiedad es crear una nueva clase y su ejemplo, con su nueva propiedad.
class Holder: p = property(lambda x: vs[i], self.fn_readonly)
setattr(self, k, Holder().p)
Hace poco me encontré con un problema similar, la solución que se me ocurrió usos __getattr__
y __setattr__
de las propiedades que quiero que le permite manejar, todo lo demás se transmite 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
Muchas de las respuestas suministradas requiere tantas líneas por propiedad, es decir, / y / o - lo que yo considero una implementación feo o aburrido, debido a la repetitividad requerida para múltiples propiedades, etc. Yo prefiero dejar hervir las cosas / simplificando hasta que no se pueden simplificar más, o hasta que no ayuda demasiado para hacerlo.
En resumen: en obras terminadas, si repito 2 líneas de código, normalmente convertirlo en una función auxiliar sola línea, y así sucesivamente ... simplifico matemáticas o impares argumentos tales como (start_x, start_y, end_x, end_y) a (x, y, w, h), es decir x, y, x + w, y + h (a veces requieren min / max o si w / h son negativos y la aplicación no le gusta, voy a restar de x / y y abs w / h. etc ..).
Anulación de los getters / setters internos es una manera bien para ir, pero el problema es que hay que hacer que para cada clase, o los padres de la clase a esa base ... Esto no funciona para mí ya que había prefieren tener libertad para elegir los niños / padres por herencia, los nodos secundarios, etc.
He creado una solución que responde a la pregunta sin necesidad de utilizar un tipo de datos Dict para suministrar los datos ya que me parece que para ser tedioso para introducir los datos, etc ...
Mi solución requiere que se agregue 2 líneas adicionales por encima de su clase para crear una clase base para la clase que desea agregar las propiedades a, a continuación, 1 línea por y usted tiene la opción de añadir devoluciones de llamada para controlar los datos, se informará cuando los cambios de datos, restringir los datos que pueden ajustarse basa en el valor y / o de tipo de datos, y mucho más.
También tiene la opción de utilizar _object.x, _object.x = valor, _object.GetX (), _object.SetX (valor) y que se manejan de forma equivalente.
Además, los valores son los únicos datos no estática que se asigna a la instancia de clase, pero la propiedad real se asignan a la clase que significa que las cosas que no desea repetir, no es necesario repetir. .. se puede asignar un valor por defecto por lo que el comprador no cada vez que necesita, aunque no es una opción para anular el valor por defecto por defecto, y no hay otra opción por lo que el comprador devuelve el valor almacenado en bruto anulando vuelve por defecto (nota : este método significa el valor en bruto solamente se asigna cuando se asigna un valor, de lo contrario es Ninguno - cuando el valor se pone a cero, entonces se asigna Ninguno, etc ..)
Hay muchas funciones de ayuda también - la primera propiedad que se agrega agrega 2 o más ayudantes de la clase para hacer referencia a los valores de instancia ... Son ResetAccessors (_key, ..) varargs repetida (todo se puede repetir utilizando el args primer nombre) y SetAccessors (_key, _value) con la opción de más que se añade a la clase principal para ayudar en la eficiencia - los previstos son: una forma de descriptores de acceso de grupo juntos, así que si usted tiende a restablecer unos pocos a la vez , cada vez, puede asignarlos a un grupo y restablecer el grupo en lugar de repetir los nombres de clave cada vez, y más.
El / valor almacenado en bruto ejemplo se almacena a clase. , la __CLASS. referencia a la clase de acceso cuyo sostiene Vars / valores / funciones estáticas para la propiedad. _clase. es la propiedad en sí, que es llamado cuando se accede a través de la clase ejemplo, durante el fraguado / conseguir, etc.
El descriptor de acceso _class .__ puntos a la clase, sino porque es interna tiene que ser asignado en la clase por lo que he optado por utilizar __Name = AccessorFunc (...) para asignarlo, una sola línea por propiedad con muchos argumentos opcionales para usarlos (usando varargs con llave porque son más fáciles y más eficientes para identificar y mantener) ...
También se crea una gran cantidad de funciones, como se ha mencionado, algunos de los cuales utilizan información de la función de acceso por lo que no necesita ser llamado (ya que es un poco incómodo en el momento - en este momento es necesario utilizar _class .FunctionName (_class_instance, args) -. me torno al uso de la pila / trace para agarrar la referencia de instancia para tomar el valor añadiendo elfunciones que, o bien se ejecutan este maratón poco, o mediante la adición de los descriptores de acceso al objeto y el uso de uno mismo (llamado esta señalar que son para la instancia y para mantener el acceso a la libre, la referencia de clase AccessorFunc, y otra información desde dentro de la función definiciones).
Es todavía no están hechos, pero es un fantástico punto de apoyo. Nota: Si usted no usa __Name = AccessorFunc (...) para crear las propiedades, que no tendrá acceso a la clave __ a pesar de que yo lo defino dentro de la función init. Si lo hace, entonces no hay problemas.
También: Tenga en cuenta que Nombre y Clave son diferentes ... Nombre es 'formal', que se utiliza en la creación Nombre de función, y la clave es que el almacenamiento de datos y acceso. es decir _class.x donde minúscula x es la clave, el nombre sería mayúscula X de modo que GetX () es la función en lugar de getX () que se parece un poco extraño. esto permite self.x a trabajar y cuidar adecuada, sino que también permiten GetX () y mirar apropiado.
Tengo una clase de ejemplo creado con llave / nombre idéntico, y diferente para mostrar. una gran cantidad de funciones de ayuda creados con el fin de dar salida a los datos. (Nota: No todo esto es completa) para que pueda ver lo que está pasando
La lista actual de funciones utilizando clave: x, nombre: salidas X como:
Esto es de ninguna manera una lista exhaustiva - hay unos pocos que no lo han hecho en este en el momento de la publicación ...
_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 siendo de datos de salida es:
Esto es para una nueva clase marca creada usando la clase de demostración sin ningún tipo de datos asignados, aparte del nombre (por lo que puede ser la salida) que es _foo, el nombre de la variable 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 todos _foo propiedades (excepto el nombre) los siguientes valores en el mismo orden: 'cadena', 1,0, True, 9, 10, false
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 restringidas o restricciones de valor, algunos datos no se le asignó - esto es por diseño. El colocador prohíbe malos tipos de datos o valores de ser asignado, incluso de ser asignado como valor por defecto (a menos que redefinir el comportamiento de protección de valor por defecto)
El código no se ha publicado aquí porque yo no tenía habitación después de los ejemplos y explicaciones ... También porque va a cambiar.
Nota: en el momento de este anuncio, el archivo es desordenado - esto va a cambiar. Sin embargo, si se ejecuta en Sublime Text y compila o ejecuta desde Python, se compilará y escupir un montón de información - la parte AccessorDB no se hace (que se utiliza para actualizar los captadores de impresión y ayudante GetKeyOutput funciones además de ser cambiados a una función Instancia, probablemente, poner en una sola función y renombrado - buscarlo ..)
A continuación: No todo lo que se requiere para que se ejecute - un montón de las cosas comentado en la parte inferior es para obtener más información utilizada para la depuración - puede no estar presente cuando se descarga. Si es así, usted debe ser capaz de eliminar el comentario y volver a compilar para obtener más información.
Busco un trabajo en torno a la necesidad de MyClassBase: pasar, MiClase (MyClassBase.): ... - si usted sabe de una solución - que lo ponga
La única cosa necesaria en la clase son las líneas __ - la str es para la depuración como es el init - que se puede retirar de la clase de demostración, pero se quiere necesitará comentar o eliminar algunas de las líneas de abajo (_foo / 2/3) ..
La Cuerda, Dict clases, y Util en la parte superior son una parte de mi biblioteca de Python - no son completas. Copié sobre algunas cosas que necesitaba de la biblioteca, y he creado algunos nuevos. El código completo se vinculará a la biblioteca completa y lo incluirá además de proporcionar llamadas actualizadas y eliminar el código (en realidad, el único código de la izquierda será la clase de demostración y las declaraciones de impresión - el sistema AccessorFunc se moverá a la biblioteca). ..
Parte de 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 muy fácil crear nuevas clasescon propiedades añadidas de forma dinámica con AccessorFuncs / devoluciones de llamada / de tipo de datos / aplicación de valor, etc.
Por ahora, el enlace es al (Este enlace debe reflejar cambios en el documento.): https://www.dropbox.com/s/6gzi44i7dh58v61/dynamic_properties_accessorfuncs_and_more.py?dl=0
También: Si no se utiliza Sublime Text, lo recomiendo sobre Notepad ++, Atom, código de Visual, y otros a causa de las implementaciones de roscado adecuados por lo que es mucho, mucho más rápido de usar ... También estoy trabajando en un IDE sistema de mapeo de código -como por ello - echar un vistazo a: https://bitbucket.org/ ACECOOL / acecoolcodemappingsystem / src / maestro / (Añadir Repo en Administrador de paquetes primero, y luego instalar el plugin - cuando la versión 1.0.0 está listo, voy a añadir a la lista principal complemento ...)
Espero que esta solución ayuda ... y, como siempre:
El hecho de que funciona, no significa que sea correcto - Josh 'ACECOOL' Moser