2010-03-20 74 views
5

假設我有一個函數,它可以將iterable/iterator或非iterable作爲參數。使用try: iter(arg)檢查可用性。刪除迭代的Python方法

根據輸入是否是可迭代的,方法的結果將會不同。不是當我想將非迭代作爲可迭代輸入傳遞時,很容易做到:我只是用一個元組來包裝它。

當我想要傳遞一個iterable(例如一個字符串),但希望該函數將它當作不可迭代時,我該做什麼?例如。使iter(str)失敗。

編輯 - 我的初衷:

我想概括zip功能,它可以與非iterables拉鍊iterables。不可迭代將會像其他迭代器尚未完成一樣經常爲repeat本身。

我現在唯一的一般解決方案似乎是,我不應該檢查general_zip函數(因爲字符串問題);但是相反,我必須將repeat迭代器添加到參數之前調用zip。 (這實際上可以節省我發明general_zip的功能 - 儘管我仍然可能會因爲使用非迭代作爲輸入而無需多餘的重複,這將是明確的。)

+3

Python的方式是調用者是顯式的,並將非迭代轉換爲迭代。 'zip(my_list,itertools.repeat(42))' 這與添加int和字符串時必須編寫42 + int('100')'相同。 添加魔術轉換會導致猜測和混淆。 – 2010-03-20 15:31:08

+0

是的,沒錯。但是我需要多次調用這個函數,然後每次調用之前都必須進行檢查。這看起來有點多餘。 - 所以,我需要這個功能的原因是相當狹窄和明確的,但是我希望儘可能普遍地具有該功能的潛在能力。 – Debilski 2010-03-20 17:46:30

回答

3

我越想它,似乎無法進行類型檢查或將參數傳遞給函數。

然而,根據功能的意圖,一個方法來處理它可能是:

from itertools import repeat 
func(repeat(string_iterable)) 

func仍然看到了一個迭代的,但它不會通過串本身的構成特徵迭代。實際上,這個論證的作用就好像它是一個不變的不可迭代的。

+0

'repeat(string_iterable)'將無限地返回字符串。你的意思是'[string_iterable]'(它只會返回一次字符串)? – jfs 2010-03-20 15:58:57

+0

不,對於我的問題[string_iterable]將是錯誤的解決方案。這個比喻應該是在一個維度上的點 - 當你在兩個維度上展開時 - 對應於一整行點而不僅僅是一個點。 – Debilski 2010-03-20 17:36:41

0

專門化它。

def can_iter(arg): 
    if isinstance(arg, str): 
    return False 
    try: 
    ... 
+0

但是這意味着我需要在功能內部做出決定,不能從偶然的場合做出新的決定。 – Debilski 2010-03-20 13:11:55

2

Wh!看起來你希望能夠將迭代器件作爲迭代器件進行傳遞,將可迭代器件作爲非能量器件進行迭代,將非迭代器件作爲迭代器件進行迭代,將非可用事件作爲非可用事件進行傳遞。 既然你要能夠處理所有的可能性,可以和電腦(還)沒有讀心術,你將不得不告訴函數要如何參數進行處理:

def foo_iterable(iterable): 
    ... 
def foo_noniterable(noniterable): 
    ... 

def foo(thing,isiterable=True): 
    if isiterable: 
     foo_iterable(thing) 
    else: 
     foo_noniterable(thing) 

應用FOO到一個可迭代

foo(iterable) 

應用FOO到一個可迭代作爲noniterable:

foo_noniterable(iterable)  # or 
foo(iterable, isiterable=False) 

應用FOO到noniterable作爲noniterable:

foo_noniterable(noniterable)  # or 
foo(noniterable,isiterable=False) 

應用FOO到noniterable視爲可迭代:

foo((noniterable,)) 

PS。我是一個能夠很好地完成單項工作的小職能的信徒。他們更容易調試和單元測試。一般來說,我會建議避免單片函數的行爲因類型而異。是的,它給開發人員帶來了一點額外的負擔,要完全調用預期的功能,但我認爲調試和單元測試方面的優勢不僅僅是彌補它的缺點。

+0

問題是,該函數可能有幾個參數;那麼會變得有點複雜。 – Debilski 2010-03-20 13:12:56

+0

@Debilski:爲什麼不給「foo」添加幾個參數?也許我對你的情況不夠了解。爲什麼它很複雜? – unutbu 2010-03-20 13:26:09

+0

