Hinzufügen einer Methode zu einer vorhandenen Objektinstanz in Python

Lesezeit: 12 Minuten

Benutzeravatar von akdom
Akdom

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

Ich verstehe, dass es nicht immer gut ist, dies zu tun. Aber wie könnte man das tun?

Benutzeravatar von Jason Pratt
Jason Pratt

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 als erstes Argument übergeben, wenn die Methode aufgerufen wird.

Callables, die Attribute einer Klasse sind (im Gegensatz zu einer Instanz), sind jedoch immer 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 nicht selbst ü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 wird nicht automatisch gebunden, wenn sie direkt an eine Instanz angehängt wird:

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

Um es zu binden, können wir die verwenden MethodType-Funktion im Typenmodul:

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

Diesmal 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 unter Lesen über Beschreibungen Und Metaklasse Programmierung.

  • Anstatt manuell eine zu erstellen MethodTyperufen Sie die auf Deskriptor-Protokoll manuell und lassen Sie die Funktion Ihre Instanz erzeugen: barFighters.__get__(a) erzeugt eine gebundene Methode für barFighters gebunden an a.

    – Martijn Pieters

    16. September 2016 um 13:13 Uhr

  • @MartijnPieters alle Vorteile der Verwendung der descriptor protocol vs Erstellen von a MethodType Abgesehen davon, dass es vielleicht etwas besser lesbar ist.

    – EndermanAPM

    10. Mai 2017 um 7:57 Uhr

  • @EndermanAPM: Mehrere: Es ist wahrscheinlicher, dass es weiterhin genauso funktioniert wie der Zugriff auf das Attribut in einer Instanz. Es wird funktionieren classmethod Und staticmethod und andere Deskriptoren auch. Es vermeidet, den Namensraum mit einem weiteren Import zu überladen.

    – Martijn Pieters

    10. Mai 2017 um 8:10 Uhr

  • Der vollständige Code für den vorgeschlagenen Deskriptoransatz lautet a.barFighters = barFighters.__get__(a)

    – eqzx

    14. September 2017 um 17:45 Uhr

  • Nur eine Anmerkung, es gibt keine ungebundene Methode in python3, da der Unterschied zwischen einer Funktion und einer ungebundenen Methode ziemlich minimal ist, beseitigt Python 3 die Unterscheidung.

    – öter

    1. Februar 2021 um 9:21 Uhr

Russland muss Putins Benutzer-Avatar entfernen
Russland muss Putin entfernen

Vorwort – ein Hinweis zur Kompatibilität: Andere Antworten funktionieren möglicherweise nur in Python 2 – diese Antwort sollte in Python 2 und 3 einwandfrei funktionieren. Wenn Sie nur Python 3 schreiben, können Sie das explizite Erben von weglassen objectaber ansonsten sollte der Code gleich bleiben.

Hinzufügen einer Methode zu einer vorhandenen Objektinstanz

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

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

Ja, es ist möglich – aber nicht empfohlen

Ich empfehle das nicht. Das ist eine schlechte Idee. Tu es nicht.

Hier sind ein paar Gründe:

  • Sie fügen jeder Instanz, bei der Sie dies tun, ein gebundenes Objekt hinzu. Wenn Sie dies häufig tun, werden Sie wahrscheinlich viel Speicher verschwenden. Gebundene Methoden werden in der Regel nur für die kurze Dauer ihres Aufrufs erstellt und verschwinden dann, wenn sie automatisch von der Garbage Collection erfasst werden. Wenn Sie dies manuell tun, haben Sie eine Namensbindung, die auf die gebundene Methode verweist – was ihre Garbage Collection bei der Verwendung verhindert.
  • Objektinstanzen eines bestimmten Typs haben im Allgemeinen ihre Methoden auf allen Objekten dieses Typs. Wenn Sie Methoden an anderer Stelle hinzufügen, haben einige Instanzen diese Methoden und andere nicht. Programmierer werden dies nicht erwarten, und Sie riskieren, die zu verletzen Regel der geringsten Überraschung.
  • Da es andere wirklich gute Gründe gibt, dies nicht zu tun, verschaffst du dir zusätzlich einen schlechten Ruf, wenn du es tust.

