str method magic len getitem data python class methods assignment-operator magic-methods

len - magic methods python



¿Es posible sobrecargar la asignación de Python? (8)

¿Hay algún método mágico que pueda sobrecargar al operador de asignación, como __assign__(self, new_value) ?

Me gustaría prohibir un nuevo enlace para una instancia:

class Protect(): def __assign__(self, value): raise Exception("This is an ex-parrot") var = Protect() # once assigned... var = 1 # this should raise Exception()

¿Es posible? ¿Es loco? ¿Debo tomar medicamentos?


En el espacio de nombres global esto no es posible, pero podría aprovechar la metaprogramación más avanzada de Python para evitar que se creen múltiples instancias de un objeto Protect . El patrón de Singleton es un buen ejemplo de esto.

En el caso de Singleton, se aseguraría de que una vez instanciado, incluso si se reasigna la variable original que hace referencia a la instancia, el objeto persistiría. Cualquier instancia posterior simplemente devolvería una referencia al mismo objeto.

A pesar de este patrón, nunca podrá evitar que se reasigne un nombre de variable global.


La forma en que la describes es absolutamente imposible. La asignación a un nombre es una característica fundamental de Python y no se han proporcionado ganchos para cambiar su comportamiento.

Sin embargo, la asignación a un miembro en una instancia de clase se puede controlar como lo desee, anulando .__setattr__() .

class MyClass(object): def __init__(self, x): self.x = x self._locked = True def __setattr__(self, name, value): if self.__dict__.get("_locked", False) and name == "x": raise AttributeError("MyClass does not allow assignment to .x member") self.__dict__[name] = value >>> m = MyClass(3) >>> m.x 3 >>> m.x = 4 Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 7, in __setattr__ AttributeError: MyClass does not allow assignment to .x member

Tenga en cuenta que hay una variable miembro, _locked , que controla si la asignación está permitida. Puedes desbloquearlo para actualizar el valor.


No creo que sea posible. La forma en que lo veo, la asignación a una variable no le hace nada al objeto al que se refería anteriormente: simplemente es que la variable "apunta" a un objeto diferente ahora.

In [3]: class My(): ...: def __init__(self, id): ...: self.id=id ...: In [4]: a = My(1) In [5]: b = a In [6]: a = 1 In [7]: b Out[7]: <__main__.My instance at 0xb689d14c> In [8]: b.id Out[8]: 1 # the object is unchanged!

Sin embargo, puede imitar el comportamiento deseado al crear un objeto contenedor con los __setitem__() o __setattr__() que __setattr__() una excepción y mantienen dentro el material "inmutable".


No, no hay

Piénselo, en su ejemplo está volviendo a vincular el nombre var a un nuevo valor. En realidad, no está tocando la instancia de Protect.

Si el nombre que desea vincular es, de hecho, una propiedad de alguna otra entidad, es decir, myobj.var, entonces puede evitar asignar un valor a la propiedad / atributo de la entidad. Pero supongo que eso no es lo que quieres de tu ejemplo.



Sí, es posible, puede manejar __assign__ través de modify ast .

pip install assign

Prueba con:

class T(): def __assign__(self, v): print(''called with %s'' % v) b = T() c = b

Conseguirás

>>> import magic >>> import test called with c

El proyecto está en https://github.com/RyanKung/assign Y la idea más simple: https://gist.github.com/RyanKung/4830d6c8474e6bcefa4edd13f122b4df


Una solución fea es reasignar en destructor. Pero no es una tarea de sobrecarga real.

import copy global a class MyClass(): def __init__(self): a = 1000 # ... def __del__(self): a = copy.copy(self) a = MyClass() a = 1


Usando el espacio de nombres de nivel superior, esto es imposible. Cuando corres

`var = 1`

Almacena la var clave y el valor 1 en el diccionario global. Es más o menos equivalente a llamar a globals().__setitem__(''var'', 1) . El problema es que no se puede reemplazar el diccionario global en un script en ejecución (es probable que se pueda jugar con la pila, pero esa no es una buena idea). Sin embargo, puede ejecutar código en un espacio de nombres secundario y proporcionar un diccionario personalizado para sus globales.

class myglobals(dict): def __setitem__(self, key, value): if key==''val'': raise TypeError() dict.__setitem__(self, key, value) myg = myglobals() dict.__setitem__(myg, ''val'', ''protected'') import code code.InteractiveConsole(locals=myg).interact()

Eso disparará un REPL que casi funciona normalmente, pero rechaza cualquier intento de establecer la variable val . También podría usar execfile(filename, myg) . Tenga en cuenta que esto no protege contra el código malicioso.