Как перегрузить метод __init__ на основе типа аргумента?
Скажем, у меня есть класс, у которого есть член, называемый данными, который является списком.
Я хочу, чтобы иметь возможность инициализировать класс с, например, именем файла (который содержит данные для инициализации списка) или с фактическим списком.
Какую технику вы делаете?
Вы просто проверяете тип, глядя на __class__
?
Есть ли какой-то трюк, который я могу пропустить?
Я использую С++, где перегрузка по типу аргументов проста.
Ответы
Ответ 1
Более простой способ получить "альтернативные конструкторы" - использовать методы класса. Например:
>>> 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 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)
заключается в том, что вызывающий может не сказать вам, например, что хотя тип не является базой, вы должны рассматривать его как строку (а не другую последовательность). И, возможно, вызывающий хотел бы использовать один и тот же тип для разных целей, иногда как отдельный элемент, а иногда и как последовательность элементов. Быть явным принимает все сомнения и ведет к более надежному и понятному коду.
Ответ 2
Отличный вопрос. Я также решил эту проблему, и, хотя я согласен с тем, что "фабрики" (конструкторы классов) являются хорошим методом, я хотел бы предложить другой, который я также нашел очень полезным:
Здесь образец (это метод 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")
Таким образом, у пользователя есть только один метод для вызова. Обработка его внутри, как вы видели, не слишком сложна.
Ответ 3
Быстрое и грязное исправление
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()
Ответ 4
Лучшим способом было бы использовать преобразование isinstance и type. Если я понимаю вас правильно, вы хотите:
def __init__ (self, filename):
if isinstance (filename, basestring):
# filename is a string
else:
# try to convert to a list
self.path = list (filename)
Ответ 5
с python3 вы можете использовать реализацию множественной отправки с аннотациями функций, как написано в 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)
и это работает как:
>>> d = Date(2012, 12, 21)
>>> d.year
2012
>>> e = Date()
>>> e.year
2018
Ответ 6
Вы должны использовать 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 type.
The form using a tuple, isinstance(x, (A, B, ...)), is a shortcut for
isinstance(x, A) or isinstance(x, B) or ... (etc.).
Ответ 7
Вероятно, вы хотите встроенную функцию isinstance
:
self.data = data if isinstance(data, list) else self.parse(data)
Ответ 8
Мое предпочтительное решение:
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])
.
Надеюсь, что это поможет. Счастливое кодирование!
Ответ 9
Хорошо, отлично. Я просто собрал этот пример с кортежем, а не с именем файла, но это просто. Спасибо всем.
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]
Ответ 10
Почему бы тебе не пойти еще более питонично?
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?