我在某处读到函数应该总是只返回一种类型 所以下面的代码被认为是错误的代码:
def x(foo):
if 'bar' in foo:
return (foo, 'bar')
return None
我想更好的解决方案是
def x(foo):
if 'bar' in foo:
return (foo, 'bar')
return ()
返回一个None然后创建一个新的空元组不是更便宜的记忆吗?或者这个时间差太小而不能注意到即使在较大的项目中也是如此?
答案 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_Match
和NoneType
都是对象的实例,因此从广义上讲,它们属于同一类型。所以“函数应该总是只返回一种类型”的规则是没有意义的。
话虽如此,返回所有共享相同属性的对象的函数有一个美丽的简单性。 (鸭子打字,不是静态打字,是蟒蛇的方式!)它可以让你把功能链接在一起: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)
过早优化是万恶之源。微小的效率提升可能很重要,但直到你证明你需要它们。
无论您的语言是什么:函数定义一次,但往往会在任意数量的地方使用。具有一致的返回类型(更不用说记录的前后条件)意味着您必须花费更多精力定义该函数,但是您极大地简化了函数的用法。猜猜一次性成本是否会超过重复储蓄......?