En Python, ¿cómo puedo crear atributos no asignables (como los marcados con `final` en Java)?

8 minutos de lectura

Avatar de usuario de Jason Coon
jason coon

¿Hay algo en Python que funcione como el final palabra clave en Java, es decir, para no permitir la asignación a un atributo específico de las instancias de una clase, después de que se hayan creado esas instancias? No pude encontrar nada como esto en la documentación.

Estoy creando una instantánea de un objeto (utilizado para la restauración si algo falla); una vez que se asigna esta variable de respaldo, no debe modificarse; finalLa función similar en Python sería buena para esto.

  • posible duplicado de Creando constante en Python

    –Anderson Green

    28/02/2014 a las 20:51

  • @ApurvaSingh Si bien estoy de acuerdo en que Python no es adecuado para eso, hay tuplas y frozenset en la biblioteca estándar y MagicStack/inmutables es una implementación de un mapeo mutable que también se propondrá agregar a la biblioteca estándar.

    – antonagesta

    14 de septiembre de 2019 a las 8:26

Avatar de usuario de Stephan202
Stephan202

No hay final equivalente en Python. Para crear campos de solo lectura de instancias de clase, puede usar el propiedad función, o podría hacer algo como esto:

class WriteOnceReadWhenever:
    def __setattr__(self, attr, value):
        if hasattr(self, attr):
            raise Exception("Attempting to alter read-only value")
    
        self.__dict__[attr] = value

También tenga en cuenta que mientras hay @typing.final a partir de Python 3.8 (como menciona Cerno), eso no en realidad hacer valores final en tiempo de ejecución.

  • siempre reemplazo self.__dict__[attr] = value con return super(WriteORW, self).__setattr__(attr, value)

    – John Strod

    11 de enero de 2019 a las 8:44

  • Es posible que desee actualizar su respuesta, ya que allí se introdujo en Python 3.8: docs.python.org/3.8/library/…

    – Cerno

    23 de agosto de 2022 a las 12:50

Tener una variable en Java ser final básicamente significa que una vez que asigna una variable, no puede reasignar esa variable para que apunte a otro objeto. En realidad, no significa que el objeto no se pueda modificar. Por ejemplo, el siguiente código Java funciona perfectamente bien:

public final List<String> messages = new LinkedList<String>();

public void addMessage()
{
    messages.add("Hello World!");  // this mutates the messages list
}

pero lo siguiente ni siquiera compilaría:

public final List<String> messages = new LinkedList<String>();

public void changeMessages()
{
    messages = new ArrayList<String>();  // can't change a final variable
}

Entonces tu pregunta es sobre si final existe en Python. No es asi.

Sin embargo, Python tiene estructuras de datos inmutables. Por ejemplo, si bien puede mutar un listno puedes mutar un tuple. Puedes mutar un set pero no un frozensetetc.

Mi consejo sería simplemente no preocuparse por hacer cumplir la no mutación a nivel de idioma y simplemente concentrarse en asegurarse de no escribir ningún código que mute estos objetos después de que se asignen.

  • En Java, una variable no primitiva apunta a un objeto. Designar una variable como final significa que no puede hacer que la variable apunte a un objeto diferente, sin embargo, el objeto al que se apunta puede mutar.

    – Ardor de Hielo

    17 de febrero de 2015 a las 18:49

  • Hay otro uso para final en Java, creo que es lo mismo que static en C++, y es que la línea de código que se asigna a una variable final no se ejecuta más de una vez, incluso si se declara dentro de una función. Le permite ocultar la variable del resto del código sin construir constantemente nuevas instancias cada vez que se llama a las funciones. En Python, debe usar una variable en el ámbito global o aceptar el costo de construir constantemente nuevas instancias. cpp.sh/24gj

    –Hatoru Hansou

    5 de noviembre de 2015 a las 3:08


  • Me parece una sugerencia horrible para simply concentrate on making sure that you don't write any code which mutates these objects after they're assigned. ¿Cómo comunicarás esto a otras personas? ¿O a tu futuro yo? ¿Con un comentario o una cadena de documentación? Una construcción de lenguaje formal es mucho menos volátil que un comentario. Python que carece de esto es, de hecho, preocupante.

    – delucasvb

    7 de enero de 2019 a las 14:13

  • Ya no es preciso para versiones >= 3.8. Vea la respuesta de @congusbongus a continuación.

    – Charles Merriam

    2 de noviembre de 2019 a las 21:38

  • Después de leer todas las respuestas y bromas del “experto en Python” sobre la declaración de constantes, parece que Python es para un “proyecto de desarrollador único”.

    – Tristán

    17 de agosto de 2022 a las 16:22

avatar de usuario de congusbongus
congusbongo

