2 Stimmen

Python: Aufruf einer Methode eines Instanzmitglieds nach Name mit __getattribute__

Ich habe eine Klasse, die ein Mitgliedsobjekt enthält. Ich möchte die Methoden des Mitglieds aufrufen, als ob sie Methoden der Containerklasse wären.

Das folgende Beispiel veranschaulicht (allerdings in einem anderen Kontext), was ich gerne tun würde.

Angenommen, wir haben zwei Klassen, die zwei verschiedene Arten von Produkten repräsentieren: Spielzeug y Schuhe . Jede Klasse hat zwei Methoden, sagen wir mal, weight() y description() die natürlich das Gewicht des Artikels und die Beschreibung des Artikels zurückgeben.

Jetzt möchte ich diese Produkte in einzelnen Boxen - jedes Feld enthält entweder ein einzelnes Toy entweder eine einzelne Shoe .

Ich möchte weiterhin auf die Methoden zugreifen können weight() y description() Ich möchte jedoch, dass sie Mitglieder der Box Instanz. Außerdem möchte ich die Methoden nicht speziell implementieren weight() y description() im Box Klasse, denn ich möchte nicht noch mehr Methoden in die Box Klasse, wenn eine neue Methode zu Toy und Shoe hinzugefügt wird.

Hier ist ein Beispiel für das "Endergebnis", das mich glücklich machen würde:

# Define our products
product1 = Toy('plastic gun', 200)
product2 = Shoe('black leather shoes', 500)

# Now place them in boxes
box1 = Box(product1, box_code='A1234')
box2 = Box(product2, box_code='V4321')

# I want to know the contents of the boxes
box1.description()
box2.description()

# Desired results
>> plastic gun
>> black leather shoes

Meine Idee zur Umsetzung ist folgende.

class Product():
    def __init__(self, description, weight):
        self.desc = description
        self.wght = weight

    def description(self):
        return self.desc

    def weight(self):
        return self.weight

class Toy(Product):
    pass

class Shoe(Product):
    pass

class Box(object):
    def __init__(self, product, box_code):
        self.box_code = box_code
        self.product = product

    def __getattribute__(self, name):
        # Any method that is accessed in the Box instance should be replaced by
        # the correspondent method in self.product 
        return self.product.__getattribute__(name)

Das funktioniert aber nicht! Wenn ich das "gewünschte" Beispiel, das im ersten Codeblock gezeigt wird, ausführe, erhalte ich eine unendliche Rekursion. Wie kann man das elegant lösen?

Beachten Sie, dass Box hat einen eigenen Parameter box_code und dass ich in Zukunft vielleicht neue Methoden zu Toy y Shoes (z.B. einen Werbecode oder was auch immer), ohne die Klasse ändern zu müssen Box .

Okay Leute, ich warte gespannt auf eure Antworten.


Update : Antwort auf das Problem Vielen Dank an Interjay und an Mike Boer für ihre Hilfe.

Ich musste nur die Definition der Methode ersetzen __getattribute__ in der Klasse Box von __getattr__ und es funktionierte perfekt, wie von Interjay vorgeschlagen. Die korrigierte Definition der Klasse Box lautet:

class Box(object):
    def __init__(self, product, box_code):
        self.box_code = box_code
        self.product = product

    def __getattr__(self, name):
        # Any method that is accessed in the Box instance should be replaced by
        # the correspondent method in self.product 
        return self.product.__getattribute__(name)

Update : Vervollständigung des Beispiels mit __setattr__

Nachdem ich das Problem mit der getattr-Methode behoben hatte, bemerkte ich, dass meine Klassen nicht das gewünschte Verhalten zeigten, wenn ich ein Attribut setzen wollte. Zum Beispiel, wenn ich versuchte:

box1.desc = 'plastic sword'

Anstatt die Beschreibung von Produkt1 zu ändern (die in Feld1 eingekapselt war), wurde ein neues Mitglied namens "desc" in Feld1 erstellt. Um dieses Problem zu lösen, musste ich das Element __setattr__ Funktion, wie unten dargestellt:

def __setattr__(self, name, value):
    setattr(self.product, name, value)

Durch das Hinzufügen dieser Funktion wird jedoch ein rekursiver Aufruf erzeugt, da in __init__ Ich versuche zuzuordnen self.product = product ', die die Funktion __setattr__ die das Mitglied nicht findet self.product auf, ruft also die Funktion __setattr__ wieder, rekursiv ad infinitum.

Um dieses Problem zu vermeiden, muss ich die __init__ Methode, um das neue Mitglied über das Wörterbuch der Klasse zuzuweisen. Die vollständige Definition der Klasse Box ist dann:

class Box(object):
    def __init__(self, product, box_code):
        self.__dict__['product'] = product
        self.box_code = box_code

    def __getattr__(self, name):
        # Any method that is accessed in the Box instance should be replaced by
        # the correspondent method in self.product 
        return getattr(self.product, name)

    def __setattr__(self, name, value):
        setattr(self.product, name, value)

Jetzt eine lustige Sache... Wenn ich zuerst das Mitglied definiere self.product wie oben gezeigt, funktioniert das Programm normal. Wenn ich jedoch versuche, zuerst das Mitglied zu definieren self.box_code leidet das Programm erneut unter dem Problem der rekursiven Schleife. Weiß jemand, warum?

3voto

Mike Boers Punkte 6555

Sie könnten verwenden __getattr__ anstelle von __getattribute__ wie es vorgeschlagen wurde. Dies wird bei jedem Attributzugriff auf ein Attribut, das nicht existiert, aufgerufen.

Wenn Sie wirklich den Zugriff auf (fast) ALLE Attribute außer Kraft setzen wollen, dann verwenden Sie weiterhin __getattribute__ und definieren sie folgendermaßen:

def __getattribute__(self, name):
    return getattr(object.__getattribute__(self, 'product'), name)

2voto

interjay Punkte 101333

Utilisez __getattr__ anstelle von __getattribute__ . Wenn Sie __getattribute__ erhalten Sie eine unendliche Rekursion bei der Auswertung von self.product .

__getattr__ wird nur aufgerufen, wenn das Attribut nicht anderweitig gefunden wurde, und führt daher nicht zu diesem Problem.

1voto

Sze Punkte 11

Jetzt kommt etwas Lustiges... Wenn ich zuerst das Mitglied self.product definiere, wie wie oben gezeigt, funktioniert das Programm normal. Wenn ich jedoch versuche zu definieren den Member self.box_code zu definieren, hat das Programm wieder das Problem der rekursiven Schleife Problem wieder. Weiß jemand, warum?

Wenn Sie schreiben self.box_code = box_code Zunächst geschieht Folgendes:

__setattr__ wird aufgerufen und versucht, nachzuschauen self.product die undefiniert ist. Weil es undefiniert ist __getattr__ wird aufgerufen, um das Attribut aufzulösen "product" sondern innerhalb __getattr__ gibt es eine weitere self.product was dazu führt, dass es zu einer rekursiven Schleife wird.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X