webentwicklung-frage-antwort-db.com.de

Wie kann ich die __init__-Methode basierend auf dem Argumenttyp überladen?

Angenommen, ich habe eine Klasse mit einem Member namens data, das eine Liste ist.

Ich möchte die Klasse beispielsweise mit einem Dateinamen (der Daten zum Initialisieren der Liste enthält) oder mit einer tatsächlichen Liste initialisieren können.

Was ist Ihre Technik dafür?

Überprüfen Sie den Typ nur durch einen Blick auf __class__?

Gibt es einen Trick, den ich vermissen könnte?

Ich bin an C++ gewöhnt, wo das Überladen nach Argumenttyp einfach ist.

293
Baltimark

Eine viel bessere Möglichkeit, alternative Konstruktoren zu finden, ist die Verwendung von Klassenmethoden. Zum Beispiel:

>>> class MyData:
...     def __init__(self, data):
...         "Initialize MyData from a sequence"
...         self.data = data
...     
...     @classmethod
...     def fromfilename(cls, filename):
...         "Initialize MyData from a file"
...         data = open(filename).readlines()
...         return cls(data)
...     
...     @classmethod
...     def fromdict(cls, datadict):
...         "Initialize MyData from a dict's items"
...         return cls(datadict.items())
... 
>>> MyData([1, 2, 3]).data
[1, 2, 3]
>>> MyData.fromfilename("/tmp/foobar").data
['foo\n', 'bar\n', 'baz\n']
>>> MyData.fromdict({"spam": "ham"}).data
[('spam', 'ham')]

Der Grund dafür ist, dass kein Zweifel darüber besteht, welcher Typ erwartet wird, und Sie nicht gezwungen sind, zu erraten, was der Anrufer mit dem Datentyp, den er Ihnen gegeben hat, beabsichtigt hat. Das Problem mit isinstance(x, basestring) besteht darin, dass der Aufrufer Ihnen beispielsweise nicht mitteilen kann, dass Sie den Typ, obwohl er kein Basisstring ist, als Zeichenfolge (und nicht als eine andere Sequenz) behandeln sollten. Und vielleicht möchte der Anrufer den gleichen Typ für verschiedene Zwecke verwenden, manchmal als einzelnes Element und manchmal als eine Abfolge von Elementen. Wenn Sie explizit sind, werden alle Zweifel ausgeräumt und der Code wird robuster und klarer.

401
Thomas Wouters

Hervorragende Frage. Ich habe mich auch mit diesem Problem befasst, und obwohl ich der Meinung bin, dass "factories" (Klassenmethoden-Konstruktoren) eine gute Methode sind, möchte ich eine andere vorschlagen, die ich auch als sehr nützlich empfunden habe:

Hier ist ein Beispiel (dies ist eine read -Methode und kein Konstruktor, aber die Idee ist dieselbe):

def read(self, str=None, filename=None, addr=0):
    """ Read binary data and return a store object. The data
        store is also saved in the interal 'data' attribute.

        The data can either be taken from a string (str 
        argument) or a file (provide a filename, which will 
        be read in binary mode). If both are provided, the str 
        will be used. If neither is provided, an ArgumentError 
        is raised.
    """
    if str is None:
        if filename is None:
            raise ArgumentError('Please supply a string or a filename')

        file = open(filename, 'rb')
        str = file.read()
        file.close()
    ...
    ... # rest of code

Die Schlüsselidee dabei ist, Pythons hervorragende Unterstützung für benannte Argumente zu verwenden, um dies zu implementieren. Wenn ich nun die Daten aus einer Datei lesen möchte, sage ich:

obj.read(filename="blob.txt")

Und um es aus einer Zeichenfolge zu lesen, sage ich:

obj.read(str="\x34\x55")

Auf diese Weise hat der Benutzer nur eine Methode zum Aufrufen. Die Handhabung im Inneren ist, wie Sie gesehen haben, nicht allzu komplex

33
Eli Bendersky

Schnelle und schmutzige Lösung

class MyData:
    def __init__(string=None,list=None):
        if string is not None:
            #do stuff
        Elif list is not None:
            #do other stuff
        else:
            #make data empty

Dann können Sie es mit aufrufen

MyData(astring)
MyData(None, alist)
MyData()
10
Ben

Ein besserer Weg wäre, isinstance und type conversion zu verwenden. Wenn ich Sie richtig verstehe, möchten Sie Folgendes:

def __init__ (self, filename):
    if isinstance (filename, basestring):
        # filename is a string
    else:
        # try to convert to a list
        self.path = list (filename)
8
John Millikin

mit Python3 können Sie Implementieren von Multiple Dispatch mit Funktionsanmerkungen als Python Cookbook schrieb:

import time


class Date(metaclass=MultipleMeta):
    def __init__(self, year:int, month:int, day:int):
        self.year = year
        self.month = month
        self.day = day

    def __init__(self):
        t = time.localtime()
        self.__init__(t.tm_year, t.tm_mon, t.tm_mday)

und es funktioniert so:

>>> d = Date(2012, 12, 21)
>>> d.year
2012
>>> e = Date()
>>> e.year
2018
6
carton.swing

Sie sollten isinstance verwenden

isinstance(...)
    isinstance(object, class-or-type-or-Tuple) -> bool

    Return whether an object is an instance of a class or of a subclass thereof.
    With a type as second argument, return whether that is the object's type.
    The form using a Tuple, isinstance(x, (A, B, ...)), is a shortcut for
    isinstance(x, A) or isinstance(x, B) or ... (etc.).
4
Moe

Sie möchten wahrscheinlich die eingebaute Funktion isinstance:

self.data = data if isinstance(data, list) else self.parse(data)
2
Eli Courtwright

Meine bevorzugte Lösung ist:

class MyClass:
    _data = []
    __init__(self,data=None):
        # do init stuff
        if not data: return
        self._data = list(data) # list() copies the list, instead of pointing to it.

Rufen Sie es dann entweder mit MyClass() oder MyClass([1,2,3]) auf.

Ich hoffe, das hilft. Viel Spaß beim Codieren!

1
Fydo