1

necesito crear una clase inmmutable de tipo namedtuple. El código es el siguiente pero a la hora de ejecutar me devuelve un error de tipo AttributeError: can't set attribute

from collections import namedtuple

class Operacion (namedtuple("Operacion", ("operandoA", "operandoB"))):
    __slots__ = ()  
    def __init__(self, operandoA, operandoB):    
        self.operandoA = operandoA
        self.operandoB = operandoB

    def producto(self):

        return self.operandoA * self.operandoB

if __name__ == "__main__":

    # Ejemplos
    r = Operacion(25, 78)
    print(r.operandoA) # Prints 25
    print(r.operandoB)  # Prints 78
    print(r.producto()) # Prints 1950
Lorena
  • 33
  • 3

1 Answers1

0

El problema es la asignación de atributos que haces en el método __init__, dicho método se llama después de que la instancia ya se ha creado y, por lo tanto, ya no podría mutar la tupla.

Podrías usar el constructor __new__ en vez del inicializdor (__init__) si verdaderamente se requiere interponer alguna funcionalidad extra en la asignación de valor a los atributos. En tu ejemplo realmente es innecesario en principio, podemos prescindir del inicializador, el constructor de namedtuple ya se encarga de crear y asignar el valor de los atributos pasados como argumento:

from collections import namedtuple

class Operacion (namedtuple("Operacion", ("operandoA", "operandoB"))):
    __slots__ = ()

    def producto(self):
        return self.operandoA * self.operandoB

Si se usa Python >= 3.7 se podría hacer uso de dataclass en vez usar nametuples a no ser que requieras alguna funcionalidad de ésta. Si usas el argumento frozen=True te permite emular instancias congeladas de solo lectura:

from dataclasses import dataclass
from numbers import Real

@dataclass(frozen=True)
class Operacion:
    operandoA: Real
    operandoB: Real

    def producto(self):
        return self.operandoA * self.operandoB
FJSevilla
  • 55,603
  • 7
  • 35
  • 58