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

Bal*_*ark 297 python constructor operator-overloading

假设我有一个类,其成员名为data,这是一个列表.

我希望能够使用例如文件名(包含初始化列表的数据)或实际列表来初始化类.

你这样做的技巧是什么?

你只是看看类型__class__吗?

我可能会缺少一些技巧吗?

我已经习惯了C++,其中按参数类型重载很容易.

Tho*_*ers 411

获得"替代构造函数"的一种更简洁的方法是使用类方法.例如:

>>> 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')]
Run Code Online (Sandbox Code Playgroud)

它更整洁的原因是毫无疑问预期会出现什么类型,并且您不必猜测调用者打算如何处理它为您提供的数据类型.问题isinstance(x, basestring)是调用者没有办法告诉你,例如,即使类型不是基本字符串,你应该把它当作一个字符串(而不是另一个序列).也许调用者想要为不同的目的使用相同的类型,有时作为单个项目,有时作为一系列项目.明确表达了所有怀疑并导致更强大和更清晰的代码.

  • http://www.python.org/download/releases/2.2.3/descrintro/是一个很好的来源. (3认同)
  • 你在哪里定义了cls()的行为? (3认同)
  • 为什么不使用`@staticmethod`,因为这个例子中的`__init__` 几乎没有任何用处,而目标是首先使用例如`fromfilename`? (2认同)

Eli*_*sky 35

好问题.我也解决了这个问题,虽然我同意"工厂"(类方法构造函数)是一个很好的方法,但我想建议另一个,我也发现它非常有用:

这是一个示例(这是一个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
Run Code Online (Sandbox Code Playgroud)

关键的想法是使用Python对命名参数的出色支持来实现它.现在,如果我想从文件中读取数据,我会说:

obj.read(filename="blob.txt")
Run Code Online (Sandbox Code Playgroud)

为了从字符串中读取它,我说:

obj.read(str="\x34\x55")
Run Code Online (Sandbox Code Playgroud)

这样,用户只需一种方法即可调用.如你所见,在里面处理它并不是太复杂

  • 另一个问题是作为调用者,我不知道我应该使用哪些参数来构造对象,除非我阅读文档.在上面的示例中,调用者可以同时提供str和filename,但只考虑str,因为它在if语句层次结构中更高.文档可以提供帮助,但我们可以更好地设计界面而不会产生歧义. (7认同)
  • 当你想要重载许多版本的构造函数时,有一点可能使这个解决方案不是优雅的,例如你想要从一个整数构建一个对象,或者一个文件,或一个字符串,或者......或者......或者......或者......或者......然后你会得到一个很长的__init__参数列表. (4认同)
  • @brainstorm 我认为处理非 None 字符串的代码位于“#其余代码”中。:-) (2认同)

Ben*_*Ben 12

快速而肮脏的修复

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
Run Code Online (Sandbox Code Playgroud)

然后你可以用它来调用它

MyData(astring)
MyData(None, alist)
MyData()
Run Code Online (Sandbox Code Playgroud)

  • 第二个可能更好地写成`MyData(list = alist)`. (5认同)
  • 你不会想念`__init__`中的`self`吗?并且您可能不想使用 `list` 作为输入名称,因为它隐藏了内置类型 `list`。 (5认同)

Joh*_*kin 8

更好的方法是使用isinstance和类型转换.如果我理解你,你想要这个:

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


car*_*ing 8

使用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)
Run Code Online (Sandbox Code Playgroud)

它的工作原理如下:

>>> d = Date(2012, 12, 21)
>>> d.year
2012
>>> e = Date()
>>> e.year
2018
Run Code Online (Sandbox Code Playgroud)