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.
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.
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
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()
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)
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
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.).
Sie möchten wahrscheinlich die eingebaute Funktion isinstance
:
self.data = data if isinstance(data, list) else self.parse(data)
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!