Cuándo y cómo utilizar la propiedad de función incorporada () en python

Me parece que a excepción de un poco de azúcar sintáctica, la propiedad () no hace nada bueno.

Claro, es bueno poder escribir ab=2 lugar de a.setB(2) , pero ocultar el hecho de que ab = 2 no es una tarea simple, parece una receta para problemas, ya sea porque puede ocurrir un resultado inesperado, por ejemplo, ab=2 realidad hace que ab sea 1 . O se levanta una excepción. O un problema de rendimiento. O simplemente ser confuso.

¿Puedes darme un ejemplo concreto para un buen uso de él? (usarlo para parchar el código problemático no cuenta 😉

En lenguajes que dependen de captadores y definidores, como Java, no se supone que se espera que hagan nada más que lo que dicen: sería sorprendente que x.getB() hiciera algo más que devolver el valor actual del atributo lógico b , o si x.setB(2) hizo algo más que la pequeña cantidad de trabajo interno necesario para que x.getB() devuelva 2 .

Sin embargo, no hay garantías impuestas por el lenguaje acerca de este comportamiento esperado, es decir, restricciones impuestas por el comstackdor en el cuerpo de métodos cuyos nombres comienzan con get o set : más bien, se deja al sentido común, convención social, “guías de estilo”, y pruebas.

El comportamiento de los accesos xb , y las asignaciones como xb = 2 , en idiomas que tienen propiedades (un conjunto de idiomas que incluye pero no se limita a Python) es exactamente el mismo que para los métodos de obtención y configuración en, por ejemplo, Java: Las mismas expectativas, la misma falta de garantías impuestas por el lenguaje.

La primera victoria para las propiedades es la syntax y la legibilidad. Tener que escribir, por ejemplo,

 x.setB(x.getB() + 1) 

en lugar de lo obvio

 xb += 1 

clama por venganza a los dioses. En los idiomas que admiten propiedades, no hay absolutamente ninguna buena razón para obligar a los usuarios de la clase a pasar por los giros de dicha placa de caligrafía bizantina, lo que afecta la legibilidad de su código sin ninguna ventaja.

Específicamente en Python, hay una gran ventaja adicional de usar propiedades (u otros descriptores) en lugar de captadores y definidores: si y cuando reorganiza su clase para que el establecedor y el captador subyacentes ya no sean necesarios, puede hacerlo (sin romper la clasificación de la clase). API publicada) simplemente elimine esos métodos y la propiedad que se basa en ellos, haciendo de b un atributo “almacenado” normal de la clase x lugar de uno “lógico” obtenido y establecido computacionalmente.

En Python, hacer las cosas directamente (cuando sea posible) en lugar de a través de métodos es una optimización importante, y el uso sistemático de las propiedades le permite realizar esta optimización siempre que sea posible (siempre exponiendo los “atributos normales almacenados” directamente, y solo aquellos que necesitan cálculos en el acceso y / o configuración a través de métodos y propiedades).

Por lo tanto, si utiliza captadores y definidores en lugar de propiedades, además de impactar la legibilidad del código de sus usuarios, también está perdiendo ciclos de máquinas de forma gratuita (y la energía que va a su computadora durante esos ciclos ;-), de nuevo sin ninguna razón. lo que.

Su único argumento en contra de las propiedades es, por ejemplo, que “un usuario externo no esperaría ningún efecto secundario como resultado de una asignación, generalmente”; pero se pierde el hecho de que el mismo usuario (en un lenguaje como Java en el que los captadores y los definidores están generalizados) no esperaría “efectos secundarios” (observables) como resultado de llamar a un definidor, tampoco (y aún menos para un captador ;-). Son expectativas razonables y depende de usted, como autor de la clase, tratar de satisfacerlas, ya sea que su colocador y su adquirente se usen directamente o a través de una propiedad, no hace ninguna diferencia. Si tiene métodos con efectos secundarios observables importantes, no los nombre getThis , setThat y no los use a través de propiedades.

La queja de que las propiedades “ocultan la implementación” es totalmente injustificada: la mayor parte de la POO consiste en implementar la ocultación de la información, lo que hace a una clase responsable de presentar una interfaz lógica al mundo exterior e implementarla internamente lo mejor que pueda. Getters y setters, exactamente como propiedades, son herramientas para este objective. Las propiedades simplemente hacen un mejor trabajo en eso (en los idiomas que las admiten ;-).

La idea es permitirle evitar tener que escribir getters y setters hasta que realmente los necesite.

