处理重载的最“Pythonic”方式

Posted

技术标签:

【中文标题】处理重载的最“Pythonic”方式【英文标题】:The most 'Pythonic' way to handle overloading 【发布时间】:2013-04-25 03:33:35 【问题描述】:

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

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

我在Scikit-Learn 的feature extraction module 中找到的一个示例只是在初始化对象时将输入的类型作为参数传递。我假设一旦设置了这个参数,它只是在内部处理不同情况的问题:

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

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

【问题讨论】:

绝对不是答案,也绝对不是 pythonic,但你可以(我已经)在 python 中实现type signature style method overloading 来娱乐。 【参考方案1】:

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

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

如果您有许多不同类型的输入要处理,这将变得笨拙,但如果它是像这样相对包含的东西(例如,一个对象或可用于获取该对象的字符串“名称”),它可以更简单比我展示的第一种方法。

【讨论】:

【参考方案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-like 对象、Sentence-like 对象等)。

如果你想支持很多不同的类型,这种方法会变得相当繁重,但我认为这是糟糕的代码设计。接受超过 2,3,4 种类型作为初始值设定项可能会使任何使用您的类的程序员感到困惑,因为他总是不得不思考“等等,X 是否也接受了Y,或者是Z 接受了@ 987654330@..."。

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

【讨论】:

以上是关于处理重载的最“Pythonic”方式的主要内容,如果未能解决你的问题,请参考以下文章

逻辑组合布尔值列表的最“pythonic”方式是啥?

Pythonic 函数重载?

实现具有自动递增实例属性的类的最 Pythonic 方式是啥?

在满足条件之前不断迭代列表的最 Pythonic 方式?

将 0 和 1 字符串转换为布尔值的最 Pythonic 方式是啥? [复制]

交错两个字符串的最pythonic方法