簡體   English   中英

Python中更好的“如果不是沒有返回”

[英]Better “return if not None” in Python

有沒有更好的方法在python中編寫這段代碼?

result = slow_function()
if result:
    return result
[...]

函數slow_function可以返回一個值或None並且它很慢,所以這是不可行的:

if slow_function():
    return slow_function()

第一種方式沒有任何問題,但使用臨時變量似乎對python來說太過分了。

當您使用f和本地假設使用遞歸調用解決問題時,此代碼非常有用,例如,您從列表中選擇項目,然后檢查是否存在可行解決方案,否則您必須選擇另一個。 就像是:

def f(n):
    for x in xrange(n):
        result = slow_function(x):
        if result:
            return result
        [...]

不是更好的東西,如:

def f(n):
    for x in xrange(n):
        return slow_function(x) if is not None

這可以擴展到檢查任何類型的值。 如果聲明,它將是一個易於閱讀的返回


代碼愛好者的附加示例

想象一下,你有一個數字列表列表:

lists = [[1,2,3],[4,5],[6,7,8],[9,10],...]

並且您希望為每個列表選擇一個項目,以便在選擇中最多有一個偶數。 可能有很多列表,所以嘗試每個組合都會浪費,因為你已經可以告訴你,如果你開始選擇[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

這樣的語法不會更好嗎:

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

到目前為止,我所做的最好的事情是在可行解決方案的生成器中轉換函數:

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

在不知道您可能想要返回的其他內容的情況下,有一些選擇。

  1. 你可以只返回函數的結果, None或不:

     return slow_function() 

    在這種情況下,您依賴於調用者知道如何處理None值,並且實際上只是轉移到邏輯的位置。

  2. 如果您有一個默認值而不是None,則可以執行以下操作:

     return slow_function() or default 

    在上面這里,如果slow_functionNone (這是“falsy”),它將返回后一個值,否則,如果slow_function返回“truthy”值,它將返回該值。 請注意,如果slow_function可以返回其他“假”值,如False[]或0,則會忽略這些值。

  3. 或者,有時您所擁有的是完全有效的代碼。 您想要與值進行比較,如果是值,則返回它。 你所擁有的代碼在它的作用中是顯而易見的,有時這比代碼的“聰明”更重要。

根據評論,如果您的代碼必須繼續運行,如果值為None那么最明顯的方法是將其存儲為臨時值。 但是,這並不是一件壞事,因為它得很干凈。

  • 計算一個值並將其存儲為結果
  • 如果有有效結果,請將其返回。
  • 否則,繼續做事以獲得更好的結果。

更好的通常是非常主觀的,我無法從計算的角度看到任何明顯的方法來改進這一點,並且正如所寫,它是非常人性化的,這是一個明顯的優勢。 其他解決方案可能更短或更聰明,但人類可讀性通常是代碼的優勢。

您的最新評論可能會讓您更清楚自己想要做什么:

想象一下,你傳遞fa列表並選擇一個項目,然后調用自己通過列表而沒有項目,依此類推,直到你沒有更多的項目。 您檢查解決方案是否可行,如果可行,您將返回解決方案,這需要一直通過調用堆棧,否則返回None。 通過這種方式,您將以拓撲順序探索所有問題,但是當您知道先前選擇的項目將無法創建可行解決方案時,您也可以跳過檢查。

也許你可以嘗試使用yield而不是return 也就是說,您的遞歸函數不會生成一個解決方案,但會產生所有可能的解決方案。 如果沒有一個具體的例子,我無法確定你在做什么,但在它之前說:

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

現在它看起來像這樣:

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

好處是:

  • 您生成所有答案而不僅僅是第一個答案,但如果您仍然只想要一個答案,那么您可以獲得第一個結果。
  • 在使用返回值進行錯誤檢查和答案之前。 現在你只有在得到答案時才會屈服。

基本上你想要計算一個表達式,然后使用它兩次而不將它綁定到局部變量。 由於我們沒有匿名變量,唯一的方法就是將它傳遞給函數。 幸運的是,當前函數返回的控制流程不受它調用的函數的控制......但是,異常會傳播到調用堆棧中。

我不會說這更好,但你可以濫用例外來獲得你想要的東西。 這應該永遠不會被使用,而且更多是好奇心的練習。 結果最終看起來像這樣(注意裝飾器的使用):

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])

輸出是:

Checking 2...
Checking 3...
Checking 4...
Checking 5...
1000

訣竅是捎帶異常處理,因為那些跨函數調用傳播。 如果你喜歡它,這是實現:

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

對於slow_function在循環上運行的問題,生成器表達式似乎是要走的路。 在Python 3中,這里的一切都是懶惰的,所以你可以免費獲得過濾器:

f = filter(slow_function(x) for x in range(...))

在Python 2中,您只需要itertools:

from itertools import ifilter

f = ifilter(slow_function(x) for x in xrange(...))

每次迭代只會在您要求時進行。 如果你需要在函數返回false時繼續操作,那么你需要False ness作為標記,所以你的解決方案很好:

def f():
  for x in xrange(...):
    sentinel = slow_function(x)
    if sentinel:
      return sentinel
    # continue processing

或者你也可以在這里使用生成器來保存變量:

from itertools import imap

def f():
  for x in imap(slow_function, xrange(...)):
    if x:
      return x
    # continue processing

不是一個推薦,但你可以濫用列表理解並按照以下方式做一些事情:

# 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...

你寫的內容看起來很好,但是如果你想避免多個return語句,你可以這樣做:

def f():
    result = slow_function()
    if result is None:
        [...]
        result = [...]
    return result

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM