2012-06-13 8 views
45

Gibt es eine magische Methode, die den Zuweisungsoperator überladen kann, wie __assign__(self, new_value)?Kann die Python-Zuweisung überladen werden?

Ich möchte ein Re-bind für eine Instanz verbieten:

class Protect(): 
    def __assign__(self, value): 
    raise Exception("This is an ex-parrot") 

var = Protect() # once assigned... 
var = 1   # this should raise Exception() 

Ist es möglich? Ist es verrückt? Sollte ich auf Medizin sein?

+1

Was ist der Anwendungsfall? – msw

+0

Anwendungsfall: Leute werden kleine Skripts mit meiner Service-API schreiben, und ich möchte verhindern, dass sie interne Daten ändern und diese Änderung auf das nächste Skript übertragen. – Caruccio

+3

Python vermeidet ausdrücklich, dass ein böswilliger oder ignoranter Coder vom Zugriff ausgeschlossen wird. Andere Sprachen erlauben es, Programmierfehler aufgrund von Ignoranz zu vermeiden, aber die Leute haben eine unheimliche Fähigkeit, um sie herum zu programmieren. – msw

Antwort

7

Ich glaube nicht, dass es möglich ist. So wie ich es sehe, tut die Zuweisung zu einer Variablen nichts zu dem Objekt, auf das sie vorher Bezug genommen hat: Es ist nur so, dass die Variable jetzt auf ein anderes Objekt "zeigt".

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! 

Sie können jedoch das gewünschte Verhalten durch die Schaffung eines Wrapper-Objekt mit __setitem__() oder __setattr__() Methoden nachzuahmen, die eine Ausnahme auslösen, und halten Sie die „unveränderliche“ Sachen im Inneren.

25

Nein, als Zuweisung ist ein language intrinsic, der keinen Modifikationshaken hat.

+2

Seien Sie versichert, dies wird in Python 4.x nicht passieren. –

+4

Jetzt bin ich versucht zu schreiben, ein PEP für Unterklassen und den aktuellen Bereich zu ersetzen. – zigg

2

Nein, es ist nicht

darüber nachdenkt, in Ihrem Beispiel Sie den Namen var auf einen neuen Wert sind rebinding. Sie berühren die Instanz von Protect nicht.

Wenn der Name, den Sie binden möchten, tatsächlich eine Eigenschaft eines anderen Elements ist, d. H. myobj.var, können Sie die Zuweisung eines Werts an die Eigenschaft/das Attribut der Entität verhindern. Aber ich nehme an das ist nicht was du von deinem Beispiel willst.

+0

Fast da! Ich habe versucht, das '__dict __.__ setattr__' des Moduls zu überladen, aber 'module .__ dict__' selbst ist schreibgeschützt. Geben Sie (mymodule) == ein, und es ist nicht instanziierbar. – Caruccio

39

Die Art, wie Sie es beschreiben, ist absolut nicht möglich. Die Zuweisung zu einem Namen ist ein grundlegendes Merkmal von Python und es wurden keine Hooks bereitgestellt, um das Verhalten zu ändern.

Die Zuordnung zu einem Mitglied in einer Klasseninstanz kann jedoch so gesteuert werden, wie Sie möchten, indem Sie .__setattr__() überschreiben.

class MyClass(object): 
    def __init__(self, x): 
     self.x = x 
     self._locked = True 
    def __setattr__(self, name, value): 
     if self.__dict__.get("_locked") 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 

Hinweis, dass es einen Elementvariable, _locked, die steuert, ob die Zuordnung erlaubt ist. Sie können es entsperren, um den Wert zu aktualisieren.

+1

Die Verwendung von '@ property' mit einem Getter, aber keinem Setter, ist eine ähnliche Methode zur Zuweisung von Pseudo-Overloads. – jtpereyda

2

Im globalen Namespace ist dies nicht möglich, aber Sie könnten die erweiterte Python-Metaprogrammierung nutzen, um zu verhindern, dass mehrere Instanzen eines Protect-Objekts erstellt werden. Die Singleton pattern ist ein gutes Beispiel dafür.

Im Falle eines Singleton würden Sie sicherstellen, dass das Objekt auch dann bestehen bleibt, wenn es einmal instanziiert wurde, selbst wenn die ursprüngliche Variable, die auf die Instanz verweist, neu zugewiesen wird. Alle nachfolgenden Instanzen würden nur einen Verweis auf das gleiche Objekt zurückgeben.

Trotz dieses Musters könnten Sie niemals verhindern, dass ein globaler Variablenname selbst neu zugewiesen wird.

+0

Ein Singleton ist nicht genug, da 'var = 1' den Singleton-Mechanismus nicht aufruft. – Caruccio

+0

Verstanden. Ich entschuldige mich, wenn ich nicht klar war. Ein Singleton würde verhindern, dass weitere Instanzen eines Objekts (z.B. 'Protect()') erzeugt werden. Es gibt keine Möglichkeit, den ursprünglich zugewiesenen Namen (z. B. "var") zu schützen. – jathanism

1

Eine hässliche Lösung ist es, auf Destruktor neu zuzuweisen. Aber es ist keine echte Überlastungsaufgabe.

import copy 
global a 

class MyClass(): 
    def __init__(self): 
      a = 1000 
      # ... 

    def __del__(self): 
      a = copy.copy(self) 


a = MyClass() 
a = 1 
2

Mit dem Namespace der obersten Ebene ist dies unmöglich.Es speichert den Schlüssel var und den Wert 1 im globalen Wörterbuch Wenn Sie

`var = 1` 

laufen. Es entspricht in etwa dem Aufruf globals().__setitem__('var', 1). Das Problem ist, dass Sie das globale Wörterbuch in einem laufenden Skript nicht ersetzen können (Sie können wahrscheinlich mit dem Stapel herumspielen, aber das ist keine gute Idee). Sie können jedoch Code in einem sekundären Namespace ausführen und ein benutzerdefiniertes Wörterbuch für seine Globals bereitstellen.

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() 

dass ein REPL anwerfen wird, die normalerweise fast betreibt, weigert sich aber alle Versuche, die Variable val einzustellen. Sie könnten auch execfile(filename, myg) verwenden. Beachten Sie, dass dies keinen Schutz vor bösartigem Code bietet.

+0

Das ist dunkle Magie! Ich habe erwartet, dass ich nur eine Menge Antworten finde, wo Leute vorschlagen, ein Objekt explizit mit einem überschriebenen Setattr zu verwenden, nicht daran gedacht haben, Globals und Locals mit einem benutzerdefinierten Objekt zu überschreiben, wow. Dies muss PyPy jedoch zum Weinen bringen. –

1

Ja, es ist möglich, Sie können __assign__ über ändern bearbeiten ast.

pip install assign

-Test mit:

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

Sie

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

Das Projekt erhalten wird, ist bei https://github.com/RyanKung/assign Und je einfacher Kern: https://gist.github.com/RyanKung/4830d6c8474e6bcefa4edd13f122b4df

+0

Da ist etwas, was ich nicht bekomme ... Sollte es nicht 'print ('mit% s'% self) aufgerufen werden? – zezollo