发电机可以用在python string.format吗? [英] Can generators be used with string.format in python?

查看:192
本文介绍了发电机可以用在python string.format吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

 {},{},{}。格式(*(1,2,3,4,5))

打印:

 '1,2,3' 

这个工作,只要 {} in format 不超过元组的长度。我想使它适用于任意长度的元组,如果长度不足,可以用 - s填充。为了避免假设 {} 的数量,我想使用一个生成器。下面是我想到的:

  def tup(* args):
for itertools.chain(args ,itertools.repeat(' - ')):
yield s
$ b $ print{},{},{}。format(* tup(1,2))

预计:

 '1,2, - '

但它永远不会返回。你能使它与发电机一起工作吗?有没有更好的方法?

解决方案

如果你想一想,除了可变参数解包全部解包外, format 也不一定按照'{2} {1} {0}'格式只需要一个序列而不需要单独的参数,那么你可以解决这个问题,通过建立一个正确的事情序列。这里有一个简单的例子:

$ p $ class DefaultList(list):
def __getitem __(self,idx):
尝试:
返回超级(DefaultList,self).__ getitem __(idx)
除了IndexError:
返回' - '

当然你的真实版本会包装一个任意的可迭代的,而不是子类 list ,并且可能必须使用 tee 或内部缓存,并根据请求提取新值,只有当您通过结束时才会默认值。 (你可能想在ActiveState中搜索lazy list或lazy sequence食谱,因为有一些这样做。)但是这足以显示这个例子。

现在,这对我们有什么帮助?它不; * lst 在一个 DefaultList 上只会尝试使用一个元组,我们已经有了论点。但是如果你有一个格式的版本,那么这个版本只需要一系列的参数呢?然后你可以传递你的 DefaultList ,它就可以工作了。

你确实有这个: Formatter.vformat

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


$ b


Formatter 明确地而不是隐式地通过 str 方法。您可以覆盖它的 get_value 方法和/或它的 check_unused_args

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

#允许多余的参数
def check_unused_args(self,used_args,args,kwargs):
传递

#填入缺少的参数
def get_value(self,key,args ,kwargs):
try:
返回超级(DefaultFormatter,self).get_value(key,args,kwargs)
除了IndexError:
return' - '

f = DefaultFormatter(' - ')

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








当然,您仍然需要将您的迭代器包装在提供Sequence协议的东西中。当我们在这时,你的问题可以被解决如果语言具有可迭代拆包协议,则直接进行。请参阅此处提出了一个python-ideas线程来提出这样的事情,以及这个想法所有的问题。 (还要注意,格式函数会使这个更棘手,因为它必须直接使用解包协议,而不是依靠解释器来神奇地完成它。那么你只需要在处理 __ unpack __ 的迭代器上编写一个非常简单和通用的包装器。)


"{}, {}, {}".format(*(1,2,3,4,5))

Prints:

'1, 2, 3'

This works, as long as the number of {} in format does not exceed the length of a tuple. I want to make it work for a tuple of arbitrary length, padding it with -s if it is of insufficient length. And to avoid making assumptions about the number of {}'s, I wanted to use a generator. Here's what I had in mind:

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

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

Expected:

'1, 2, -'

But it never returns. Can you make it work with generators? Is there a better approach?

解决方案

If you think about it, besides the fact that variable argument unpacking unpacks all at once, there's also the fact that format doesn't necessarily take its arguments in order, as in '{2} {1} {0}'.

You could work around this if format just took a sequence instead of requiring separate arguments, by building a sequence that does the right thing. Here's a trivial example:

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

Of course your real-life version would wrap an arbitrary iterable, not subclass list, and would probably have to use tee or an internal cache and pull in new values as requested, only defaulting when you've passed the end. (You may want to search for "lazy list" or "lazy sequence" recipes at ActiveState, because there are a few of them that do this.) But this is enough to show the example.

Now, how does this help us? It doesn't; *lst on a DefaultList will just try to make a tuple out of the thing, giving us exactly the same number of arguments we already had. But what if you had a version of format that could just take a sequence of args instead? Then you could just pass your DefaultList and it would work.

And you do have that: Formatter.vformat.

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


However, there's an even easier way, once you're using Formatter explicitly instead of implicitly via the str method. You can just override its get_value method and/or its 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], {}))

Of course you're still going to need to wrap your iterator in something that provides the Sequence protocol.


While we're at it, your problem could be solved more directly if the language had an "iterable unpacking" protocol. See here for a python-ideas thread proposing such a thing, and all of the problems the idea has. (Also note that the format function would make this trickier, because it would have to use the unpacking protocol directly instead of relying on the interpreter to do it magically. But, assuming it did so, then you'd just need to write a very simple and general-purpose wrapper around any iterable that handles __unpack__ for it.)

这篇关于发电机可以用在python string.format吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