Hinzufügen einer Methode zu einer vorhandenen Objektinstanz

  • Ich habe gelesen, dass es möglich ist, einem vorhandenen Objekt (d. h. nicht in der Klassendefinition) in Python eine Methode hinzuzufügen.

    Ich verstehe, dass es nicht immer gut ist, dies zu tun. Aber wie kann man das machen?

    22 December 2017
    Paul RatazziDylanJ
17 answers
  • In Python gibt es einen Unterschied zwischen Funktionen und gebundenen Methoden.

     >>> def foo():
    ...     print "foo"
    ...
    >>> class A:
    ...     def bar( self ):
    ...         print "bar"
    ...
    >>> a = A()
    >>> foo
    <function foo at 0x00A98D70>
    >>> a.bar
    <bound method A.bar of <__main__.A instance at 0x00A9BC88>>
    >>>
     

    Gebundene Methoden wurden an eine Instanz "gebunden" (wie beschreibend), und diese Instanz wird bei jedem Aufruf der Methode als erstes Argument übergeben.

    Callables, die Attribute einer Klasse sind ( sind im Gegensatz zu einer Instanz noch ungebunden, sodass Sie die Klassendefinition jederzeit ändern können:

     >>> def fooFighters( self ):
    ...     print "fooFighters"
    ...
    >>> A.fooFighters = fooFighters
    >>> a2 = A()
    >>> a2.fooFighters
    <bound method A.fooFighters of <__main__.A instance at 0x00A9BEB8>>
    >>> a2.fooFighters()
    fooFighters
     

    Zuvor definierte Instanzen werden ebenfalls aktualisiert (sofern sie das Attribut selbst nicht überschrieben haben):

     >>> a.fooFighters()
    fooFighters
     

    Das Problem tritt auf, wenn Sie eine Methode an eine einzelne Instanz anhängen möchten:

     >>> def barFighters( self ):
    ...     print "barFighters"
    ...
    >>> a.barFighters = barFighters
    >>> a.barFighters()
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    TypeError: barFighters() takes exactly 1 argument (0 given)
     

    Die Funktion ist nicht vorhanden Wird automatisch gebunden, wenn es direkt an eine Instanz angehängt wird:

     >>> a.barFighters
    <function barFighters at 0x00A98EF0>
     

    Zum Binden können Sie die MethodType-Funktion in der Typenmodul :

     >>> import types
    >>> a.barFighters = types.MethodType( barFighters, a )
    >>> a.barFighters
    <bound method ?.barFighters of <__main__.A instance at 0x00A9BC88>>
    >>> a.barFighters()
    barFighters
     

    Dieses Mal sind andere Instanzen der Klasse nicht betroffen:

     >>> a2.barFighters()
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    AttributeError: A instance has no attribute 'barFighters'
     

    Weitere Informationen finden Sie in Deskriptoren und metaclass Programmierung .

    25 September 2011
    Ethan Furmangm70560
  • Das Modul new ist seit Python 2.6 veraltet und wurde in 3.0 entfernt. Verwenden Sie Typen

    siehe http://docs.python.org/library/new.html

    Im folgenden Beispiel habe ich den Rückgabewert absichtlich aus der Funktion patch_me() entfernt. Ich denke, der Rückgabewert lässt vermuten, dass der Patch ein neues Objekt zurückgibt ändert die eingehende. Wahrscheinlich kann dies eine diszipliniertere Verwendung von Monkeypatching ermöglichen.

     import types
    
    class A(object):#but seems to work for old style objects too
        pass
    
    def patch_me(target):
        def method(target,x):
            print "x=",x
            print "called from", target
        target.method = types.MethodType(method,target)
        #add more if needed
    
    a = A()
    print a
    #out: <__main__.A object at 0x2b73ac88bfd0>  
    patch_me(a)    #patch instance
    a.method(5)
    #out: x= 5
    #out: called from <__main__.A object at 0x2b73ac88bfd0>
    patch_me(A)
    A.method(6)        #can patch class too
    #out: x= 6
    #out: called from <class '__main__.A'>
     
    07 February 2013
    olga-in-space
  • Hinzufügen einer Methode zu einer vorhandenen Objektinstanz

    Ich habe gelesen, dass es möglich ist, einem vorhandenen Objekt eine Methode hinzuzufügen ( zB nicht in der Klassendefinition) in Python.

    Ich verstehe, dass es nicht immer eine gute Entscheidung ist, dies zu tun. Aber wie kann man das machen?

    Ja, es ist möglich - wird aber nicht empfohlen

    Ich kann das nicht empfehlen. Das ist eine schlechte Idee. Tu es nicht.

    Hier sind einige Gründe:

    • Sie fügen jeder Instanz, die Sie ausführen, ein gebundenes Objekt hinzu dies zu. Wenn Sie dies häufig tun, verschwenden Sie wahrscheinlich viel Speicher. Gebundene Methoden werden normalerweise nur für die kurze Dauer ihres Aufrufs erstellt. Sie bestehen dann nicht mehr, wenn automatisch Müll gesammelt wird. Wenn Sie dies manuell tun, haben Sie eine Namensbindung, die auf die gebundene Methode verweist. Dadurch wird die Garbage Collection bei der Verwendung verhindert.
    • Objektinstanzen eines bestimmten Typs haben im Allgemeinen für alle Objekte ihre Methoden von diesem Typ. Wenn Sie Methoden an anderer Stelle hinzufügen, haben einige Instanzen diese Methoden und andere nicht. Programmierer erwarten dies nicht, und Sie riskieren einen Verstoß gegen die Regel der geringsten Überraschung
    • Da es andere wirklich gute Gründe gibt, dies nicht zu tun, verschaffen Sie sich zusätzlich einen schlechten Ruf, wenn Sie es tun.

    Ich schlage daher vor, dies nicht zu tun, es sei denn, Sie haben einen guten Grund. Es ist weitaus besser, die richtige Methode in der Klassendefinition zu definieren oder less , um die Klasse direkt zu kopieren, wie folgt:

     Foo.sample_method = sample_method
     

    Da es jedoch lehrreich ist, zeige ich Ihnen einige Möglichkeiten, dies zu tun.

    < h2> Wie geht das?

    Hier ist der Setup-Code. Wir brauchen eine Klassendefinition. Es könnte importiert werden, aber es ist wirklich nicht möglichtter.

     class Foo(object):
        '''An empty class to demonstrate adding a method to an instance'''
     

    Erstellen Sie eine Instanz:

     foo = Foo()
     

    Erstellen Sie eine Methode, die hinzugefügt werden soll:

     def sample_method(self, bar, baz):
        print(bar + baz)
     

    Methode 0 (0) - Verwenden Sie die Deskriptormethode __get__

    Gepunktete Lookups für Funktionen rufen die Methode __get__ der Funktion mit der Instanz auf und binden das Objekt an die Methode und damit eine "gebundene Methode".

     foo.sample_method = sample_method.__get__(foo)
     

    und jetzt:

     >>> foo.sample_method(1,2)
    3
     

    Methode eins - Types.MethodType

    Zuerst importieren Sie Typen, von denen wir ausgehen Rufen Sie den Methodenkonstruktor auf:

     import types
     

    Nun fügen wir die Methode der Instanz hinzu. Dazu benötigen wir den MethodType-Konstruktor aus dem Modul types (das wir oben importiert haben).

    Die Argumentsignatur für types.MethodType lautet (function, instance, class):

     foo.sample_method = types.MethodType(sample_method, foo, Foo)
     

    und Verwendung:

     >>> foo.sample_method(1,2)
    3
     

    Methode zwei: lexikalische Bindung

    Zuerst erstellen wir eine Wrapper-Funktion, die die Methode an die Instanz bindet:

     def bind(instance, method):
        def binding_scope_fn(*args, **kwargs): 
            return method(instance, *args, **kwargs)
        return binding_scope_fn
     

    Nutzung:

     >>> foo.sample_method = bind(foo, sample_method)    
    >>> foo.sample_method(1,2)
    3
     

    Methode drei: functools.partial

    Eine partielle Funktion wendet das erste Argument bzw. die ersten Argumente auf eine Funktion (und optional Schlüsselwortargumente) an und kann später mit den restlichen Argumenten aufgerufen werden ( und Überschreiben von Schlüsselwortargumenten). Also:

     >>> from functools import partial
    >>> foo.sample_method = partial(sample_method, foo)
    >>> foo.sample_method(1,2)
    3    
     

    Dies ist sinnvoll, wenn Sie bedenken, dass gebundene Methoden Teilfunktionen der Instanz sind.

    Ungebundene Funktion als Objektattribut - Warum funktioniert das nicht:

    Wenn wir versuchen, die sample_method auf dieselbe Weise wie wir hinzuzufügen Es könnte der Klasse hinzugefügt werden, es ist nicht mit der Instanz verknüpft und übernimmt nicht das implizite Ich als erstes Argument.

     >>> foo.sample_method = sample_method
    >>> foo.sample_method(1,2)
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    TypeError: sample_method() takes exactly 3 arguments (2 given)
     

    Wir können die ungebundene Funktion zum Laufen bringen, indem wir die Instanz explizit übergeben (oder etwas anderes, da diese Methode die Argumentvariable self nicht wirklich verwendet), dies wäre jedoch nicht konsistent

    31 January 2018
    Aaron HallTarkaDaal
  • Ich denke, dass die obigen Antworten den entscheidenden Punkt verfehlt haben.

    Lassen Sie uns eine Klasse mit einer Methode haben:

     class A(object):
        def m(self):
            pass
     

    Jetzt spielen wir in ipython damit:

     In [2]: A.m
    Out[2]: <unbound method A.m>
     

    Ok, also m () wird irgendwie eine ungebundene Methode von A . Aber ist das wirklich so?

     In [5]: A.__dict__['m']
    Out[5]: <function m at 0xa66b8b4>
     

    Es stellt sich heraus, dass m () ist nur eine Funktion, deren Referenz zu einem A Klassenwörterbuch hinzugefügt wird - es gibt keine Magie. Warum gibt uns A.m eine ungebundene Methode? Der Punkt wird nicht in eine einfache Wörterbuchsuche übersetzt. Es ist de facto ein Aufruf von A. Klasse __.__ getattribute__ (A, 'm'):

     In [11]: class MetaA(type):
       ....:     def __getattribute__(self, attr_name):
       ....:         print str(self), '-', attr_name
    
    In [12]: class A(object):
       ....:     __metaclass__ = MetaA
    
    In [23]: A.m
    <class '__main__.A'> - m
    <class '__main__.A'> - m
     

    Nun, Ich weiß nicht genau, warum die letzte Zeile zweimal gedruckt wird, aber es ist immer noch klar, was dort vor sich geht.

    Nun ist der Standardwert __getattribute__ dass es prüft, ob das Attribut ein sogenannter Deskriptor ist oder nicht, dh wenn eine spezielle __get__-Methode implementiert wird. Wenn diese Methode implementiert wird, wird das Ergebnis des Aufrufs dieser __get__-Methode zurückgegeben. Zurück zur ersten Version unserer Klasse A haben wir Folgendes:

     In [28]: A.__dict__['m'].__get__(None, A)
    Out[28]: <unbound method A.m>
     

    Und da Python-Funktionen das Deskriptorprotokoll implementieren, binden sie sich, wenn sie für ein Objekt aufgerufen werden, in ihrer __get__-Methode an dieses Objekt.

    Ok, wie eine Methode zu einem vorhandenen Objekt hinzufügen? Angenommen, Sie kümmern sich nicht um das Patchen von Klassen, es ist so einfach:

     B.m = m
     

    Dann Bm "wird" dank des Deskriptors zu einer ungebundenen Methode.

    Wenn Sie eine Methode nur zu einem einzelnen Objekt hinzufügen möchten, müssen Sie die Maschinerie selbst emulieren , indem Sie types.MethodType verwenden:

     b.m = types.MethodType(m, b)
     

    Übrigens:

     In [2]: A.m
    Out[2]: <unbound method A.m>
    
    In [59]: type(A.m)
    Out[59]: <type 'instancemethod'>
    
    In [60]: type(b.m)
    Out[60]: <type 'instancemethod'>
    
    In [61]: types.MethodType
    Out[61]: <type 'instancemethod'>
     
    06 March 2016
    Sнаđошƒаӽ
  • In Python Monkey Patching werden in der Regel Klassen- oder Funktionssignaturen mit Ihrer eigenen überschrieben. Unten ist ein Beispiel aus dem Zope-Wiki :

     from SomeOtherProduct.SomeModule import SomeClass
    def speak(self):
       return "ook ook eee eee eee!"
    SomeClass.speak = speak
     

    Dieser Code überschreibt / erstellt eine Methode, die "Speak" für die Klasse heißt. In Jeff Atwoods neuester Beitrag zum Affen-Patching . Er zeigt ein Beispiel in C # 3.0, der aktuellen Sprache, die ich für die Arbeit verwende.

    04 August 2008
    John Downey
  • Es gibt mindestens zwei Möglichkeiten, eine Methode ohne types.MethodType an eine Instanz anzuhängen:

     >>> class A:
    ...  def m(self):
    ...   print 'im m, invoked with: ', self
    
    >>> a = A()
    >>> a.m()
    im m, invoked with:  <__main__.A instance at 0x973ec6c>
    >>> a.m
    <bound method A.m of <__main__.A instance at 0x973ec6c>>
    >>> 
    >>> def foo(firstargument):
    ...  print 'im foo, invoked with: ', firstargument
    
    >>> foo
    <function foo at 0x978548c>
     

    1:

     >>> a.foo = foo.__get__(a, A) # or foo.__get__(a, type(a))
    >>> a.foo()
    im foo, invoked with:  <__main__.A instance at 0x973ec6c>
    >>> a.foo
    <bound method A.foo of <__main__.A instance at 0x973ec6c>>
     

    2:

     [pre> >>> instancemethod = type(A.m)
    >>> instancemethod
    <type 'instancemethod'>
    >>> a.foo2 = instancemethod(foo, a, type(a))
    >>> a.foo2()
    im foo, invoked with:  <__main__.A instance at 0x973ec6c>
    >>> a.foo2
    <bound method instance.foo of <__main__.A instance at 0x973ec6c>>
     

    Nützliche Links:
    Datenmodell - Aufruf von Deskriptoren
    Descriptor HowTo Guide - Aufrufen von Deskriptoren

    26 April 2013
    ndpu
  • Sie können Lambda verwenden, um eine Methode an eine Instanz zu binden:

     def run(self):
        print self._instanceString
    
    class A(object):
        def __init__(self):
            self._instanceString = "This is instance string"
    
    a = A()
    a.run = lambda: run(a)
    a.run()
     

    Dies ist eine Instanz Zeichenfolge

    Der Vorgang wurde mit dem Exit-Code 0

    beendet
    21 July 2014
    Evgeny Prokurat
  • Da in dieser Frage nach Nicht-Python-Versionen gefragt wurde, folgt hier JavaScript:

     a.methodname = function () { console.log("Yay, a new method!") }
     
    09 March 2012
    Thom Blake
  • Was Sie suchen, ist setattr Ich glaube. Verwenden Sie diese Option, um ein Attribut für ein Objekt festzulegen.

     >>> def printme(s): print repr(s)
    >>> class A: pass
    >>> setattr(A,'printme',printme)
    >>> a = A()
    >>> a.printme() # s becomes the implicit 'self' variable
    < __ main __ . A instance at 0xABCDEFG>
     
    07 August 2008
    HS.
  • Konsolidierung der Antworten von Jason Pratt und des Community-Wikis mit einem Blick auf die Ergebnisse der verschiedenen Bindungsmethoden:

    Beachten Sie insbesondere, wie die Bindung als Klasse hinzugefügt wird Methode funktioniert , der Verweisungsbereich ist jedoch falsch.

     #!/usr/bin/python -u
    import types
    import inspect
    
    ## dynamically adding methods to a unique instance of a class
    
    
    # get a list of a class's method type attributes
    def listattr(c):
        for m in [(n, v) for n, v in inspect.getmembers(c, inspect.ismethod) if isinstance(v,types.MethodType)]:
            print m[0], m[1]
    
    # externally bind a function as a method of an instance of a class
    def ADDMETHOD(c, method, name):
        c.__dict__[name] = types.MethodType(method, c)
    
    class C():
        r = 10 # class attribute variable to test bound scope
    
        def __init__(self):
            pass
    
        #internally bind a function as a method of self's class -- note that this one has issues!
        def addmethod(self, method, name):
            self.__dict__[name] = types.MethodType( method, self.__class__ )
    
        # predfined function to compare with
        def f0(self, x):
            print 'f0\tx = %d\tr = %d' % ( x, self.r)
    
    a = C() # created before modified instnace
    b = C() # modified instnace
    
    
    def f1(self, x): # bind internally
        print 'f1\tx = %d\tr = %d' % ( x, self.r )
    def f2( self, x): # add to class instance's .__dict__ as method type
        print 'f2\tx = %d\tr = %d' % ( x, self.r )
    def f3( self, x): # assign to class as method type
        print 'f3\tx = %d\tr = %d' % ( x, self.r )
    def f4( self, x): # add to class instance's .__dict__ using a general function
        print 'f4\tx = %d\tr = %d' % ( x, self.r )
    
    
    b.addmethod(f1, 'f1')
    b.__dict__['f2'] = types.MethodType( f2, b)
    b.f3 = types.MethodType( f3, b)
    ADDMETHOD(b, f4, 'f4')
    
    
    b.f0(0) # OUT: f0   x = 0   r = 10
    b.f1(1) # OUT: f1   x = 1   r = 10
    b.f2(2) # OUT: f2   x = 2   r = 10
    b.f3(3) # OUT: f3   x = 3   r = 10
    b.f4(4) # OUT: f4   x = 4   r = 10
    
    
    k = 2
    print 'changing b.r from {0} to {1}'.format(b.r, k)
    b.r = k
    print 'new b.r = {0}'.format(b.r)
    
    b.f0(0) # OUT: f0   x = 0   r = 2
    b.f1(1) # OUT: f1   x = 1   r = 10  !!!!!!!!!
    b.f2(2) # OUT: f2   x = 2   r = 2
    b.f3(3) # OUT: f3   x = 3   r = 2
    b.f4(4) # OUT: f4   x = 4   r = 2
    
    c = C() # created after modifying instance
    
    # let's have a look at each instance's method type attributes
    print '\nattributes of a:'
    listattr(a)
    # OUT:
    # attributes of a:
    # __init__ <bound method C.__init__ of <__main__.C instance at 0x000000000230FD88>>
    # addmethod <bound method C.addmethod of <__main__.C instance at 0x000000000230FD88>>
    # f0 <bound method C.f0 of <__main__.C instance at 0x000000000230FD88>>
    
    print '\nattributes of b:'
    listattr(b)
    # OUT:
    # attributes of b:
    # __init__ <bound method C.__init__ of <__main__.C instance at 0x000000000230FE08>>
    # addmethod <bound method C.addmethod of <__main__.C instance at 0x000000000230FE08>>
    # f0 <bound method C.f0 of <__main__.C instance at 0x000000000230FE08>>
    # f1 <bound method ?.f1 of <class __main__.C at 0x000000000237AB28>>
    # f2 <bound method ?.f2 of <__main__.C instance at 0x000000000230FE08>>
    # f3 <bound method ?.f3 of <__main__.C instance at 0x000000000230FE08>>
    # f4 <bound method ?.f4 of <__main__.C instance at 0x000000000230FE08>>
    
    print '\nattributes of c:'
    listattr(c)
    # OUT:
    # attributes of c:
    # __init__ <bound method C.__init__ of <__main__.C instance at 0x0000000002313108>>
    # addmethod <bound method C.addmethod of <__main__.C instance at 0x0000000002313108>>
    # f0 <bound method C.f0 of <__main__.C instance at 0x0000000002313108>>
     

    Ich persönlich bevorzuge Die externe ADDMETHOD-Funktion routet, da sie mir erlaubt, neue Methodennamen auch dynamisch innerhalb eines Iterators zuzuweisen.

     def y(self, x):
        pass
    d = C()
    for i in range(1,5):
        ADDMETHOD(d, y, 'f%d' % i)
    print '\nattributes of d:'
    listattr(d)
    # OUT:
    # attributes of d:
    # __init__ <bound method C.__init__ of <__main__.C instance at 0x0000000002303508>>
    # addmethod <bound method C.addmethod of <__main__.C instance at 0x0000000002303508>>
    # f0 <bound method C.f0 of <__main__.C instance at 0x0000000002303508>>
    # f1 <bound method ?.y of <__main__.C instance at 0x0000000002303508>>
    # f2 <bound method ?.y of <__main__.C instance at 0x0000000002303508>>
    # f3 <bound method ?.y of <__main__.C instance at 0x0000000002303508>>
    # f4 <bound method ?.y of <__main__.C instance at 0x0000000002303508>>
     
    28 January 2012
    Nisan.H