Pergunta

Vamos dizer que eu tenho uma classe que tem um membro da chamada de dados que é uma lista.

Eu quero ser capaz de inicializar a classe com, por exemplo, um nome de arquivo (que contém dados para inicializar a lista) ou com uma lista real.

Qual é a sua técnica para fazer isso?

Você apenas verificar o tipo de olhar para __class__?

Existe algum truque que eu poderia estar faltando?

Estou acostumado a C ++, onde a sobrecarga por tipo de argumento é fácil.

Foi útil?

Solução

Uma maneira muito mais limpa para obter construtores alternativas 'é usar classmethods. Por exemplo:

>>> 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')]

A razão do mais puro é que não há nenhuma dúvida sobre o que é esperado tipo, e você não é obrigado a adivinhar o que o chamador destinado a você a ver com o tipo de dados que você deu. O problema com isinstance(x, basestring) é que não há nenhuma maneira para o chamador para dizer-lhe, por exemplo, que mesmo que o tipo não é um basestring, você deve tratá-lo como uma string (e não outra sequência.) E talvez o chamador gostaria usar o mesmo tipo para diferentes fins, às vezes como um único item, e às vezes como uma seqüência de itens. Sendo explícita tem qualquer dúvida de distância e leva a código mais robusto e mais clara.

Outras dicas

Excelente pergunta. Eu já abordou este problema também, e enquanto eu concordo que as "fábricas" (construtores de classe-Método) são um bom método, gostaria de sugerir uma outra, que também tenho encontrado muito útil:

Aqui está um exemplo (este é um método read e não um construtor, mas a idéia é a mesma):

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

A ideia-chave é aqui é usar um excelente suporte do Python para argumentos nomeados para implementar isso. Agora, se eu quero ler os dados de um arquivo, eu digo:?

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

E a lê-lo a partir de uma corda, eu digo:

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

Desta forma, o usuário tem apenas um único método de chamada. Manuseá-lo dentro, como você viu, não é excessivamente complexa

Rápido e correção sujo

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

Depois, você pode chamá-lo com

MyData(astring)
MyData(None, alist)
MyData()

A melhor maneira seria usar isinstance e conversão de tipo. Se eu estou entendendo-lo bem, você quer este:

def __init__ (self, filename):
    if isinstance (filename, basestring):
        # filename is a string
    else:
        # try to convert to a list
        self.path = list (filename)

com python3, você pode usar implementação de várias expedição com anotações de função como Python Cookbook escreveu:

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)

e funciona como:

>>> d = Date(2012, 12, 21)
>>> d.year
2012
>>> e = Date()
>>> e.year
2018

Você deve usar 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.).

Você provavelmente quer o isinstance builtin função:

self.data = data if isinstance(data, list) else self.parse(data)

A minha solução preferida é:

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.

Em seguida, invocá-lo com qualquer MyClass() ou MyClass([1,2,3]).

Espero que ajude. Feliz codificação!

OK, ótimo. Eu só jogou juntos este exemplo com uma tupla, não um nome, mas isso é fácil. Obrigado a todos.

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 = MeusDados (um)

d = MeusDados (b)

c.GetData ()

d.GetData ()

[1, 2]

[2, 3]

Por que você não ir ainda mais Python?

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?
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top