簡體   English   中英

Python Lambda 計數/循環函數

[英]Python Lambda Count/Loop Function

如果這是在其他地方回答的問題,我很抱歉。 通過 Google 和 Stackforum 搜索,我沒有找到任何可以推斷答案的內容; 但我覺得其中的一部分是我。

我正在嘗試將 lambdas 作為一個概念來解決,作為其中的一部分,我正在尋找使用它的方法。

所以,如果從函數的角度來看,這對於 lambda 來說是一件非常愚蠢的事情,請隨時讓我知道並解釋。 但無論哪種方式,我仍然想知道答案/仍然想知道如何使用 python 語言來做到這一點。

因此,出於測試目的,我有:

my_test = 'test_name'
testlist = ['test_name', 'test_name_dup', 'test_name_dup_1', 'test_name_dup_3']

我希望使用 lambda 來創建一個循環並返回不在測試列表中的第一個 test_name_# 的函數。 該功能最終將應用於文件名,但出於測試目的,我不得不遠離實際讀取文件名——給了我太多的方法來搞砸了。

但是 my_test 必須能夠更改,並且測試列表將是文件路徑列表。

所以,我正在尋找一個類似的功能:

new_name = lambda x: my_test + '_' + str(x)

但是初始值應該是 x = 1,它應該一直持續到 new_name 不在 testlist 中。 似乎:

bool(new_name not in testlist)

可能是有用的東西。

但是我想不出一種方法來將初始 x 設置為 1,並讓它循環使用 (x+1) 直到 bool 為真。

