2013-08-27 39 views
6
"{}, {}, {}".format(*(1,2,3,4,5)) 

打印:可以使用python中的string.format生成器嗎?

'1, 2, 3' 

這個工作,只要{}format數量不超過一個元組的長度。我想讓它適用於任意長度的元組,如果長度不夠,可以用- s填充。爲了避免假設{}的數量,我想使用一個發生器。這是我腦子裏想的:

def tup(*args): 
    for s in itertools.chain(args, itertools.repeat('-')): 
     yield s 

print "{}, {}, {}".format(*tup(1,2)) 

預計:

'1, 2, -' 

但它永遠不會返回。你能使它與發電機一起工作嗎?有更好的方法嗎?

回答

3

如果你仔細想想,除了可變參數解包全部解包的事實之外,還有一個事實,即format不一定按順序參數化參數,如'{2} {1} {0}'

如果format只需要一個序列而不需要單獨的參數,那麼可以通過構建一個正確的序列來解決此問題。這裏有一個簡單的例子:

class DefaultList(list): 
    def __getitem__(self, idx): 
     try: 
      return super(DefaultList, self).__getitem__(idx) 
     except IndexError: 
      return '-' 

當然,您的真實版本將包裹任意迭代,而不是繼承list,並有可能不得不使用tee或內部緩存和新的價值觀拉的要求,只有違約當你通過結束。(你可能想在ActiveState中搜索「lazy list」或「lazy sequence」食譜,因爲有一些這樣做。)但這足以證明這個例子。

現在,這對我們有什麼幫助?它不; *lst上的DefaultList只會試圖將一個元組從事件中提供給我們完全相同數量的我們已經擁有的參數。但是如果你有一個版本爲format的版本,那麼它可能只需要一系列參數呢?那麼你可以通過你的DefaultList,它會工作。

而你確實有:Formatter.vformat

>>> string.Formatter().vformat('{0} {1} {2}', DefaultList([0, 1]), {}) 
'0 1 -' 

然而,有一個更簡單的方法,一旦你使用Formatter明確,而不是隱含通過str方法。你可以只覆蓋其get_value方法和/或其check_unused_args

class DefaultFormatter(string.Formatter): 
    def __init__(self, default): 
     self.default = default 

    # Allow excess arguments 
    def check_unused_args(self, used_args, args, kwargs): 
     pass 

    # Fill in missing arguments 
    def get_value(self, key, args, kwargs): 
     try: 
      return super(DefaultFormatter, self).get_value(key, args, kwargs) 
     except IndexError: 
      return '-' 

f = DefaultFormatter('-') 

print(f.vformat('{0} {2}', [0], {})) 
print(f.vformat('{0} {2}', [0, 1, 2, 3], {})) 

當然,你仍然會需要來包裝你的迭代器的東西,它提供的序列協議。


雖然我們在這樣做,但如果語言有一個「可迭代拆包」協議,則可以更直接地解決問題。請參閱here瞭解提議這樣的事情的python-ideas線程,以及這個想法所具有的所有問題。 (另外請注意,format函數會讓這個技巧更棘手,因爲它必須直接使用解包協議,而不是依靠解釋器來神奇地完成它。但是,假設它這樣做了,那麼你只需要寫一個非常簡單的任何可處理__unpack__的iterable都是簡單和通用的包裝。)

4

您不能使用無盡的發電機來填充任何*args任意參數調用。

Python遍歷生成器以加載所有參數以傳遞給可調用對象,並且如果生成器是無止境的,那永遠不會完成。

您可以使用沒有問題的無限制發電機。你可以使用itertools.islice()封頂發電機:

from itertools import islice 

print "{}, {}, {}".format(*islice(tup(1,2), 3)) 

畢竟,你已經知道你的模板有多少位了。

+0

明白了。你能提出一個更好的方法嗎?我對產生一些最大長度的發電機並不滿意,這是浪費的(破壞了使用發電機的目的,列表會這樣做),並且不能保證總能正常工作。 – user443854

+0

@ user443854:您可以使用'itertools.islice()'來限制一個生成器。 –

+0

我知道'itertools.islice()',但我不明白它在這裏如何應用。在使用它之前,我需要知道所需的元素數量。我希望能取得不同的成就。用簡單的英語,我想告訴解釋者:這是一個生成器,根據需要多次迭代它,但不會更多。 – user443854

3

Martijn Pieters有直接的答案,但如果您想爲format自動填充創建某種通用包裝器/幫助器,您可以查看string.Formatter.parse。使用它,您可以獲得format如何看到格式字符串的表示,並且刪除參數計數/命名參數名稱以動態計算迭代器需要多長時間。

1

天真的做法是爲格式函數提供L/2參數,其中L是格式字符串的長度。由於更換令牌是至少2個字符長,你一定總是有足夠的值來解壓:

def tup(l, *args): 
    for s in args + (('-',) * l): 
     yield s 
s = "{}, {}, {}" 
print s.format(*list(tup(len(s)//2, 1, 2))) 

至於建議由西拉雷更精細的上限可以使用發現string.Formatter.parse

import string 
def tup(l, *args): 
    for s in args + (('-',) * l): 
     yield s 
s = "{}, {}, {}" 
l = len(list(string.Formatter().parse(s))) 
print s.format(*list(tup(l, 1, 2))) 
相關問題