处理超载的最“Pythonic”方式

时间:2013-05-01 05:07:48

标签: python oop polymorphism overloading

免责声明:这可能是一个非常主观的问题,没有“正确”的答案,但我很感激有关最佳做法和程序设计的任何反馈。所以这里:

我正在编写一个库,其中文本文件被读入Text个对象。现在可以使用文件名列表或直接使用Sentence对象列表初始化这些文件。我想知道最好/最Pythonic的方法是什么,因为,如果我理解正确,Python不直接支持方法重载。

我在Scikit-Learn的{​​{3}}中找到的一个例子只是在初始化对象时将输入的类型作为参数传递。我假设一旦设置了这个参数,只需要在内部处理不同的情况:

if input == 'filename':
    # glob and read files
elif input == 'content':
    # do something else

虽然这很容易实现,但它看起来并不是一个非常优雅的解决方案。所以我想知道是否有更好的方法来处理多种类型的输入来初始化我忽略的类。

2 个答案:

答案 0 :(得分:4)

一种方法是只为不同的实例化对象的方法创建具有不同名称的类方法:

class Text(object):
    def __init__(self, data):
        # handle data in whatever "basic" form you need

    @classmethod
    def fromFiles(cls, files):
        # process list of filenames into the form that `__init__` needs
        return cls(processed_data)

    @classmethod
    def fromSentences(cls, sentences):
        # process list of Sentence objects into the form that `__init__` needs
        return cls(processed_data)

这样您就可以创建一个“真实”或“规范”初始化方法,该方法接受您想要的任何“最小公分母”格式。专用的fromXXX方法可以预处理不同类型的输入,将它们转换为需要传递给规范实例化的形式。我们的想法是,您拨打Text.fromFiles(...)从文件名中提取Text,或Text.fromSentences(...)从句子对象中提出Text

如果您只想接受一些可枚举的输入中的一种,那么进行一些简单的类型检查也是可以接受的。例如,类接受文件名(作为字符串)或文件对象的情况并不少见。在那种情况下你会这样做:

def __init__(self, file):
    if isinstance(file, basestring):
        # If a string filename was passed in, open the file before proceeding
        file = open(file)
    # Now you can handle file as a file object

如果要处理许多不同类型的输入,这会变得难以处理,但是如果它是相对包含的东西(例如,可以用来获取该对象的对象或字符串“name”),它可以更简单比我展示的第一种方法。

答案 1 :(得分:2)

您可以使用duck typing。首先,您认为参数类似于X类型,如果它们引发异常,那么您认为它们属于Y类型等:

class Text(object):
    def __init__(self, *init_vals):
        try:
            fileobjs = [open(fname) for fname in init_vals]
        except TypeError:
            # Then we consider them as file objects.
            fileobjs = init_vals

        try:
            senteces = [parse_sentences(fobj) for fobj in fileobjs]
        except TypeError:
            # Then init_vals are Sentence objects.
            senteces = fileobjs

请注意,缺少类型检查意味着该方法实际上接受任何实现您实际使用的接口的类型(例如file - 类似对象,Sentence - 类似对象等)。

如果你想支持很多不同的类型,这个方法会变得很重,但我会考虑糟糕的代码设计。接受超过2,3,4种类型的初始化者可能会混淆任何使用你的班级的程序员,因为他总是要“等待,X也接受Y,或者是{{{ 1}}接受Z ...“。

最好设计构造函数只接受2,3个不同的接口,并为用户提供一些函数/类,允许他将一些常用的类型转换为这些接口。