我知道這是可能的,因為我發現了一些 CRAZY lambda 示例,它們在文件中的行中循環。 我只是不能完全理解它們(並且沒有任何方法可以與它們一起玩,因為它們正在處理我編程水平之外的事情。

在相關說明中,我可以在此循環的開頭添加值嗎? (即我可以讓它檢查 test_name,然后是 test_name_dup,然后是 test_name_dup_#)?

先謝謝您的幫助! Lambdas(雖然很酷)完全搞砸了我的腦袋。

Lambda 只是定義函數的另一種方式

def foo(x):
    return x + x

是相同的

foo = lambda x: x + x

所以讓我們從一個函數開始做你想做的事:

def first_missing(items, base):
    for number in itertools.count():
        text = base + '_' + str(number)
        if text not in items:
             return text

首先要注意的是,您不能在 lambda 中使用循環。 所以我們需要在沒有循環的情況下重寫它。 相反,我們將使用遞歸:

def first_missing(items, base, number = 0):
        text = base + '_' + str(number)
        if text not in items:
             return text
        else:
             return first_missing(items, base, number + 1)

現在,我們也不能在 lambda 中使用 if/else 塊。 但是我們可以使用三元表達式:

def first_missing(items, base, number = 0):
        text = base + '_' + str(number)
        return text if text not in items else first_missing(items, base, number + 1)

我們不能在 lambda 中使用局部變量,所以我們將使用一個技巧,默認參數:

def first_missing(items, base, number = 0):
        def inner(text = base + '_' + str(number)):
            return text if text not in items else first_missing(items, base, number + 1)
        return inner()

此時我們可以將inner重寫為一個lambda:

def first_missing(items, base, number = 0):
        inner = lambda text = base + '_' + str(number): text if text not in items else first_missing(items, base, number + 1)
        return inner()

我們可以結合兩行來去掉內部局部變量:

def first_missing(items, base, number = 0):
    return (lambda text = base + '_' + str(number): text if text not in items else first_missing(items, base, number + 1))()

最后,我們可以將整個事情變成一個 lambda:

first_missing = lambda: items, base, number = 0: (lambda text = base + '_' + str(number): text if text not in items else first_missing(items, base, number + 1))()

希望這能讓您對自己可以做什么有所了解。 但是永遠不要這樣做,因為正如您所知,lambda 會使您的代碼非常難以閱讀。

在這種情況下不需要使用lambda ,一個簡單的for循環就可以:

my_test  = 'test_name_dup'  
testlist = ['test_name', 'test_name_dup','test_name_dup_1', 'test_name_dup_3']

for i in xrange(1, len(testlist)):
    if my_test + '_' + str(i) not in testlist:
        break

print my_test + '_' + str(i)
> test_name_dup_2

如果你真的,真的想使用lambda來解決這個問題,你還必須了解 itertools、迭代器、過濾器等。 我將建立在 thg435 的答案上,以更慣用的方式編寫並解釋它:

import itertools as it

iterator = it.dropwhile(
    lambda n: '{0}_{1}'.format(my_test, n) in testlist,
    it.count(1))

print my_test + '_' + str(iterator.next())
> test_name_dup_2

理解上述解決方案的關鍵在於dropwhile()過程。 它接受兩個參數:一個謂詞和一個可迭代對象,並返回一個迭代器,只要謂詞為真,它就會從可迭代對象中刪除元素; 之后,返回每個元素。

對於可迭代對象,我正在傳遞count(1) ,這是一個迭代器,它生成從1開始的無限數量的整數。

然后dropwhile()開始消耗整數,直到謂詞為假; 這是傳遞內聯定義函數的好機會 - 這是我們的lambda 它依次接收每個生成的整數,檢查字符串 test_name_dup_# 是否存在於列表中。

當謂詞返回falsedropwhile()返回,我們可以通過調用next()來檢索使其停止的值。

您可以將 lambda 與 itertools.dropwhile 結合使用:

import itertools
n = itertools.dropwhile(lambda n: 'test_name_dup_%d' % n in testlist, range(1, len(testlist))).next()

至於您的最后一個問題,您可以為名稱編寫一個生成器,例如:

def possible_names(prefix):
    yield prefix
    yield prefix + '_dup'
    n = 0
    while True:
        n += 1
        yield '%s_dup_%d' % (prefix, n)

然后在 dropwhile 中使用這個生成器:

unique_name = itertools.dropwhile(lambda x: x in testlist, possible_names('test_name')).next()
print unique_name

你有點跑題了。 Lambda 只不過是“簡單”的函數,通常用於函數式編程中的快速語法。 它們是完美的配套內置函數“map”、“reduce”、“filter”,也適用於定義到itertools 中的更復雜的函數。 因此,對它們做的最有用的事情是生成/操作可迭代對象(尤其是列表)。 請注意,與列表推導式/普通循環相比,在大多數情況下,lambda 會減慢您的代碼速度,並且會使其更難閱讀。 下面是一個你想用 lambdas 做什么的例子。

>>> filter(lambda i: i!=(0 if len(testlist[i].split("_"))==3 else int(testlist[i].split("_")[-1])), range(len(testlist)))[0]
2

或者您可以通過 itertools 使用更復雜的函數。 無論如何,我強烈建議您不要將 lambda 用於此類任務,因為可讀性很差。 我寧願使用結構良好的 for 循環,它也更快。

[編輯]

為了證明 lambdas+builtins 並不比列表推導式快:考慮一個簡單的問題,因為 x in range(1000) 創建一個 x 移位 5 的列表。

$ python -m timeit 'map(lambda x: x>>5, range(1000))' 1000 個循環,最好的 3 個:每個循環225 微秒

$ python -m timeit '[x>>5 for x in range(1000)]'10000 個循環,最好的 3 個:每個循環99.1 usec

在沒有 lambda 的情況下,您的性​​能提高了 100% 以上。

我更喜歡列表理解或迭代器方法。 使我覺得很容易閱讀和維護的簡單襯墊。 坦率地說,lambdas 屬於某些地方,在這里我認為它不太優雅的解決方案。

my_test = 'test_name'
prefix = 'test_name_dup_'
testlist = ['test_name','test_name_dup','test_name_dup_1','test_name_dup_3']

from itertools import count
print next('%s%d' % (prefix, i) for i in count(1) if '%s%d' % (prefix, i) not in testlist)

這將返回序列中第一個未找到的實例,我認為這是最干凈的。

當然,如果你更喜歡一個確定范圍的列表,你可以修改它成為一個列表理解:

print ['%s%d' % (prefix, i) for i in xrange(0,5) if '%s%d' % (prefix, i) not in testlist]

返回:

['test_name_dup_0', 'test_name_dup_2', 'test_name_dup_4']

暫無
暫無

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

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