2013-05-01 7 views
4

Avertissement: C'est peut-être une question assez subjective sans réponse correcte mais j'apprécierais tout commentaire sur les meilleures pratiques et la conception de programme. Alors voilà:La façon la plus «pythonique» de gérer la surcharge

J'écris une bibliothèque où les fichiers texte sont lus dans Text objets. Maintenant, ceux-ci peuvent être initialisés avec une liste de noms de fichiers ou directement avec une liste d'objets Sentence. Je me demande quelle est la meilleure façon de faire cela, car si je comprends bien, Python ne supporte pas directement la surcharge de méthodes. Un exemple que j'ai trouvé dans Scikit-Learnfeature extraction module passe simplement le type de l'entrée comme un argument lors de l'initialisation de l'objet. Je suppose qu'une fois que ce paramètre est défini, il est juste une question de traiter les différents cas en interne:

if input == 'filename': 
    # glob and read files 
elif input == 'content': 
    # do something else 

Bien que ce soit facile à mettre en œuvre, il ne ressemble pas à une solution très élégante. Je me demande donc s'il y a une meilleure façon de gérer plusieurs types d'intrants pour initialiser une classe que je néglige.

+0

Certainement pas une réponse, et certainement pas pythonique, mais vous pouvez (j'ai) mis en œuvre [méthode de style de signature de type surcharge] (http://stackoverflow.com/a/11379721/1142167) en python pour le plaisir. –

Répondre

4

Une façon est de créer simplement classmethods avec des noms différents pour les différentes façons de instanciation de l'objet:

class Text(object): 
    def __init__(self, data): 
     # handle data in whatever "basic" form you need 

    @classmethod 
    def fromFiles(cls, files): 
     # process list of filenames into the form that `__init__` needs 
     return cls(processed_data) 

    @classmethod 
    def fromSentences(cls, sentences): 
     # process list of Sentence objects into the form that `__init__` needs 
     return cls(processed_data) 

De cette façon, vous venez de créer une méthode d'initialisation « vrai » ou « canonique » qui accepte tout « le plus bas dénominateur commun "format que vous voulez. Les méthodes spécialisées fromXXX peuvent prétraiter différents types d'entrée pour les convertir en la forme dont ils ont besoin pour passer à cette instanciation canonique. L'idée est que vous appelez Text.fromFiles(...) pour créer un Text à partir de noms de fichiers, ou Text.fromSentences(...) pour créer un Text à partir d'objets de la phrase.

Il peut également être acceptable de procéder à une vérification de type simple si vous souhaitez simplement accepter l'un des types d'entrée énumérables. Par exemple, il n'est pas rare qu'une classe accepte un nom de fichier (en tant que chaîne) ou un objet fichier. Dans ce cas, vous feriez:

def __init__(self, file): 
    if isinstance(file, basestring): 
     # If a string filename was passed in, open the file before proceeding 
     file = open(file) 
    # Now you can handle file as a file object 

Cela devient difficile à manier si vous avez beaucoup de différents types d'entrée pour gérer, mais si elle est quelque chose de relativement contenu comme celui-ci (par exemple, un objet ou la chaîne « nom » qui peut être utilisé pour obtenir cet objet), il peut être plus simple que la première méthode que j'ai montrée.

+0

Vous avez oublié de formater le dernier morceau de code. – Bakuriu

+0

Formaté. Merci beaucoup pour les réponses détaillées. – primelens

2

Vous pouvez utiliser duck typing. D'abord, vous considérez comme si les arguments sont du type X, si elles soulèvent une exception, vous supposez qu'ils sont de type Y, etc:

class Text(object): 
    def __init__(self, *init_vals): 
     try: 
      fileobjs = [open(fname) for fname in init_vals] 
     except TypeError: 
      # Then we consider them as file objects. 
      fileobjs = init_vals 

     try: 
      senteces = [parse_sentences(fobj) for fobj in fileobjs] 
     except TypeError: 
      # Then init_vals are Sentence objects. 
      senteces = fileobjs 

Notez que l'absence de moyens de contrôle de type que la méthode accepte effectivement tout type implémentant l'une des interfaces que vous utilisez réellement (par exemple file-like object, Sentence -like object etc.).

Cette méthode devient assez lourde si vous voulez supporter beaucoup de types différents, mais je considérerais cette mauvaise conception de code. Accepter plus de 2, 3, 4 types comme initialiseurs confondra probablement n'importe quel programmeur qui utilise votre classe, puisqu'il devra toujours penser "attendre, X a également accepté Y, ou était-ce Z qui a accepté Y ...".

Il est probablement préférable de concevoir le constructeur pour n'accepter que 2,3 interfaces différentes et de fournir à l'utilisateur une fonction/classe qui lui permet de convertir certains types souvent utilisés à ces interfaces.

Questions connexes