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.ab
devuelve un objeto de propiedad en su lugar.
Reemplazar la setattr
lí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?
python
properties
runtime
monkeypatching
Anthony Kong
fuente
fuente
:
y__init__
referenciasself.fn_readyonly
.Respuestas:
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 .
A
property
es 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 unif
árbol enorme__getattribute__
.Cuando pido
foo.b
en el ejemplo anterior, Python ve que elb
definido 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 llamaFoo.b.__get__(foo, Foo)
, y el valor de retorno se le devuelve como el valor del atributo. En el caso deproperty
cada uno de estos métodos sólo llama a lafget
,fset
ofdel
se pasan alproperty
constructor.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
.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: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 leerFoo.b
aún llamadasproperty.__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.
fuente
setattr (Foo, 'name', property (func))
Entonces, ¿qué quieres es un diccionario donde puedas deletrear a ['b'] como ab?
Eso es fácil:
fuente
d.items = 1
,d.items
retornos<built-in method items of atdict object at ...>
. Todavía podría hacerd["items"]
o usar en__getattribute__
lugar de__getattr__
, pero esto evita el uso de la mayoría de los métodos del dict.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.Si absolutamente necesita escribir su propio setter, tendrá que hacer la metaprogramación a nivel de clase;
property()
no funciona en instancias.fuente
namedtuple
merece un premio por hacer suave y elegante ser fieles a los principios orientados a objetos.property()
? no hay nada en ninguna de las respuestas que sea específico de las propiedades de solo lectura.No necesita usar una propiedad para eso. Simplemente anule
__setattr__
para que sean de solo lectura.Tada
fuente
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 para
property
, creemos una clase de objeto con un atributo "oculto" y creemos una instancia del mismo: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:
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:
Y uso:
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:
Y asigne el objeto de propiedad con sus establecedores y eliminadores aprovisionados a la clase:
Y uso:
fuente
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:
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).
fuente
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:fuente
self.__dict__[key] = value
Para aquellos que provienen de motores de búsqueda, estas son las dos cosas que estaba buscando al hablar de propiedades dinámicas :
__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 () .
fuente
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.fuente
La mejor manera de lograrlo es definiendo
__slots__
. De esa manera, sus instancias no pueden tener nuevos atributos.Que imprime
12
Eso da:
AttributeError: 'C' object has no attribute 'ab'
fuente
Solo otro ejemplo de cómo lograr el efecto deseado
Entonces ahora podemos hacer cosas como:
fuente
Aquí hay una solución que:
Una vez que se ha definido la clase, simplemente haga esto para agregarle una propiedad dinámicamente:
Aquí hay un ejemplo completo, probado en Python 3:
fuente
Puede usar el siguiente código para actualizar los atributos de clase utilizando un objeto de diccionario:
fuente
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.
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!
Probemos todo ahora ...
¿Es confuso? Sí, lo siento, no pude encontrar ningún ejemplo significativo del mundo real. Además, esto no es para los alegres.
fuente
Esto parece funcionar (pero ver más abajo):
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:
fuente
Para responder al objetivo principal de su pregunta, desea un atributo de solo lectura de un dict como fuente de datos inmutable:
Demostraré cómo usar un
namedtuple
delcollections
módulo para lograr esto:devoluciones
100
fuente
Y la salida es:
fuente
Extendiendo la idea desde kjfletch
Salida:
fuente
Aunque se dan muchas respuestas, no pude encontrar una con la que estoy contento. Descubrí mi propia solución que hace que el
property
trabajo sea dinámico. La fuente para responder la pregunta original:fuente
Algo que funciona para mí es esto:
Salida
fuente
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.fuente
Aquí está el ejemplo simple para crear objetos de propiedad mediante programación.
fuente
La única forma de adjuntar dinámicamente una propiedad es crear una nueva clase y su instancia con su nueva propiedad.
fuente
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 ...
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é ...
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
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:
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
fuente