Disons que j'ai une classe qui a un membre appelé data qui est une liste.
Je veux pouvoir initialiser la classe avec, par exemple, un nom de fichier (qui contient des données pour initialiser la liste) ou avec une liste réelle.
Quelle est votre technique pour faire ça?
Est-ce que vous vérifiez le type en regardant __class__
?
Y a-t-il une astuce qui pourrait me manquer?
Je suis habitué au C++ où la surcharge par type d'argument est facile.
Une méthode beaucoup plus simple pour obtenir des "constructeurs alternatifs" consiste à utiliser des méthodes de classe. Par exemple:
>>> 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')]
La raison pour laquelle il est plus judicieux est qu'il n'y a aucun doute sur le type attendu, et vous n'êtes pas obligé de deviner ce que l'appelant vous a demandé de faire avec le type de données qu'il vous a fourni. Le problème avec isinstance(x, basestring)
est qu’il n’ya aucun moyen pour l’appelant de vous dire, par exemple, que même si le type n’est pas une chaîne de base, vous devez le traiter comme une chaîne (et non une autre séquence). l'appelant souhaite utiliser le même type à des fins différentes, parfois sous la forme d'un élément unique et parfois sous la forme d'une séquence d'éléments. Etre explicite enlève tout doute et conduit à un code plus robuste et plus clair.
Excellente question. J'ai également abordé ce problème et bien que je convienne que les "usines" (constructeurs de méthodes de classes) sont une bonne méthode, je voudrais en suggérer une autre, que j'ai également trouvée très utile:
Voici un exemple (c'est une méthode read
et non un constructeur, mais l'idée est la même):
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
L'idée principale est d'utiliser l'excellent support de Python pour les arguments nommés afin de l'implémenter. Maintenant, si je veux lire les données d'un fichier, je dis:
obj.read(filename="blob.txt")
Et pour le lire d'une chaîne, je dis:
obj.read(str="\x34\x55")
De cette façon, l'utilisateur n'a qu'une seule méthode à appeler. Le manipuler à l'intérieur, comme vous l'avez vu, n'est pas trop complexe
Solution rapide et sale
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
Ensuite, vous pouvez l'appeler avec
MyData(astring)
MyData(None, alist)
MyData()
Un meilleur moyen serait d'utiliser la conversion de type et d'instance. Si je vous comprends bien, vous voulez ceci:
def __init__ (self, filename):
if isinstance (filename, basestring):
# filename is a string
else:
# try to convert to a list
self.path = list (filename)
avec python3, vous pouvez utiliser implémentation de plusieurs messages avec des annotations de fonctions comme Python Cookbook a écrit:
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)
et cela fonctionne comme:
>>> d = Date(2012, 12, 21)
>>> d.year
2012
>>> e = Date()
>>> e.year
2018
Vous devriez utiliser isinstance
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.).
Vous voulez probablement la fonction intégrée isinstance
:
self.data = data if isinstance(data, list) else self.parse(data)
Ma solution préférée est:
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.
Invoquez-le ensuite avec MyClass()
ou MyClass([1,2,3])
.
J'espère que ça t'as aidé. Bon codage!