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

プリント:Pythonでstring.formatでジェネレータを使用できますか?

'1, 2, 3' 

これは限りformat{}数はタプルの長さを超えないように、動作します。私はそれが不十分な長さの場合-とそれを埋めて任意の長さのタプルのために働かせたいです。そして、{}の数についての仮定を避けるために、私はジェネレータを使いたいと思っていました。期待

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

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

'1, 2, -' 

をしかし、それは決して戻らないここで私が念頭に置いていたものです。ジェネレータと連携させることはできますか?より良いアプローチがありますか?

答えて

3

これについて考えると、可変引数展開が一度にすべて展開されるという事実の他に、'{2} {1} {0}'のように、formatが必ずしも引数を順番に取るわけではないという事実もあります。

formatが、別々の引数を必要とするのではなく、正しいことをするシーケンスを構築するだけで、これを回避することができます。ここでは簡単な例です:もちろん

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

あなたの現実のバージョンは、任意のiterableをラップlistをサブクラス化していない、そしておそらく、teeまたは内部キャッシュを使用して、要求に応じて新しい値を引き出すために持っている唯一の不履行なりますあなたが終わりを過ぎたとき。(ActiveStateで「レイジーリスト」や「レイジーシーケンス」のレシピを検索したい場合がありますが、これにはいくつかのレシピがあります)。

これはどうやって私たちを助けますか?それはしません。 のDefaultListは、タプルを作成しようとするだけで、私たちがすでに持っているのと同じ数の引数を与えることができます。しかし、代わりにargsのシーケンスを取ることができるformatのバージョンをお持ちでしたらどうでしょうか?その後、あなたはDefaultListを渡すことができ、それはうまくいくでしょう。

あなたはそれを持っています:Formatter.vformat

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

あなたはstrメソッドを介して明示的に代わりの暗黙的Formatterを使用しているいったんしかし、さらに簡単な方法は、あります。もちろん

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], {})) 

それでもシーケンスプロトコルを提供し、何かにあなたのイテレータをラップする必要があるとしている:あなたは、ちょうどそのget_value方法および/またはそのcheck_unused_argsを上書きすることができます。


言語が「iterable unpacking」プロトコルを使用している場合は、問題はより解決します。そのようなことを提案するpython-ideasスレッド、およびそのアイデアが持つすべての問題については、hereを参照してください。 (format関数は、これを難解にしてしまうことに注意してください。なぜなら、インタプリタを魔法に使うのではなく、アンパックプロトコルを直接使用しなければならないからです。しかし、それを仮定すると、単純な汎用ラッパーで、そのためには__unpack__を処理します。)

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がフォーマット文字列をどのように見えるかを表現し、引数のcount/named引数名を取り除いて、イテレータの必要時間を動的に把握することができます。

1

単純なアプローチは、L/2引数を書式文字列の長さとする書式関数に提供することです。交換用のトークンは、少なくとも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)))