Pitón 3.8 (a través de PEP 591) agrega Final variables, funciones, métodos y clases. Aquí hay algunas formas de usarlo:

@final Decorador (clases, métodos)

from typing import final

@final
class Base:
    # Cannot inherit from Base

class Base:
    @final
    def foo(self):
        # Cannot override foo in subclass

Final anotación

from typing import Final

PI: Final[float] = 3.14159     # Cannot set PI to another value
KM_IN_MILES: Final = 0.621371  # Type annotation is optional

class Foo:
    def __init__(self):
        self.bar: Final = "baz"   # Final instance attributes only allowed in __init__

Tenga en cuenta que, al igual que otras sugerencias de escritura, no le impiden anular los tipos, pero ayudan a que los linters o IDE le adviertan sobre el uso incorrecto de tipos.

  • Creo que esta respuesta es engañosa, ya que es fácil tener la impresión de que los calificadores finales evitarán que se reasignen o anulen en el tiempo de ejecución, lo cual no es el caso.

    – antonagesta

    25 de noviembre de 2019 a las 9:01

  • @antonagestam es una característica del idioma. Los IDE como Visual Studio Code y PyCharm probablemente se adaptarán y mostrarán automáticamente una advertencia si intenta reasignar una instancia final. Este ya es el caso cuando intenta usar una variable de instancia con un prefijo de subrayado fuera de la clase.

    – momo

    4 de enero de 2020 a las 13:55

  • @momo Al igual que una pregunta sobre propiedades privadas debería tener una respuesta que explique que nada es realmente privado en Python, creo que una respuesta a esta pregunta sugiere usar el final/Final calificadores debe explicar que no hacen nada en tiempo de ejecución. Tenga en cuenta que también respondí esta pregunta.

    – antonagesta

    5 de enero de 2020 a las 20:38

  • Estoy de acuerdo con el comentario de @antonagestam. @typing.final solo lo usa el verificador de tipo estático. La respuesta debería dejar más claro que este decorador se ignora por completo al ejecutar el script. Las clases creadas dinámicamente ignorarán silenciosamente este decorador.

    – Conquilicultor

    20 de enero de 2020 a las 20:31

Avatar de usuario de S. Lott
S. Lott

Una variable de asignación única es un problema de diseño. Usted diseña su aplicación de manera que la variable se establezca una vez y solo una vez.

Sin embargo, si desea verificar su diseño en tiempo de ejecución, puede hacerlo con un envoltorio alrededor del objeto.

class OnePingOnlyPleaseVassily(object):
    def __init__(self):
        self.value = None
    def set(self, value):
        if self.value is not None:
            raise Exception("Already set.")
        self.value = value

someStateMemo = OnePingOnlyPleaseVassily()
someStateMemo.set(aValue) # works
someStateMemo.set(aValue) # fails

Eso es torpe, pero detectará problemas de diseño en tiempo de ejecución.

No existe tal cosa. En general, la actitud de Python es “si no quieres que esto se modifique, simplemente no lo modifiques”. Es poco probable que los clientes de una API solo hurguen en sus partes internas no documentadas de todos modos.

Supongo que podría solucionar esto usando tuplas o tuplas con nombre para las partes relevantes de su modelo, que son intrínsecamente inmutables. Eso todavía no ayuda con ninguna parte de su modelo que tenga que ser mutable, por supuesto.

  • Muchas otras clases también son inmutables: strings, ints, longs, floats.

    – S. Lott

    29 de abril de 2009 a las 14:37

Avatar de usuario de UncleZeiv
tíoZeiv

puedes simular algo así a través del protocolo descriptorya que permite definir la lectura y configuración de una variable de la forma que se desee.

class Foo(object):

  @property
  def myvar(self):
     # return value here

  @myvar.setter
  def myvar(self, newvalue):
     # do nothing if some condition is met

a = Foo()
print a.myvar
a.myvar = 5 # does nothing if you don't want to

  • Muchas otras clases también son inmutables: strings, ints, longs, floats.

    – S. Lott

    29 de abril de 2009 a las 14:37

A partir de 2019 y PEP 591Python tiene un Final tipo. No estará disponible en la biblioteca estándar hasta el lanzamiento de Python 3.8, pero hasta entonces puede usarlo a través de la extensiones de escritura biblioteca. no funcionará como final funciona en Java, ya que Python sigue siendo un lenguaje de tipo dinámico. Pero si lo usa junto con un verificador de tipo estático como mipy te dará beneficios muy similares.

También hay una final decorador que se puede aplicar para marcar métodos de clase como finales y evitar que se anulen. Nuevamente, esto solo se verifica en el “tiempo de compilación”, por lo que deberá incluir un verificador de tipo estático en su flujo de trabajo.

¿Ha sido útil esta solución?