Python函数根据传入的参数返回不同类型的值,这是否是良好的代码风格?

5

如果一个Python函数根据提供的参数返回不同的类型,这是否是良好的代码风格?

def foo(bar):
  if bar is None:
    return None
  elif bar == 1:
    return 1*1
  else:
    return [b*b for b in bar]

foo会在bar为None时返回None

foo会在bar == 1时返回1

foo会在bar为整数的元组/列表时返回一个int列表

例子:

>> foo(None)
None
>> foo(1)
1
>> foo(1, 2, 3, 4)
[1, 4, 9, 16]

返回Noneint应该是可以的,但是基于函数参数返回intint列表是否可以呢?你可能会认为这是可以的,因为用户知道期望的返回类型并且不需要进行类型检查(在这种情况下我会说这是不可以的),但是也有人认为最好将该函数拆分成两个函数,一个期望int并返回int,另一个期望int列表并返回int列表。


5
完全取决于使用情况 - 例如,json.loads(x)的返回值类型取决于x的值。 - Eric
1
在我看来,如果你对它进行文档记录,那就没问题。 - bb1950328
@Guy:我知道这个例子不完整,需要更多的检查来处理不同的输入。但我想让这个例子保持简短。 - dev15
1
简短回答:不行。长话短说:绝对不行。返回最受限制的类型。这样想:如果使用typing模块来输入此函数,类型要么是“Any”,要么是“Union”。通常这是一个麻烦的迹象。当然也有例外,但它们非常具体。 - Konrad Rudolph
在Haskell中,你有sumtypes,这与你所描述的非常相似。因此我认为只要您进行文档记录,就可以这样做。 - Elmex80s
显示剩余6条评论
2个回答

5

这完全取决于您的使用情况。这里有一个标准库中的例子,其中结果的类型取决于输入的类型

>>> import operator
>>> operator.add(1, 2)
3
>>> operator.add(1.0, 2.0)
3.0

这种行为通常是可以接受的,并且可以使用@typing.overload进行文档记录。


以下是一个例子,其中结果类型取决于输入的

>>> import json
>>> json.loads('1')
1
>>> json.loads('[1]')
[1]

这种类型的行为通常保留用于序列化和反序列化,或者模糊类型/值边界的API,比如在 np.int_(3).astype(bool) 中使用的 astype


另一方面,下面是一个明显设计不良的函数示例:

from typing import Union  # make sure to document the mixed return type

def is_even(x: int) -> Union[bool, str]:
    if x % 2 == 0:
        return True
    else:
        return "no"

如果不知道您的具体应用场景,很难在这里提供建议。


0

对于这个问题,没有明确的答案,因此制定一个清晰全面的答案将会很困难,就像在this非常相似的问题中所看到的那样。

一般来说,应该避免从函数返回不同类型的值。例如,我曾经参与过一个项目,在其中一个函数看起来像这样:

def get_df(url):
    data = get_data(url)
    df = reformat_data(data)

    if df_is_empty(df):
        return 'Empty dataframe'

    df_interp = interpolate_df(df)
    return df_interp

你可能会想象当我遇到这个错误时

AttributeError: 'str' object has no attribute 'iloc'

这对我来说非常令人困惑,花了大约半天的时间才弄清楚这个错误来自哪里。一个更好的解决方案是引发一个ValueError异常。

现在,总会有一些奇怪的情况,这个经验法则可能不正确。例如,在爬取网站时,有时也能够检索图像可能会很有用。在这种情况下,我会在您的函数中包含一个标志:

def scrape_website(url, get_images=False):
    data = do_stuff(url)
    ordered_data = order_data(data, get_images)
    return ordered_data

然而,我更倾向于将其拆分为两个函数,一个返回非图像数据,另一个仅返回图像数据。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接