284 votes

N'est-il pas possible de définir plusieurs constructeurs en Python ?

Duplicata possible :
Quelle est la façon propre et pythonique d'avoir plusieurs constructeurs en Python ?

N'est-il pas possible de définir plusieurs constructeurs en Python, avec des signatures différentes ? Si ce n'est pas le cas, quel est le moyen général de contourner ce problème ?

Par exemple, disons que vous souhaitez définir une classe City .

J'aimerais pouvoir dire someCity = City() ou someCity = City("Berlin") où le premier donne simplement une valeur de nom par défaut, et le second la définit.

2 votes

Cela me fait penser à cette question - stackoverflow.com/questions/682504/

1 votes

1 votes

Cette question est trompeuse en raison de la relation entre le titre et le corps du texte. Pire, elle semble être la réponse la plus fréquente sur Google ; j'aimerais qu'elle soit modifiée ou supprimée, car la question posée dans le corps du texte est vraiment élémentaire. Les réponses au titre peuvent être trouvées dans l'éventuel lien dupliqué.

357voto

Andrew Sledge Points 4883

Contrairement à Java, vous ne pouvez pas définir plusieurs constructeurs. Cependant, vous pouvez définir une valeur par défaut si aucune valeur n'est transmise.

def __init__(self, city="Berlin"):
  self.city = city

0 votes

Grande découverte en 2021

297voto

mzz Points 738

Si vos signatures ne diffèrent que par le numéro d'arguments, l'utilisation d'arguments par défaut est la bonne façon de procéder. Si vous voulez être en mesure de passer dans différents types d'argument, j'essaierais d'éviter le isinstance mentionné dans une autre réponse, et utiliser à la place des arguments par mots-clés.

Si l'utilisation des arguments par mot-clé devient difficile, vous pouvez les combiner avec des classmethods (le code de bzrlib aime cette approche). Ce n'est qu'un exemple stupide, mais j'espère que vous avez compris l'idée :

class C(object):

    def __init__(self, fd):
        # Assume fd is a file-like object.
        self.fd = fd

    @classmethod
    def fromfilename(cls, name):
        return cls(open(name, 'rb'))

# Now you can do:
c = C(fd)
# or:
c = C.fromfilename('a filename')

Remarquez que toutes ces méthodes de classe passent toujours par le même __init__ mais l'utilisation de classmethods peut s'avérer beaucoup plus pratique que de devoir se souvenir des combinaisons d'arguments de mot-clé pour __init__ travail.

isinstance est à éviter, car le typage en canard de Python rend difficile la détermination du type d'objet réellement transmis. Par exemple, si vous voulez prendre un nom de fichier ou un objet de type fichier, vous ne pouvez pas utiliser la commande isinstance(arg, file) parce qu'il existe de nombreux objets de type fichier qui ne sous-classent pas file (comme ceux retournés par urllib, ou StringIO, ou...). Il est généralement préférable que l'appelant vous dise explicitement quel type d'objet était visé, en utilisant des arguments de mots-clés différents.

3 votes

Comme vous le dites, c'est une excellente approche si vous devez passer différents types d'arguments. Dans ces cas-là, j'ai tendance à utiliser une méthode très basique, le __init__ et faire plus d'une méthode de classe pour chaque cas.

1 votes

Il s'agit de la solution la plus généralement applicable

0 votes

Existe-t-il un bon moyen (votre recommandation) de ne pas exécuter le programme habituel de la __init__ du second constructeur ? par exemple parce que les deux construiront l'objet à partir de zéro.

14voto

pavpanchekha Points 1348

Pour l'exemple que vous avez donné, utilisez les valeurs par défaut :

class City:
    def __init__(self, name="Default City Name"):
        ...
    ...

En général, vous avez deux possibilités :

1) Faites if - elif blocs en fonction du type :

def __init__(self, name):
    if isinstance(name, str):
        ...
    elif isinstance(name, City):
        ...
    ...

2) Utilisez la saisie en canard --- c'est-à-dire supposez que l'utilisateur de votre classe est suffisamment intelligent pour l'utiliser correctement. C'est généralement l'option préférée.

5voto

Jack M. Points 8224

La méthode la plus simple consiste à utiliser des arguments sous forme de mots-clés :

class City():
  def __init__(self, city=None):
    pass

someCity = City(city="Berlin")

C'est plutôt basique. Peut-être que vous pouvez regarder la documentation Python ?

0 votes

Fautes de frappe dans les commentaires ; de plus, ce n'est pas la question qu'il a posée --- il a posé une question sur la commutation des types, il a juste fourni un mauvais exemple.

1 votes

@pavpanchekha : c'est exactement ce qui a été demandé. sauf bien sûr les manques. self .

16 votes

@Jack, votre exemple ne stocke pas réellement city nulle part non plus, donc pour un néophyte, la réponse pourrait être assez confuse.

5voto

telliott99 Points 1570

Jack M. a raison. Fais-le comme ça :

>>> class City:
...     def __init__(self, city=None):
...         self.city = city
...     def __repr__(self):
...         if self.city:  return self.city
...         return ''
...
>>> c = City('Berlin')
>>> print c
Berlin
>>> c = City()
>>> print c

>>>

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