Is there a better way to write this code in python?
有更好的方法用python编写代码吗?
result = slow_function()
if result:
return result
[...]
The function slow_function
can return a value or None
and it's slow, so this is not feasible:
函数slow_function可以返回值或不返回值,而且很慢,所以这是不可行的:
if slow_function():
return slow_function()
There is nothing wrong with the first way, but using a temporary variable seems overkill for python.
第一种方法没有任何问题,但是对python来说使用临时变量似乎有些过分。
This code is pretty useful when you are solving a problem using recursive calls over f
and with local assumption, for example you select an item from a list and then check if there is a feasible solution, otherwise you have to choose another one. Something like:
当您使用递归调用f和局部假设来解决问题时,这段代码非常有用,例如,您从列表中选择一个项,然后检查是否存在可行的解决方案,否则您必须选择另一个。喜欢的东西:
def f(n):
for x in xrange(n):
result = slow_function(x):
if result:
return result
[...]
Wouldn't it be better something more idiomatic like:
这不是更好的说法吗?
def f(n):
for x in xrange(n):
return slow_function(x) if is not None
This can be extended to check any kind of value. It would be an easy-to-read return if statement.
这可以扩展为检查任何类型的值。这将是一个易于阅读的返回if语句。
Additional example for code lovers
Imagine you have a list of lists of numbers:
假设你有一个数字列表:
lists = [[1,2,3],[4,5],[6,7,8],[9,10],...]
and you want to select one item for each list such that there is at most one even number in the selection. There could be a lot of lists, so trying each combination would be wasteful since you can already tell that if you start selecting [1,2,4,...] there could be no feasible solutions.
你想要为每个列表选择一个项目这样在选择中最多有一个偶数。可能会有很多列表,所以尝试每个组合都是浪费时间的,因为如果你开始选择[1,2,4,…]没有可行的解决办法。
def check(selected):
even_numbers = filter(lambda n: (n % 2) == 0, selected)
return len(even_numbers) < 2
def f(lists, selected=[]):
if not lists:
return selected
for n in lists[0]:
if check(selected + [n]):
result = f(lists[1:], selected + [n])
if result:
return result
Wouldn't it be better a syntax like:
这样的语法不是更好吗?
def f(lists, selected=[]):
return selected if not lists
for n in lists[0]:
if check(selected + [n]):
return f(lists[1:], selected + [n]) if is not None
The best I've done so far is to turn the function in a generator of feasible solutions:
到目前为止,我所做的最好的事情是将函数转换成可行解的生成器:
def f(lists, selected=[]):
if not lists:
yield selected
else:
for n in lists[0]:
if check(selected + [n]):
for solution in f(lists[1:], selected + [n]):
yield solution
6 个解决方案
#1
8
Your latest comment maybe makes it clearer what you want to do:
你最近的评论可能会让你更清楚自己想做什么:
Imagine that you pass f a list and it select an item, then calls itself passing the list without the item and so on until you have no more items. The you check if the solution is feasible, if it is feasible you'll return the solution and this needs to go all the way through the call stack, otherwise you return None. In this way you'll explore all the problems in a topological order but you can also skip checks when you know that the previous chosen items won't be able to create a feasible solution.
假设您传递一个列表,它选择一个项目,然后调用自己传递没有项目的列表,等等,直到您没有更多的项目为止。您检查解决方案是否可行,如果可行,您将返回解决方案,这需要一直通过调用堆栈,否则返回None。通过这种方式,您将以拓扑的顺序研究所有问题,但当您知道前面所选择的项无法创建可行的解决方案时,也可以跳过检查。
Maybe you can try using yield
instead of return
. That is, your recursive function won't generate one solution, but will yield all possible solutions. Without a specific example I can't be sure what you're doing exactly, but say before it was like this:
也许你可以用收益率代替收益率。也就是说,递归函数不会生成一个解决方案,但会生成所有可能的解决方案。没有一个具体的例子,我不知道你到底在做什么,但在这之前,我要说:
def solve(args, result_so_far):
if not slow_check_is_feasible(result_so_far):
#dead-end
return None
if not args:
#valid and done
return result_so_far
for i, item in enumerate(args):
#pass list without args - slow
new_args = args[:]
del new_args[i]
result = solve(new_args, accumulate_result(result_so_far, item)
if result is not None:
#found it, we are done
return result
#otherwise keep going
Now it looks like this:
现在看起来是这样的:
def solve_all(args, result_so_far):
if not slow_check_is_feasible(result_so_far):
#dead-end
return
if not args:
#yield since result was good
yield result_so_far
return
for i, item in enumerate(args):
#pass list without args - slow
new_args = args[:]
del new_args[i]
for result in solve(new_args, accumulate_result(result_so_far, item):
yield result
The benefits are:
好处是:
- You generate all answers instead of just the first one, but if you still only want one answer then you can just get the first result.
- 你生成所有的答案而不是第一个,但是如果你仍然想要一个答案,那么你就可以得到第一个结果。
- Before you used return values both for false checks and for answers. Now you're just only yielding when you have an answer.
- 在您使用返回值之前,对错误检查和答案都要使用返回值。现在你只有在得到答案的时候才会屈服。
#2
14
Without knowing what else you might want to return there are a few options.
如果不知道你还想返回什么,有几个选择。
-
You could just return the result of the function,
None
or not:你可以只返回函数的结果,不返回或不返回:
return slow_function()
In this, you rely on the caller knowing what to do with the
None
value, and really just shift where your logic will be.在这种情况下,您需要依赖调用者知道如何处理None值,并将逻辑转移到需要的位置。
-
If you have a default value to return instead of None, you can do this:
如果您有一个默认值要返回而不是没有,您可以这样做:
return slow_function() or default
In this above, if
slow_function
isNone
(which is "falsy") it will return the latter value, otherwise, ifslow_function
returns a "truthy" value it will return that. Beware, ifslow_function
can return other "falsy" values, likeFalse
,[]
or 0, those will be ignored.在上面的示例中,如果slow_function没有(即“falsy”),它将返回后一个值,否则,如果slow_function返回一个“truthy”值,它将返回该值。注意,如果slow_function可以返回其他“伪”值,比如False,[]或0,那么这些值将被忽略。
-
Alternatively, sometimes what you have is perfectly valid code. You want to compare against a value, and if it is value, return it. The code you have is obvious in what it does, and sometimes that is more important than the "cleverness" of the code.
或者,有时您所拥有的是完全有效的代码。您希望与值进行比较,如果是值,则返回它。您所拥有的代码在其功能上是显而易见的,有时候这比代码的“聪明”更重要。
As per the comments, if your code must continue running if the value is None
then the most obvious way to do it is store it as a temporary value. But, thats not a bad thing, as it reads cleanly.
根据注释,如果您的代码必须继续运行(如果值为None),那么最明显的方法就是将其存储为临时值。但是,这并不是一件坏事,因为它读起来很干净。
- Compute a value and store it as the result
- 计算一个值并将其存储为结果
- If there is a valid result, return it.
- 如果有一个有效的结果,返回它。
- Otherwise, keep doing things to get a better result.
- 否则,继续做一些事情以获得更好的结果。
Better is usually very subjective, and I can't see any obvious ways to improve this from a computation perspective, and as written it is very human readable which is a clear advantage. Other solutions may be shorter or cleverer, but human readability is often an over looked advantage for code.
更好通常是非常主观的,而且我无法从计算的角度看到任何明显的方法来改进它,而且正如所写的,它是非常容易读的,这是一个明显的优势。其他的解决方案可能会更短或更聪明,但是人类的可读性对于代码来说往往是一种过度的优势。
#3
7
Essentially you want to evaluate an expression and then use it twice without binding it to a local variable. The only way to do that, since we don't have anonymous variables, is to pass it into a function. Fortunately, the control flow for whether the current function returns isn't controlled by the functions it calls... however, exceptions do propagate up the call stack.
本质上,您需要计算一个表达式,然后使用它两次,而不需要将它绑定到一个局部变量。这样做的唯一方法,因为我们没有匿名变量,就是把它传递给一个函数。幸运的是,当前函数返回的控制流不是由它调用的函数控制的……但是,异常会传播到调用堆栈。
I wouldn't say this is better, but you could abuse exceptions to get what you want. This should never really be used and it's more an exercise in curiosity. The result would end up looking like this (note the use of the decorator):
我不会说这更好,但是你可以滥用异常来得到你想要的。这永远都不应该被使用,它更多的是一种好奇的练习。结果会是这样(注意装饰者的使用):
def slow_function(x):
if x % 5 == 0:
return x * 200
@if_returner
def foobme(l):
for i in l:
print "Checking %s..." % (i,)
return_if(slow_function(i))
print foobme([2, 3, 4, 5, 6])
Output is:
输出是:
Checking 2...
Checking 3...
Checking 4...
Checking 5...
1000
The trick is to piggy-back on exception handling, since those propagate across function calls. If you like it, here's the implementation:
诀窍在于继承异常处理,因为这些异常在函数调用之间传播。如果你喜欢,这是实现方法:
class ReturnExc(Exception):
def __init__(self, val):
self.val = val
def return_if(val):
if val is not None:
raise ReturnExc(val)
def if_returner(f):
def wrapped(*args, **kwargs):
try:
return f(*args, **kwargs)
except ReturnExc, e:
return e.val
return wrapped
#4
2
For the problem where slow_function
is operating over a loop, a generator expression would seem the way to go. In Python 3 everything here is lazy, so you get your filter for free:
对于在循环上运行slow_function的问题,应该使用生成器表达式。在Python 3中,这里的一切都是惰性的,所以您可以免费获得过滤器:
f = filter(slow_function(x) for x in range(...))
In Python 2 you just need itertools:
在Python 2中,你只需要迭代工具:
from itertools import ifilter
f = ifilter(slow_function(x) for x in xrange(...))
Each iteration will only take place when you ask for it. If you need to continue the operation if the function returns false, then you need the False
ness as a sentinel, so your solution is fine:
每次迭代只会在你要求的时候发生。如果函数返回false,则需要继续操作,则需要将假函数作为前哨,这样您的解决方案就可以了:
def f():
for x in xrange(...):
sentinel = slow_function(x)
if sentinel:
return sentinel
# continue processing
or you can save yourself a variable by using a generator here, too:
或者,您也可以通过使用生成器来为自己节省一个变量:
from itertools import imap
def f():
for x in imap(slow_function, xrange(...)):
if x:
return x
# continue processing
#5
2
Not really a recommendation, but you could abuse a list comprehension and do something along these lines:
并不是真正的推荐,但你可以滥用列表理解,做一些类似的事情:
# Note: Doesn't work in python 3.
def func():
if [value for value in (slow_function(),) if value is not None]:
return value
# continue processing...
#6
-3
What you have written looks fine, but if you wanted to avoid multiple return statements you could do something like this:
你所写的看起来不错,但如果你想避免多重返回语句,你可以这样做:
def f():
result = slow_function()
if result is None:
[...]
result = [...]
return result
#1
8
Your latest comment maybe makes it clearer what you want to do:
你最近的评论可能会让你更清楚自己想做什么:
Imagine that you pass f a list and it select an item, then calls itself passing the list without the item and so on until you have no more items. The you check if the solution is feasible, if it is feasible you'll return the solution and this needs to go all the way through the call stack, otherwise you return None. In this way you'll explore all the problems in a topological order but you can also skip checks when you know that the previous chosen items won't be able to create a feasible solution.
假设您传递一个列表,它选择一个项目,然后调用自己传递没有项目的列表,等等,直到您没有更多的项目为止。您检查解决方案是否可行,如果可行,您将返回解决方案,这需要一直通过调用堆栈,否则返回None。通过这种方式,您将以拓扑的顺序研究所有问题,但当您知道前面所选择的项无法创建可行的解决方案时,也可以跳过检查。
Maybe you can try using yield
instead of return
. That is, your recursive function won't generate one solution, but will yield all possible solutions. Without a specific example I can't be sure what you're doing exactly, but say before it was like this:
也许你可以用收益率代替收益率。也就是说,递归函数不会生成一个解决方案,但会生成所有可能的解决方案。没有一个具体的例子,我不知道你到底在做什么,但在这之前,我要说:
def solve(args, result_so_far):
if not slow_check_is_feasible(result_so_far):
#dead-end
return None
if not args:
#valid and done
return result_so_far
for i, item in enumerate(args):
#pass list without args - slow
new_args = args[:]
del new_args[i]
result = solve(new_args, accumulate_result(result_so_far, item)
if result is not None:
#found it, we are done
return result
#otherwise keep going
Now it looks like this:
现在看起来是这样的:
def solve_all(args, result_so_far):
if not slow_check_is_feasible(result_so_far):
#dead-end
return
if not args:
#yield since result was good
yield result_so_far
return
for i, item in enumerate(args):
#pass list without args - slow
new_args = args[:]
del new_args[i]
for result in solve(new_args, accumulate_result(result_so_far, item):
yield result
The benefits are:
好处是:
- You generate all answers instead of just the first one, but if you still only want one answer then you can just get the first result.
- 你生成所有的答案而不是第一个,但是如果你仍然想要一个答案,那么你就可以得到第一个结果。
- Before you used return values both for false checks and for answers. Now you're just only yielding when you have an answer.
- 在您使用返回值之前,对错误检查和答案都要使用返回值。现在你只有在得到答案的时候才会屈服。
#2
14
Without knowing what else you might want to return there are a few options.
如果不知道你还想返回什么,有几个选择。
-
You could just return the result of the function,
None
or not:你可以只返回函数的结果,不返回或不返回:
return slow_function()
In this, you rely on the caller knowing what to do with the
None
value, and really just shift where your logic will be.在这种情况下,您需要依赖调用者知道如何处理None值,并将逻辑转移到需要的位置。
-
If you have a default value to return instead of None, you can do this:
如果您有一个默认值要返回而不是没有,您可以这样做:
return slow_function() or default
In this above, if
slow_function
isNone
(which is "falsy") it will return the latter value, otherwise, ifslow_function
returns a "truthy" value it will return that. Beware, ifslow_function
can return other "falsy" values, likeFalse
,[]
or 0, those will be ignored.在上面的示例中,如果slow_function没有(即“falsy”),它将返回后一个值,否则,如果slow_function返回一个“truthy”值,它将返回该值。注意,如果slow_function可以返回其他“伪”值,比如False,[]或0,那么这些值将被忽略。
-
Alternatively, sometimes what you have is perfectly valid code. You want to compare against a value, and if it is value, return it. The code you have is obvious in what it does, and sometimes that is more important than the "cleverness" of the code.
或者,有时您所拥有的是完全有效的代码。您希望与值进行比较,如果是值,则返回它。您所拥有的代码在其功能上是显而易见的,有时候这比代码的“聪明”更重要。
As per the comments, if your code must continue running if the value is None
then the most obvious way to do it is store it as a temporary value. But, thats not a bad thing, as it reads cleanly.
根据注释,如果您的代码必须继续运行(如果值为None),那么最明显的方法就是将其存储为临时值。但是,这并不是一件坏事,因为它读起来很干净。
- Compute a value and store it as the result
- 计算一个值并将其存储为结果
- If there is a valid result, return it.
- 如果有一个有效的结果,返回它。
- Otherwise, keep doing things to get a better result.
- 否则,继续做一些事情以获得更好的结果。
Better is usually very subjective, and I can't see any obvious ways to improve this from a computation perspective, and as written it is very human readable which is a clear advantage. Other solutions may be shorter or cleverer, but human readability is often an over looked advantage for code.
更好通常是非常主观的,而且我无法从计算的角度看到任何明显的方法来改进它,而且正如所写的,它是非常容易读的,这是一个明显的优势。其他的解决方案可能会更短或更聪明,但是人类的可读性对于代码来说往往是一种过度的优势。
#3
7
Essentially you want to evaluate an expression and then use it twice without binding it to a local variable. The only way to do that, since we don't have anonymous variables, is to pass it into a function. Fortunately, the control flow for whether the current function returns isn't controlled by the functions it calls... however, exceptions do propagate up the call stack.
本质上,您需要计算一个表达式,然后使用它两次,而不需要将它绑定到一个局部变量。这样做的唯一方法,因为我们没有匿名变量,就是把它传递给一个函数。幸运的是,当前函数返回的控制流不是由它调用的函数控制的……但是,异常会传播到调用堆栈。
I wouldn't say this is better, but you could abuse exceptions to get what you want. This should never really be used and it's more an exercise in curiosity. The result would end up looking like this (note the use of the decorator):
我不会说这更好,但是你可以滥用异常来得到你想要的。这永远都不应该被使用,它更多的是一种好奇的练习。结果会是这样(注意装饰者的使用):
def slow_function(x):
if x % 5 == 0:
return x * 200
@if_returner
def foobme(l):
for i in l:
print "Checking %s..." % (i,)
return_if(slow_function(i))
print foobme([2, 3, 4, 5, 6])
Output is:
输出是:
Checking 2...
Checking 3...
Checking 4...
Checking 5...
1000
The trick is to piggy-back on exception handling, since those propagate across function calls. If you like it, here's the implementation:
诀窍在于继承异常处理,因为这些异常在函数调用之间传播。如果你喜欢,这是实现方法:
class ReturnExc(Exception):
def __init__(self, val):
self.val = val
def return_if(val):
if val is not None:
raise ReturnExc(val)
def if_returner(f):
def wrapped(*args, **kwargs):
try:
return f(*args, **kwargs)
except ReturnExc, e:
return e.val
return wrapped
#4
2
For the problem where slow_function
is operating over a loop, a generator expression would seem the way to go. In Python 3 everything here is lazy, so you get your filter for free:
对于在循环上运行slow_function的问题,应该使用生成器表达式。在Python 3中,这里的一切都是惰性的,所以您可以免费获得过滤器:
f = filter(slow_function(x) for x in range(...))
In Python 2 you just need itertools:
在Python 2中,你只需要迭代工具:
from itertools import ifilter
f = ifilter(slow_function(x) for x in xrange(...))
Each iteration will only take place when you ask for it. If you need to continue the operation if the function returns false, then you need the False
ness as a sentinel, so your solution is fine:
每次迭代只会在你要求的时候发生。如果函数返回false,则需要继续操作,则需要将假函数作为前哨,这样您的解决方案就可以了:
def f():
for x in xrange(...):
sentinel = slow_function(x)
if sentinel:
return sentinel
# continue processing
or you can save yourself a variable by using a generator here, too:
或者,您也可以通过使用生成器来为自己节省一个变量:
from itertools import imap
def f():
for x in imap(slow_function, xrange(...)):
if x:
return x
# continue processing
#5
2
Not really a recommendation, but you could abuse a list comprehension and do something along these lines:
并不是真正的推荐,但你可以滥用列表理解,做一些类似的事情:
# Note: Doesn't work in python 3.
def func():
if [value for value in (slow_function(),) if value is not None]:
return value
# continue processing...
#6
-3
What you have written looks fine, but if you wanted to avoid multiple return statements you could do something like this:
你所写的看起来不错,但如果你想避免多重返回语句,你可以这样做:
def f():
result = slow_function()
if result is None:
[...]
result = [...]
return result