300 votes

surcharger __init__ en python

Disons que j'ai une classe qui a un membre appelé data qui est une liste.

Je veux être capable d'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 le faire?

Vérifiez-vous simplement le type en regardant __class__ ?

Y a-t-il un truc que je pourrais manquer?

Je suis habitué à C ++ où la surcharge par type d'argument est facile.

Merci.

416voto

Thomas Wouters Points 38811

Beaucoup plus propre façon d'obtenir des " autres constructeurs est d'utiliser classmethods. 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, c'est plus propre c'est qu'il n'y a aucun doute à propos de ce type est prévu, et vous n'êtes pas forcé de deviner ce qu'est l'appelant qui vous est destiné à faire avec le type de données, il vous a donné. Le problème avec isinstance(x, basestring) c'est qu'il n'existe aucun moyen pour l'appelant de vous dire, par exemple, que même si le type n'est pas un basestring, vous devez le traiter comme une chaîne de caractères (et pas une autre séquence.) Et peut-être l'appelant voudrais utiliser le même type à des fins différentes, parfois comme un seul élément, et parfois comme une séquence d'éléments. Être plus explicite prend tout doute à l'écart et mène à la plus robuste et le code plus clair.

35voto

Eli Bendersky Points 82298

Excellente question. J'ai abordé ce problème ainsi, et alors que je suis d'accord que les "usines" (classe-méthode constructeurs) sont une bonne méthode, je voudrais suggérer une autre, que j'ai également trouvé très utile:

Voici un exemple (c'est un read méthode et pas 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 clé est ici est à l'aide de Python est un excellent support pour les arguments nommés pour le mettre en œuvre. Maintenant, si je veux lire les données à partir d'un fichier, je dis:

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

Et de le lire à partir d'une chaîne, je dis:

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

De cette façon, l'utilisateur a juste une seule méthode à appeler. La manipulation de l'intérieur, comme vous l'avez vu, n'est pas trop complexe

12voto

Ben Points 265

Correction rapide et sale

Ensuite, vous pouvez appeler avec

8voto

John Millikin Points 86775

Une meilleure solution serait d’utiliser isinstance et la conversion de type. Si je vous comprends bien, vous le souhaitez :

4voto

Moe Points 6698

Vous devez utiliser isinstance

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X