为什么函数总是返回相同的类型?

时间:2009-12-03 11:15:28

标签: python function return-type

我在某处读到函数应该总是只返回一种类型 所以下面的代码被认为是错误的代码:

def x(foo):
 if 'bar' in foo:
  return (foo, 'bar')
 return None

我想更好的解决方案是

def x(foo):
 if 'bar' in foo:
  return (foo, 'bar')
 return ()

返回一个None然后创建一个新的空元组不是更便宜的记忆吗?或者这个时间差太小而不能注意到即使在较大的项目中也是如此?

6 个答案:

答案 0 :(得分:35)

为什么函数会返回一致类型的值?符合以下两条规则。

规则1 - 函数具有“类型” - 映射到输出的输入。它必须返回一致的结果类型,否则它不是函数。这是一团糟。

在数学上,我们说一些函数F是从域D到范围R F: D -> R的映射。域和范围构成函数的“类型”。输入类型和结果类型对于函数的定义和名称或正文一样重要。

规则2 - 当您遇到“问题”或无法返回正确的结果时,请提出异常。

def x(foo):
    if 'bar' in foo:
        return (foo, 'bar')
     raise Exception( "oh, dear me." )

可以违反上述规则,但长期可维护性和可理解性的代价是天文数字。

“返回一个无可能更便宜的记忆吗?”错误的问题。

关键是以牺牲清晰,可读和明显的代码为代价来优化内存。

答案 1 :(得分:18)

不太清楚函数必须总是返回有限类型的对象,或者返回None是错误的。例如,re.search可以返回_sre.SRE_Match对象或NoneType对象:

import re
match=re.search('a','a')

type(match)
# <type '_sre.SRE_Match'>

match=re.search('a','b')

type(match)
# <type 'NoneType'>

以这种方式设计,您可以测试与成语的匹配

if match:
    # do xyz

如果开发人员要求re.search返回_sre.SRE_Match对象,那么 这个成语必须改为

if match.group(1) is None:
    # do xyz

要求re.search始终返回_sre.SRE_Match对象,不会有任何重大收获。

所以我认为你如何设计这个功能必须取决于具体情况,特别是你打算如何使用这个功能。

另请注意,_sre.SRE_MatchNoneType都是对象的实例,因此从广义上讲,它们属于同一类型。所以“函数应该总是只返回一种类型”的规则是没有意义的。

话虽如此,返回所有共享相同属性的对象的函数有一个美丽的简单性。 (鸭子打字,不是静态打字,是蟒蛇的方式!)它可以让你把功能链接在一起:foo(bar(baz)))并确切地知道你将在另一端收到的对象类型。

这可以帮助您检查代码的正确性。通过要求函数仅返回特定有限类型的对象,检查的情况较少。 “foo总是返回一个整数,所以只要在整个地方都有一个整数,我就会使用foo,我就是金色......”

答案 2 :(得分:10)

函数返回的最佳实践因语言而异,甚至在不同的Python项目之间也存在很大差异。

对于Python来说,我同意这样的前提:如果你的函数通常返回一个iterable,那么返回None是不好的,因为没有测试的迭代变得不可能。在这种情况下只返回一个空的iterable,如果你使用Python的标准真值测试,它仍然会测试False:

ret_val = x()
if ret_val:
     do_stuff(ret_val)

并且仍允许您在不进行测试的情况下迭代它:

for child in x():
    do_other_stuff(child)

对于可能返回单个值的函数,我认为返回None是完全可以接受的,只需记录这可能发生在docstring中。

答案 3 :(得分:5)

我个人认为函数返回元组或None是完全正常的。但是,函数最多应返回2种不同的类型,第二种函数应为None。例如,函数永远不应返回字符串和列表。

答案 4 :(得分:4)

如果像这样调用x

foo, bar = x(foo)

返回None会导致

TypeError: 'NoneType' object is not iterable

如果'bar'不在foo

示例

def x(foo):
    if 'bar' in foo:
        return (foo, 'bar')
    return None

foo, bar = x(["foo", "bar", "baz"])
print foo, bar

foo, bar = x(["foo", "NOT THERE", "baz"])
print foo, bar

这导致:

['foo', 'bar', 'baz'] bar
Traceback (most recent call last):
  File "f.py", line 9, in <module>
    foo, bar = x(["foo", "NOT THERE", "baz"])
TypeError: 'NoneType' object is not iterable

答案 5 :(得分:2)

过早优化是万恶之源。微小的效率提升可能很重要,但直到你证明你需要它们。

无论您的语言是什么:函数定义一次,但往往会在任意数量的地方使用。具有一致的返回类型(更不用说记录的前后条件)意味着您必须花费更多精力定义该函数,但是您极大地简化了函数的用法。猜猜一次性成本是否会超过重复储蓄......?