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

Python 为什么函数总是返回相同的类型?,python,function,return-type,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,然后创建一个新的空元组不是更便宜吗?或者这个时差太小了,即使在更大的项目中也无法注意到吗?如果像这样调用x foo

我在某个地方读到函数应该总是只返回一种类型 因此,以下代码被视为坏代码:

def x(foo):
 if 'bar' in foo:
  return (foo, 'bar')
 return None
我想更好的解决办法是

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

在内存方面,返回一个None,然后创建一个新的空元组不是更便宜吗?或者这个时差太小了,即使在更大的项目中也无法注意到吗?

如果像这样调用
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
['foo','bar','baz']bar
回溯(最近一次呼叫最后一次):
文件“f.py”,第9行,在
foo,bar=x([“foo”,“notthere”,“baz”])
TypeError:“非类型”对象不可编辑

为什么函数应该返回一致类型的值?要满足以下两条规则

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

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

规则2——当您遇到“问题”或无法返回正确的结果时,引发异常

def x(foo):
    if 'bar' in foo:
        return (foo, 'bar')
     raise Exception( "oh, dear me." )
您可以打破上述规则,但长期可维护性和可理解性的成本是天文数字

“返回“无”不是更便宜的内存明智吗?”错误的问题


关键不在于以清晰、可读、明显的代码为代价来优化内存。

函数必须始终返回有限类型的对象,或者不返回任何对象都是错误的,这一点并不清楚。例如,re.search可以返回
\u sre.sre\u Match
对象或
NoneType
对象:

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

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

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

type(match)
# <type 'NoneType'>
如果开发人员需要重新搜索以返回
\u sre.sre\u Match
对象,则 这个成语必须改成

if match.group(1) is None:
    # do xyz
如果要求重新搜索始终返回
\u sre.sre\u Match
对象,则不会有任何重大收益

因此,我认为如何设计函数必须取决于具体情况,尤其是计划如何使用函数

还要注意的是,
\sre.sre\u Match
NoneType
都是object的实例,因此从广义上讲,它们属于同一类型。因此,“函数应该总是只返回一种类型”的规则是毫无意义的

话虽如此,返回具有相同属性的对象的函数非常简单。(python的方式是Duck类型,而不是静态类型!)它可以让您将函数链接在一起:foo(bar(baz)),并确定您将在另一端接收的对象的类型


这可以帮助您检查代码的正确性。通过要求函数只返回某个有限类型的对象,可以减少需要检查的情况。“foo总是返回一个整数,所以只要在我使用foo的任何地方都需要一个整数,我就是黄金……”

函数应该返回什么的最佳实践因语言而异,甚至在不同的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)

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

我个人认为函数返回元组或不返回元组是完全可以接受的。但是,函数最多应返回两种不同的类型,第二种类型应为None。例如,函数不应返回字符串和列表。

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


无论您使用何种语言:函数只定义一次,但往往用于任意数量的位置。具有一致的返回类型(更不用说记录在案的前置条件和后置条件)意味着您必须花费更多的精力定义函数,但您极大地简化了函数的使用。猜猜一次性成本是否会超过重复节省的成本…?

以下是我对所有这些的看法,我还将尝试解释为什么我认为公认的答案大多是错误的

首先,
编程函数!=数学函数
。你能得到的最接近数学函数是,如果你做函数编程,但即使如此,也有大量的例子表明不是这样

  • 函数不需要输入
  • 函数不必有输出
  • 函数不必将输入映射到输出(因为前面有两个要点)
就编程而言,函数可以简单地看作是一个内存块,它有一个起点(函数的入口点)、一个主体(空的或其他的)和一个出口点(一个或多个,取决于实现),所有这些都是为了重用您编写的代码。即使你看不到它,函数也总是“返回”一些东西。这实际上是函数调用后下一条语句的地址。如果你用汇编语言进行一些真正低级的编程,你会看到这一切的荣耀(我敢说,你一定要付出额外的努力,手工编写一些机器代码,就像Linus Torvalds,他在研讨会和采访中经常提到这一点:D)。此外,您还可以采取一些输入a
for child in x():
    do_other_stuff(child)
def foo():
  pass
>> sys.getsizeof(None)
16
>> sys.getsizeof(())
48