Entonces, para empezar escribes:

 class MyClass(object): def __init__(self): self.myval = 4 

Obviamente ahora puedes escribir myobj.myval = 5 .

Pero más adelante, decides que necesitas un setter, ya que quieres hacer algo inteligente al mismo tiempo. Pero no debes tener que cambiar todo el código que usa tu clase, así que envuelves el setter en el decorador @property , y todo funciona.

pero ocultar el hecho de que ab = 2 no es una tarea simple parece una receta para problemas

Sin embargo, no estás ocultando ese hecho; ese hecho nunca estuvo ahí para empezar. Esto es python, un lenguaje de alto nivel; no assembly. Pocas de las declaraciones “simples” en él se reducen a instrucciones de una sola CPU. Leer la simplicidad en una tarea es leer cosas que no están allí.

Cuando dices xb = c, probablemente todo lo que deberías pensar es que “lo que haya sucedido, xb ahora debería ser c”.

Una razón básica es simplemente que se ve mejor. Es más pythonico. Especial para bibliotecas. something.getValue () se ve menos agradable que algo.valor

En plone (un CMS bastante grande), solía tener document.setTitle () que hace muchas cosas como almacenar el valor, indexarlo de nuevo y así. Solo hacer document.title = ‘algo’ es mejor. Sabes que muchas cosas están pasando detrás de las escenas de todos modos.

Tienes razón, es solo azúcar sintáctica. Puede ser que no haya buenos usos dependiendo de su definición de código problemático.

Tenga en cuenta que tiene una clase Foo que se usa ampliamente en su aplicación. Ahora esta aplicación es bastante grande y, además, digamos que es una aplicación web que se ha vuelto muy popular.

Identificas que Foo está causando un cuello de botella. Tal vez sea posible agregar algo de caché a Foo para acelerarlo. El uso de propiedades le permitirá hacerlo sin cambiar ningún código o prueba fuera de Foo.

Sí, por supuesto, este es un código problemático, pero acaba de ahorrar una gran cantidad de $$ reparándolo rápidamente.

¿Qué pasa si Foo está en una biblioteca para la cual tienes cientos o miles de usuarios? Bueno, te ahorraste tener que decirles que hagan un refactor costoso cuando actualicen a la versión más reciente de Foo.

Las notas de la versión tienen un artículo de línea sobre Foo en lugar de una guía de adaptación de párrafos.

Los progtwigdores experimentados de Python no esperan mucho de ab=2 aparte de ab==2 , pero saben que incluso eso puede no ser cierto. Lo que sucede dentro de la clase es su propio negocio.

Aquí hay un viejo ejemplo mío. Envolví una biblioteca de C que tenía funciones como “void dt_setcharge (int atom_handle, int new_charge)” y “int dt_getcharge (int atom_handle)”. Quería que en el nivel de Python hiciera “atom.charge = atom.charge + 1”.

El decorador de “propiedad” lo hace fácil. Algo como:

 class Atom(object): def __init__(self, handle): self.handle = handle def _get_charge(self): return dt_getcharge(self.handle) def _set_charge(self, charge): dt_setcharge(self.handle, charge) charge = property(_get_charge, _set_charge) 

Hace 10 años, cuando escribí este paquete, tuve que usar __getattr__ y __setattr__ lo que lo hizo posible, pero la implementación era mucho más propensa a errores.

 class Atom: def __init__(self, handle): self.handle = handle def __getattr__(self, name): if name == "charge": return dt_getcharge(self.handle) raise AttributeError(name) def __setattr__(self, name, value): if name == "charge": dt_setcharge(self.handle, value) else: self.__dict__[name] = value 

Los captadores y los definidores son necesarios para muchos propósitos y son muy útiles porque son transparentes al código. Al tener el objeto Algo de la altura de la propiedad, asigna un valor como Algo.height = 10, pero si la altura tiene un captador y un definidor, en el momento en que asigna ese valor, puede hacer muchas cosas en los procedimientos, como validar un mínimo o máximo valor, como desencadenar un evento porque la altura cambió, configurando automáticamente otros valores en función del nuevo valor de altura, todo lo que puede ocurrir en el momento en que se asignó el valor de Something.height. Recuerde, no necesita llamarlos en su código, se ejecutan automáticamente en el momento en que lee o escribe el valor de la propiedad. De alguna manera, son como procedimientos de eventos, cuando la propiedad X cambia de valor y cuando se lee el valor de la propiedad X.