Как перегрузить метод __init__ на основе типа аргумента?

StackOverflow https://stackoverflow.com/questions/141545

Вопрос

Допустим, у меня есть класс, в котором есть элемент под названием data, представляющий собой список.

Я хочу иметь возможность инициализировать класс, например, именем файла (которое содержит данные для инициализации списка) или фактическим списком.

Какова ваша техника для этого?

Вы просто проверяете тип, взглянув на __class__?

Есть ли какой-то трюк, который я, возможно, упускаю?

Я привык к C ++, где перегрузка по типу аргумента выполняется легко.

Это было полезно?

Решение

Гораздо более аккуратный способ получить "альтернативные конструкторы" - это использовать методы классов.Например:

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

Причина, по которой это более аккуратно, заключается в том, что нет сомнений в том, какой тип ожидается, и вы не вынуждены гадать, что вызывающий абонент намеревался, чтобы вы сделали с типом данных, который он вам предоставил.Проблема с isinstance(x, basestring) заключается в том, что вызывающий объект не может сообщить вам, например, что даже если тип не является базовой строкой, вы должны рассматривать его как строку (а не другую последовательность).) И, возможно, вызывающий хотел бы использовать один и тот же тип для разных целей, иногда как отдельный элемент, а иногда как последовательность элементов.Явность устраняет все сомнения и приводит к более надежному и понятному коду.

Другие советы

Отличный вопрос.Я также рассматривал эту проблему, и хотя я согласен с тем, что "фабрики" (конструкторы методов классов) - хороший метод, я хотел бы предложить другой, который я также нашел очень полезным:

Вот пример (это read метод, а не конструктор, но идея та же):

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

Ключевая идея здесь заключается в использовании превосходной поддержки именованных аргументов Python для реализации этого.Теперь, если я хочу прочитать данные из файла, я говорю:

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

И чтобы прочитать это из строки, я говорю:

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

Таким образом, у пользователя есть только один метод для вызова.Обращаться с ним внутри, как вы видели, не слишком сложно

Быстрое и грязное решение

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

Тогда вы можете вызвать это с помощью

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

Лучшим способом было бы использовать isinstance и преобразование типов.Если я правильно тебя понимаю, ты хочешь этого:

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

с помощью python3 вы можете использовать Реализация множественной отправки с аннотациями функций как написано в Поваренной книге Python:

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)

и это работает следующим образом:

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

Вам следует использовать 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.).

Вы, вероятно, хотите, чтобы isinstance встроенная функция:

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

Мое предпочтительное решение - это:

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.

Затем вызовите его с помощью либо MyClass() или MyClass([1,2,3]).

Надеюсь, это поможет.Счастливого Кодирования!

Ладно, отлично.Я просто объединил этот пример с кортежем, а не с именем файла, но это просто.Спасибо всем.

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 = Мои данные (a)

d = Мои данные (b)

c.getData()

d.getData()

[1, 2]

[2, 3]

Почему бы тебе не стать еще более питоническим?

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?
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top