我不知道。它看起來像這樣:'foo([1,2,3],[1,2,3],「abc」,isiterable1 = True,isiterable2 = True,isiterable3 = False)'。或甚至更多的論據。我認爲你的解決方案只會有一兩個固定的參數就沒問題;我的情況是,我想讓它更一般。 (否則,我認爲我不會問關心,只會或多或少地像你提出的那樣做。) – Debilski 2010-03-20 13:32:32

0

那麼,一種告訴函數你想如何處理它的參數的方法是有合理的默認值(使得函數默認處理所有的原始類型),同時能夠指定你喜歡的任何調整(即,具有短和缺席逐默認fmt字符串),如:

def smart_func(*args, **kw): 
    """If 'kw' contains an 'fmt' parameter, 
    it must be a list containing positions of arguments, 
    that should be treated as if they were of opposite 'kind' 
    (i.e. iterables will be treated as non-iterables and vise-versa) 

    The 'kind' of a positional argument (i.e. whether it as an iterable) 
    is inferred by trying to call 'iter()' on the argument. 
    """ 

    fmt = kw.get('fmt', []) 

    def is_iter(it): 
     try: 
      iter(it) 
      return True 
     except TypeError: 
      return False 

    for i,arg in enumerate(args): 
     arg_is_iterable = is_iter(arg) 
     treat_arg_as_iterable = ((not arg_is_iterable) 
           if (i in fmt) else arg_is_iterable) 
     print arg, arg_is_iterable, treat_arg_as_iterable 

這給出:

>>> smart_func() 
>>> smart_func(1, 2, []) 
1 False False 
2 False False 
[] True True 
>>> smart_func(1, 2, [], fmt=[]) 
1 False False 
2 False False 
[] True True 
>>> smart_func(1, 2, [], fmt=[0]) 
1 False True 
2 False False 
[] True True 
>>> smart_func(1, 2, [], fmt=[0,2]) 
1 False True 
2 False False 
[] True False 

擴展該功能(尋找最長可迭代的長度,等等),一個可以構造一個smart-zip你在說什麼。

[PS] 另一種方法是調用下列方式功能:

smart_func(s='abc', 1, arr=[0,1], [1,2], fmt={'s':'non-iter','some_arr':'iter'}) 

和具備的功能與您提供的參數名稱('s''arr'注,有在函數簽名中沒有這樣的名稱,因爲它與上面的)到'fmt'「類型提示」(即'iter'使參數被認爲是可迭代的,並且'non-iter'是不可迭代的)相同。當然,這種方法可以與上述「切換式」結合使用。

+0

沒有理由在那裏使用嵌套函數。嵌套函數對於製作閉包很有用,但是使用它們在本地定義常量函數是愚蠢的。 – 2010-03-20 15:46:58

+1

@Mike Graham原因是履行函數的合約(在doc btw中指定)。確實有問題的函數可能會被移出(例如,使其可重用),因爲它實際上並不依賴於本地參數。但是不管這是愚蠢的...好吧,讓大家爲他自己選擇那個,先生:) – mlvljr 2010-03-20 15:56:03

+0

好吧,現在我看到'smart_func'可能太聰明瞭[to upvoted :) :) – mlvljr 2010-03-24 17:05:09

0

不檢查迭代性。爲了使單個功能執行不同的任務,使功能檢查有關其元素類型/功能的事情是錯誤的。如果你想做兩件不同的事情,那麼做兩個不同的功能。

這聽起來像你得出這個結論自己,並提供了一致的API,在那裏你做

from itertools import repeat 
zip([1, 2, 3], repeat(5), "bar") 

注意,它幾乎總是沒用做到這一點,因爲你可能只是做

five = 5 
for number, letter in zip([1, 2, 3], "bar") 
    # Just use five here since it never changes 

當然,除非你正在喂這個東西已經使用zip

+0

這個輸入給我五個'也可以是一個清單。這發生在我的代碼中的幾個點,所以我不想在我調用壓縮函數之前檢查這一點,而是在裏面進行檢查。我的問題是如何處理邊緣情況。 – Debilski 2010-03-20 17:42:23

+0

@Debilski如果你想讓你的函數用可迭代/不可迭代的檢查邏輯進行「預裝」,並提供「覆蓋」/交換參數處理方式的能力,爲什麼不使用簡單的默認)格式說明符? – mlvljr 2010-03-20 18:02:38

+0

@Debilski,當然是。顯然你已經發現了通過你真正需要的方式 - 一個反覆產生相同價值的反覆。 – 2010-03-20 18:21:03