如何重载__init__方法基于参数类型?

假设我有一个类,它有一个成员叫做data,它是一个列表。

我希望能够初始化类,例如,一个文件名(包含初始化列表的数据)或一个实际的列表。

你的技巧是什么?

你只是通过查看__class__来检查类型吗?

我是不是漏掉了什么技巧?

我习惯了c++,在那里根据参数类型重载很容易。

233790 次浏览

更好的方法是使用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)

你可能需要isinstance内置函数:

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

你应该使用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.).

好了,好了。我只是用一个元组拼凑了这个例子,而不是文件名,但这很简单。谢谢所有。

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)

获得“交替构造函数”的一个更简洁的方法是使用类方法。例如:

>>> 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()

你为什么不更pythonic一点?

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?

我喜欢的解决方案是:

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])调用它。

希望这能有所帮助。编码快乐!

使用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