privados - self python 3
Cuándo y cómo usar la propiedad de función incorporada() en python (7)
pero ocultar el hecho de que ab = 2 no es una tarea simple parece una receta para problemas
No estás ocultando ese hecho sin embargo; ese hecho nunca estuvo allí para comenzar. Esto es python, un lenguaje de alto nivel; no asamblea. Algunas de las afirmaciones "simples" se reducen a instrucciones de CPU únicas. Leer la simplicidad en una tarea es leer cosas que no están allí.
Cuando dices xb = c, probablemente todo lo que debes pensar es que "lo que sea que acabe de suceder, xb ahora debería ser c".
Me parece que a excepción de un poco de azúcar sintáctico, 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 suceder algún resultado inesperado, como ab=2
realidad causa que ab
sea 1
. O una excepción se plantea. O un problema de rendimiento. O simplemente ser confuso
¿Me puede dar un ejemplo concreto para un buen uso de él? (usarlo para parchar código problemático no cuenta ;-)
Aquí hay un viejo ejemplo mío. Envolví una biblioteca C que tenía funciones como "void dt_setcharge (int atom_handle, int new_charge)" y "int dt_getcharge (int atom_handle)". Quería en el nivel de Python hacer "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__ que lo hicieron 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
En los lenguajes que se basan en getters y setters, como Java, no se supone ni se espera que hagan nada más que lo que dicen; sería sorprendente si 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 cualquier pequeña cantidad de trabajo interno que se necesite para hacer que x.getB()
devuelva 2
.
Sin embargo, no existen garantías impuestas por el lenguaje sobre este comportamiento esperado, es decir, restricciones impuestas por el compilador 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 prueba.
El comportamiento de los accesos xb
y las asignaciones como xb = 2
en los idiomas que sí tienen propiedades (un conjunto de idiomas que incluye pero no se limita a Python) es exactamente el mismo que para los métodos getter y setter 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 sintaxis y la legibilidad. Tener que escribir, por ejemplo,
x.setB(x.getB() + 1)
en lugar de lo obvio
x.b += 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 dicho modelo bizantino, lo que afecta la legibilidad de su código sin ninguna ventaja.
Específicamente, en Python hay una gran ventaja adicional al usar propiedades (u otros descriptores) en lugar de getters y setters: si reorganiza su clase para que el setter y getter subyacentes ya no sean necesarios, puede (sin romper la clase API publicada) simplemente elimine esos métodos y la propiedad que se basa en ellos, haciendo b
un atributo "almacenado" normal de la clase de x
lugar de uno "lógico" obtenido y establecido computacionalmente.
En Python, hacer cosas directamente (cuando sea factible) en lugar de hacerlo a través de métodos es una optimización importante, y el uso sistemático de propiedades le permite realizar esta optimización siempre que sea posible (siempre exponiendo "atributos almacenados normales" directamente, y solo aquellos que necesitan computación al acceder y / o configuración a través de métodos y propiedades).
Entonces, si usas getters y setters en lugar de propiedades, además de afectar la legibilidad del código de tus usuarios, también estás desperdiciando ciclos de máquina (y la energía que va a su computadora durante esos ciclos ;-), sin ningún motivo 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, por lo general"; pero te pierdes el hecho de que el mismo usuario (en un lenguaje como Java donde getters y setters son omnipresentes) no esperaría (observables) "efectos secundarios" como resultado de llamar a un setter, tampoco (y aún menos por un getter) ;-). Son expectativas razonables y depende de usted, como autor de la clase, intentar acomodarlas, ya sea que su setter y getter se usen directamente o a través de una propiedad, no hace ninguna diferencia. Si tiene métodos con importantes efectos secundarios observables, no los nombre getThis
, setThat
, y no los use a través de las propiedades.
La queja de que las propiedades "ocultan la implementación" es completamente injustificada: la mayoría de los OOP se trata de implementar la ocultación de la información, haciendo que la clase sea responsable de presentar una interfaz lógica al mundo exterior e implementarla internamente de la mejor manera posible. Getters y setters, exactamente como propiedades, son herramientas para alcanzar este objetivo. Las propiedades solo hacen un mejor trabajo (en los idiomas que los admiten ;-).
La idea es permitirle evitar tener que escribir getters y setters hasta que realmente los necesite.
Entonces, para comenzar, escribe:
class MyClass(object):
def __init__(self):
self.myval = 4
Obviamente, ahora puedes escribir myobj.myval = 5
.
Pero más tarde, decides que necesitas un colocador, ya que quieres hacer algo inteligente al mismo tiempo. Pero no quiere tener que cambiar todo el código que usa su clase, de modo que envuelve el colocador en el decorador @property
y todo funciona.
Tienes razón, es solo azúcar sintáctico. Puede ser que no haya un buen uso de la misma dependiendo de su definición de código problemático.
Considere 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.
Identifica que Foo está causando un cuello de botella. Quizás es posible agregar un poco de almacenamiento en 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 acabas de guardar una gran cantidad de $$ solucionándolo rápidamente.
¿Qué pasa si Foo está en una biblioteca para la que tiene cientos o miles de usuarios? Bueno, se ahorró tener que decirles que hagan un refactorio costoso cuando actualicen a la versión más nueva de Foo.
Las notas de la versión tienen un elemento de línea sobre Foo en lugar de una guía de portación de párrafo.
Los programadores experimentados de Python no esperan mucho de ab=2
además de ab==2
, pero saben que incluso eso puede no ser cierto. Lo que sucede dentro de la clase es su propio negocio.
Una razón básica es simplemente que se ve mejor. Es más pitónico. Especialmente para bibliotecas. something.getValue () parece 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, etc. Solo hacer document.title = ''algo'' es mejor. Sabes que mucho está sucediendo detrás de escena de todos modos.
getters y setters son necesarios para muchos propósitos, y son muy útiles porque son transparentes para el código. Al tener el objeto Something como la altura de la propiedad, le asigna un valor como Something.height = 10, pero si height tiene un getter y setter, al momento de asignar 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ó, estableciendo 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 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 X de la propiedad.