Daher schlage ich vor, dass Sie dies nicht tun, es sei denn, Sie haben einen wirklich guten Grund. Es ist viel besser, die richtige Methode in der Klassendefinition zu definieren oder weniger vorzugsweise um die Klasse direkt zu patchen, wie folgt:

Foo.sample_method = sample_method

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

Wie es gemacht werden kann

Hier ist ein Setup-Code. Wir brauchen eine Klassendefinition. Es könnte importiert werden, aber es spielt wirklich keine Rolle.

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

Erstellen Sie eine Instanz:

foo = Foo()

Erstellen Sie eine Methode, um sie hinzuzufügen:

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

Methode null (0) – verwendet die Deskriptormethode, __get__

Gepunktete Suchen nach Funktionen rufen die auf __get__ Methode der Funktion mit der Instanz, bindet das Objekt an die Methode und erzeugt so eine “gebundene Methode”.

foo.sample_method = sample_method.__get__(foo)

und nun:

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

Methode eins – Typen.MethodType

Importieren Sie zuerst Typen, von denen wir den Methodenkonstruktor erhalten:

import types

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

Die Argumentsignatur für types.MethodType (in Python 3) ist (function, instance):

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

und Verwendung:

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

Übrigens war in Python 2 die Signatur (function, instance, class):

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

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

Verwendung:

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

Methode drei: functools.partial

Eine partielle Funktion wendet das/die erste(n) Argument(e) auf eine Funktion (und optional Schlüsselwortargumente) an und kann später mit den verbleibenden Argumenten (und überschreibenden Schlüsselwortargumenten) aufgerufen werden. Daher:

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

Dies ist sinnvoll, wenn man bedenkt, dass gebundene Methoden Teilfunktionen der Instanz sind.

Ungebundene Funktion als Objektattribut – warum das nicht funktioniert:

Wenn wir versuchen, sample_method auf die gleiche Weise hinzuzufügen, wie wir es der Klasse hinzufügen könnten, ist es von der Instanz ungebunden und nimmt das implizite Selbst nicht 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 irgendetwas anderes, da diese Methode die nicht wirklich verwendet self Argumentvariable), aber es wäre nicht konsistent mit der erwarteten Signatur anderer Instanzen (wenn wir diese Instanz mit Affenpatchen versehen):

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

Abschluss

Sie kennen jetzt mehrere Möglichkeiten könnte tun Sie dies, aber in aller Ernsthaftigkeit – tun Sie dies nicht.

  • Der Haftungsausschluss ist, was ich mich gefragt habe. Methodendefinitionen sind einfach Funktionen, die innerhalb der Klassendefinition verschachtelt sind.

    – Kalt

    18. Januar 2018 um 19:06 Uhr


  • @Atcold Ich habe Gründe erläutert, dies in der Einführung zu vermeiden.

    – Russland muss Putin entfernen

    18. Januar 2018 um 19:22 Uhr

  • @AidasBendoraitis Ich würde nicht sagen, dass es “braucht”, es ist ein optionaler Parameter, der beim Anwenden des Deskriptorprotokolls angegeben wird – aber Python-Funktionen verwenden das Argument nicht: github.com/python/cpython/blob/master/Objects/funcobject.c#L581

    – Russland muss Putin entfernen

    31. Januar 2018 um 13:50 Uhr

  • Schöne Erklärung und Vorgehensweise.

    – Karl Boneri

    27. Januar 2021 um 23:06 Uhr

  • @AaronHall: Ich denke, das kann beim Spotten tatsächlich eine nützliche Technik sein. Angenommen, Sie möchten Ihr Mock-Objekt mit einer definierten Implementierung mit einem Monkeypatch versehen. Das ist leicht zu denken mock.sample_method = SampleClass.sample_method wird einfach funktionieren…

    – z33k

    23. November 2021 um 16:11 Uhr


Modul neu ist seit Python 2.6 veraltet und wurde in 3.0 entfernt, use Typen

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

