2012-03-30 9 views
23

Ich schrieb einige Wrapper, die ein anderes Objekt als Attribut hat. Dieser Wrapper leitet alle Attributanforderungen mit __getattr__ und __setattr__ an das als Attribut gespeicherte Objekt weiter. Was muss ich noch für meinen Proxy angeben, damit der Wrapper unter normalen Umständen wie die eingepackte Klasse aussieht?Wie fake/Proxy eine Klasse in Python

Ich glaube, ich brauche Dinge wie Vererbung zu beheben, vielleicht __repr__ ... Was muss ich sonst noch kümmern und wie behebe ich Vererbung so dass instanceof() funktioniert?

EDIT: Mein Versuch, eine Funktion Proxy zu machen, aber als ich das Rezept nicht in vollem Umfang verstehen, ist es nicht :(

setattr_=object.__setattr__ 
getattr_=object.__getattribute__ 

class Proxy(object): 
    __slots__=["_func", "_params", "_kwargs", "_obj", "_loaded", "__weakref__"] 
    def __init__(self, func, *params, **kwargs): 
     setattr_(self, "_func", func) 
     setattr_(self, "_params", params) 
     setattr_(self, "_kwargs", kwargs) 

     setattr_(self, "_obj", None) 
     setattr_(self, "_loaded", False) 

    def _get_obj(self): 
     if getattr_(self, "_loaded")==False: 
      print("Loading") 
      setattr_(self, "_obj", getattr_(self, "_func")(*getattr_(self, "_params"), **getattr_(self, "_kwargs"))) 
      setattr_(self, "_loaded", True) 

     return getattr_(self, "_obj") 
    # 
    # proxying (special cases) 
    # 
    def __getattribute__(self, name): 
     return getattr(getattr_(self, "_get_obj")(), name) 
    def __delattr__(self, name): 
     delattr(getattr_(self, "_get_obj")(), name) 
    def __setattr__(self, name, value): 
     setattr(getattr_(self, "_get_obj")(), name, value) 

    def __nonzero__(self): 
     return bool(getattr_(self, "_get_obj")()) 
    def __str__(self): 
     return str(getattr_(self, "_get_obj")()) 
    def __repr__(self): 
     return repr(getattr_(self, "_get_obj")()) 

    # 
    # factories 
    # 
    _special_names=[ 
     '__abs__', '__add__', '__and__', '__call__', '__cmp__', '__coerce__', 
     '__contains__', '__delitem__', '__delslice__', '__div__', '__divmod__', 
     '__eq__', '__float__', '__floordiv__', '__ge__', '__getitem__', 
     '__getslice__', '__gt__', '__hash__', '__hex__', '__iadd__', '__iand__', 
     '__idiv__', '__idivmod__', '__ifloordiv__', '__ilshift__', '__imod__', 
     '__imul__', '__int__', '__invert__', '__ior__', '__ipow__', '__irshift__', 
     '__isub__', '__iter__', '__itruediv__', '__ixor__', '__le__', '__len__', 
     '__long__', '__lshift__', '__lt__', '__mod__', '__mul__', '__ne__', 
     '__neg__', '__oct__', '__or__', '__pos__', '__pow__', '__radd__', 
     '__rand__', '__rdiv__', '__rdivmod__', '__reduce__', '__reduce_ex__', 
     '__repr__', '__reversed__', '__rfloorfiv__', '__rlshift__', '__rmod__', 
     '__rmul__', '__ror__', '__rpow__', '__rrshift__', '__rshift__', '__rsub__', 
     '__rtruediv__', '__rxor__', '__setitem__', '__setslice__', '__sub__', 
     '__truediv__', '__xor__', 'next', 
    ] 

    @classmethod 
    def _create_class_proxy(cls, theclass): 
     """creates a proxy for the given class""" 

     def make_method(name): 
      def method(self, *args, **kw): 
       return getattr(getattr_(self, "_get_obj")(), name)(*args, **kw) 
      return method 

     namespace={} 
     for name in cls._special_names: 
      if hasattr(theclass, name): 
       namespace[name]=make_method(name) 
     return type("%s(%s)"%(cls.__name__, theclass.__name__), (cls,), namespace) 

    def __new__(cls, obj, *args, **kwargs): 
     """ 
     creates an proxy instance referencing `obj`. (obj, *args, **kwargs) are 
     passed to this class' __init__, so deriving classes can define an 
     __init__ method of their own. 
     note: _class_proxy_cache is unique per deriving class (each deriving 
     class must hold its own cache) 
     """ 
     try: 
      cache=cls.__dict__["_class_proxy_cache"] 
     except KeyError: 
      cls._class_proxy_cache=cache={} 
     try: 
      theclass=cache[obj.__class__] 
     except KeyError: 
      cache[obj.__class__]=theclass=cls._create_class_proxy(obj.__class__) 
     ins=object.__new__(theclass) 
     theclass.__init__(ins, obj, *args, **kwargs) 
     return ins 

if __name__=='__main__': 
    def t(x, y): 
     print("Running t") 
     return x+y 

    a=Proxy(t, "a", "b") 
    print("Go") 
    print(a+"c") 
+1

Aus Interesse, warum erben Sie nicht einfach? – brice

+1

Da dieser Wrapper alle Arten von Objekten umhüllen soll. Es ist ein allgemeiner Stellvertreter. – Gerenuk

+0

Sie haben das Rezept ein wenig geändert. Es sieht so aus, als ob Sie wirklich wollen, ist 'functools.partial', aber vielleicht nicht; Was ist das Problem, das Sie wirklich versuchen zu lösen? – SingleNegationElimination

Antwort

20

Dieses Problem einigermaßen gut durch dieses Rezept angesprochen wird:

Object Proxying (Python recipe)

Die allgemeine Idee, die Sie folgen müssen, ist, dass die meisten Methoden auf Klassen durch eine Kombination von 0 zugegriffenund __getattribute__ entweder auf der Klasse selbst oder auf seiner Metaklasse, aber das gilt nicht für Python-Spezialmethoden, die mit doppelten Unterstrichen beginnen und enden, damit sie gefunden werden können, müssen sie tatsächliche Methoden der aktuellen Klasse sein. kein Attribut-Proxying ist möglich.

Welche dieser Methoden Sie bereitstellen müssen, hängt natürlich davon ab, welche dieser Methoden die proxied Klasse selbst anbietet. Die speziellen Methoden, die Sie für isinstance() bereitstellen müssen, sind die __instancecheck__ and __subclasscheck__ Methoden. für repr() zu arbeiten, müssen Sie auch eine entsprechende __repr__() auf der Proxy-Klasse selbst definieren.

+0

Das scheint sehr interessant, aber ich finde es sehr schwer, alle Details zu verstehen :(Ich habe versucht, es zu einem Proxy für Funktionsaufrufe - siehe bearbeiten. Allerdings nehme ich an, dass ich brauche Irgendwie, nachdem das Objekt initialisiert wurde, kann die spezielle Namensgenerierung durchgeführt werden Kann mir helfen zu verstehen, was ich ändern muss? – Gerenuk

3

Im Allgemeinen können Sie die wrapt Bibliothek (pypi) verwenden, die für Sie das schwere Heben tut:

Das wrapt Modul konzentriert sich sehr stark auf Korrektheit. Es geht damit weit über bestehende Mechanismen wie functools.wraps() hinaus, um sicherzustellen, dass Dekorierer Introspectability, Signaturen, Typprüfungsfähigkeiten usw. bewahren. [...]

Um sicherzustellen, dass der Overhead so minimal wie möglich ist, ein C Erweiterungsmodul wird für leistungskritische Komponenten verwendet

Es supports creating custom wrapper classes. Um eigene Attribute hinzuzufügen, müssen Sie diese so deklarieren, dass wrapt nicht versucht, sie an die umbrochene Instanz weiterzugeben. Sie können:

  • Präfix das Attribut mit _self_ und den Zugang für Eigenschaften hinzufügen
  • das Attribut auf Klassenebene deklarieren, zusätzlich zu den in __init__
  • Verwenden Slots ggf. für Ihre Klasse (nicht erwähnt in der Dokumentation), wie folgt aus:

    class ExtendedMesh(ObjectProxy): 
        __slots__ = ('foo') 
    
        def __init__(self, subject): 
         super().__init__(subject) 
         self.foo = "bar" 
    

Es ist auch supports function wrappers, die migh Das passt zu deinem Zweck.

Verwandte Themen