什么是 python 类型重载的等价物? [英] What would be the python equivalent of type-wise overloading?

查看:58
本文介绍了什么是 python 类型重载的等价物?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

据我所知,有两种类型的重载,一种基于参数数量,另一种基于参数类型

To my knowledge, there are two types of overloading, one based on number of arguments, and one based on argument types

虽然已经涵盖了基于参数数量的重载此处,但我似乎找不到按参数类型重载函数的指南.

While overloading based on number of arguments has been covered here, I can't seem to find guidelines on function overloading by argument type.

因此,由于使用 type() 进行类型检查似乎通常不受欢迎,我该如何以 Pythonic 的方式执行此操作?

So since type checking with type() seems to be generally frowned upon, how do I do this in a pythonic way?

这似乎没有我想象的那么优雅......

This seems less elegant than I would expect...

def overloaded_func(arg):
    try:
        do_list_action(arg)
    except:
        try:
            do_dict_action(arg)
        except:
            pass

推荐答案

使用 type()(或者,更好,isinstance())进行类型检查不会皱眉在 Python 中一般.令人不悦的是使用类型作为行为的代理,而您只需检查行为就可以逃脱.换句话说,当您需要一个对象具有某些功能时,而在其他一些语言中,您必须明确检查其类型是否支持该功能,而在 Python 中,您只需假设该对象执行您需要它做的事情,并信任如果不是这种情况,则会引发异常.但是,如果您要在不同类型的功能之间进行选择,其中任何一种都可以为您完成工作,那就是另一回事了.

Type checking with type() (or, better, isinstance()) isn't frowned upon in general in Python. What's frowned upon is using type as a proxy for behavior when you can get away with only checking the behavior. In other words, when you need an object to have some certain functionality, whereas in some other languages you would have to explicitly check whether its type supports that functionality, in Python you just assume the object does what you need it to do, and trust that an exception will be raised if that's not the case. But if you're choosing between different types of functionality, any of which will do the job for you, that's a different story.

例如,假设您有一些代码可以使用列表或字典来实现整数索引数组.

For example, suppose you have some code that can use either lists or dicts to implement integer-indexed arrays.

class Array:
    def __init__(self, keys_and_values):
        self.store = ... # either a list or a dict

如果只有少数非常大的索引分配了值,则它可能使用字典,否则使用列表.如果你想访问某个索引处的元素,如果有,那么你只需要写self.store[index].

Perhaps it uses a dict if only a few, very large indices have values assigned, and a list otherwise. If you want to access the element at an index, if there is one, then you just write self.store[index].

    def __getitem__(self, index):
        return self.store[index]

您不必费心先检查它是列表还是字典,因为您想要的行为 - 被整数索引的能力 - 无论哪种方式都存在.

You don't bother to check whether it's a list or a dict first, because the behavior you want - the ability to be indexed by an integer - exists either way.

但是如果你想在一个索引处设置元素,如果它是一个列表,你需要先将它扩展到适当的长度.现在,正确的鸭子类型可能会建议您这样做:

But if you want to set the element at an index, if it's a list, you need to extend it to the proper length first. Now, proper duck typing would probably suggest you do this:

    def __setitem__(self, index, value):
        if index >= len(self.store):
            try:
                self.store.extend([None] * (index - len(self.store) + 1))
            except AttributeError:
                pass
        self.store[index] = value

但我认为大多数 Python 程序员会说 isinstance() 在这种情况下更好.(不,真的.没关系.)

But I think most Python programmers would say isinstance() is better in this case. (No, really. It's okay.)

    def __setitem__(self, index, value):
        if isinstance(self.store, list) and index >= len(self.store):
            self.store.extend([None] * (index - len(self.store) + 1))
        self.store[index] = value

当您只有几种类型要测试时,我通常会推荐这条路线.

I would generally recommend this route when you've only got a few types to test.

如果您有更多类型要测试,则使用调度程序模式更实用,这是一种函数式方法.您构建类型到处理该类型的函数的映射,并根据您获得的对象类型选择调用哪个.在这个例子中,这将是这样的:

If you have many more types to test, it's more practical to use a dispatcher pattern, which is a functional approach. You build a mapping of types to functions that handle that type, and choose which one to call based on the type of the object you get. In this example, that would play out like this:

    def __setitem__dict(self, index, value):
        self.store[index] = value
    def __setitem__list(self, index, value):
        if index >= len(self.store):
            self.store.extend([None] * (index - len(self.store) + 1))
        self.store[index] = value
    __setitem__dispatch = {list: __setitem__list, dict: __setitem__dict}
    def __setitem__(self, index, value):
        self.__setitem__dispatch[type(self.store)](index, value)

在这个简单的例子中这样做很愚蠢,但在更复杂的场景中它可以派上用场.一般的模式是

It's pretty silly to do this in this simple example, but in more complicated scenarios it can come in very handy. The pattern in general is

dispatch = {list: handle_list, dict: handle_dict, ...}
def function(arg):
    return dispatch[type(arg)](arg)

它甚至可以让您稍后为新类型动态添加处理程序.这基本上是 functools.singledispatch 所做的(如 another answer 提到的).这种方式看起来很复杂,因为它隐藏了 dispatch 字典作为原始函数本身的属性.

It even lets you dynamically add handlers for new types later on. This is basically what functools.singledispatch does (as another answer mentioned). That way just looks complicated because it hides the dispatch dictionary as an attribute of the original function itself.

一般情况下不可能说是使用duck类型、类型检查、调度还是其他什么,因为这有点主观,取决于你的情况的细节:你需要处理不同类型的代码有多大不同?你处理多少种类型?您是否需要能够轻松处理新类型?等等.您在问题中没有提供足够的信息来让其他人告诉您哪种方式看起来最好,但它们都有其用途.

It's impossible to say in general whether to use duck typing, type checking, dispatching, or something else, because it's somewhat subjective and depends on the details of your situation: how different is the code you need to handle different types? How many types are you dealing with? Do you need to be able to handle new types easily? And so on. You haven't given enough information in the question to allow anyone else to tell you which way seems best, but they all have their uses.

这篇关于什么是 python 类型重载的等价物?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