Im folgenden Beispiel habe ich absichtlich den Rückgabewert entfernt patch_me() Funktion. Ich denke, dass die Angabe eines Rückgabewerts einen glauben machen könnte, dass Patch ein neues Objekt zurückgibt, was nicht wahr ist – es modifiziert das eingehende Objekt. Vermutlich kann dies einen disziplinierteren Umgang mit Monkeypatching erleichtern.

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'>

  • Wie würde dies funktionieren, wenn die hinzugefügte Methode auf sich selbst verweisen muss? Mein erster Versuch führt zu einem Syntaxfehler, aber das Hinzufügen von self in der Definition der Methode scheint nicht zu funktionieren. Importtypen Klasse A(Objekt):#scheint aber auch für Objekte im alten Stil zu funktionieren ax = ‘ax’ pass def patch_me(target): def method(target,x): print (self.ax) print (“x=” ,x) print (“aufgerufen von”, Ziel) target.method = types.MethodType(method,target) #füge bei Bedarf weitere hinzu a = A() print(a.ax)

    – WesR

    31. Januar 2020 um 6:36 Uhr


Benutzeravatar von Tomasz Zieliński
Tomasz Zielinski

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 damit in Ipython:

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

OK, also M() irgendwie wird eine ungebundene Methode von A. Aber ist es wirklich so?

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

Es stellt sich heraus, dass M() ist nur eine Funktion, zu der eine Referenz hinzugefügt wird A Klassenwörterbuch – es gibt keine Magie. Warum dann Bin gibt uns eine ungebundene Methode? Das liegt daran, dass der Punkt nicht in eine einfache Wörterbuchsuche übersetzt wird. Es ist de facto ein Aufruf von A.__class__.__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

Jetzt bin ich mir aus dem Kopf nicht sicher, warum die letzte Zeile zweimal gedruckt wird, aber es ist immer noch klar, was dort los ist.

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

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

Und weil Python-Funktionen das Deskriptorprotokoll implementieren, binden sie sich, wenn sie im Namen eines Objekts aufgerufen werden, in ihrer __get__-Methode an dieses Objekt.

Ok, wie fügt man einem bestehenden Objekt eine Methode hinzu? Angenommen, es macht Ihnen nichts aus, die Klasse zu patchen, ist es so einfach wie:

B.m = m

Dann Bm “wird” dank der Deskriptormagie zu einer ungebundenen Methode.

Und wenn Sie nur einem einzelnen Objekt eine Methode 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'>

Benutzeravatar von John Downey
John Downey

In Python funktioniert Monkeypatching im Allgemeinen, indem die Signatur einer Klasse oder Funktion mit Ihrer eigenen überschrieben wird. Nachfolgend ein Beispiel aus der Zope-Wiki:

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

Dieser Code überschreibt/erstellt eine aufgerufene Methode speak in der Klasse. Bei Jeff Atwood Letzter Beitrag zum Patchen von Affenzeigte er ein Beispiel in C# 3.0, der aktuellen Sprache, die ich für die Arbeit verwende.

  • Aber es beeinflusst alle Instanzen der Klasse, nicht nur eine.

    – glglgl

    3. Juni 2014 um 10:06 Uhr

  • Danke, ich bin hierher gekommen, um nach einer Möglichkeit zu suchen, alle Instanzen zu beeinflussen.

    – Johann Henckel

    28. April 2022 um 21:15 Uhr

Benutzeravatar von sanyassh
Sanjasch

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

Ausgang:

This is instance string

  • Aber es beeinflusst alle Instanzen der Klasse, nicht nur eine.

    – glglgl

    3. Juni 2014 um 10:06 Uhr

  • Danke, ich bin hierher gekommen, um nach einer Möglichkeit zu suchen, alle Instanzen zu beeinflussen.

    – Johann Henckel

    28. April 2022 um 21:15 Uhr

Benutzeravatar von HS
HS.

Was Sie suchen, ist setattr Ich glaube. Verwenden Sie dies, 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>

  • Dies patcht die Klasse Anicht die Instanz a.

    – Ethan Furmann

    24. September 2011 um 20:04 Uhr

  • Gibt es einen Grund zu verwenden setattr(A,'printme',printme) statt einfach A.printme = printme?

    – Tobias Kienzler

    9. August 2013 um 10:04 Uhr

  • Sinnvoll ist es, wenn man Methodennamen zur Laufzeit aufbaut.

    – rr-

    2. April 2016 um 7:35 Uhr

1443470cookie-checkHinzufügen einer Methode zu einer vorhandenen Objektinstanz in Python

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy