Question

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 cela?

Vérifiez-vous simplement le type en consultant __ classe __ ?

Y a-t-il une astuce qui pourrait me manquer?

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

Était-ce utile?

La solution

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, baseestring) 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 basestring, vous devez le traiter comme une chaîne ( et pas une autre séquence.) Et l’appelant voudra peut-être 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 explicite enlève tout doute et conduit à un code plus robuste et plus clair.

Autres conseils

Excellente question. J'ai également abordé ce problème, et bien que je convienne que les "usines" (constructeurs de classe) sont une bonne méthode, je voudrais en proposer 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 ici 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 à partir d'une chaîne, je dis:

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

Ainsi, 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

Correctif rapide et incorrect

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 une conversion d’instance et de type. 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 Mise en place de plusieurs Envoi avec les annotations de fonction , écrit Python Cookbook:

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 que 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 aide. Bon codage!

OK, génial. Je viens de lancer cet exemple avec un tuple, pas un nom de fichier, mais c'est facile. Merci à tous.

class MyData:
    def __init__(self, data):
        self.myList = []
        if isinstance(data, tuple):
            for i in data:
                self.myList.append(i)
        else:
            self.myList = data

    def GetData(self):
        print self.myList

a = [1,2]

b = (2,3)

c = MyData (a)

d = MyData (b)

c.GetData ()

d.GetData ()

[1, 2]

[2, 3]

Pourquoi ne vas-tu pas encore plus pythonique?

class AutoList:
def __init__(self, inp):
    try:                        ## Assume an opened-file...
        self.data = inp.read()
    except AttributeError:
        try:                    ## Assume an existent filename...
            with open(inp, 'r') as fd:
                self.data = fd.read()
        except:
            self.data = inp     ## Who cares what that might be?
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top