标签归档:loops

Python:字典列表,如果存在,则增加一个字典值,如果不增加新字典

问题:Python:字典列表,如果存在,则增加一个字典值,如果不增加新字典

我想做类似的事情。

list_of_urls = ['http://www.google.fr/', 'http://www.google.fr/', 
                'http://www.google.cn/', 'http://www.google.com/', 
                'http://www.google.fr/', 'http://www.google.fr/', 
                'http://www.google.fr/', 'http://www.google.com/', 
                'http://www.google.fr/', 'http://www.google.com/', 
                'http://www.google.cn/']

urls = [{'url': 'http://www.google.fr/', 'nbr': 1}]

for url in list_of_urls:
    if url in [f['url'] for f in urls]:
         urls[??]['nbr'] += 1
    else:
         urls.append({'url': url, 'nbr': 1})

我能怎么做 ?我不知道该选择元组来编辑它还是找出元组索引?

有什么帮助吗?

I would like do something like that.

list_of_urls = ['http://www.google.fr/', 'http://www.google.fr/', 
                'http://www.google.cn/', 'http://www.google.com/', 
                'http://www.google.fr/', 'http://www.google.fr/', 
                'http://www.google.fr/', 'http://www.google.com/', 
                'http://www.google.fr/', 'http://www.google.com/', 
                'http://www.google.cn/']

urls = [{'url': 'http://www.google.fr/', 'nbr': 1}]

for url in list_of_urls:
    if url in [f['url'] for f in urls]:
         urls[??]['nbr'] += 1
    else:
         urls.append({'url': url, 'nbr': 1})

How can I do ? I don’t know if I should take the tuple to edit it or figure out the tuple indices?

Any help ?


回答 0

那是组织事情的一种非常奇怪的方式。如果存储在字典中,这很容易:

# This example should work in any version of Python.
# urls_d will contain URL keys, with counts as values, like: {'http://www.google.fr/' : 1 }
urls_d = {}
for url in list_of_urls:
    if not url in urls_d:
        urls_d[url] = 1
    else:
        urls_d[url] += 1

这段更新计数字典的代码是Python中常见的“模式”。常见的是defaultdict,创建了一个特殊的数据结构,以使其变得更加容易:

from collections import defaultdict  # available in Python 2.5 and newer

urls_d = defaultdict(int)
for url in list_of_urls:
    urls_d[url] += 1

如果您defaultdict使用键访问,而该键尚未在中defaultdict,则该键会自动添加一个默认值。将defaultdict采取调用您传递,并调用它来获得默认值。在这种情况下,我们在课堂上通过了int;当Python调用时,int()它返回零值。因此,第一次引用URL时,其计数将初始化为零,然后将一个添加到计数中。

但是充满计数的字典也是一种常见的模式,因此Python提供了一个现成的类:containers.Counter 您只需Counter通过调用该类并传递任何可迭代的类来创建实例;它会建立一个字典,其中的键是可迭代的值,而值是键在可迭代中出现的次数的计数。上面的示例变为:

from collections import Counter  # available in Python 2.7 and newer

urls_d = Counter(list_of_urls)

如果您确实需要按照显示的方式进行操作,则最简单,最快的方法是使用这三个示例中的任何一个,然后构建所需的示例。

from collections import defaultdict  # available in Python 2.5 and newer

urls_d = defaultdict(int)
for url in list_of_urls:
    urls_d[url] += 1

urls = [{"url": key, "nbr": value} for key, value in urls_d.items()]

如果您使用的是Python 2.7或更高版本,则可以单行执行:

from collections import Counter

urls = [{"url": key, "nbr": value} for key, value in Counter(list_of_urls).items()]

That is a very strange way to organize things. If you stored in a dictionary, this is easy:

# This example should work in any version of Python.
# urls_d will contain URL keys, with counts as values, like: {'http://www.google.fr/' : 1 }
urls_d = {}
for url in list_of_urls:
    if not url in urls_d:
        urls_d[url] = 1
    else:
        urls_d[url] += 1

This code for updating a dictionary of counts is a common “pattern” in Python. It is so common that there is a special data structure, defaultdict, created just to make this even easier:

from collections import defaultdict  # available in Python 2.5 and newer

urls_d = defaultdict(int)
for url in list_of_urls:
    urls_d[url] += 1

If you access the defaultdict using a key, and the key is not already in the defaultdict, the key is automatically added with a default value. The defaultdict takes the callable you passed in, and calls it to get the default value. In this case, we passed in class int; when Python calls int() it returns a zero value. So, the first time you reference a URL, its count is initialized to zero, and then you add one to the count.

But a dictionary full of counts is also a common pattern, so Python provides a ready-to-use class: containers.Counter You just create a Counter instance by calling the class, passing in any iterable; it builds a dictionary where the keys are values from the iterable, and the values are counts of how many times the key appeared in the iterable. The above example then becomes:

from collections import Counter  # available in Python 2.7 and newer

urls_d = Counter(list_of_urls)

If you really need to do it the way you showed, the easiest and fastest way would be to use any one of these three examples, and then build the one you need.

from collections import defaultdict  # available in Python 2.5 and newer

urls_d = defaultdict(int)
for url in list_of_urls:
    urls_d[url] += 1

urls = [{"url": key, "nbr": value} for key, value in urls_d.items()]

If you are using Python 2.7 or newer you can do it in a one-liner:

from collections import Counter

urls = [{"url": key, "nbr": value} for key, value in Counter(list_of_urls).items()]

回答 1

使用默认值可以,但是:

urls[url] = urls.get(url, 0) + 1

使用.get,可以获取默认返回值(如果不存在)。默认情况下为None,但如果我发送给您,则为0。

Using the default works, but so does:

urls[url] = urls.get(url, 0) + 1

using .get, you can get a default return if it doesn’t exist. By default it’s None, but in the case I sent you, it would be 0.


回答 2

使用defaultdict

from collections import defaultdict

urls = defaultdict(int)

for url in list_of_urls:
    urls[url] += 1

Use defaultdict:

from collections import defaultdict

urls = defaultdict(int)

for url in list_of_urls:
    urls[url] += 1

回答 3

这对我来说总是正常的:

for url in list_of_urls:
    urls.setdefault(url, 0)
    urls[url] += 1

This always works fine for me:

for url in list_of_urls:
    urls.setdefault(url, 0)
    urls[url] += 1

回答 4

完全按照您的方式来做?您可以使用for … else结构

for url in list_of_urls:
    for url_dict in urls:
        if url_dict['url'] == url:
            url_dict['nbr'] += 1
            break
    else:
        urls.append(dict(url=url, nbr=1))

但这是很不雅观的。您是否真的必须将访问的URL存储为LIST?例如,如果将其排序为dict,并以url字符串索引,则它会更干净:

urls = {'http://www.google.fr/': dict(url='http://www.google.fr/', nbr=1)}

for url in list_of_urls:
    if url in urls:
        urls[url]['nbr'] += 1
    else:
        urls[url] = dict(url=url, nbr=1)

在第二个示例中需要注意的几件事:

  • 了解测试单个测试时如何使用dict urls消除整个urls列表的需求url。这种方法将更快。
  • 使用dict( )大括号代替您的代码
  • 使用list_of_urlsurlsurl作为变量名使代码挺难解析。这是更好地找到一些更清晰的,如urls_to_visiturls_already_visitedcurrent_url。我知道,时间更长。但这更清楚。

当然,我假设这dict(url='http://www.google.fr', nbr=1)是您自己的数据结构的简化,因为否则,urls可能只是:

urls = {'http://www.google.fr':1}

for url in list_of_urls:
    if url in urls:
        urls[url] += 1
    else:
        urls[url] = 1

使用defaultdict姿势可以很优雅:

urls = collections.defaultdict(int)
for url in list_of_urls:
    urls[url] += 1

To do it exactly your way? You could use the for…else structure

for url in list_of_urls:
    for url_dict in urls:
        if url_dict['url'] == url:
            url_dict['nbr'] += 1
            break
    else:
        urls.append(dict(url=url, nbr=1))

But it is quite inelegant. Do you really have to store the visited urls as a LIST? If you sort it as a dict, indexed by url string, for example, it would be way cleaner:

urls = {'http://www.google.fr/': dict(url='http://www.google.fr/', nbr=1)}

for url in list_of_urls:
    if url in urls:
        urls[url]['nbr'] += 1
    else:
        urls[url] = dict(url=url, nbr=1)

A few things to note in that second example:

  • see how using a dict for urls removes the need for going through the whole urls list when testing for one single url. This approach will be faster.
  • Using dict( ) instead of braces makes your code shorter
  • using list_of_urls, urls and url as variable names make the code quite hard to parse. It’s better to find something clearer, such as urls_to_visit, urls_already_visited and current_url. I know, it’s longer. But it’s clearer.

And of course I’m assuming that dict(url='http://www.google.fr', nbr=1) is a simplification of your own data structure, because otherwise, urls could simply be:

urls = {'http://www.google.fr':1}

for url in list_of_urls:
    if url in urls:
        urls[url] += 1
    else:
        urls[url] = 1

Which can get very elegant with the defaultdict stance:

urls = collections.defaultdict(int)
for url in list_of_urls:
    urls[url] += 1

回答 5

除了第一次以外,每次看到一个单词时,if语句的测试都会失败。如果您要计算大量的单词,许多单词可能会多次出现。在一个值的初始化仅发生一次且该值的增加将发生多次的情况下,使用try语句会更便宜:

urls_d = {}
for url in list_of_urls:
    try:
        urls_d[url] += 1
    except KeyError:
        urls_d[url] = 1

您可以阅读有关此内容的更多信息:https : //wiki.python.org/moin/PythonSpeed/PerformanceTips

Except for the first time, each time a word is seen the if statement’s test fails. If you are counting a large number of words, many will probably occur multiple times. In a situation where the initialization of a value is only going to occur once and the augmentation of that value will occur many times it is cheaper to use a try statement:

urls_d = {}
for url in list_of_urls:
    try:
        urls_d[url] += 1
    except KeyError:
        urls_d[url] = 1

you can read more about this: https://wiki.python.org/moin/PythonSpeed/PerformanceTips


单行嵌套循环

问题:单行嵌套循环

在转置矩阵的python中编写此函数:

def transpose(m):
    height = len(m)
    width = len(m[0])
    return [ [ m[i][j] for i in range(0, height) ] for j in range(0, width) ]

在此过程中,我意识到我不完全了解嵌套在循环中的单行如何执行。请回答以下问题,以帮助我理解:

  1. 此for循环执行的顺序是什么?
  2. 如果我有一个三重嵌套的for循环,它将执行什么顺序?
  3. 等于未嵌套for循环等于什么?

鉴于

[ function(i,j) for i,j in object ]
  1. 为了将其用于循环结构,对象必须是哪种类型?
  2. 将i和j分配给object中的元素的顺序是什么?
  3. 可以用不同的for循环结构模拟吗?
  4. 可以将此for循环嵌套在相似或不同的for循环结构中吗?看起来如何?

附加信息也将不胜感激。

Wrote this function in python that transposes a matrix:

def transpose(m):
    height = len(m)
    width = len(m[0])
    return [ [ m[i][j] for i in range(0, height) ] for j in range(0, width) ]

In the process I realized I don’t fully understand how single line nested for loops execute. Please help me understand by answering the following questions:

  1. What is the order in which this for loop executes?
  2. If I had a triple nested for loop, what order would it execute?
  3. What would be equal the equal unnested for loop?

Given,

[ function(i,j) for i,j in object ]
  1. What type must object be in order to use this for loop structure?
  2. What is the order in which i and j are assigned to elements in object?
  3. Can it be simulated by a different for loop structure?
  4. Can this for loop be nested with a similar or different structure for loop? And how would it look?

Additional information is appreciated as well.


回答 0

最好的信息来源是有关列表理解官方Python教程。列表理解与for循环几乎相同(当然,任何列表理解都可以写为for循环),但它们通常比使用for循环更快。

在教程中查看这个更长的列表理解(该if部分过滤了理解,只有通过if语句的部分才被传递到列表理解的最后一部分(此处(x,y)):

>>> [(x, y) for x in [1,2,3] for y in [3,1,4] if x != y]
[(1, 3), (1, 4), (2, 3), (2, 1), (2, 4), (3, 1), (3, 4)]

它与嵌套的for循环完全相同(并且,如本教程所述,请注意for和if的顺序如何相同)。

>>> combs = []
>>> for x in [1,2,3]:
...     for y in [3,1,4]:
...         if x != y:
...             combs.append((x, y))
...
>>> combs
[(1, 3), (1, 4), (2, 3), (2, 1), (2, 4), (3, 1), (3, 4)]

列表理解和for循环之间的主要区别在于for循环的最后部分(您在其中进行操作)始于而不是结束。

关于您的问题:

为了将其用于循环结构,对象必须是哪种类型?

一个可迭代的。可以生成(有限)元素集的任何对象。这些包括任何容器,列表,集合,生成器等。

将i和j分配给object中的元素的顺序是什么?

它们的分配顺序与从每个列表生成的顺序完全相同,就好像它们在嵌套的for循环中一样(对于第一次理解,您将为i获得1个元素,然后将j中的每个值,第2个元素赋给i,然后来自j等的每个值)

可以用不同的for循环结构模拟吗?

是的,上面已经显示了。

可以将此for循环嵌套在相似或不同的for循环结构中吗?看起来如何?

是的,但这不是一个好主意。例如,在此处为您提供字符列表:

[[ch for ch in word] for word in ("apple", "banana", "pear", "the", "hello")]

The best source of information is the official Python tutorial on list comprehensions. List comprehensions are nearly the same as for loops (certainly any list comprehension can be written as a for-loop) but they are often faster than using a for loop.

Look at this longer list comprehension from the tutorial (the if part filters the comprehension, only parts that pass the if statement are passed into the final part of the list comprehension (here (x,y)):

>>> [(x, y) for x in [1,2,3] for y in [3,1,4] if x != y]
[(1, 3), (1, 4), (2, 3), (2, 1), (2, 4), (3, 1), (3, 4)]

It’s exactly the same as this nested for loop (and, as the tutorial says, note how the order of for and if are the same).

>>> combs = []
>>> for x in [1,2,3]:
...     for y in [3,1,4]:
...         if x != y:
...             combs.append((x, y))
...
>>> combs
[(1, 3), (1, 4), (2, 3), (2, 1), (2, 4), (3, 1), (3, 4)]

The major difference between a list comprehension and a for loop is that the final part of the for loop (where you do something) comes at the beginning rather than at the end.

On to your questions:

What type must object be in order to use this for loop structure?

An iterable. Any object that can generate a (finite) set of elements. These include any container, lists, sets, generators, etc.

What is the order in which i and j are assigned to elements in object?

They are assigned in exactly the same order as they are generated from each list, as if they were in a nested for loop (for your first comprehension you’d get 1 element for i, then every value from j, 2nd element into i, then every value from j, etc.)

Can it be simulated by a different for loop structure?

Yes, already shown above.

Can this for loop be nested with a similar or different structure for loop? And how would it look?

Sure, but it’s not a great idea. Here, for example, gives you a list of lists of characters:

[[ch for ch in word] for word in ("apple", "banana", "pear", "the", "hello")]

回答 1

您可能对感兴趣itertools.product,它会从您传递的所有可迭代对象中返回一个可迭代的值元组。也就是说,itertools.product(A, B)产生形式的所有值(a, b),其中a值来自Ab值来自B。例如:

import itertools

A = [50, 60, 70]
B = [0.1, 0.2, 0.3, 0.4]

print [a + b for a, b in itertools.product(A, B)]

打印:

[50.1, 50.2, 50.3, 50.4, 60.1, 60.2, 60.3, 60.4, 70.1, 70.2, 70.3, 70.4]

请注意,传递给最终参数的参数itertools.product是“内部” 参数。通常等于itertools.product(a0, a1, ... an)[(i0, i1, ... in) for in in an for in-1 in an-1 ... for i0 in a0]

You might be interested in itertools.product, which returns an iterable yielding tuples of values from all the iterables you pass it. That is, itertools.product(A, B) yields all values of the form (a, b), where the a values come from A and the b values come from B. For example:

import itertools

A = [50, 60, 70]
B = [0.1, 0.2, 0.3, 0.4]

print [a + b for a, b in itertools.product(A, B)]

This prints:

[50.1, 50.2, 50.3, 50.4, 60.1, 60.2, 60.3, 60.4, 70.1, 70.2, 70.3, 70.4]

Notice how the final argument passed to itertools.product is the “inner” one. Generally, itertools.product(a0, a1, ... an) is equal to [(i0, i1, ... in) for in in an for in-1 in an-1 ... for i0 in a0]


回答 2

首先,您的第一个代码本身并不使用for循环,而是使用列表推导

  1. 相当于

    对于范围(0,宽度)中的j:对于范围(0,高度)中的i:m [i] [j]

  2. 大致相同,它通常像for循环一样从右到左嵌套。但是列表理解语法更复杂。

  3. 我不确定这个问题在问什么


  1. 产生可迭代对象且恰好产生两个对象的任何可迭代对象(实际上[(1,2),'ab']是有效的)

  2. 对象在迭代时产生的顺序。i转到第一个Yield,j第二个。

  3. 是的,但是不那么漂亮。我相信它在功能上等同于:

    l = list()
    对于对象中的i,j:
        l.append(function(i,j))
    

    甚至更好地使用map

    map(function, object)

    不过,当然功能必须得到ij本身。

  4. 这不是3个相同的问题吗?

First of all, your first code doesn’t use a for loop per se, but a list comprehension.

  1. Would be equivalent to

    for j in range(0, width): for i in range(0, height): m[i][j]

  2. Much the same way, it generally nests like for loops, right to left. But list comprehension syntax is more complex.

  3. I’m not sure what this question is asking


  1. Any iterable object that yields iterable objects that yield exactly two objects (what a mouthful – i.e [(1,2),'ab'] would be valid )

  2. The order in which the object yields upon iteration. i goes to the first yield, j the second.

  3. Yes, but not as pretty. I believe it is functionally equivalent to:

    l = list()
    for i,j in object:
        l.append(function(i,j))
    

    or even better use map:

    map(function, object)
    

    But of course function would have to get i, j itself.

  4. Isn’t this the same question as 3?


回答 3

您可以使用zip函数在同一行中使用两个for循环

码:

list1 = ['Abbas', 'Ali', 'Usman']
list2 = ['Kamran', 'Asgar', 'Hamza', 'Umer']
list3 = []
for i,j in zip(list1,list2):
    list3.append(i)
    list3.append(j)
print(list3)

输出:

['Abbas', 'Kamran', 'Ali', 'Asgar', 'Usman', 'Hamza']

因此,通过使用zip函数,我们可以使用两个for循环,也可以迭代同一行中的两个列表。

You can use two for loops in same line by using zip function

Code:

list1 = ['Abbas', 'Ali', 'Usman']
list2 = ['Kamran', 'Asgar', 'Hamza', 'Umer']
list3 = []
for i,j in zip(list1,list2):
    list3.append(i)
    list3.append(j)
print(list3)

Output:

['Abbas', 'Kamran', 'Ali', 'Asgar', 'Usman', 'Hamza']

So, by using zip function, we can use two for loops or we can iterate two lists in same row.


回答 4

下面的代码提供了嵌套循环的最佳示例,同时使用两个for循环时,请记住第一个循环的输出是第二个循环的输入。使用嵌套循环时,循环终止也很重要

for x in range(1, 10, 1):
     for y in range(1,x):
             print y,
        print
OutPut :
1
1 2
1 2 3
1 2 3 4
1 2 3 4 5
1 2 3 4 5 6
1 2 3 4 5 6 7
1 2 3 4 5 6 7 8

Below code for best examples for nested loops, while using two for loops please remember the output of the first loop is input for the second loop. Loop termination also important while using the nested loops

for x in range(1, 10, 1):
     for y in range(1,x):
             print y,
        print
OutPut :
1
1 2
1 2 3
1 2 3 4
1 2 3 4 5
1 2 3 4 5 6
1 2 3 4 5 6 7
1 2 3 4 5 6 7 8

我如何理解Python循环的`else`子句?

问题:我如何理解Python循环的`else`子句?

许多Python程序员可能没有意识到while循环和for循环的语法包含一个可选else:子句:

for val in iterable:
    do_something(val)
else:
    clean_up()

else子句的主体是执行某些类型的清理操作的好地方,并在循环的正常终止时执行:即,使用returnbreak跳过else子句退出循环;continue执行后退出。我知道这只是因为我只是看着它(再次),因为我永远记得else子句被执行。

总是?顾名思义,在循环的“失败”上?定期终止吗?即使循环退出return?我永远不能完全确定,不查它。

我将不确定性归咎于关键字的选择:我发现else这种语义难以置信。我的问题不是“为什么要为此目的使用该关键字”(虽然只有在阅读了答案和评论之后,我才可能会投票关闭关键字),但我如何考虑该else关键字,以便其语义有意义,我因此可以记住吗?

我敢肯定,对此进行了大量讨论,并且我可以想象做出这一选择是为了与try语句的else:子句(我也必须查找)保持一致,并且其目标是不添加到该语句的列表中。 Python的保留字。也许选择的原因else将阐明其功能并使之更加令人难忘,但我是将名称与功能联系在一起,而不是出于历史解释本身。

这个问题的答案(我的问题作为与之重复的问题简短地结束了)包含很多有趣的背景故事。我的问题有不同的侧重点(如何将特定语义else与关键字选择联系起来),但是我觉得应该在某个地方链接到该问题。

Many Python programmers are probably unaware that the syntax of while loops and for loops includes an optional else: clause:

for val in iterable:
    do_something(val)
else:
    clean_up()

The body of the else clause is a good place for certain kinds of clean-up actions, and is executed on normal termination of the loop: I.e., exiting the loop with return or break skips the else clause; exiting after a continue executes it. I know this only because I just looked it up (yet again), because I can never remember when the else clause is executed.

Always? On “failure” of the loop, as the name suggests? On regular termination? Even if the loop is exited with return? I can never be entirely sure without looking it up.

I blame my persisting uncertainty on the choice of keyword: I find else incredibly unmnemonic for this semantics. My question is not “why is this keyword used for this purpose” (which I would probably vote to close, though only after reading the answers and comments), but how can I think about the else keyword so that its semantics make sense, and I can therefore remember it?

I’m sure there was a fair amount of discussion about this, and I can imagine that the choice was made for consistency with the try statement’s else: clause (which I also have to look up), and with the goal of not adding to the list of Python’s reserved words. Perhaps the reasons for choosing else will clarify its function and make it more memorable, but I’m after connecting name to function, not after historical explanation per se.

The answers to this question, which my question was briefly closed as a duplicate of, contain a lot of interesting back story. My question has a different focus (how to connect the specific semantics of else with the keyword choice), but I feel there should be a link to this question somewhere.


回答 0

(这受@Mark Tolonen的回答启发。)

一个if语句运行它else,如果它的条件计算为false条款。同样,while如果条件的条件评估为false ,则循环将运行else子句。

此规则与您描述的行为匹配:

  • 在正常执行中,while循环会重复运行直到条件评估为false为止,因此自然退出循环将运行else子句。
  • 当执行一条break语句时,您退出循环而不评估条件,因此条件不能评估为false,并且您从不运行else子句。
  • 当您执行一条continue语句时,您将再次评估条件,并按照循环迭代开始时的正常方式进行操作。因此,如果条件为true,则继续循环,但是如果条件为false,则运行else子句。
  • 其他退出循环的方法(例如)return不会评估条件,因此不会运行else子句。

for循环的行为方式相同。如果迭代器具有更多元素,则仅将条件视为true,否则将其视为false。

(This is inspired by @Mark Tolonen’s answer.)

An if statement runs its else clause if its condition evaluates to false. Identically, a while loop runs the else clause if its condition evaluates to false.

This rule matches the behavior you described:

  • In normal execution, the while loop repeatedly runs until the condition evaluates to false, and therefore naturally exiting the loop runs the else clause.
  • When you execute a break statement, you exit out of the loop without evaluating the condition, so the condition cannot evaluate to false and you never run the else clause.
  • When you execute a continue statement, you evaluate the condition again, and do exactly what you normally would at the beginning of a loop iteration. So, if the condition is true, you keep looping, but if it is false you run the else clause.
  • Other methods of exiting the loop, such as return, do not evaluate the condition and therefore do not run the else clause.

for loops behave the same way. Just consider the condition as true if the iterator has more elements, or false otherwise.


回答 1

最好这样考虑:如果前一个块中的所有内容都正确,以至于用尽,else始终执行该块。for

在这种情况下,正确意味着不exception,不break,不return。劫持控件的任何语句for都将导致该else块被绕过。


在中搜索商品时,找到了一个常见的用例iterable,当找到该商品或"not found"通过以下else块引发/打印一个标志时,将针对该商品取消搜索:

for items in basket:
    if isinstance(item, Egg):
        break
else:
    print("No eggs in basket")  

A continue不会从中劫持控制权for,因此控制权将elsefor用尽后继续进行。

Better to think of it this way: The else block will always be executed if everything goes right in the preceding for block such that it reaches exhaustion.

Right in this context will mean no exception, no break, no return. Any statement that hijacks control from for will cause the else block to be bypassed.


A common use case is found when searching for an item in an iterable, for which the search is either called off when the item is found or a "not found" flag is raised/printed via the following else block:

for items in basket:
    if isinstance(item, Egg):
        break
else:
    print("No eggs in basket")  

A continue does not hijack control from for, so control will proceed to the else after the for is exhausted.


回答 2

什么时候if执行else?当其条件为假时。正是在相同while/ else。因此,您可以将while/ else视为if一直运行其真实条件直到其评估为false的。A break不会改变这一点。它只是跳出包含循环而没有评估。该else如果仅执行评估if/ while条件为假。

for是相似的,除了它的假条件被消耗其迭代器。

continue并且break不要执行else。那不是他们的功能。在break退出循环含有。将continue返回到循环包含,其中,所述循环条件被评估的顶部。评估if/ while将错误(或for没有更多项目)执行的动作是else没有其他方法的。

When does an if execute an else? When its condition is false. It is exactly the same for the while/else. So you can think of while/else as just an if that keeps running its true condition until it evaluates false. A break doesn’t change that. It just jumps out of the containing loop with no evaluation. The else is only executed if evaluating the if/while condition is false.

The for is similar, except its false condition is exhausting its iterator.

continue and break don’t execute else. That isn’t their function. The break exits the containing loop. The continue goes back to the top of the containing loop, where the loop condition is evaluated. It is the act of evaluating if/while to false (or for has no more items) that executes else and no other way.


回答 3

这实际上是什么意思:

for/while ...:
    if ...:
        break
if there was a break:
    pass
else:
    ...

这是编写这种常见模式的一种更好的方式:

found = False
for/while ...:
    if ...:
        found = True
        break
if not found:
    ...

else如果有一个return原因,那么该子句将不会执行return,这意味着它会这样做。您可能想到的唯一exceptions是finally,其目的是确保始终执行该exceptions。

continue与这件事没有什么特别的关系。它导致循环的当前迭代结束,而这可能恰好结束了整个循环,显然,在这种情况下,循环并未以结束break

try/else 很相似:

try:
    ...
except:
    ...
if there was an exception:
    pass
else:
    ...

This is what it essentially means:

for/while ...:
    if ...:
        break
if there was a break:
    pass
else:
    ...

It’s a nicer way of writing of this common pattern:

found = False
for/while ...:
    if ...:
        found = True
        break
if not found:
    ...

The else clause will not be executed if there is a return because return leaves the function, as it is meant to. The only exception to that which you may be thinking of is finally, whose purpose is to be sure that it is always executed.

continue has nothing special to do with this matter. It causes the current iteration of the loop to end which may happen to end the entire loop, and clearly in that case the loop wasn’t ended by a break.

try/else is similar:

try:
    ...
except:
    ...
if there was an exception:
    pass
else:
    ...

回答 4

如果您将循环视为与此类似的结构(某种伪代码):

loop:
if condition then

   ... //execute body
   goto loop
else
   ...

这可能更有意义。循环本质上只是if重复一个语句,直到条件为为止false。这是重要的一点。循环检查其条件并查看是否为false,因此执行else(就像正常if/else),然后完成循环。

因此请注意,else 检查条件后,将执行唯一的获取。这意味着,如果在执行过程中使用a return或a 退出循环的主体break,因为不会再次检查条件,else则不会执行大小写。

continue另一方面,A 停止当前执行,然后跳回以再次检查循环条件,这就是else在这种情况下可以达到的原因。

If you think of your loops as a structure similar to this (somewhat pseudo-code):

loop:
if condition then

   ... //execute body
   goto loop
else
   ...

it might make a little bit more sense. A loop is essentially just an if statement that is repeated until the condition is false. And this is the important point. The loop checks its condition and sees that it’s false, thus executes the else (just like a normal if/else) and then the loop is done.

So notice that the else only get’s executed when the condition is checked. That means that if you exit the body of the loop in the middle of execution with for example a return or a break, since the condition is not checked again, the else case won’t be executed.

A continue on the other hand stops the current execution and then jumps back to check the condition of the loop again, which is why the else can be reached in this scenario.


回答 5

我对循环的else子句的了解是在我观看Raymond Hettinger的演讲时,他讲了一个有关他认为应该如何称呼它的故事nobreak。看下面的代码,您认为它将做什么?

for i in range(10):
    if test(i):
        break
    # ... work with i
nobreak:
    print('Loop completed')

您会怎么做?好吧,nobreak只有在break语句未在循环中命中的情况下,才会执行所说的部分。

My gotcha moment with the loop’s else clause was when I was watching a talk by Raymond Hettinger, who told a story about how he thought it should have been called nobreak. Take a look at the following code, what do you think it would do?

for i in range(10):
    if test(i):
        break
    # ... work with i
nobreak:
    print('Loop completed')

What would you guess it does? Well, the part that says nobreak would only be executed if a break statement wasn’t hit in the loop.


回答 6

通常,我倾向于想到这样的循环结构:

for item in my_sequence:
    if logic(item):
        do_something(item)
        break

非常像可变数量的if/elif语句:

if logic(my_seq[0]):
    do_something(my_seq[0])
elif logic(my_seq[1]):
    do_something(my_seq[1])
elif logic(my_seq[2]):
    do_something(my_seq[2])
....
elif logic(my_seq[-1]):
    do_something(my_seq[-1])

在这种情况下,elsefor循环上的语句的工作原理elseelifs 链上的语句完全相同,仅在条件为True之前没有条件时才执行。(或者用return异常中断执行)如果我的循环通常不符合此规范,则出于for: else您发布此问题的确切原因,我选择不使用:这是不直观的。

Usually I tend to think of a loop structure like this:

for item in my_sequence:
    if logic(item):
        do_something(item)
        break

To be a lot like a variable number of if/elif statements:

if logic(my_seq[0]):
    do_something(my_seq[0])
elif logic(my_seq[1]):
    do_something(my_seq[1])
elif logic(my_seq[2]):
    do_something(my_seq[2])
....
elif logic(my_seq[-1]):
    do_something(my_seq[-1])

In this case the else statement on the for loop works exactly like the else statement on the chain of elifs, it only executes if none of the conditions before it evaluate to True. (or break execution with return or an exception) If my loop does not fit this specification usually I choose to opt out of using for: else for the exact reason you posted this question: it is non-intuitive.


回答 7

其他人已经解释了的机制while/for...else,并且Python 3语言参考具有权威性的定义(请参见whilefor),但这是我的个人助记符FWIW。我想对我来说关键在于将其分解为两部分:一个部分用于理解else与循环条件相关的的含义,另一部分用于理解循环控制。

我发现从理解开始是最容易的while...else

while你有更多物品,做东西,else如果用完了,这样做

for...else助记符是基本相同的:

for每个项目,做一些事情,但是else如果用完了,请这样做

在这两种情况下,else仅在没有更多要处理的项目并且以常规方式(即,否breakreturn)处理了最后一个项目时才到达该零件。一个continue刚刚回到如果有任何更多的项目看。这些规则的助记符适用于whilefor

break荷兰国际集团或returnING,没有什么是else做的,
当我说continue,这是“环回至开始”为您服务

–显然,“循环返回开始”的意思是循环的开始,在该循环中,我们检查可迭代项中是否还有其他项,else就而言,它continue实际上根本没有作用。

Others have already explained the mechanics of while/for...else, and the Python 3 language reference has the authoritative definition (see while and for), but here is my personal mnemonic, FWIW. I guess the key for me has been to break this down into two parts: one for understanding the meaning of the else in relation to the loop conditional, and one for understanding loop control.

I find it’s easiest to start by understanding while...else:

while you have more items, do stuff, else if you run out, do this

The for...else mnemonic is basically the same:

for every item, do stuff, but else if you run out, do this

In both cases, the else part is only reached once there are no more items to process, and the last item has been processed in a regular manner (i.e. no break or return). A continue just goes back and sees if there are any more items. My mnemonic for these rules applies to both while and for:

when breaking or returning, there’s nothing else to do,
and when I say continue, that’s “loop back to start” for you

– with “loop back to start” meaning, obviously, the start of the loop where we check whether there are any more items in the iterable, so as far as the else is concerned, continue really plays no role at all.


回答 8

测试驱动的开发(TDD)中,当使用转换优先级前提范式时,您将循环视为条件语句的概括。

如果仅考虑简单的if/else(no elif)语句,则此方法可以与以下语法很好地结合:

if cond:
    # 1
else:
    # 2

概括为:

while cond:  # <-- generalization
    # 1
else:
    # 2

很好

用其他语言,TDD从单个案例到具有集合案例的步骤需要更多的重构。


这是8thlight博客的示例:

在8thlight博客的链接文章中,考虑了自动换行kata:在字符串(s下面的片段中的变量)上添加换行符以使其适合给定的宽度(length下面的片段中的变量)。一方面,实现看起来如下(Java):

String result = "";
if (s.length() > length) {
    result = s.substring(0, length) + "\n" + s.substring(length);
} else {
    result = s;
}
return result;

下一个当前失败的测试是:

@Test
public void WordLongerThanTwiceLengthShouldBreakTwice() throws Exception {
    assertThat(wrap("verylongword", 4), is("very\nlong\nword"));
    }

因此,我们有条件运行的代码:当满足特定条件时,将添加一个换行符。我们想要改进代码以处理多个换行符。本文中提出的解决方案建议应用(if-> while)转换,但是作者评论说:

虽然循环不能包含else子句,所以我们需要else通过减少路径来消除if路径。同样,这是重构。

在一次失败的测试中,这迫使对代码进行更多更改:

String result = "";
while (s.length() > length) {
    result += s.substring(0, length) + "\n";
    s = s.substring(length);
}
result += s;

在TDD中,我们希望编写尽可能少的代码以使测试通过。借助Python的语法,可以进行以下转换:

从:

result = ""
if len(s) > length:
    result = s[0:length] + "\n"
    s = s[length:]
else:
    result += s

至:

result = ""
while len(s) > length:
    result += s[0:length] + "\n"
    s = s[length:]
else:
    result += s

In Test-driven development (TDD), when using the Transformation Priority Premise paradigm, you treat loops as a generalization of conditional statements.

This approach combines well with this syntax, if you consider only simple if/else (no elif) statements:

if cond:
    # 1
else:
    # 2

generalizes to:

while cond:  # <-- generalization
    # 1
else:
    # 2

nicely.

In other languages, TDD steps from a single case to cases with collections require more refactoring.


Here is an example from 8thlight blog:

In the linked article at 8thlight blog, the Word Wrap kata is considered: adding line breaks to strings (the s variable in the snippets below) to make them fit a given width (the length variable in the snippets below). At one point the implementation looks as follows (Java):

String result = "";
if (s.length() > length) {
    result = s.substring(0, length) + "\n" + s.substring(length);
} else {
    result = s;
}
return result;

and the next test, that currently fails is:

@Test
public void WordLongerThanTwiceLengthShouldBreakTwice() throws Exception {
    assertThat(wrap("verylongword", 4), is("very\nlong\nword"));
    }

So we have code that works conditionally: when a particular condition is met, a line break is added. We want to improve the code to handle multiple line breaks. The solution presented in the article proposes to apply the (if->while) transformation, however the author makes a comment that:

While loops can’t have else clauses, so we need to eliminate the else path by doing less in the if path. Again, this is a refactoring.

which forces to do more changes to the code in the context of one failing test:

String result = "";
while (s.length() > length) {
    result += s.substring(0, length) + "\n";
    s = s.substring(length);
}
result += s;

In TDD we want to write as less code as possible to make tests pass. Thanks to Python’s syntax the following transformation is possible:

from:

result = ""
if len(s) > length:
    result = s[0:length] + "\n"
    s = s[length:]
else:
    result += s

to:

result = ""
while len(s) > length:
    result += s[0:length] + "\n"
    s = s[length:]
else:
    result += s

回答 9

else:当您遍历循环结束时,就会触发我的观察方式。

如果你break或者return或者raise你没有过去的迭代循环的结尾,你停下immeadiately,因而else:块将不会运行。如果您continue仍然循环结束,因为continue会跳到下一个迭代。它不会停止循环。

The way I see it, else: fires when you iterate past the end of the loop.

If you break or return or raise you don’t iterate past the end of loop, you stop immeadiately, and thus the else: block won’t run. If you continue you still iterate past the end of loop, since continue just skips to the next iteration. It doesn’t stop the loop.


回答 10

else子句视为循环构造的一部分;break完全脱离循环构造,从而跳过该else子句。

但实际上,我的思维导图只是它是模式C / C ++模式的“结构化”版本:

  for (...) {
    ...
    if (test) { goto done; }
    ...
  }
  ...
done:
  ...

因此,当我for...else自己遇到或编写它时,而不是直接了解它,我会在思维上将其转化为对模式的上述理解,然后确定python语法的哪些部分映射到模式的哪些部分。

(我将“结构化”用惊吓语括起来,因为区别不在于代码是结构化的还是非结构化的,而仅仅是是否有专门针对特定结构的关键字和语法)

Think of the else clause as being part of the loop construct; break breaks out of the loop construct entirely, and thus skips the else clause.

But really, my mental mapping is simply that it’s the ‘structured’ version of the pattern C/C++ pattern:

  for (...) {
    ...
    if (test) { goto done; }
    ...
  }
  ...
done:
  ...

So when I encounter for...else or write it myself, rather than understand it directly, I mentally translate it into the above understanding of the pattern and then work out which parts of the python syntax map to which parts of the pattern.

(I put ‘structured’ in scare quotes because the difference is not whether the code is structured or unstructured, but merely whether there are keywords and grammar dedicated to the particular structure)


回答 11

如果else与配对for,可能会造成混淆。我认为关键字不是else此语法的理想选择,但如果elseifcontains 配对,则break可以看到它确实有意义。else如果没有前面的if语句,则几乎没有用,我相信这就是语法设计者选择关键字的原因。

让我用人类语言来演示它。

for犯罪嫌疑if人组中的每个人都是犯罪分子 break进行调查。else报告失败。

If you pair else with for, it could be confusing. I don’t think the keyword else was a great choice for this syntax, but if you pair else with if which contains break, you can see it actually makes sense. else is barely useful if there is no preceding if statement and I believe this is why the syntax designer chose the keyword.

Let me demonstrate it in human language.

for each person in a group of suspects if anyone is the criminal break the investigation. else report failure.


回答 12

我的思考方式,关键是要考虑continue而不是的含义else

您提到的其他关键字会跳出循环(异常退出),而continue不会跳出循环,只会跳过循环内代码块的其余部分。它可以在循环终止之前发生的事实是偶然的:终止实际上是通过评估循环条件表达式以正常方式完成的。

然后,您只需要记住该else子句是在正常循环终止后执行的。

The way I think about it, the key is to consider the meaning of continue rather than else.

The other keywords you mention break out of the loop (exit abnormally) whilst continue does not, it just skips the remainder of the code block inside the loop. The fact that it can precede loop termination is incidental: the termination is actually done in the normal way by evaluation of the loop conditional expression.

Then you just need to remember that the else clause is executed after normal loop termination.


回答 13

# tested in Python 3.6.4
def buy_fruit(fruits):
    '''I translate the 'else' below into 'if no break' from for loop '''
    for fruit in fruits:
        if 'rotten' in fruit:
            print(f'do not want to buy {fruit}')
            break
    else:  #if no break
        print(f'ready to buy {fruits}')


if __name__ == '__main__':
    a_bag_of_apples = ['golden delicious', 'honeycrisp', 'rotten mcintosh']
    b_bag_of_apples = ['granny smith', 'red delicious', 'honeycrisp', 'gala', 'fuji']
    buy_fruit(a_bag_of_apples)
    buy_fruit(b_bag_of_apples)

'''
do not want to buy rotten mcintosh
ready to buy ['granny smith', 'red delicious', 'honeycrisp', 'gala', 'fuji']
'''
# tested in Python 3.6.4
def buy_fruit(fruits):
    '''I translate the 'else' below into 'if no break' from for loop '''
    for fruit in fruits:
        if 'rotten' in fruit:
            print(f'do not want to buy {fruit}')
            break
    else:  #if no break
        print(f'ready to buy {fruits}')


if __name__ == '__main__':
    a_bag_of_apples = ['golden delicious', 'honeycrisp', 'rotten mcintosh']
    b_bag_of_apples = ['granny smith', 'red delicious', 'honeycrisp', 'gala', 'fuji']
    buy_fruit(a_bag_of_apples)
    buy_fruit(b_bag_of_apples)

'''
do not want to buy rotten mcintosh
ready to buy ['granny smith', 'red delicious', 'honeycrisp', 'gala', 'fuji']
'''

异常后如何重试?

问题:异常后如何重试?

我有一个以开头的循环for i in range(0, 100)。正常情况下,它可以正常运行,但有时由于网络条件而失败。目前,我已对其进行了设置,以便在失败时,它将continue在except子句中(继续到的下一个数字i)。

我是否可以将相同的数字重新分配给i循环并再次执行失败的循环?

I have a loop starting with for i in range(0, 100). Normally it runs correctly, but sometimes it fails due to network conditions. Currently I have it set so that on failure, it will continue in the except clause (continue on to the next number for i).

Is it possible for me to reassign the same number to i and run through the failed iteration of the loop again?


回答 0

做一个while True内部的for循环,把你的try代码中,并突破从while只有当你的代码的成功循环。

for i in range(0,100):
    while True:
        try:
            # do stuff
        except SomeSpecificException:
            continue
        break

Do a while True inside your for loop, put your try code inside, and break from that while loop only when your code succeeds.

for i in range(0,100):
    while True:
        try:
            # do stuff
        except SomeSpecificException:
            continue
        break

回答 1

我更喜欢限制重试的次数,这样,如果该特定项目有问题,您最终将继续进行下一个,因此:

for i in range(100):
  for attempt in range(10):
    try:
      # do thing
    except:
      # perhaps reconnect, etc.
    else:
      break
  else:
    # we failed all the attempts - deal with the consequences.

I prefer to limit the number of retries, so that if there’s a problem with that specific item you will eventually continue onto the next one, thus:

for i in range(100):
  for attempt in range(10):
    try:
      # do thing
    except:
      # perhaps reconnect, etc.
    else:
      break
  else:
    # we failed all the attempts - deal with the consequences.

回答 2

重试包是重试的代码块失败的好办法。

例如:

@retry(wait_random_min=1000, wait_random_max=2000)
def wait_random_1_to_2_s():
    print("Randomly wait 1 to 2 seconds between retries")

The retrying package is a nice way to retry a block of code on failure.

For example:

@retry(wait_random_min=1000, wait_random_max=2000)
def wait_random_1_to_2_s():
    print("Randomly wait 1 to 2 seconds between retries")

回答 3

这是一个与其他解决方案类似的解决方案,但是如果未按规定的次数或重试次数失败,则会引发异常。

tries = 3
for i in range(tries):
    try:
        do_the_thing()
    except KeyError as e:
        if i < tries - 1: # i is zero indexed
            continue
        else:
            raise
    break

Here is a solution similar to others, but it will raise the exception if it doesn’t succeed in the prescribed number or retries.

tries = 3
for i in range(tries):
    try:
        do_the_thing()
    except KeyError as e:
        if i < tries - 1: # i is zero indexed
            continue
        else:
            raise
    break

回答 4

没有使用那些难看的while循环的更“实用”的方法:

def tryAgain(retries=0):
    if retries > 10: return
    try:
        # Do stuff
    except:
        retries+=1
        tryAgain(retries)

tryAgain()

The more “functional” approach without using those ugly while loops:

def tryAgain(retries=0):
    if retries > 10: return
    try:
        # Do stuff
    except:
        retries+=1
        tryAgain(retries)

tryAgain()

回答 5

最清晰的方法是显式设置i。例如:

i = 0
while i < 100:
    i += 1
    try:
        # do stuff

    except MyException:
        continue

The clearest way would be to explicitly set i. For example:

i = 0
while i < 100:
    i += 1
    try:
        # do stuff

    except MyException:
        continue

回答 6

带有超时的通用解决方案:

import time

def onerror_retry(exception, callback, timeout=2, timedelta=.1):
    end_time = time.time() + timeout
    while True:
        try:
            yield callback()
            break
        except exception:
            if time.time() > end_time:
                raise
            elif timedelta > 0:
                time.sleep(timedelta)

用法:

for retry in onerror_retry(SomeSpecificException, do_stuff):
    retry()

A generic solution with a timeout:

import time

def onerror_retry(exception, callback, timeout=2, timedelta=.1):
    end_time = time.time() + timeout
    while True:
        try:
            yield callback()
            break
        except exception:
            if time.time() > end_time:
                raise
            elif timedelta > 0:
                time.sleep(timedelta)

Usage:

for retry in onerror_retry(SomeSpecificException, do_stuff):
    retry()

回答 7

for _ in range(5):
    try:
        # replace this with something that may fail
        raise ValueError("foo")

    # replace Exception with a more specific exception
    except Exception as e:
        err = e
        continue

    # no exception, continue remainder of code
    else:
        break

# did not break the for loop, therefore all attempts
# raised an exception
else:
    raise err

我的版本与上述几种类似,但是没有使用单独的while循环,并且如果所有重试均失败,则会重新引发最新的异常。可以err = None在顶部显式设置,但不是严格必需的,因为只有在else出现错误并因此err被设置时才执行最后一个块。

for _ in range(5):
    try:
        # replace this with something that may fail
        raise ValueError("foo")

    # replace Exception with a more specific exception
    except Exception as e:
        err = e
        continue

    # no exception, continue remainder of code
    else:
        break

# did not break the for loop, therefore all attempts
# raised an exception
else:
    raise err

My version is similar to several of the above, but doesn’t use a separate while loop, and re-raises the latest exception if all retries fail. Could explicitly set err = None at the top, but not strictly necessary as it should only execute the final else block if there was an error and therefore err is set.


回答 8

Python Decorator库中有类似的东西

请记住,它不会测试异常,而是返回值。重试,直到修饰的函数返回True。

稍加修改的版本应该可以解决问题。

There is something similar in the Python Decorator Library.

Please bear in mind that it does not test for exceptions, but the return value. It retries until the decorated function returns True.

A slightly modified version should do the trick.


回答 9

使用递归

for i in range(100):
    def do():
        try:
            ## Network related scripts
        except SpecificException as ex:
            do()
    do() ## invoke do() whenever required inside this loop

Using recursion

for i in range(100):
    def do():
        try:
            ## Network related scripts
        except SpecificException as ex:
            do()
    do() ## invoke do() whenever required inside this loop

回答 10

使用while和计数器:

count = 1
while count <= 3:  # try 3 times
    try:
        # do_the_logic()
        break
    except SomeSpecificException as e:
        # If trying 3rd time and still error?? 
        # Just throw the error- we don't have anything to hide :)
        if count == 3:
            raise
        count += 1

Using while and a counter:

count = 1
while count <= 3:  # try 3 times
    try:
        # do_the_logic()
        break
    except SomeSpecificException as e:
        # If trying 3rd time and still error?? 
        # Just throw the error- we don't have anything to hide :)
        if count == 3:
            raise
        count += 1

回答 11

您可以使用Python重试包。 重试

它是用Python编写的,以简化将重试行为添加到几乎所有内容的任务。

You can use Python retrying package. Retrying

It is written in Python to simplify the task of adding retry behavior to just about anything.


回答 12

retryingtenacitybackoff(2020更新)的替代方案

重试库是以前的路要走,但可悲的是它有一些缺陷,自2016年其他选择似乎还没有得到任何更新补偿坚韧。在撰写本文时,坚韧程度更高的GItHub星星(2.3k和1.2k)并已更新,因此我选择使用它。这是一个例子:

from functools import partial
import random # producing random errors for this example

from tenacity import retry, stop_after_delay, wait_fixed, retry_if_exception_type

# Custom error type for this example
class CommunicationError(Exception):
    pass

# Define shorthand decorator for the used settings.
retry_on_communication_error = partial(
    retry,
    stop=stop_after_delay(10),  # max. 10 seconds wait.
    wait=wait_fixed(0.4),  # wait 400ms 
    retry=retry_if_exception_type(CommunicationError),
)()


@retry_on_communication_error
def do_something_unreliable(i):
    if random.randint(1, 5) == 3:
        print('Run#', i, 'Error occured. Retrying.')
        raise CommunicationError()

上面的代码输出类似:

Run# 3 Error occured. Retrying.
Run# 5 Error occured. Retrying.
Run# 6 Error occured. Retrying.
Run# 6 Error occured. Retrying.
Run# 10 Error occured. Retrying.
.
.
.

tenacity.retry韧度GitHub页面上列出了的更多设置。

Alternatives to retrying: tenacity and backoff (2020 update)

The retrying library was previously the way to go, but sadly it has some bugs and it hasn’t got any updates since 2016. Other alternatives seem to be backoff and tenacity. During the time of writing this, the tenacity had more GItHub stars (2.3k vs 1.2k) and was updated more recently, hence I chose to use it. Here is an example:

from functools import partial
import random # producing random errors for this example

from tenacity import retry, stop_after_delay, wait_fixed, retry_if_exception_type

# Custom error type for this example
class CommunicationError(Exception):
    pass

# Define shorthand decorator for the used settings.
retry_on_communication_error = partial(
    retry,
    stop=stop_after_delay(10),  # max. 10 seconds wait.
    wait=wait_fixed(0.4),  # wait 400ms 
    retry=retry_if_exception_type(CommunicationError),
)()


@retry_on_communication_error
def do_something_unreliable(i):
    if random.randint(1, 5) == 3:
        print('Run#', i, 'Error occured. Retrying.')
        raise CommunicationError()

for i in range(100):
    do_something_unreliable(i)

The above code outputs something like:

Run# 3 Error occured. Retrying.
Run# 5 Error occured. Retrying.
Run# 6 Error occured. Retrying.
Run# 6 Error occured. Retrying.
Run# 10 Error occured. Retrying.
.
.
.

More settings for the tenacity.retry are listed on the tenacity GitHub page.


回答 13

如果您想要一个没有嵌套循环且无需break成功调用的解决方案,则可retriable以为任何可迭代的对象开发一个快速包装。这是我经常遇到的网络问题的示例-保存的身份验证过期。它的使用将如下所示:

client = get_client()
smart_loop = retriable(list_of_values):

for value in smart_loop:
    try:
        client.do_something_with(value)
    except ClientAuthExpired:
        client = get_client()
        smart_loop.retry()
        continue
    except NetworkTimeout:
        smart_loop.retry()
        continue

If you want a solution without nested loops and invoking break on success you could developer a quick wrap retriable for any iterable. Here’s an example of a networking issue that I run into often – saved authentication expires. The use of it would read like this:

client = get_client()
smart_loop = retriable(list_of_values):

for value in smart_loop:
    try:
        client.do_something_with(value)
    except ClientAuthExpired:
        client = get_client()
        smart_loop.retry()
        continue
    except NetworkTimeout:
        smart_loop.retry()
        continue

回答 14

我在代码中使用以下代码,

   for i in range(0, 10):
    try:
        #things I need to do
    except ValueError:
        print("Try #{} failed with ValueError: Sleeping for 2 secs before next try:".format(i))
        time.sleep(2)
        continue
    break

I use following in my codes,

   for i in range(0, 10):
    try:
        #things I need to do
    except ValueError:
        print("Try #{} failed with ValueError: Sleeping for 2 secs before next try:".format(i))
        time.sleep(2)
        continue
    break

回答 15

attempts = 3
while attempts:
  try:
     ...
     ...
     <status ok>
     break
  except:
    attempts -=1
else: # executed only break was not  raised
   <status failed>

attempts = 3
while attempts:
  try:
     ...
     ...
     <status ok>
     break
  except:
    attempts -=1
else: # executed only break was not  raised
   <status failed>

回答 16

这是我对这个问题的看法。以下retry功能支持以下功能:

  • 成功时返回被调用函数的值
  • 如果尝试已用尽,则引发被调用函数的异常
  • 尝试次数的限制(0表示无限制)
  • 尝试之间等待(线性或指数)
  • 仅当异常是特定异常类型的实例时才重试。
  • 可选的尝试记录
import time

def retry(func, ex_type=Exception, limit=0, wait_ms=100, wait_increase_ratio=2, logger=None):
    attempt = 1
    while True:
        try:
            return func()
        except Exception as ex:
            if not isinstance(ex, ex_type):
                raise ex
            if 0 < limit <= attempt:
                if logger:
                    logger.warning("no more attempts")
                raise ex

            if logger:
                logger.error("failed execution attempt #%d", attempt, exc_info=ex)

            attempt += 1
            if logger:
                logger.info("waiting %d ms before attempt #%d", wait_ms, attempt)
            time.sleep(wait_ms / 1000)
            wait_ms *= wait_increase_ratio

用法:

def fail_randomly():
    y = random.randint(0, 10)
    if y < 10:
        y = 0
    return x / y


logger = logging.getLogger()
logger.setLevel(logging.INFO)
logger.addHandler(logging.StreamHandler(stream=sys.stdout))

logger.info("starting")
result = retry.retry(fail_randomly, ex_type=ZeroDivisionError, limit=20, logger=logger)
logger.info("result is: %s", result)

有关更多信息,请参见我的帖子

Here is my take on this issue. The following retry function supports the following features:

  • Returns the value of the invoked function when it succeeds
  • Raises the exception of the invoked function if attempts exhausted
  • Limit for the number of attempts (0 for unlimited)
  • Wait (linear or exponential) between attempts
  • Retry only if the exception is an instance of a specific exception type.
  • Optional logging of attempts
import time

def retry(func, ex_type=Exception, limit=0, wait_ms=100, wait_increase_ratio=2, logger=None):
    attempt = 1
    while True:
        try:
            return func()
        except Exception as ex:
            if not isinstance(ex, ex_type):
                raise ex
            if 0 < limit <= attempt:
                if logger:
                    logger.warning("no more attempts")
                raise ex

            if logger:
                logger.error("failed execution attempt #%d", attempt, exc_info=ex)

            attempt += 1
            if logger:
                logger.info("waiting %d ms before attempt #%d", wait_ms, attempt)
            time.sleep(wait_ms / 1000)
            wait_ms *= wait_increase_ratio

Usage:

def fail_randomly():
    y = random.randint(0, 10)
    if y < 10:
        y = 0
    return x / y


logger = logging.getLogger()
logger.setLevel(logging.INFO)
logger.addHandler(logging.StreamHandler(stream=sys.stdout))

logger.info("starting")
result = retry.retry(fail_randomly, ex_type=ZeroDivisionError, limit=20, logger=logger)
logger.info("result is: %s", result)

See my post for more info.


回答 17

这是我的解决方法:

j = 19
def calc(y):
    global j
    try:
        j = j + 8 - y
        x = int(y/j)   # this will eventually raise DIV/0 when j=0
        print("i = ", str(y), " j = ", str(j), " x = ", str(x))
    except:
        j = j + 1   # when the exception happens, increment "j" and retry
        calc(y)
for i in range(50):
    calc(i)

Here’s my idea on how to fix this:

j = 19
def calc(y):
    global j
    try:
        j = j + 8 - y
        x = int(y/j)   # this will eventually raise DIV/0 when j=0
        print("i = ", str(y), " j = ", str(j), " x = ", str(x))
    except:
        j = j + 1   # when the exception happens, increment "j" and retry
        calc(y)
for i in range(50):
    calc(i)

回答 18

我最近与我的python合作解决了这个问题,很高兴与stackoverflow访问者分享它,如果需要的话请提供反馈。

print("\nmonthly salary per day and year converter".title())
print('==' * 25)


def income_counter(day, salary, month):
    global result2, result, is_ready, result3
    result = salary / month
    result2 = result * day
    result3 = salary * 12
    is_ready = True
    return result, result2, result3, is_ready


i = 0
for i in range(5):
    try:
        month = int(input("\ntotal days of the current month: "))
        salary = int(input("total salary per month: "))
        day = int(input("Total Days to calculate> "))
        income_counter(day=day, salary=salary, month=month)
        if is_ready:
            print(f'Your Salary per one day is: {round(result)}')
            print(f'your income in {day} days will be: {round(result2)}')
            print(f'your total income in one year will be: {round(result3)}')
            break
        else:
            continue
    except ZeroDivisionError:
        is_ready = False
        i += 1
        print("a month does'nt have 0 days, please try again")
        print(f'total chances left: {5 - i}')
    except ValueError:
        is_ready = False
        i += 1
        print("Invalid value, please type a number")
        print(f'total chances left: {5 - i}')

i recently worked with my python on a solution to this problem and i am happy to share it with stackoverflow visitors please give feedback if it is needed.

print("\nmonthly salary per day and year converter".title())
print('==' * 25)


def income_counter(day, salary, month):
    global result2, result, is_ready, result3
    result = salary / month
    result2 = result * day
    result3 = salary * 12
    is_ready = True
    return result, result2, result3, is_ready


i = 0
for i in range(5):
    try:
        month = int(input("\ntotal days of the current month: "))
        salary = int(input("total salary per month: "))
        day = int(input("Total Days to calculate> "))
        income_counter(day=day, salary=salary, month=month)
        if is_ready:
            print(f'Your Salary per one day is: {round(result)}')
            print(f'your income in {day} days will be: {round(result2)}')
            print(f'your total income in one year will be: {round(result3)}')
            break
        else:
            continue
    except ZeroDivisionError:
        is_ready = False
        i += 1
        print("a month does'nt have 0 days, please try again")
        print(f'total chances left: {5 - i}')
    except ValueError:
        is_ready = False
        i += 1
        print("Invalid value, please type a number")
        print(f'total chances left: {5 - i}')

回答 19

仅在try子句成功时增加循环变量

increment your loop variable only when the try clause succeeds


结合FOR循环和IF语句的Python方法

问题:结合FOR循环和IF语句的Python方法

我知道如何在单独的行上同时使用for循环和if语句,例如:

>>> a = [2,3,4,5,6,7,8,9,0]
... xyz = [0,12,4,6,242,7,9]
... for x in xyz:
...     if x in a:
...         print(x)
0,4,6,7,9

而且我知道当语句很简单时,我可以使用列表推导来组合这些内容,例如:

print([x for x in xyz if x in a])

但是,我找不到任何地方(复制和学习)的好例子,展示了一组复杂的命令(不仅是“ print x”),这些命令是在for循环和某些if语句组合后发生的。我期望的是:

for x in xyz if x not in a:
    print(x...)

难道这不是python应该工作的方式吗?

I know how to use both for loops and if statements on separate lines, such as:

>>> a = [2,3,4,5,6,7,8,9,0]
... xyz = [0,12,4,6,242,7,9]
... for x in xyz:
...     if x in a:
...         print(x)
0,4,6,7,9

And I know I can use a list comprehension to combine these when the statements are simple, such as:

print([x for x in xyz if x in a])

But what I can’t find is a good example anywhere (to copy and learn from) demonstrating a complex set of commands (not just “print x”) that occur following a combination of a for loop and some if statements. Something that I would expect looks like:

for x in xyz if x not in a:
    print(x...)

Is this just not the way python is supposed to work?


回答 0

您可以使用以下生成器表达式

gen = (x for x in xyz if x not in a)

for x in gen:
    print x

You can use generator expressions like this:

gen = (x for x in xyz if x not in a)

for x in gen:
    print x

回答 1

按照《 Python的禅宗》(如果您想知道代码是否是“ Pythonic”,那就去吧):

  • 美丽胜于丑陋。
  • 显式胜于隐式。
  • 简单胜于复杂。
  • 扁平比嵌套更好。
  • 可读性很重要。

获得两个s 的Pythonic方法是:sorted intersectionset

>>> sorted(set(a).intersection(xyz))
[0, 4, 6, 7, 9]

或那些xyz不在中的元素a

>>> sorted(set(xyz).difference(a))
[12, 242]

但是对于更复杂的循环,您可能希望通过迭代名称良好的生成器表达式和/或调出名称良好的函数来使其扁平化。试图将所有内容都放在一条线上很少是“ Pythonic”的。


在对您的问题和已接受的答案进行其他评论后进行更新

我不确定您要使用的是什么enumerate,但是如果a是字典,则可能要使用这些键,如下所示:

>>> a = {
...     2: 'Turtle Doves',
...     3: 'French Hens',
...     4: 'Colly Birds',
...     5: 'Gold Rings',
...     6: 'Geese-a-Laying',
...     7: 'Swans-a-Swimming',
...     8: 'Maids-a-Milking',
...     9: 'Ladies Dancing',
...     0: 'Camel Books',
... }
>>>
>>> xyz = [0, 12, 4, 6, 242, 7, 9]
>>>
>>> known_things = sorted(set(a.iterkeys()).intersection(xyz))
>>> unknown_things = sorted(set(xyz).difference(a.iterkeys()))
>>>
>>> for thing in known_things:
...     print 'I know about', a[thing]
...
I know about Camel Books
I know about Colly Birds
I know about Geese-a-Laying
I know about Swans-a-Swimming
I know about Ladies Dancing
>>> print '...but...'
...but...
>>>
>>> for thing in unknown_things:
...     print "I don't know what happened on the {0}th day of Christmas".format(thing)
...
I don't know what happened on the 12th day of Christmas
I don't know what happened on the 242th day of Christmas

As per The Zen of Python (if you are wondering whether your code is “Pythonic”, that’s the place to go):

  • Beautiful is better than ugly.
  • Explicit is better than implicit.
  • Simple is better than complex.
  • Flat is better than nested.
  • Readability counts.

The Pythonic way of getting the sorted intersection of two sets is:

>>> sorted(set(a).intersection(xyz))
[0, 4, 6, 7, 9]

Or those elements that are xyz but not in a:

>>> sorted(set(xyz).difference(a))
[12, 242]

But for a more complicated loop you may want to flatten it by iterating over a well-named generator expression and/or calling out to a well-named function. Trying to fit everything on one line is rarely “Pythonic”.


Update following additional comments on your question and the accepted answer

I’m not sure what you are trying to do with enumerate, but if a is a dictionary, you probably want to use the keys, like this:

>>> a = {
...     2: 'Turtle Doves',
...     3: 'French Hens',
...     4: 'Colly Birds',
...     5: 'Gold Rings',
...     6: 'Geese-a-Laying',
...     7: 'Swans-a-Swimming',
...     8: 'Maids-a-Milking',
...     9: 'Ladies Dancing',
...     0: 'Camel Books',
... }
>>>
>>> xyz = [0, 12, 4, 6, 242, 7, 9]
>>>
>>> known_things = sorted(set(a.iterkeys()).intersection(xyz))
>>> unknown_things = sorted(set(xyz).difference(a.iterkeys()))
>>>
>>> for thing in known_things:
...     print 'I know about', a[thing]
...
I know about Camel Books
I know about Colly Birds
I know about Geese-a-Laying
I know about Swans-a-Swimming
I know about Ladies Dancing
>>> print '...but...'
...but...
>>>
>>> for thing in unknown_things:
...     print "I don't know what happened on the {0}th day of Christmas".format(thing)
...
I don't know what happened on the 12th day of Christmas
I don't know what happened on the 242th day of Christmas

回答 2

我个人认为这是最漂亮的版本:

a = [2,3,4,5,6,7,8,9,0]
xyz = [0,12,4,6,242,7,9]
for x in filter(lambda w: w in a, xyz):
  print x

编辑

如果您非常想避免使用lambda,则可以使用部分函数应用程序并使用运算符模块(该模块提供大多数运算符的功能)。

https://docs.python.org/2/library/operator.html#module-operator

from operator import contains
from functools import partial
print(list(filter(partial(contains, a), xyz)))

I personally think this is the prettiest version:

a = [2,3,4,5,6,7,8,9,0]
xyz = [0,12,4,6,242,7,9]
for x in filter(lambda w: w in a, xyz):
  print x

Edit

if you are very keen on avoiding to use lambda you can use partial function application and use the operator module (that provides functions of most operators).

https://docs.python.org/2/library/operator.html#module-operator

from operator import contains
from functools import partial
print(list(filter(partial(contains, a), xyz)))

回答 3

以下是接受的答案的一种简化/一种解释:

a = [2,3,4,5,6,7,8,9,0]
xyz = [0,12,4,6,242,7,9]

for x in (x for x in xyz if x not in a):
    print(x)

12
242

请注意,generator保持内联。已在python2.7python3.6 (请注意print;)中的括号中对此进行了测试)

The following is a simplification/one liner from the accepted answer:

a = [2,3,4,5,6,7,8,9,0]
xyz = [0,12,4,6,242,7,9]

for x in (x for x in xyz if x not in a):
    print(x)

12
242

Notice that the generator was kept inline. This was tested on python2.7 and python3.6 (notice the parens in the print ;) )


回答 4

我可能会使用:

for x in xyz: 
    if x not in a:
        print x...

I would probably use:

for x in xyz: 
    if x not in a:
        print x...

回答 5

a = [2,3,4,5,6,7,8,9,0]
xyz = [0,12,4,6,242,7,9]  
set(a) & set(xyz)  
set([0, 9, 4, 6, 7])
a = [2,3,4,5,6,7,8,9,0]
xyz = [0,12,4,6,242,7,9]  
set(a) & set(xyz)  
set([0, 9, 4, 6, 7])

回答 6

如果生成器表达式变得过于复杂或复杂,您也可以使用生成器:

def gen():
    for x in xyz:
        if x in a:
            yield x

for x in gen():
    print x

You can use generators too, if generator expressions become too involved or complex:

def gen():
    for x in xyz:
        if x in a:
            yield x

for x in gen():
    print x

回答 7

使用intersectionintersection_update

  • 交叉点

    a = [2,3,4,5,6,7,8,9,0]
    xyz = [0,12,4,6,242,7,9]
    ans = sorted(set(a).intersection(set(xyz)))
  • junction_update

    a = [2,3,4,5,6,7,8,9,0]
    xyz = [0,12,4,6,242,7,9]
    b = set(a)
    b.intersection_update(xyz)

    b是你的答案

Use intersection or intersection_update

  • intersection :

    a = [2,3,4,5,6,7,8,9,0]
    xyz = [0,12,4,6,242,7,9]
    ans = sorted(set(a).intersection(set(xyz)))
    
  • intersection_update:

    a = [2,3,4,5,6,7,8,9,0]
    xyz = [0,12,4,6,242,7,9]
    b = set(a)
    b.intersection_update(xyz)
    

    then b is your answer


回答 8

我喜欢Alex的答案,因为过滤器恰好是应用于列表的if,所以如果您想探索给定条件的列表子集,这似乎是最自然的方法

mylist = [1,2,3,4,5]
another_list = [2,3,4]

wanted = lambda x:x in another_list

for x in filter(wanted, mylist):
    print(x)

此方法对于分离关注点很有用,如果条件函数发生变化,则唯一需要摆弄的代码就是函数本身

mylist = [1,2,3,4,5]

wanted = lambda x:(x**0.5) > 10**0.3

for x in filter(wanted, mylist):
    print(x)

当您不希望列表的成员时,使用generator方法似乎更好,但是可以对所述成员进行修改,这似乎更适合生成器

mylist = [1,2,3,4,5]

wanted = lambda x:(x**0.5) > 10**0.3

generator = (x**0.5 for x in mylist if wanted(x))

for x in generator:
    print(x)

此外,过滤器可与生成器一起使用,尽管在这种情况下效率不高

mylist = [1,2,3,4,5]

wanted = lambda x:(x**0.5) > 10**0.3

generator = (x**0.9 for x in mylist)

for x in filter(wanted, generator):
    print(x)

但是,当然,这样写仍然会很好:

mylist = [1,2,3,4,5]

wanted = lambda x:(x**0.5) > 10**0.3

# for x in filter(wanted, mylist):
for x in mylist if wanted(x):
    print(x)

I liked Alex’s answer, because a filter is exactly an if applied to a list, so if you want to explore a subset of a list given a condition, this seems to be the most natural way

mylist = [1,2,3,4,5]
another_list = [2,3,4]

wanted = lambda x:x in another_list

for x in filter(wanted, mylist):
    print(x)

this method is useful for the separation of concerns, if the condition function changes, the only code to fiddle with is the function itself

mylist = [1,2,3,4,5]

wanted = lambda x:(x**0.5) > 10**0.3

for x in filter(wanted, mylist):
    print(x)

The generator method seems better when you don’t want members of the list, but a modification of said members, which seems more fit to a generator

mylist = [1,2,3,4,5]

wanted = lambda x:(x**0.5) > 10**0.3

generator = (x**0.5 for x in mylist if wanted(x))

for x in generator:
    print(x)

Also, filters work with generators, although in this case it isn’t efficient

mylist = [1,2,3,4,5]

wanted = lambda x:(x**0.5) > 10**0.3

generator = (x**0.9 for x in mylist)

for x in filter(wanted, generator):
    print(x)

But of course, it would still be nice to write like this:

mylist = [1,2,3,4,5]

wanted = lambda x:(x**0.5) > 10**0.3

# for x in filter(wanted, mylist):
for x in mylist if wanted(x):
    print(x)

回答 9

查找列表a和b的唯一公共元素的简单方法:

a = [1,2,3]
b = [3,6,2]
for both in set(a) & set(b):
    print(both)

A simple way to find unique common elements of lists a and b:

a = [1,2,3]
b = [3,6,2]
for both in set(a) & set(b):
    print(both)

如何避免“ RuntimeError:字典在迭代过程中更改大小”错误?

问题:如何避免“ RuntimeError:字典在迭代过程中更改大小”错误?

我检查了所有其他问题,并发现了相同的错误,但没有找到有帮助的解决方案= /

我有一个列表字典:

d = {'a': [1], 'b': [1, 2], 'c': [], 'd':[]}

其中某些值为空。在创建这些列表的最后,我想在返回字典之前删除这些空列表。当前,我正在尝试执行以下操作:

for i in d:
    if not d[i]:
        d.pop(i)

但是,这给了我运行时错误。我知道您无法在字典中进行迭代时添加/删除字典中的元素…那么解决这个问题的方法是什么?

I have checked all of the other questions with the same error yet found no helpful solution =/

I have a dictionary of lists:

d = {'a': [1], 'b': [1, 2], 'c': [], 'd':[]}

in which some of the values are empty. At the end of creating these lists, I want to remove these empty lists before returning my dictionary. Current I am attempting to do this as follows:

for i in d:
    if not d[i]:
        d.pop(i)

however, this is giving me the runtime error. I am aware that you cannot add/remove elements in a dictionary while iterating through it…what would be a way around this then?


回答 0

在Python 2.x中,调用keys会生成密钥的副本,您可以在修改时进行迭代dict

for i in d.keys():

请注意,这在Python 3.x中不起作用,因为它keys返回一个迭代器而不是列表。

另一种方法是用于list强制复制密钥。这也可以在Python 3.x中使用:

for i in list(d):

In Python 2.x calling keys makes a copy of the key that you can iterate over while modifying the dict:

for i in d.keys():

Note that this doesn’t work in Python 3.x because keys returns an iterator instead of a list.

Another way is to use list to force a copy of the keys to be made. This one also works in Python 3.x:

for i in list(d):

回答 1

只需使用字典理解将相关项目复制到新字典中

>>> d
{'a': [1], 'c': [], 'b': [1, 2], 'd': []}
>>> d = { k : v for k,v in d.iteritems() if v}
>>> d
{'a': [1], 'b': [1, 2]}

为此,在Python 3

>>> d
{'a': [1], 'c': [], 'b': [1, 2], 'd': []}
>>> d = { k : v for k,v in d.items() if v}
>>> d
{'a': [1], 'b': [1, 2]}

Just use dictionary comprehension to copy the relevant items into a new dict

>>> d
{'a': [1], 'c': [], 'b': [1, 2], 'd': []}
>>> d = { k : v for k,v in d.iteritems() if v}
>>> d
{'a': [1], 'b': [1, 2]}

For this in Python 3

>>> d
{'a': [1], 'c': [], 'b': [1, 2], 'd': []}
>>> d = { k : v for k,v in d.items() if v}
>>> d
{'a': [1], 'b': [1, 2]}

回答 2

您只需要使用“复制”:

这样,您就可以遍历原始词典字段,并且可以随时更改所需的字典(d dict)。它适用于每个python版本,因此更加清晰。

In [1]: d = {'a': [1], 'b': [1, 2], 'c': [], 'd':[]}

In [2]: for i in d.copy():
   ...:     if not d[i]:
   ...:         d.pop(i)
   ...:         

In [3]: d
Out[3]: {'a': [1], 'b': [1, 2]}

You only need to use “copy”:

On that’s way you iterate over the original dictionary fields and on the fly can change the desired dict (d dict). It’s work on each python version, so it’s more clear.

In [1]: d = {'a': [1], 'b': [1, 2], 'c': [], 'd':[]}

In [2]: for i in d.copy():
   ...:     if not d[i]:
   ...:         d.pop(i)
   ...:         

In [3]: d
Out[3]: {'a': [1], 'b': [1, 2]}

回答 3

我会尽量避免一开始就插入空列表,但是通常会使用:

d = {k: v for k,v in d.iteritems() if v} # re-bind to non-empty

如果2.7之前:

d = dict( (k, v) for k,v in d.iteritems() if v )

要不就:

empty_key_vals = list(k for k in k,v in d.iteritems() if v)
for k in empty_key_vals:
    del[k]

I would try to avoid inserting empty lists in the first place, but, would generally use:

d = {k: v for k,v in d.iteritems() if v} # re-bind to non-empty

If prior to 2.7:

d = dict( (k, v) for k,v in d.iteritems() if v )

or just:

empty_key_vals = list(k for k in k,v in d.iteritems() if v)
for k in empty_key_vals:
    del[k]

回答 4

对于Python 3:

{k:v for k,v in d.items() if v}

For Python 3:

{k:v for k,v in d.items() if v}

回答 5

在for循环中更改字典时,无法迭代字典。进行列表投射并遍历该列表,它对我有用。

    for key in list(d):
        if not d[key]: 
            d.pop(key)

You cannot iterate through a dictionary while its changing during for loop. Make a casting to list and iterate over that list, it works for me.

    for key in list(d):
        if not d[key]: 
            d.pop(key)

回答 6

在这种情况下,我喜欢制作一个深层副本并在修改原始字典的同时循环遍历该副本。

如果查找字段在列表中,则可以枚举列表的for循环,然后将位置指定为索引以访问原始字典中的字段。

For situations like this, i like to make a deep copy and loop through that copy while modifying the original dict.

If the lookup field is within a list, you can enumerate in the for loop of the list and then specify the position as index to access the field in the original dict.


回答 7

这为我工作:

dict = {1: 'a', 2: '', 3: 'b', 4: '', 5: '', 6: 'c'}
for key, value in list(dict.items()):
    if (value == ''):
        del dict[key]
print(dict)
# dict = {1: 'a', 3: 'b', 6: 'c'}  

将字典项目转换为列表会创建其项目列表,因此您可以对其进行迭代并避免使用RuntimeError

This worked for me:

dict = {1: 'a', 2: '', 3: 'b', 4: '', 5: '', 6: 'c'}
for key, value in list(dict.items()):
    if (value == ''):
        del dict[key]
print(dict)
# dict = {1: 'a', 3: 'b', 6: 'c'}  

Casting the dictionary items to list creates a list of its items, so you can iterate over it and avoid the RuntimeError.


回答 8

dictc = {“ stName”:“ asas”} keys = dictc.keys()用于键入列表(键):dictc [key.upper()] =“新值” print(str(dictc))

dictc={“stName”:”asas”} keys=dictc.keys() for key in list(keys): dictc[key.upper()] =’New value’ print(str(dictc))


回答 9

运行时错误的原因是,您无法在迭代期间更改数据结构时对其进行迭代。

实现所需内容的一种方法是,使用列表附加要删除的键,然后在字典中遍历列表时在字典上使用pop函数删除标识的键。

d = {'a': [1], 'b': [1, 2], 'c': [], 'd':[]}
pop_list = []

for i in d:
        if not d[i]:
                pop_list.append(i)

for x in pop_list:
        d.pop(x)
print (d)

The reason for the runtime error is that you cannot iterate through a data structure while its structure is changing during iteration.

One way to achieve what you are looking for is to use list to append the keys you want to remove and then use pop function on dictionary to remove the identified key while iterating through the list.

d = {'a': [1], 'b': [1, 2], 'c': [], 'd':[]}
pop_list = []

for i in d:
        if not d[i]:
                pop_list.append(i)

for x in pop_list:
        d.pop(x)
print (d)

回答 10

Python 3不允许在迭代(使用上面的循环)字典时删除。有多种选择可做。一种简单的方法是更改​​以下行

for i in x.keys():

for i in list(x)

Python 3 does not allow deletion while iterating (using for loop above) dictionary. There are various alternatives to do; one simple way is the to change following line

for i in x.keys():

With

for i in list(x)

使用Python中的索引向后循环?

问题:使用Python中的索引向后循环?

我正在尝试从100循环到0。如何在Python中执行此操作?

for i in range (100,0) 不起作用。

I am trying to loop from 100 to 0. How do I do this in Python?

for i in range (100,0) doesn’t work.


回答 0

试试看range(100,-1,-1),第三个参数是要使用的增量(在此处记录)。

此处记录 “范围”选项,开始,停止,步骤)

Try range(100,-1,-1), the 3rd argument being the increment to use (documented here).

(“range” options, start, stop, step are documented here)


回答 1

我认为这是最易读的:

for i in reversed(xrange(101)):
    print i,

In my opinion, this is the most readable:

for i in reversed(xrange(101)):
    print i,

回答 2

for i in range(100, -1, -1)

和一些稍长(且较慢)的解决方案:

for i in reversed(range(101))

for i in range(101)[::-1]
for i in range(100, -1, -1)

and some slightly longer (and slower) solution:

for i in reversed(range(101))

for i in range(101)[::-1]

回答 3

通常在Python中,您可以使用负索引从背面开始:

numbers = [10, 20, 30, 40, 50]
for i in xrange(len(numbers)):
    print numbers[-i - 1]

结果:

50
40
30
20
10

Generally in Python, you can use negative indices to start from the back:

numbers = [10, 20, 30, 40, 50]
for i in xrange(len(numbers)):
    print numbers[-i - 1]

Result:

50
40
30
20
10

回答 4

为什么您的代码不起作用

您的代码for i in range (100, 0)很好,除了

step默认情况下,第三个参数()是+1。因此,必须向range()指定第三个参数才能-1向后退一步。

for i in range(100, -1, -1):
    print(i)

注意:这在输出中包括100&0。

有多种方法。

更好的方法

对于pythonic方式,请检查PEP 0322

这是Python3 pythonic示例,可从100打印到0(包括100和0)。

for i in reversed(range(101)):
    print(i)

Why your code didn’t work

You code for i in range (100, 0) is fine, except

the third parameter (step) is by default +1. So you have to specify 3rd parameter to range() as -1 to step backwards.

for i in range(100, -1, -1):
    print(i)

NOTE: This includes 100 & 0 in the output.

There are multiple ways.

Better Way

For pythonic way, check PEP 0322.

This is Python3 pythonic example to print from 100 to 0 (including 100 & 0).

for i in reversed(range(101)):
    print(i)

回答 5

另一个解决方案:

z = 10
for x in range (z):
   y = z-x
   print y

结果:

10
9
8
7
6
5
4
3
2
1

提示:如果您使用此方法对列表中的索引进行计数,则您希望从’y’值开始为-1,因为列表索引将从0开始。

Another solution:

z = 10
for x in range (z):
   y = z-x
   print y

Result:

10
9
8
7
6
5
4
3
2
1

Tip: If you are using this method to count back indices in a list, you will want to -1 from the ‘y’ value, as your list indices will begin at 0.


回答 6

解决您的问题的简单答案可能是这样的:

for i in range(100):
    k = 100 - i
    print(k)

The simple answer to solve your problem could be like this:

for i in range(100):
    k = 100 - i
    print(k)

回答 7

for var in range(10,-1,-1) 作品

for var in range(10,-1,-1) works


回答 8

简短而甜美。这是我参加codeAcademy类时的解决方案。以rev顺序打印字符串。

def reverse(text):
    string = ""
    for i in range(len(text)-1,-1,-1):
        string += text[i]
    return string    

Short and sweet. This was my solution when doing codeAcademy course. Prints a string in rev order.

def reverse(text):
    string = ""
    for i in range(len(text)-1,-1,-1):
        string += text[i]
    return string    

回答 9

在您的情况下100 - i,您始终可以增加范围并从变量中减去i in range( 0, 101 )

for i in range( 0, 101 ):
    print 100 - i

You can always do increasing range and subtract from a variable in your case 100 - i where i in range( 0, 101 ).

for i in range( 0, 101 ):
    print 100 - i

回答 10

我在一种代码学院练习中尝试过此操作(在不使用reversed或:: -1的情况下反转字符串中的字符)

def reverse(text):
    chars= []
    l = len(text)
    last = l-1
    for i in range (l):
        chars.append(text[last])
        last-=1

    result= ""   
    for c in chars:
        result += c
    return result
print reverse('hola')

I tried this in one of the codeacademy exercises (reversing chars in a string without using reversed nor :: -1)

def reverse(text):
    chars= []
    l = len(text)
    last = l-1
    for i in range (l):
        chars.append(text[last])
        last-=1

    result= ""   
    for c in chars:
        result += c
    return result
print reverse('hola')

回答 11

我想同时向后遍历两个列表,所以我需要负索引。这是我的解决方案:

a= [1,3,4,5,2]
for i in range(-1, -len(a), -1):
    print(i, a[i])

结果:

-1 2
-2 5
-3 4
-4 3
-5 1

I wanted to loop through a two lists backwards at the same time so I needed the negative index. This is my solution:

a= [1,3,4,5,2]
for i in range(-1, -len(a), -1):
    print(i, a[i])

Result:

-1 2
-2 5
-3 4
-4 3
-5 1

回答 12

哦,好吧,我读错了问题,我想这是关于在数组中向后移动吗?如果是这样,我有这个:

array = ["ty", "rogers", "smith", "davis", "tony", "jack", "john", "jill", "harry", "tom", "jane", "hilary", "jackson", "andrew", "george", "rachel"]


counter = 0   

for loop in range(len(array)):
    if loop <= len(array):
        counter = -1
        reverseEngineering = loop + counter
        print(array[reverseEngineering])

Oh okay read the question wrong, I guess it’s about going backward in an array? if so, I have this:

array = ["ty", "rogers", "smith", "davis", "tony", "jack", "john", "jill", "harry", "tom", "jane", "hilary", "jackson", "andrew", "george", "rachel"]


counter = 0   

for loop in range(len(array)):
    if loop <= len(array):
        counter = -1
        reverseEngineering = loop + counter
        print(array[reverseEngineering])

回答 13

您还可以在python中创建自定义反向机制。可以在任何地方用于循环迭代向后

class Reverse:
    """Iterator for looping over a sequence backwards"""
    def __init__(self, seq):
        self.seq = seq
        self.index = len(seq)

    def __iter__(self):
        return self

    def __next__(self):
        if self.index == 0:
            raise StopIteration
        self.index -= 1
        return self.seq[self.index]


>>> d = [1,2,3,4,5]
>>> for i in Reverse(d):
...   print(i)
... 
5
4
3
2
1

You can also create a custom reverse mechanism in python. Which can be use anywhere for looping an iterable backwards

class Reverse:
    """Iterator for looping over a sequence backwards"""
    def __init__(self, seq):
        self.seq = seq
        self.index = len(seq)

    def __iter__(self):
        return self

    def __next__(self):
        if self.index == 0:
            raise StopIteration
        self.index -= 1
        return self.seq[self.index]


>>> d = [1,2,3,4,5]
>>> for i in Reverse(d):
...   print(i)
... 
5
4
3
2
1

回答 14

a = 10
for i in sorted(range(a), reverse=True):
    print i
a = 10
for i in sorted(range(a), reverse=True):
    print i

要求用户提供输入,直到他们给出有效的答复

问题:要求用户提供输入,直到他们给出有效的答复

我正在编写一个接受用户输入的程序。

#note: Python 2.7 users should use `raw_input`, the equivalent of 3.X's `input`
age = int(input("Please enter your age: "))
if age >= 18: 
    print("You are able to vote in the United States!")
else:
    print("You are not able to vote in the United States.")

只要用户输入有意义的数据,该程序就会按预期运行。

C:\Python\Projects> canyouvote.py
Please enter your age: 23
You are able to vote in the United States!

但是如果用户输入无效数据,它将失败:

C:\Python\Projects> canyouvote.py
Please enter your age: dickety six
Traceback (most recent call last):
  File "canyouvote.py", line 1, in <module>
    age = int(input("Please enter your age: "))
ValueError: invalid literal for int() with base 10: 'dickety six'

除了崩溃,我希望程序再次请求输入。像这样:

C:\Python\Projects> canyouvote.py
Please enter your age: dickety six
Sorry, I didn't understand that.
Please enter your age: 26
You are able to vote in the United States!

输入非意义的数据时,如何使程序要求有效的输入而不是崩溃?

在这种情况下-1,我如何才能拒绝类似的值,这是有效的int,但却毫无意义?

I am writing a program that accepts an input from the user.

#note: Python 2.7 users should use `raw_input`, the equivalent of 3.X's `input`
age = int(input("Please enter your age: "))
if age >= 18: 
    print("You are able to vote in the United States!")
else:
    print("You are not able to vote in the United States.")

The program works as expected as long as the the user enters meaningful data.

C:\Python\Projects> canyouvote.py
Please enter your age: 23
You are able to vote in the United States!

But it fails if the user enters invalid data:

C:\Python\Projects> canyouvote.py
Please enter your age: dickety six
Traceback (most recent call last):
  File "canyouvote.py", line 1, in <module>
    age = int(input("Please enter your age: "))
ValueError: invalid literal for int() with base 10: 'dickety six'

Instead of crashing, I would like the program to ask for the input again. Like this:

C:\Python\Projects> canyouvote.py
Please enter your age: dickety six
Sorry, I didn't understand that.
Please enter your age: 26
You are able to vote in the United States!

How can I make the program ask for valid inputs instead of crashing when non-sensical data is entered?

How can I reject values like -1, which is a valid int, but nonsensical in this context?


回答 0

完成此操作的最简单方法是将input方法置于while循环中。continue当输入错误时使用,break当您感到满意时使用。

当您的输入可能引发异常时

使用tryexcept检测用户何时输入无法解析的数据。

while True:
    try:
        # Note: Python 2.x users should use raw_input, the equivalent of 3.x's input
        age = int(input("Please enter your age: "))
    except ValueError:
        print("Sorry, I didn't understand that.")
        #better try again... Return to the start of the loop
        continue
    else:
        #age was successfully parsed!
        #we're ready to exit the loop.
        break
if age >= 18: 
    print("You are able to vote in the United States!")
else:
    print("You are not able to vote in the United States.")

实施您自己的验证规则

如果要拒绝Python可以成功解析的值,则可以添加自己的验证逻辑。

while True:
    data = input("Please enter a loud message (must be all caps): ")
    if not data.isupper():
        print("Sorry, your response was not loud enough.")
        continue
    else:
        #we're happy with the value given.
        #we're ready to exit the loop.
        break

while True:
    data = input("Pick an answer from A to D:")
    if data.lower() not in ('a', 'b', 'c', 'd'):
        print("Not an appropriate choice.")
    else:
        break

结合异常处理和自定义验证

以上两种技术都可以组合成一个循环。

while True:
    try:
        age = int(input("Please enter your age: "))
    except ValueError:
        print("Sorry, I didn't understand that.")
        continue

    if age < 0:
        print("Sorry, your response must not be negative.")
        continue
    else:
        #age was successfully parsed, and we're happy with its value.
        #we're ready to exit the loop.
        break
if age >= 18: 
    print("You are able to vote in the United States!")
else:
    print("You are not able to vote in the United States.")

将其全部封装在一个函数中

如果您需要询问用户许多不同的值,则将此代码放入函数中可能会很有用,因此不必每次都重新键入。

def get_non_negative_int(prompt):
    while True:
        try:
            value = int(input(prompt))
        except ValueError:
            print("Sorry, I didn't understand that.")
            continue

        if value < 0:
            print("Sorry, your response must not be negative.")
            continue
        else:
            break
    return value

age = get_non_negative_int("Please enter your age: ")
kids = get_non_negative_int("Please enter the number of children you have: ")
salary = get_non_negative_int("Please enter your yearly earnings, in dollars: ")

放在一起

您可以扩展此思想,以创建非常通用的输入函数:

def sanitised_input(prompt, type_=None, min_=None, max_=None, range_=None):
    if min_ is not None and max_ is not None and max_ < min_:
        raise ValueError("min_ must be less than or equal to max_.")
    while True:
        ui = input(prompt)
        if type_ is not None:
            try:
                ui = type_(ui)
            except ValueError:
                print("Input type must be {0}.".format(type_.__name__))
                continue
        if max_ is not None and ui > max_:
            print("Input must be less than or equal to {0}.".format(max_))
        elif min_ is not None and ui < min_:
            print("Input must be greater than or equal to {0}.".format(min_))
        elif range_ is not None and ui not in range_:
            if isinstance(range_, range):
                template = "Input must be between {0.start} and {0.stop}."
                print(template.format(range_))
            else:
                template = "Input must be {0}."
                if len(range_) == 1:
                    print(template.format(*range_))
                else:
                    expected = " or ".join((
                        ", ".join(str(x) for x in range_[:-1]),
                        str(range_[-1])
                    ))
                    print(template.format(expected))
        else:
            return ui

用法如下:

age = sanitised_input("Enter your age: ", int, 1, 101)
answer = sanitised_input("Enter your answer: ", str.lower, range_=('a', 'b', 'c', 'd'))

常见的陷阱以及为什么要避免它们

冗余input语句的冗余使用

此方法有效,但通常被认为是较差的样式:

data = input("Please enter a loud message (must be all caps): ")
while not data.isupper():
    print("Sorry, your response was not loud enough.")
    data = input("Please enter a loud message (must be all caps): ")

由于它比while True方法短,一开始可能看起来很吸引人,但是它违反了软件开发的“ 不要重复自己”的原理。这增加了系统中错误的可能性。如果要更改inputraw_input,将其反向移植到2.7 input,怎么办?这SyntaxError只是一个等待发生的事情。

递归会毁了你的栈

如果您刚刚了解了递归,则可能会想使用它,get_non_negative_int以便可以处理while循环。

def get_non_negative_int(prompt):
    try:
        value = int(input(prompt))
    except ValueError:
        print("Sorry, I didn't understand that.")
        return get_non_negative_int(prompt)

    if value < 0:
        print("Sorry, your response must not be negative.")
        return get_non_negative_int(prompt)
    else:
        return value

在大多数情况下,这似乎可以正常工作,但是如果用户输入无效数据的次数足够多,脚本将以终止RuntimeError: maximum recursion depth exceeded。您可能会认为“没有傻瓜会连续犯1000个错误”,但是您却低估了傻瓜的创造力!

The simplest way to accomplish this is to put the input method in a while loop. Use continue when you get bad input, and break out of the loop when you’re satisfied.

When Your Input Might Raise an Exception

Use try and except to detect when the user enters data that can’t be parsed.

while True:
    try:
        # Note: Python 2.x users should use raw_input, the equivalent of 3.x's input
        age = int(input("Please enter your age: "))
    except ValueError:
        print("Sorry, I didn't understand that.")
        #better try again... Return to the start of the loop
        continue
    else:
        #age was successfully parsed!
        #we're ready to exit the loop.
        break
if age >= 18: 
    print("You are able to vote in the United States!")
else:
    print("You are not able to vote in the United States.")

Implementing Your Own Validation Rules

If you want to reject values that Python can successfully parse, you can add your own validation logic.

while True:
    data = input("Please enter a loud message (must be all caps): ")
    if not data.isupper():
        print("Sorry, your response was not loud enough.")
        continue
    else:
        #we're happy with the value given.
        #we're ready to exit the loop.
        break

while True:
    data = input("Pick an answer from A to D:")
    if data.lower() not in ('a', 'b', 'c', 'd'):
        print("Not an appropriate choice.")
    else:
        break

Combining Exception Handling and Custom Validation

Both of the above techniques can be combined into one loop.

while True:
    try:
        age = int(input("Please enter your age: "))
    except ValueError:
        print("Sorry, I didn't understand that.")
        continue

    if age < 0:
        print("Sorry, your response must not be negative.")
        continue
    else:
        #age was successfully parsed, and we're happy with its value.
        #we're ready to exit the loop.
        break
if age >= 18: 
    print("You are able to vote in the United States!")
else:
    print("You are not able to vote in the United States.")

Encapsulating it All in a Function

If you need to ask your user for a lot of different values, it might be useful to put this code in a function, so you don’t have to retype it every time.

def get_non_negative_int(prompt):
    while True:
        try:
            value = int(input(prompt))
        except ValueError:
            print("Sorry, I didn't understand that.")
            continue

        if value < 0:
            print("Sorry, your response must not be negative.")
            continue
        else:
            break
    return value

age = get_non_negative_int("Please enter your age: ")
kids = get_non_negative_int("Please enter the number of children you have: ")
salary = get_non_negative_int("Please enter your yearly earnings, in dollars: ")

Putting It All Together

You can extend this idea to make a very generic input function:

def sanitised_input(prompt, type_=None, min_=None, max_=None, range_=None):
    if min_ is not None and max_ is not None and max_ < min_:
        raise ValueError("min_ must be less than or equal to max_.")
    while True:
        ui = input(prompt)
        if type_ is not None:
            try:
                ui = type_(ui)
            except ValueError:
                print("Input type must be {0}.".format(type_.__name__))
                continue
        if max_ is not None and ui > max_:
            print("Input must be less than or equal to {0}.".format(max_))
        elif min_ is not None and ui < min_:
            print("Input must be greater than or equal to {0}.".format(min_))
        elif range_ is not None and ui not in range_:
            if isinstance(range_, range):
                template = "Input must be between {0.start} and {0.stop}."
                print(template.format(range_))
            else:
                template = "Input must be {0}."
                if len(range_) == 1:
                    print(template.format(*range_))
                else:
                    expected = " or ".join((
                        ", ".join(str(x) for x in range_[:-1]),
                        str(range_[-1])
                    ))
                    print(template.format(expected))
        else:
            return ui

With usage such as:

age = sanitised_input("Enter your age: ", int, 1, 101)
answer = sanitised_input("Enter your answer: ", str.lower, range_=('a', 'b', 'c', 'd'))

Common Pitfalls, and Why you Should Avoid Them

The Redundant Use of Redundant input Statements

This method works but is generally considered poor style:

data = input("Please enter a loud message (must be all caps): ")
while not data.isupper():
    print("Sorry, your response was not loud enough.")
    data = input("Please enter a loud message (must be all caps): ")

It might look attractive initially because it’s shorter than the while True method, but it violates the Don’t Repeat Yourself principle of software development. This increases the likelihood of bugs in your system. What if you want to backport to 2.7 by changing input to raw_input, but accidentally change only the first input above? It’s a SyntaxError just waiting to happen.

Recursion Will Blow Your Stack

If you’ve just learned about recursion, you might be tempted to use it in get_non_negative_int so you can dispose of the while loop.

def get_non_negative_int(prompt):
    try:
        value = int(input(prompt))
    except ValueError:
        print("Sorry, I didn't understand that.")
        return get_non_negative_int(prompt)

    if value < 0:
        print("Sorry, your response must not be negative.")
        return get_non_negative_int(prompt)
    else:
        return value

This appears to work fine most of the time, but if the user enters invalid data enough times, the script will terminate with a RuntimeError: maximum recursion depth exceeded. You may think “no fool would make 1000 mistakes in a row”, but you’re underestimating the ingenuity of fools!


回答 1

您为什么要先执行a while True然后再退出此循环,而您也可以只将需求放在while语句中,因为您想要的只是在达到年龄后就停止?

age = None
while age is None:
    input_value = input("Please enter your age: ")
    try:
        # try and convert the string input to a number
        age = int(input_value)
    except ValueError:
        # tell the user off
        print("{input} is not a number, please enter a number only".format(input=input_value))
if age >= 18:
    print("You are able to vote in the United States!")
else:
    print("You are not able to vote in the United States.")

这将导致以下结果:

Please enter your age: *potato*
potato is not a number, please enter a number only
Please enter your age: *5*
You are not able to vote in the United States.

这是可行的,因为年龄永远不会有没有意义的值,并且代码遵循“业务流程”的逻辑

Why would you do a while True and then break out of this loop while you can also just put your requirements in the while statement since all you want is to stop once you have the age?

age = None
while age is None:
    input_value = input("Please enter your age: ")
    try:
        # try and convert the string input to a number
        age = int(input_value)
    except ValueError:
        # tell the user off
        print("{input} is not a number, please enter a number only".format(input=input_value))
if age >= 18:
    print("You are able to vote in the United States!")
else:
    print("You are not able to vote in the United States.")

This would result in the following:

Please enter your age: *potato*
potato is not a number, please enter a number only
Please enter your age: *5*
You are not able to vote in the United States.

this will work since age will never have a value that will not make sense and the code follows the logic of your “business process”


回答 2

尽管公认的答案是惊人的。我也想分享一个快速解决此问题的方法。(这也解决了负面的年龄问题。)

f=lambda age: (age.isdigit() and ((int(age)>=18  and "Can vote" ) or "Cannot vote")) or \
f(input("invalid input. Try again\nPlease enter your age: "))
print(f(input("Please enter your age: ")))

PS此代码适用于python3.x。

Though the accepted answer is amazing. I would also like to share a quick hack for this problem. (This takes care of the negative age problem as well.)

f=lambda age: (age.isdigit() and ((int(age)>=18  and "Can vote" ) or "Cannot vote")) or \
f(input("invalid input. Try again\nPlease enter your age: "))
print(f(input("Please enter your age: ")))

P.S. This code is for python 3.x.


回答 3

因此,我最近在搞些类似的事情,于是我想到了以下解决方案,该解决方案使用了一种获取垃圾输入的方式,甚至可以以任何逻辑方式对其进行检查。

read_single_keypress()https://stackoverflow.com/a/6599441/4532996提供

def read_single_keypress() -> str:
    """Waits for a single keypress on stdin.
    -- from :: https://stackoverflow.com/a/6599441/4532996
    """

    import termios, fcntl, sys, os
    fd = sys.stdin.fileno()
    # save old state
    flags_save = fcntl.fcntl(fd, fcntl.F_GETFL)
    attrs_save = termios.tcgetattr(fd)
    # make raw - the way to do this comes from the termios(3) man page.
    attrs = list(attrs_save) # copy the stored version to update
    # iflag
    attrs[0] &= ~(termios.IGNBRK | termios.BRKINT | termios.PARMRK
                  | termios.ISTRIP | termios.INLCR | termios. IGNCR
                  | termios.ICRNL | termios.IXON )
    # oflag
    attrs[1] &= ~termios.OPOST
    # cflag
    attrs[2] &= ~(termios.CSIZE | termios. PARENB)
    attrs[2] |= termios.CS8
    # lflag
    attrs[3] &= ~(termios.ECHONL | termios.ECHO | termios.ICANON
                  | termios.ISIG | termios.IEXTEN)
    termios.tcsetattr(fd, termios.TCSANOW, attrs)
    # turn off non-blocking
    fcntl.fcntl(fd, fcntl.F_SETFL, flags_save & ~os.O_NONBLOCK)
    # read a single keystroke
    try:
        ret = sys.stdin.read(1) # returns a single character
    except KeyboardInterrupt:
        ret = 0
    finally:
        # restore old state
        termios.tcsetattr(fd, termios.TCSAFLUSH, attrs_save)
        fcntl.fcntl(fd, fcntl.F_SETFL, flags_save)
    return ret

def until_not_multi(chars) -> str:
    """read stdin until !(chars)"""
    import sys
    chars = list(chars)
    y = ""
    sys.stdout.flush()
    while True:
        i = read_single_keypress()
        _ = sys.stdout.write(i)
        sys.stdout.flush()
        if i not in chars:
            break
        y += i
    return y

def _can_you_vote() -> str:
    """a practical example:
    test if a user can vote based purely on keypresses"""
    print("can you vote? age : ", end="")
    x = int("0" + until_not_multi("0123456789"))
    if not x:
        print("\nsorry, age can only consist of digits.")
        return
    print("your age is", x, "\nYou can vote!" if x >= 18 else "Sorry! you can't vote")

_can_you_vote()

您可以在此处找到完整的模块。

例:

$ ./input_constrain.py
can you vote? age : a
sorry, age can only consist of digits.
$ ./input_constrain.py 
can you vote? age : 23<RETURN>
your age is 23
You can vote!
$ _

请注意,此实现的性质是,一旦读取了不是数字的内容,它将立即关闭stdin。我没有按回车键a,但是我需要按数字。

您可以将此thismany()功能与同一模块中的功能合并,以仅允许输入三位数。

So, I was messing around with something similar to this recently, and I came up with the following solution, which uses a way of getting input that rejects junk, before it’s even checked in any logical way.

read_single_keypress() courtesy https://stackoverflow.com/a/6599441/4532996

def read_single_keypress() -> str:
    """Waits for a single keypress on stdin.
    -- from :: https://stackoverflow.com/a/6599441/4532996
    """

    import termios, fcntl, sys, os
    fd = sys.stdin.fileno()
    # save old state
    flags_save = fcntl.fcntl(fd, fcntl.F_GETFL)
    attrs_save = termios.tcgetattr(fd)
    # make raw - the way to do this comes from the termios(3) man page.
    attrs = list(attrs_save) # copy the stored version to update
    # iflag
    attrs[0] &= ~(termios.IGNBRK | termios.BRKINT | termios.PARMRK
                  | termios.ISTRIP | termios.INLCR | termios. IGNCR
                  | termios.ICRNL | termios.IXON )
    # oflag
    attrs[1] &= ~termios.OPOST
    # cflag
    attrs[2] &= ~(termios.CSIZE | termios. PARENB)
    attrs[2] |= termios.CS8
    # lflag
    attrs[3] &= ~(termios.ECHONL | termios.ECHO | termios.ICANON
                  | termios.ISIG | termios.IEXTEN)
    termios.tcsetattr(fd, termios.TCSANOW, attrs)
    # turn off non-blocking
    fcntl.fcntl(fd, fcntl.F_SETFL, flags_save & ~os.O_NONBLOCK)
    # read a single keystroke
    try:
        ret = sys.stdin.read(1) # returns a single character
    except KeyboardInterrupt:
        ret = 0
    finally:
        # restore old state
        termios.tcsetattr(fd, termios.TCSAFLUSH, attrs_save)
        fcntl.fcntl(fd, fcntl.F_SETFL, flags_save)
    return ret

def until_not_multi(chars) -> str:
    """read stdin until !(chars)"""
    import sys
    chars = list(chars)
    y = ""
    sys.stdout.flush()
    while True:
        i = read_single_keypress()
        _ = sys.stdout.write(i)
        sys.stdout.flush()
        if i not in chars:
            break
        y += i
    return y

def _can_you_vote() -> str:
    """a practical example:
    test if a user can vote based purely on keypresses"""
    print("can you vote? age : ", end="")
    x = int("0" + until_not_multi("0123456789"))
    if not x:
        print("\nsorry, age can only consist of digits.")
        return
    print("your age is", x, "\nYou can vote!" if x >= 18 else "Sorry! you can't vote")

_can_you_vote()

You can find the complete module here.

Example:

$ ./input_constrain.py
can you vote? age : a
sorry, age can only consist of digits.
$ ./input_constrain.py 
can you vote? age : 23<RETURN>
your age is 23
You can vote!
$ _

Note that the nature of this implementation is it closes stdin as soon as something that isn’t a digit is read. I didn’t hit enter after a, but I needed to after the numbers.

You could merge this with the thismany() function in the same module to only allow, say, three digits.


回答 4

功能性方法或“ 看起来没有循环! ”:

from itertools import chain, repeat

prompts = chain(["Enter a number: "], repeat("Not a number! Try again: "))
replies = map(input, prompts)
valid_response = next(filter(str.isdigit, replies))
print(valid_response)
Enter a number:  a
Not a number! Try again:  b
Not a number! Try again:  1
1

或者,如果您想将“错误输入”消息与输入提示分开,如其他答案所示:

prompt_msg = "Enter a number: "
bad_input_msg = "Sorry, I didn't understand that."
prompts = chain([prompt_msg], repeat('\n'.join([bad_input_msg, prompt_msg])))
replies = map(input, prompts)
valid_response = next(filter(str.isdigit, replies))
print(valid_response)
Enter a number:  a
Sorry, I didn't understand that.
Enter a number:  b
Sorry, I didn't understand that.
Enter a number:  1
1

它是如何工作的?

  1. prompts = chain(["Enter a number: "], repeat("Not a number! Try again: "))
    的组合itertools.chainitertools.repeat将创建一个迭代器,这将产生串"Enter a number: "一次,"Not a number! Try again: "中无数次:
    for prompt in prompts:
        print(prompt)
    Enter a number: 
    Not a number! Try again: 
    Not a number! Try again: 
    Not a number! Try again: 
    # ... and so on
  2. replies = map(input, prompts)-这里map会将prompts上一步中的所有字符串应用于input函数。例如:
    for reply in replies:
        print(reply)
    Enter a number:  a
    a
    Not a number! Try again:  1
    1
    Not a number! Try again:  it doesn't care now
    it doesn't care now
    # and so on...
  3. 我们使用filterstr.isdigit过滤掉那些只包含数字的字符串:
    only_digits = filter(str.isdigit, replies)
    for reply in only_digits:
        print(reply)
    Enter a number:  a
    Not a number! Try again:  1
    1
    Not a number! Try again:  2
    2
    Not a number! Try again:  b
    Not a number! Try again: # and so on...
    并且仅使用第一个数字字符串next

其他验证规则:

  1. 字符串方法:当然,您可以使用其他字符串方法,例如str.isalpha仅获取字母字符串或str.isupper仅获取大写字母。请参阅文档以获取完整列表。

  2. 成员资格测试:
    有几种不同的执行方式。其中之一是通过使用__contains__方法:

    from itertools import chain, repeat
    
    fruits = {'apple', 'orange', 'peach'}
    prompts = chain(["Enter a fruit: "], repeat("I don't know this one! Try again: "))
    replies = map(input, prompts)
    valid_response = next(filter(fruits.__contains__, replies))
    print(valid_response)
    Enter a fruit:  1
    I don't know this one! Try again:  foo
    I don't know this one! Try again:  apple
    apple
  3. 数字比较:
    这里有一些有用的比较方法。例如,对于__lt__<):

    from itertools import chain, repeat
    
    prompts = chain(["Enter a positive number:"], repeat("I need a positive number! Try again:"))
    replies = map(input, prompts)
    numeric_strings = filter(str.isnumeric, replies)
    numbers = map(float, numeric_strings)
    is_positive = (0.).__lt__
    valid_response = next(filter(is_positive, numbers))
    print(valid_response)
    Enter a positive number: a
    I need a positive number! Try again: -5
    I need a positive number! Try again: 0
    I need a positive number! Try again: 5
    5.0

    或者,如果您不喜欢使用dunder方法(dunder =双下划线),则始终可以定义自己的函数,也可以使用 operator模块中。

  4. 路径存在:
    这里可以使用pathlib库及其Path.exists方法:

    from itertools import chain, repeat
    from pathlib import Path
    
    prompts = chain(["Enter a path: "], repeat("This path doesn't exist! Try again: "))
    replies = map(input, prompts)
    paths = map(Path, replies)
    valid_response = next(filter(Path.exists, paths))
    print(valid_response)
    Enter a path:  a b c
    This path doesn't exist! Try again:  1
    This path doesn't exist! Try again:  existing_file.txt
    existing_file.txt

限制尝试次数:

如果您不想无限次地问某人来折磨他,可以在呼叫中指定一个限制itertools.repeat。这可以与为next函数提供默认值结合使用:

from itertools import chain, repeat

prompts = chain(["Enter a number:"], repeat("Not a number! Try again:", 2))
replies = map(input, prompts)
valid_response = next(filter(str.isdigit, replies), None)
print("You've failed miserably!" if valid_response is None else 'Well done!')
Enter a number: a
Not a number! Try again: b
Not a number! Try again: c
You've failed miserably!

预处理输入数据:

有时,如果用户不小心以大写形式提供了输入,或者在字符串的开头或结尾有空格,我们就不想拒绝输入。为了考虑这些简单的错误,我们可以通过应用str.lowerstr.strip方法对输入数据进行预处理。例如,对于成员资格测试,代码如下所示:

from itertools import chain, repeat

fruits = {'apple', 'orange', 'peach'}
prompts = chain(["Enter a fruit: "], repeat("I don't know this one! Try again: "))
replies = map(input, prompts)
lowercased_replies = map(str.lower, replies)
stripped_replies = map(str.strip, lowercased_replies)
valid_response = next(filter(fruits.__contains__, stripped_replies))
print(valid_response)
Enter a fruit:  duck
I don't know this one! Try again:     Orange
orange

如果要使用许多函数进行预处理,则使用执行函数合成的函数可能会更容易。例如,使用此处的一个:

from itertools import chain, repeat

from lz.functional import compose

fruits = {'apple', 'orange', 'peach'}
prompts = chain(["Enter a fruit: "], repeat("I don't know this one! Try again: "))
replies = map(input, prompts)
process = compose(str.strip, str.lower)  # you can add more functions here
processed_replies = map(process, replies)
valid_response = next(filter(fruits.__contains__, processed_replies))
print(valid_response)
Enter a fruit:  potato
I don't know this one! Try again:   PEACH
peach

合并验证规则:

例如,在一个简单的情况下,当程序要求输入1到120岁之间的年龄时,可以添加另一个filter

from itertools import chain, repeat

prompt_msg = "Enter your age (1-120): "
bad_input_msg = "Wrong input."
prompts = chain([prompt_msg], repeat('\n'.join([bad_input_msg, prompt_msg])))
replies = map(input, prompts)
numeric_replies = filter(str.isdigit, replies)
ages = map(int, numeric_replies)
positive_ages = filter((0).__lt__, ages)
not_too_big_ages = filter((120).__ge__, positive_ages)
valid_response = next(not_too_big_ages)
print(valid_response)

但是,在规则很多的情况下,最好实现执行逻辑合取的函数。在下面的例子中我将使用一个现成的一个位置

from functools import partial
from itertools import chain, repeat

from lz.logical import conjoin


def is_one_letter(string: str) -> bool:
    return len(string) == 1


rules = [str.isalpha, str.isupper, is_one_letter, 'C'.__le__, 'P'.__ge__]

prompt_msg = "Enter a letter (C-P): "
bad_input_msg = "Wrong input."
prompts = chain([prompt_msg], repeat('\n'.join([bad_input_msg, prompt_msg])))
replies = map(input, prompts)
valid_response = next(filter(conjoin(*rules), replies))
print(valid_response)
Enter a letter (C-P):  5
Wrong input.
Enter a letter (C-P):  f
Wrong input.
Enter a letter (C-P):  CDE
Wrong input.
Enter a letter (C-P):  Q
Wrong input.
Enter a letter (C-P):  N
N

不幸的是,如果有人需要为每个失败的情况下,自定义消息,然后,我很害怕,也没有漂亮的功能性的方式。或者,至少,我找不到一个。

Functional approach or “look mum no loops!“:

from itertools import chain, repeat

prompts = chain(["Enter a number: "], repeat("Not a number! Try again: "))
replies = map(input, prompts)
valid_response = next(filter(str.isdigit, replies))
print(valid_response)
Enter a number:  a
Not a number! Try again:  b
Not a number! Try again:  1
1

or if you want to have a “bad input” message separated from an input prompt as in other answers:

prompt_msg = "Enter a number: "
bad_input_msg = "Sorry, I didn't understand that."
prompts = chain([prompt_msg], repeat('\n'.join([bad_input_msg, prompt_msg])))
replies = map(input, prompts)
valid_response = next(filter(str.isdigit, replies))
print(valid_response)
Enter a number:  a
Sorry, I didn't understand that.
Enter a number:  b
Sorry, I didn't understand that.
Enter a number:  1
1

How does it work?

  1. prompts = chain(["Enter a number: "], repeat("Not a number! Try again: "))
    
    This combination of itertools.chain and itertools.repeat will create an iterator which will yield strings "Enter a number: " once, and "Not a number! Try again: " an infinite number of times:
    for prompt in prompts:
        print(prompt)
    
    Enter a number: 
    Not a number! Try again: 
    Not a number! Try again: 
    Not a number! Try again: 
    # ... and so on
    
  2. replies = map(input, prompts) – here map will apply all the prompts strings from the previous step to the input function. E.g.:
    for reply in replies:
        print(reply)
    
    Enter a number:  a
    a
    Not a number! Try again:  1
    1
    Not a number! Try again:  it doesn't care now
    it doesn't care now
    # and so on...
    
  3. We use filter and str.isdigit to filter out those strings that contain only digits:
    only_digits = filter(str.isdigit, replies)
    for reply in only_digits:
        print(reply)
    
    Enter a number:  a
    Not a number! Try again:  1
    1
    Not a number! Try again:  2
    2
    Not a number! Try again:  b
    Not a number! Try again: # and so on...
    
    And to get only the first digits-only string we use next.

Other validation rules:

  1. String methods: Of course you can use other string methods like str.isalpha to get only alphabetic strings, or str.isupper to get only uppercase. See docs for the full list.

  2. Membership testing:
    There are several different ways to perform it. One of them is by using __contains__ method:

    from itertools import chain, repeat
    
    fruits = {'apple', 'orange', 'peach'}
    prompts = chain(["Enter a fruit: "], repeat("I don't know this one! Try again: "))
    replies = map(input, prompts)
    valid_response = next(filter(fruits.__contains__, replies))
    print(valid_response)
    
    Enter a fruit:  1
    I don't know this one! Try again:  foo
    I don't know this one! Try again:  apple
    apple
    
  3. Numbers comparison:
    There are useful comparison methods which we can use here. For example, for __lt__ (<):

    from itertools import chain, repeat
    
    prompts = chain(["Enter a positive number:"], repeat("I need a positive number! Try again:"))
    replies = map(input, prompts)
    numeric_strings = filter(str.isnumeric, replies)
    numbers = map(float, numeric_strings)
    is_positive = (0.).__lt__
    valid_response = next(filter(is_positive, numbers))
    print(valid_response)
    
    Enter a positive number: a
    I need a positive number! Try again: -5
    I need a positive number! Try again: 0
    I need a positive number! Try again: 5
    5.0
    

    Or, if you don’t like using dunder methods (dunder = double-underscore), you can always define your own function, or use the ones from the operator module.

  4. Path existance:
    Here one can use pathlib library and its Path.exists method:

    from itertools import chain, repeat
    from pathlib import Path
    
    prompts = chain(["Enter a path: "], repeat("This path doesn't exist! Try again: "))
    replies = map(input, prompts)
    paths = map(Path, replies)
    valid_response = next(filter(Path.exists, paths))
    print(valid_response)
    
    Enter a path:  a b c
    This path doesn't exist! Try again:  1
    This path doesn't exist! Try again:  existing_file.txt
    existing_file.txt
    

Limiting number of tries:

If you don’t want to torture a user by asking him something an infinite number of times, you can specify a limit in a call of itertools.repeat. This can be combined with providing a default value to the next function:

from itertools import chain, repeat

prompts = chain(["Enter a number:"], repeat("Not a number! Try again:", 2))
replies = map(input, prompts)
valid_response = next(filter(str.isdigit, replies), None)
print("You've failed miserably!" if valid_response is None else 'Well done!')
Enter a number: a
Not a number! Try again: b
Not a number! Try again: c
You've failed miserably!

Preprocessing input data:

Sometimes we don’t want to reject an input if the user accidentally supplied it IN CAPS or with a space in the beginning or an end of the string. To take these simple mistakes into account we can preprocess the input data by applying str.lower and str.strip methods. For example, for the case of membership testing the code will look like this:

from itertools import chain, repeat

fruits = {'apple', 'orange', 'peach'}
prompts = chain(["Enter a fruit: "], repeat("I don't know this one! Try again: "))
replies = map(input, prompts)
lowercased_replies = map(str.lower, replies)
stripped_replies = map(str.strip, lowercased_replies)
valid_response = next(filter(fruits.__contains__, stripped_replies))
print(valid_response)
Enter a fruit:  duck
I don't know this one! Try again:     Orange
orange

In the case when you have many functions to use for preprocessing, it might be easier to use a function performing a function composition. For example, using the one from here:

from itertools import chain, repeat

from lz.functional import compose

fruits = {'apple', 'orange', 'peach'}
prompts = chain(["Enter a fruit: "], repeat("I don't know this one! Try again: "))
replies = map(input, prompts)
process = compose(str.strip, str.lower)  # you can add more functions here
processed_replies = map(process, replies)
valid_response = next(filter(fruits.__contains__, processed_replies))
print(valid_response)
Enter a fruit:  potato
I don't know this one! Try again:   PEACH
peach

Combining validation rules:

For a simple case, for example, when the program asks for age between 1 and 120, one can just add another filter:

from itertools import chain, repeat

prompt_msg = "Enter your age (1-120): "
bad_input_msg = "Wrong input."
prompts = chain([prompt_msg], repeat('\n'.join([bad_input_msg, prompt_msg])))
replies = map(input, prompts)
numeric_replies = filter(str.isdigit, replies)
ages = map(int, numeric_replies)
positive_ages = filter((0).__lt__, ages)
not_too_big_ages = filter((120).__ge__, positive_ages)
valid_response = next(not_too_big_ages)
print(valid_response)

But in the case when there are many rules, it’s better to implement a function performing a logical conjunction. In the following example I will use a ready one from here:

from functools import partial
from itertools import chain, repeat

from lz.logical import conjoin


def is_one_letter(string: str) -> bool:
    return len(string) == 1


rules = [str.isalpha, str.isupper, is_one_letter, 'C'.__le__, 'P'.__ge__]

prompt_msg = "Enter a letter (C-P): "
bad_input_msg = "Wrong input."
prompts = chain([prompt_msg], repeat('\n'.join([bad_input_msg, prompt_msg])))
replies = map(input, prompts)
valid_response = next(filter(conjoin(*rules), replies))
print(valid_response)
Enter a letter (C-P):  5
Wrong input.
Enter a letter (C-P):  f
Wrong input.
Enter a letter (C-P):  CDE
Wrong input.
Enter a letter (C-P):  Q
Wrong input.
Enter a letter (C-P):  N
N

Unfortunately, if someone needs a custom message for each failed case, then, I’m afraid, there is no pretty functional way. Or, at least, I couldn’t find one.


回答 5

使用点击

请点击是一个用于命令行界面的库,它提供了向用户询问有效响应的功能。

简单的例子:

import click

number = click.prompt('Please enter a number', type=float)
print(number)
Please enter a number: 
 a
Error: a is not a valid floating point value
Please enter a number: 
 10
10.0

注意如何将字符串值自动转换为浮点数。

检查值是否在范围内:

提供了不同的自定义类型。要获得特定范围内的数字,我们可以使用IntRange

age = click.prompt("What's your age?", type=click.IntRange(1, 120))
print(age)
What's your age?: 
 a
Error: a is not a valid integer
What's your age?: 
 0
Error: 0 is not in the valid range of 1 to 120.
What's your age?: 
 5
5

我们还可以只指定其中一个限制,minmax

age = click.prompt("What's your age?", type=click.IntRange(min=14))
print(age)
What's your age?: 
 0
Error: 0 is smaller than the minimum valid value 14.
What's your age?: 
 18
18

会员资格测试:

使用click.Choice类型。默认情况下,此检查区分大小写。

choices = {'apple', 'orange', 'peach'}
choice = click.prompt('Provide a fruit', type=click.Choice(choices, case_sensitive=False))
print(choice)
Provide a fruit (apple, peach, orange): 
 banana
Error: invalid choice: banana. (choose from apple, peach, orange)
Provide a fruit (apple, peach, orange): 
 OrAnGe
orange

使用路径和文件:

使用click.Path类型,我们可以检查现有路径并解决它们:

path = click.prompt('Provide path', type=click.Path(exists=True, resolve_path=True))
print(path)
Provide path: 
 nonexistent
Error: Path "nonexistent" does not exist.
Provide path: 
 existing_folder
'/path/to/existing_folder

读写文件可以通过以下方式完成click.File

file = click.prompt('In which file to write data?', type=click.File('w'))
with file.open():
    file.write('Hello!')
# More info about `lazy=True` at:
# https://click.palletsprojects.com/en/7.x/arguments/#file-opening-safety
file = click.prompt('Which file you wanna read?', type=click.File(lazy=True))
with file.open():
    print(file.read())
In which file to write data?: 
         # <-- provided an empty string, which is an illegal name for a file
In which file to write data?: 
 some_file.txt
Which file you wanna read?: 
 nonexistent.txt
Error: Could not open file: nonexistent.txt: No such file or directory
Which file you wanna read?: 
 some_file.txt
Hello!

其他例子:

确认密码:

password = click.prompt('Enter password', hide_input=True, confirmation_prompt=True)
print(password)
Enter password: 
 ······
Repeat for confirmation: 
 ·
Error: the two entered values do not match
Enter password: 
 ······
Repeat for confirmation: 
 ······
qwerty

默认值:

在这种情况下,只需按Enter(或您使用的任何键)而不输入值,即可得到默认值:

number = click.prompt('Please enter a number', type=int, default=42)
print(number)
Please enter a number [42]: 
 a
Error: a is not a valid integer
Please enter a number [42]: 

42

Using Click:

Click is a library for command-line interfaces and it provides functionality for asking a valid response from a user.

Simple example:

import click

number = click.prompt('Please enter a number', type=float)
print(number)
Please enter a number: 
 a
Error: a is not a valid floating point value
Please enter a number: 
 10
10.0

Note how it converted the string value to a float automatically.

Checking if a value is within a range:

There are different custom types provided. To get a number in a specific range we can use IntRange:

age = click.prompt("What's your age?", type=click.IntRange(1, 120))
print(age)
What's your age?: 
 a
Error: a is not a valid integer
What's your age?: 
 0
Error: 0 is not in the valid range of 1 to 120.
What's your age?: 
 5
5

We can also specify just one of the limits, min or max:

age = click.prompt("What's your age?", type=click.IntRange(min=14))
print(age)
What's your age?: 
 0
Error: 0 is smaller than the minimum valid value 14.
What's your age?: 
 18
18

Membership testing:

Using click.Choice type. By default this check is case-sensitive.

choices = {'apple', 'orange', 'peach'}
choice = click.prompt('Provide a fruit', type=click.Choice(choices, case_sensitive=False))
print(choice)
Provide a fruit (apple, peach, orange): 
 banana
Error: invalid choice: banana. (choose from apple, peach, orange)
Provide a fruit (apple, peach, orange): 
 OrAnGe
orange

Working with paths and files:

Using a click.Path type we can check for existing paths and also resolve them:

path = click.prompt('Provide path', type=click.Path(exists=True, resolve_path=True))
print(path)
Provide path: 
 nonexistent
Error: Path "nonexistent" does not exist.
Provide path: 
 existing_folder
'/path/to/existing_folder

Reading and writing files can be done by click.File:

file = click.prompt('In which file to write data?', type=click.File('w'))
with file.open():
    file.write('Hello!')
# More info about `lazy=True` at:
# https://click.palletsprojects.com/en/7.x/arguments/#file-opening-safety
file = click.prompt('Which file you wanna read?', type=click.File(lazy=True))
with file.open():
    print(file.read())
In which file to write data?: 
         # <-- provided an empty string, which is an illegal name for a file
In which file to write data?: 
 some_file.txt
Which file you wanna read?: 
 nonexistent.txt
Error: Could not open file: nonexistent.txt: No such file or directory
Which file you wanna read?: 
 some_file.txt
Hello!

Other examples:

Password confirmation:

password = click.prompt('Enter password', hide_input=True, confirmation_prompt=True)
print(password)
Enter password: 
 ······
Repeat for confirmation: 
 ·
Error: the two entered values do not match
Enter password: 
 ······
Repeat for confirmation: 
 ······
qwerty

Default values:

In this case, simply pressing Enter (or whatever key you use) without entering a value, will give you a default one:

number = click.prompt('Please enter a number', type=int, default=42)
print(number)
Please enter a number [42]: 
 a
Error: a is not a valid integer
Please enter a number [42]: 

42

回答 6

def validate_age(age):
    if age >=0 :
        return True
    return False

while True:
    try:
        age = int(raw_input("Please enter your age:"))
        if validate_age(age): break
    except ValueError:
        print "Error: Invalid age."
def validate_age(age):
    if age >=0 :
        return True
    return False

while True:
    try:
        age = int(raw_input("Please enter your age:"))
        if validate_age(age): break
    except ValueError:
        print "Error: Invalid age."

回答 7

在Daniel Q和Patrick Artner的出色建议的基础上,这是一个更为通用的解决方案。

# Assuming Python3
import sys

class ValidationError(ValueError):  # thanks Patrick Artner
    pass

def validate_input(prompt, cast=str, cond=(lambda x: True), onerror=None):
    if onerror==None: onerror = {}
    while True:
        try:
            data = cast(input(prompt))
            if not cond(data): raise ValidationError
            return data
        except tuple(onerror.keys()) as e:  # thanks Daniel Q
            print(onerror[type(e)], file=sys.stderr)

我选择了显式ifraise语句而不是assert,因为断言检查可能已关闭,而验证应始终处于打开状态以提供鲁棒性。

这可用于获取具有不同验证条件的不同种类的输入。例如:

# No validation, equivalent to simple input:
anystr = validate_input("Enter any string: ")

# Get a string containing only letters:
letters = validate_input("Enter letters: ",
    cond=str.isalpha,
    onerror={ValidationError: "Only letters, please!"})

# Get a float in [0, 100]:
percentage = validate_input("Percentage? ",
    cast=float, cond=lambda x: 0.0<=x<=100.0,
    onerror={ValidationError: "Must be between 0 and 100!",
             ValueError: "Not a number!"})

或者,回答原始问题:

age = validate_input("Please enter your age: ",
        cast=int, cond=lambda a:0<=a<150,
        onerror={ValidationError: "Enter a plausible age, please!",
                 ValueError: "Enter an integer, please!"})
if age >= 18: 
    print("You are able to vote in the United States!")
else:
    print("You are not able to vote in the United States.")

Building upon Daniel Q’s and Patrick Artner’s excellent suggestions, here is an even more generalized solution.

# Assuming Python3
import sys

class ValidationError(ValueError):  # thanks Patrick Artner
    pass

def validate_input(prompt, cast=str, cond=(lambda x: True), onerror=None):
    if onerror==None: onerror = {}
    while True:
        try:
            data = cast(input(prompt))
            if not cond(data): raise ValidationError
            return data
        except tuple(onerror.keys()) as e:  # thanks Daniel Q
            print(onerror[type(e)], file=sys.stderr)

I opted for explicit if and raise statements instead of an assert, because assertion checking may be turned off, whereas validation should always be on to provide robustness.

This may be used to get different kinds of input, with different validation conditions. For example:

# No validation, equivalent to simple input:
anystr = validate_input("Enter any string: ")

# Get a string containing only letters:
letters = validate_input("Enter letters: ",
    cond=str.isalpha,
    onerror={ValidationError: "Only letters, please!"})

# Get a float in [0, 100]:
percentage = validate_input("Percentage? ",
    cast=float, cond=lambda x: 0.0<=x<=100.0,
    onerror={ValidationError: "Must be between 0 and 100!",
             ValueError: "Not a number!"})

Or, to answer the original question:

age = validate_input("Please enter your age: ",
        cast=int, cond=lambda a:0<=a<150,
        onerror={ValidationError: "Enter a plausible age, please!",
                 ValueError: "Enter an integer, please!"})
if age >= 18: 
    print("You are able to vote in the United States!")
else:
    print("You are not able to vote in the United States.")

回答 8

试试这个:

def takeInput(required):
  print 'ooo or OOO to exit'
  ans = raw_input('Enter: ')

  if not ans:
      print "You entered nothing...!"
      return takeInput(required) 

      ##  FOR Exit  ## 
  elif ans in ['ooo', 'OOO']:
    print "Closing instance."
    exit()

  else:
    if ans.isdigit():
      current = 'int'
    elif set('[~!@#$%^&*()_+{}":/\']+$').intersection(ans):
      current = 'other'
    elif isinstance(ans,basestring):
      current = 'str'        
    else:
      current = 'none'

  if required == current :
    return ans
  else:
    return takeInput(required)

## pass the value in which type you want [str/int/special character(as other )]
print "input: ", takeInput('str')

Try this one:-

def takeInput(required):
  print 'ooo or OOO to exit'
  ans = raw_input('Enter: ')

  if not ans:
      print "You entered nothing...!"
      return takeInput(required) 

      ##  FOR Exit  ## 
  elif ans in ['ooo', 'OOO']:
    print "Closing instance."
    exit()

  else:
    if ans.isdigit():
      current = 'int'
    elif set('[~!@#$%^&*()_+{}":/\']+$').intersection(ans):
      current = 'other'
    elif isinstance(ans,basestring):
      current = 'str'        
    else:
      current = 'none'

  if required == current :
    return ans
  else:
    return takeInput(required)

## pass the value in which type you want [str/int/special character(as other )]
print "input: ", takeInput('str')

回答 9

尽管try/ except块可以工作,但使用可以更快,更干净地完成此任务str.isdigit()

while True:
    age = input("Please enter your age: ")
    if age.isdigit():
        age = int(age)
        break
    else:
        print("Invalid number '{age}'. Try again.".format(age=age))

if age >= 18: 
    print("You are able to vote in the United States!")
else:
    print("You are not able to vote in the United States.")

While a try/except block will work, a much faster and cleaner way to accomplish this task would be to use str.isdigit().

while True:
    age = input("Please enter your age: ")
    if age.isdigit():
        age = int(age)
        break
    else:
        print("Invalid number '{age}'. Try again.".format(age=age))

if age >= 18: 
    print("You are able to vote in the United States!")
else:
    print("You are not able to vote in the United States.")

回答 10

好问题!您可以尝试以下代码。=)

此代码使用ast.literal_eval()找到输入的数据类型age)。然后遵循以下算法:

  1. 要求用户输入她/他的age

    1.1。如果agefloatint数据类型:

    • 检查是否age>=18。如果为age>=18,则输出适当的输出并退出。

    • 检查是否0<age<18。如果为0<age<18,则输出适当的输出并退出。

    • 如果为age<=0,则要求用户再次输入有效的年龄编号(返回步骤1)。

    1.2。如果age不是floatint数据类型,则要求用户再次输入他/他的年龄(返回步骤1)。

这是代码。

from ast import literal_eval

''' This function is used to identify the data type of input data.'''
def input_type(input_data):
    try:
        return type(literal_eval(input_data))
    except (ValueError, SyntaxError):
        return str

flag = True

while(flag):
    age = raw_input("Please enter your age: ")

    if input_type(age)==float or input_type(age)==int:
        if eval(age)>=18: 
            print("You are able to vote in the United States!") 
            flag = False 
        elif eval(age)>0 and eval(age)<18: 
            print("You are not able to vote in the United States.") 
            flag = False
        else: print("Please enter a valid number as your age.")

    else: print("Sorry, I didn't understand that.") 

Good question! You can try the following code for this. =)

This code uses ast.literal_eval() to find the data type of the input (age). Then it follows the following algorithm:

  1. Ask user to input her/his age.

    1.1. If age is float or int data type:

    • Check if age>=18. If age>=18, print appropriate output and exit.

    • Check if 0<age<18. If 0<age<18, print appropriate output and exit.

    • If age<=0, ask the user to input a valid number for age again, (i.e. go back to step 1.)

    1.2. If age is not float or int data type, then ask user to input her/his age again (i.e. go back to step 1.)

Here is the code.

from ast import literal_eval

''' This function is used to identify the data type of input data.'''
def input_type(input_data):
    try:
        return type(literal_eval(input_data))
    except (ValueError, SyntaxError):
        return str

flag = True

while(flag):
    age = raw_input("Please enter your age: ")

    if input_type(age)==float or input_type(age)==int:
        if eval(age)>=18: 
            print("You are able to vote in the United States!") 
            flag = False 
        elif eval(age)>0 and eval(age)<18: 
            print("You are not able to vote in the United States.") 
            flag = False
        else: print("Please enter a valid number as your age.")

    else: print("Sorry, I didn't understand that.") 

回答 11

您始终可以应用简单的if-else逻辑,并if在代码和for循环中添加一个或多个逻辑。

while True:
     age = int(input("Please enter your age: "))
     if (age >= 18)  : 
         print("You are able to vote in the United States!")
     if (age < 18) & (age > 0):
         print("You are not able to vote in the United States.")
     else:
         print("Wrong characters, the input must be numeric")
         continue

这将是一个无限的厕所,并且您将被要求无限期地输入年龄。

You can always apply simple if-else logic and add one more if logic to your code along with a for loop.

while True:
     age = int(input("Please enter your age: "))
     if (age >= 18)  : 
         print("You are able to vote in the United States!")
     if (age < 18) & (age > 0):
         print("You are not able to vote in the United States.")
     else:
         print("Wrong characters, the input must be numeric")
         continue

This will be an infinite loo and you would be asked to enter the age, indefinitely.


回答 12

您可以编写更通用的逻辑,以允许用户仅输入特定的次数,因为在许多实际应用程序中会出现相同的用例。

def getValidInt(iMaxAttemps = None):
  iCount = 0
  while True:
    # exit when maximum attempt limit has expired
    if iCount != None and iCount > iMaxAttemps:
       return 0     # return as default value

    i = raw_input("Enter no")
    try:
       i = int(i)
    except ValueError as e:
       print "Enter valid int value"
    else:
       break

    return i

age = getValidInt()
# do whatever you want to do.

You can write more general logic to allow user to enter only specific number of times, as the same use-case arises in many real-world applications.

def getValidInt(iMaxAttemps = None):
  iCount = 0
  while True:
    # exit when maximum attempt limit has expired
    if iCount != None and iCount > iMaxAttemps:
       return 0     # return as default value

    i = raw_input("Enter no")
    try:
       i = int(i)
    except ValueError as e:
       print "Enter valid int value"
    else:
       break

    return i

age = getValidInt()
# do whatever you want to do.

回答 13

您可以将输入语句设置为True循环,以便它反复询问用户输入,然后在用户输入您想要的响应时中断该循环。您可以使用try和except块来处理无效响应。

while True:

    var = True

    try:
        age = int(input("Please enter your age: "))

    except ValueError:
        print("Invalid input.")
        var = False

    if var == True:
        if age >= 18:
                print("You are able to vote in the United States.")
                break
        else:
            print("You are not able to vote in the United States.")

var变量只是这样,如果用户输入字符串而不是整数,程序将不会返回“您无法在美国投票”。

You can make the input statement a while True loop so it repeatedly asks for the users input and then break that loop if the user enters the response you would like. And you can use try and except blocks to handle invalid responses.

while True:

    var = True

    try:
        age = int(input("Please enter your age: "))

    except ValueError:
        print("Invalid input.")
        var = False

    if var == True:
        if age >= 18:
                print("You are able to vote in the United States.")
                break
        else:
            print("You are not able to vote in the United States.")

The var variable is just so that if the user enters a string instead of a integer the program wont return “You are not able to vote in the United States.”


回答 14

使用“ while”语句,直到用户输入一个真值,并且如果输入值不是数字或它是一个空值,请跳过该语句并尝试再次询问,依此类推。例如,我试图真正回答您的问题。如果我们认为年龄在1到150之间,则接受输入值,否则输入的值是错误的。对于终止程序,用户可以使用0键并将其作为值输入。

注意:阅读代码顶部的注释。

# If your input value is only a number then use "Value.isdigit() == False".
# If you need an input that is a text, you should remove "Value.isdigit() == False".
def Input(Message):
    Value = None
    while Value == None or Value.isdigit() == False:
        try:        
            Value = str(input(Message)).strip()
        except InputError:
            Value = None
    return Value

# Example:
age = 0
# If we suppose that our age is between 1 and 150 then input value accepted,
# else it's a wrong value.
while age <=0 or age >150:
    age = int(Input("Please enter your age: "))
    # For terminating program, the user can use 0 key and enter it as an a value.
    if age == 0:
        print("Terminating ...")
        exit(0)

if age >= 18 and age <=150: 
    print("You are able to vote in the United States!")
else:
    print("You are not able to vote in the United States.")

Use “while” statement till user enter a true value and if the input value is not a number or it’s a null value skip it and try to ask again and so on. In example I tried to answer truly your question. If we suppose that our age is between 1 and 150 then input value accepted, else it’s a wrong value. For terminating program, the user can use 0 key and enter it as a value.

Note: Read comments top of code.

# If your input value is only a number then use "Value.isdigit() == False".
# If you need an input that is a text, you should remove "Value.isdigit() == False".
def Input(Message):
    Value = None
    while Value == None or Value.isdigit() == False:
        try:        
            Value = str(input(Message)).strip()
        except InputError:
            Value = None
    return Value

# Example:
age = 0
# If we suppose that our age is between 1 and 150 then input value accepted,
# else it's a wrong value.
while age <=0 or age >150:
    age = int(Input("Please enter your age: "))
    # For terminating program, the user can use 0 key and enter it as an a value.
    if age == 0:
        print("Terminating ...")
        exit(0)

if age >= 18 and age <=150: 
    print("You are able to vote in the United States!")
else:
    print("You are not able to vote in the United States.")

回答 15

使用输入验证的另一种解决方案是ValidationError对整数输入使用定制的(可选)范围验证:

class ValidationError(ValueError): 
    """Special validation error - its message is supposed to be printed"""
    pass

def RangeValidator(text,num,r):
    """Generic validator - raises 'text' as ValidationError if 'num' not in range 'r'."""
    if num in r:
        return num
    raise ValidationError(text)

def ValidCol(c): 
    """Specialized column validator providing text and range."""
    return RangeValidator("Columns must be in the range of 0 to 3 (inclusive)", 
                          c, range(4))

def ValidRow(r): 
    """Specialized row validator providing text and range."""
    return RangeValidator("Rows must be in the range of 5 to 15(exclusive)",
                          r, range(5,15))

用法:

def GetInt(text, validator=None):
    """Aks user for integer input until a valid integer is given. If provided, 
    a 'validator' function takes the integer and either raises a 
    ValidationError to be printed or returns the valid number. 
    Non integers display a simple error message."""
    print()
    while True:
        n = input(text)
        try:
            n = int(n)

            return n if validator is None else validator(n)

        except ValueError as ve:
            # prints ValidationErrors directly - else generic message:
            if isinstance(ve, ValidationError):
                print(ve)
            else:
                print("Invalid input: ", n)


column = GetInt("Pleased enter column: ", ValidCol)
row = GetInt("Pleased enter row: ", ValidRow)
print( row, column)

输出:

Pleased enter column: 22
Columns must be in the range of 0 to 3 (inclusive)
Pleased enter column: -2
Columns must be in the range of 0 to 3 (inclusive)
Pleased enter column: 2
Pleased enter row: a
Invalid input:  a
Pleased enter row: 72
Rows must be in the range of 5 to 15(exclusive)
Pleased enter row: 9  

9, 2

One more solution for using input validation using a customized ValidationError and a (optional) range validation for integer inputs:

class ValidationError(ValueError): 
    """Special validation error - its message is supposed to be printed"""
    pass

def RangeValidator(text,num,r):
    """Generic validator - raises 'text' as ValidationError if 'num' not in range 'r'."""
    if num in r:
        return num
    raise ValidationError(text)

def ValidCol(c): 
    """Specialized column validator providing text and range."""
    return RangeValidator("Columns must be in the range of 0 to 3 (inclusive)", 
                          c, range(4))

def ValidRow(r): 
    """Specialized row validator providing text and range."""
    return RangeValidator("Rows must be in the range of 5 to 15(exclusive)",
                          r, range(5,15))

Usage:

def GetInt(text, validator=None):
    """Aks user for integer input until a valid integer is given. If provided, 
    a 'validator' function takes the integer and either raises a 
    ValidationError to be printed or returns the valid number. 
    Non integers display a simple error message."""
    print()
    while True:
        n = input(text)
        try:
            n = int(n)

            return n if validator is None else validator(n)

        except ValueError as ve:
            # prints ValidationErrors directly - else generic message:
            if isinstance(ve, ValidationError):
                print(ve)
            else:
                print("Invalid input: ", n)


column = GetInt("Pleased enter column: ", ValidCol)
row = GetInt("Pleased enter row: ", ValidRow)
print( row, column)

Output:

Pleased enter column: 22
Columns must be in the range of 0 to 3 (inclusive)
Pleased enter column: -2
Columns must be in the range of 0 to 3 (inclusive)
Pleased enter column: 2
Pleased enter row: a
Invalid input:  a
Pleased enter row: 72
Rows must be in the range of 5 to 15(exclusive)
Pleased enter row: 9  

9, 2

回答 16

这是一个更干净,更通用的解决方案,避免了重复的if / else块:在字典中编写一个接受(错误,错误提示)对的函数,并使用断言进行所有值检查。

def validate_input(prompt, error_map):
    while True:
        try:
            data = int(input(prompt))
            # Insert your non-exception-throwing conditionals here
            assert data > 0
            return data
        # Print whatever text you want the user to see
        # depending on how they messed up
        except tuple(error_map.keys()) as e:
            print(error_map[type(e)])

用法:

d = {ValueError: 'Integers only', AssertionError: 'Positive numbers only', 
     KeyboardInterrupt: 'You can never leave'}
user_input = validate_input("Positive number: ", d)

Here’s a cleaner, more generalized solution that avoids repetitive if/else blocks: write a function that takes (Error, error prompt) pairs in a dictionary and do all your value-checking with assertions.

def validate_input(prompt, error_map):
    while True:
        try:
            data = int(input(prompt))
            # Insert your non-exception-throwing conditionals here
            assert data > 0
            return data
        # Print whatever text you want the user to see
        # depending on how they messed up
        except tuple(error_map.keys()) as e:
            print(error_map[type(e)])

Usage:

d = {ValueError: 'Integers only', AssertionError: 'Positive numbers only', 
     KeyboardInterrupt: 'You can never leave'}
user_input = validate_input("Positive number: ", d)

回答 17

使用递归功能的持久性用户输入:

def askName():
    return input("Write your name: ").strip() or askName()

name = askName()

整数

def askAge():
    try: return int(input("Enter your age: "))
    except ValueError: return askAge()

age = askAge()

最后,问题要求:

def askAge():
    try: return int(input("Enter your age: "))
    except ValueError: return askAge()

age = askAge()

responseAge = [
    "You are able to vote in the United States!",
    "You are not able to vote in the United States.",
][int(age < 18)]

print(responseAge)

Persistent user input using recursive function:

String

def askName():
    return input("Write your name: ").strip() or askName()

name = askName()

Integer

def askAge():
    try: return int(input("Enter your age: "))
    except ValueError: return askAge()

age = askAge()

and finally, the question requirement:

def askAge():
    try: return int(input("Enter your age: "))
    except ValueError: return askAge()

age = askAge()

responseAge = [
    "You are able to vote in the United States!",
    "You are not able to vote in the United States.",
][int(age < 18)]

print(responseAge)

回答 18

简单的解决方案是:

while True:
    age = int(input("Please enter your age: "))

    if (age<=0) or (age>120):
        print('Sorry, I did not understand that.Please try again')
        continue
    else:

        if age>=18:
            print("You are able to vote in the United States!")
        else:
            print("You are not able to vote in the United States.")
        break

上面的代码说明: 为了使年龄有效,它应该是正数,并且不应超过正常的身体年龄,例如,最大年龄为120。

然后,我们可以询问用户年龄,如果年龄输入为负数或大于120,我们将其视为无效输入,然后要求用户重试。

输入有效输入后,我们将检查(使用嵌套的if-else语句)年龄是否大于等于18,反之亦然,并显示一条消息,说明用户是否有资格投票

The simple solution would be:

while True:
    age = int(input("Please enter your age: "))

    if (age<=0) or (age>120):
        print('Sorry, I did not understand that.Please try again')
        continue
    else:

        if age>=18:
            print("You are able to vote in the United States!")
        else:
            print("You are not able to vote in the United States.")
        break

Explanation of above code: In order for a valid age,it should be positive and should not be more than normal physical age,say for example maximum age is 120.

Then we can ask user for age and if age input is negative or more than 120,we consider it invalid input and ask the user to try again.

Once the valid input is entered, we perform a check (using nested if-else statement) whether the age is >=18 or vice versa and print a message whether the user is eligible to vote


回答 19

将输入作为字符串并使用isdigit()来检查输入是否只有数字,不能为空,不能为-ve

while(True):
   #take input as string
   name = input('Enter age : ')
   #check if valid age, only digits
   print( name.isdigit() ) 

run output : 
Enter age : 12
True
Enter age : 
False
Enter age : qwd
False
Enter age : dw3
False
Enter age : 21de
False
Enter age : 1
True
Enter age : -1
False

take input as string and use isdigit() to check input only has digits, not empty, cant be -ve

while(True):
   #take input as string
   name = input('Enter age : ')
   #check if valid age, only digits
   print( name.isdigit() ) 

run output : 
Enter age : 12
True
Enter age : 
False
Enter age : qwd
False
Enter age : dw3
False
Enter age : 21de
False
Enter age : 1
True
Enter age : -1
False


迭代访问列表的最“ pythonic”方法是什么?

问题:迭代访问列表的最“ pythonic”方法是什么?

我有一个Python脚本,它将一个整数列表作为输入,我需要一次处理四个整数。不幸的是,我无法控制输入,或者将其作为四元素元组的列表传递。目前,我正在以这种方式对其进行迭代:

for i in xrange(0, len(ints), 4):
    # dummy op for example code
    foo += ints[i] * ints[i + 1] + ints[i + 2] * ints[i + 3]

不过,它看起来很像“ C思维”,这使我怀疑还有一种处理这种情况的更Python的方法。该列表在迭代后被丢弃,因此不需要保留。也许这样的事情会更好?

while ints:
    foo += ints[0] * ints[1] + ints[2] * ints[3]
    ints[0:4] = []

不过,还是不太“正确”。:-/

相关问题:如何在Python中将列表分成均匀大小的块?

I have a Python script which takes as input a list of integers, which I need to work with four integers at a time. Unfortunately, I don’t have control of the input, or I’d have it passed in as a list of four-element tuples. Currently, I’m iterating over it this way:

for i in xrange(0, len(ints), 4):
    # dummy op for example code
    foo += ints[i] * ints[i + 1] + ints[i + 2] * ints[i + 3]

It looks a lot like “C-think”, though, which makes me suspect there’s a more pythonic way of dealing with this situation. The list is discarded after iterating, so it needn’t be preserved. Perhaps something like this would be better?

while ints:
    foo += ints[0] * ints[1] + ints[2] * ints[3]
    ints[0:4] = []

Still doesn’t quite “feel” right, though. :-/

Related question: How do you split a list into evenly sized chunks in Python?


回答 0

从Python的itertools文档的食谱部分进行了修改:

from itertools import zip_longest

def grouper(iterable, n, fillvalue=None):
    args = [iter(iterable)] * n
    return zip_longest(*args, fillvalue=fillvalue)

示例
用伪代码保持示例简洁。

grouper('ABCDEFG', 3, 'x') --> 'ABC' 'DEF' 'Gxx'

注意:在Python 2上,请使用izip_longest代替zip_longest

Modified from the recipes section of Python’s itertools docs:

from itertools import zip_longest

def grouper(iterable, n, fillvalue=None):
    args = [iter(iterable)] * n
    return zip_longest(*args, fillvalue=fillvalue)

Example
In pseudocode to keep the example terse.

grouper('ABCDEFG', 3, 'x') --> 'ABC' 'DEF' 'Gxx'

Note: on Python 2 use izip_longest instead of zip_longest.


回答 1

def chunker(seq, size):
    return (seq[pos:pos + size] for pos in range(0, len(seq), size))
# (in python 2 use xrange() instead of range() to avoid allocating a list)

简单。简单。快速。适用于任何序列:

text = "I am a very, very helpful text"

for group in chunker(text, 7):
   print repr(group),
# 'I am a ' 'very, v' 'ery hel' 'pful te' 'xt'

print '|'.join(chunker(text, 10))
# I am a ver|y, very he|lpful text

animals = ['cat', 'dog', 'rabbit', 'duck', 'bird', 'cow', 'gnu', 'fish']

for group in chunker(animals, 3):
    print group
# ['cat', 'dog', 'rabbit']
# ['duck', 'bird', 'cow']
# ['gnu', 'fish']
def chunker(seq, size):
    return (seq[pos:pos + size] for pos in range(0, len(seq), size))
# (in python 2 use xrange() instead of range() to avoid allocating a list)

Simple. Easy. Fast. Works with any sequence:

text = "I am a very, very helpful text"

for group in chunker(text, 7):
   print repr(group),
# 'I am a ' 'very, v' 'ery hel' 'pful te' 'xt'

print '|'.join(chunker(text, 10))
# I am a ver|y, very he|lpful text

animals = ['cat', 'dog', 'rabbit', 'duck', 'bird', 'cow', 'gnu', 'fish']

for group in chunker(animals, 3):
    print group
# ['cat', 'dog', 'rabbit']
# ['duck', 'bird', 'cow']
# ['gnu', 'fish']

回答 2

我是的粉丝

chunk_size= 4
for i in range(0, len(ints), chunk_size):
    chunk = ints[i:i+chunk_size]
    # process chunk of size <= chunk_size

I’m a fan of

chunk_size= 4
for i in range(0, len(ints), chunk_size):
    chunk = ints[i:i+chunk_size]
    # process chunk of size <= chunk_size

回答 3

import itertools
def chunks(iterable,size):
    it = iter(iterable)
    chunk = tuple(itertools.islice(it,size))
    while chunk:
        yield chunk
        chunk = tuple(itertools.islice(it,size))

# though this will throw ValueError if the length of ints
# isn't a multiple of four:
for x1,x2,x3,x4 in chunks(ints,4):
    foo += x1 + x2 + x3 + x4

for chunk in chunks(ints,4):
    foo += sum(chunk)

另一种方式:

import itertools
def chunks2(iterable,size,filler=None):
    it = itertools.chain(iterable,itertools.repeat(filler,size-1))
    chunk = tuple(itertools.islice(it,size))
    while len(chunk) == size:
        yield chunk
        chunk = tuple(itertools.islice(it,size))

# x2, x3 and x4 could get the value 0 if the length is not
# a multiple of 4.
for x1,x2,x3,x4 in chunks2(ints,4,0):
    foo += x1 + x2 + x3 + x4
import itertools
def chunks(iterable,size):
    it = iter(iterable)
    chunk = tuple(itertools.islice(it,size))
    while chunk:
        yield chunk
        chunk = tuple(itertools.islice(it,size))

# though this will throw ValueError if the length of ints
# isn't a multiple of four:
for x1,x2,x3,x4 in chunks(ints,4):
    foo += x1 + x2 + x3 + x4

for chunk in chunks(ints,4):
    foo += sum(chunk)

Another way:

import itertools
def chunks2(iterable,size,filler=None):
    it = itertools.chain(iterable,itertools.repeat(filler,size-1))
    chunk = tuple(itertools.islice(it,size))
    while len(chunk) == size:
        yield chunk
        chunk = tuple(itertools.islice(it,size))

# x2, x3 and x4 could get the value 0 if the length is not
# a multiple of 4.
for x1,x2,x3,x4 in chunks2(ints,4,0):
    foo += x1 + x2 + x3 + x4

回答 4

from itertools import izip_longest

def chunker(iterable, chunksize, filler):
    return izip_longest(*[iter(iterable)]*chunksize, fillvalue=filler)
from itertools import izip_longest

def chunker(iterable, chunksize, filler):
    return izip_longest(*[iter(iterable)]*chunksize, fillvalue=filler)

回答 5

此问题的理想解决方案适用于迭代器(而不仅仅是序列)。它也应该很快。

这是itertools文档提供的解决方案:

def grouper(n, iterable, fillvalue=None):
    #"grouper(3, 'ABCDEFG', 'x') --> ABC DEF Gxx"
    args = [iter(iterable)] * n
    return itertools.izip_longest(fillvalue=fillvalue, *args)

%timeit在我的Macbook Air 上使用ipython ,每个循环可获得47.5美元。

但是,这对我来说真的不起作用,因为结果被填充为甚至大小的组。没有填充的解决方案稍微复杂一些。最幼稚的解决方案可能是:

def grouper(size, iterable):
    i = iter(iterable)
    while True:
        out = []
        try:
            for _ in range(size):
                out.append(i.next())
        except StopIteration:
            yield out
            break

        yield out

简单但很慢:每个循环693 us

我可以想出的最佳解决方案islice用于内部循环:

def grouper(size, iterable):
    it = iter(iterable)
    while True:
        group = tuple(itertools.islice(it, None, size))
        if not group:
            break
        yield group

使用相同的数据集,每个循环可获得305 us。

无法以比这更快的速度获得纯解决方案,我为以下解决方案提供了一个重要的警告:如果输入数据中包含实例,filldata则可能会得到错误的答案。

def grouper(n, iterable, fillvalue=None):
    #"grouper(3, 'ABCDEFG', 'x') --> ABC DEF Gxx"
    args = [iter(iterable)] * n
    for i in itertools.izip_longest(fillvalue=fillvalue, *args):
        if tuple(i)[-1] == fillvalue:
            yield tuple(v for v in i if v != fillvalue)
        else:
            yield i

我真的不喜欢这个答案,但是速度更快。每个循环124 us

The ideal solution for this problem works with iterators (not just sequences). It should also be fast.

This is the solution provided by the documentation for itertools:

def grouper(n, iterable, fillvalue=None):
    #"grouper(3, 'ABCDEFG', 'x') --> ABC DEF Gxx"
    args = [iter(iterable)] * n
    return itertools.izip_longest(fillvalue=fillvalue, *args)

Using ipython’s %timeit on my mac book air, I get 47.5 us per loop.

However, this really doesn’t work for me since the results are padded to be even sized groups. A solution without the padding is slightly more complicated. The most naive solution might be:

def grouper(size, iterable):
    i = iter(iterable)
    while True:
        out = []
        try:
            for _ in range(size):
                out.append(i.next())
        except StopIteration:
            yield out
            break

        yield out

Simple, but pretty slow: 693 us per loop

The best solution I could come up with uses islice for the inner loop:

def grouper(size, iterable):
    it = iter(iterable)
    while True:
        group = tuple(itertools.islice(it, None, size))
        if not group:
            break
        yield group

With the same dataset, I get 305 us per loop.

Unable to get a pure solution any faster than that, I provide the following solution with an important caveat: If your input data has instances of filldata in it, you could get wrong answer.

def grouper(n, iterable, fillvalue=None):
    #"grouper(3, 'ABCDEFG', 'x') --> ABC DEF Gxx"
    args = [iter(iterable)] * n
    for i in itertools.izip_longest(fillvalue=fillvalue, *args):
        if tuple(i)[-1] == fillvalue:
            yield tuple(v for v in i if v != fillvalue)
        else:
            yield i

I really don’t like this answer, but it is significantly faster. 124 us per loop


回答 6

我需要一个可以与集合和生成器一起使用的解决方案。我无法提出任何简短而又漂亮的内容,但至少可以理解。

def chunker(seq, size):
    res = []
    for el in seq:
        res.append(el)
        if len(res) == size:
            yield res
            res = []
    if res:
        yield res

清单:

>>> list(chunker([i for i in range(10)], 3))
[[0, 1, 2], [3, 4, 5], [6, 7, 8], [9]]

组:

>>> list(chunker(set([i for i in range(10)]), 3))
[[0, 1, 2], [3, 4, 5], [6, 7, 8], [9]]

生成器:

>>> list(chunker((i for i in range(10)), 3))
[[0, 1, 2], [3, 4, 5], [6, 7, 8], [9]]

I needed a solution that would also work with sets and generators. I couldn’t come up with anything very short and pretty, but it’s quite readable at least.

def chunker(seq, size):
    res = []
    for el in seq:
        res.append(el)
        if len(res) == size:
            yield res
            res = []
    if res:
        yield res

List:

>>> list(chunker([i for i in range(10)], 3))
[[0, 1, 2], [3, 4, 5], [6, 7, 8], [9]]

Set:

>>> list(chunker(set([i for i in range(10)]), 3))
[[0, 1, 2], [3, 4, 5], [6, 7, 8], [9]]

Generator:

>>> list(chunker((i for i in range(10)), 3))
[[0, 1, 2], [3, 4, 5], [6, 7, 8], [9]]

回答 7

与其他建议类似,但不完全相同,我喜欢这样做,因为它简单易读:

it = iter([1, 2, 3, 4, 5, 6, 7, 8, 9])
for chunk in zip(it, it, it, it):
    print chunk

>>> (1, 2, 3, 4)
>>> (5, 6, 7, 8)

这样,您将不会得到最后的部分块。如果要获取(9, None, None, None)最后一块,只需使用izip_longestfrom即可itertools

Similar to other proposals, but not exactly identical, I like doing it this way, because it’s simple and easy to read:

it = iter([1, 2, 3, 4, 5, 6, 7, 8, 9])
for chunk in zip(it, it, it, it):
    print chunk

>>> (1, 2, 3, 4)
>>> (5, 6, 7, 8)

This way you won’t get the last partial chunk. If you want to get (9, None, None, None) as last chunk, just use izip_longest from itertools.


回答 8

如果您不介意使用外部软件包,则可以iteration_utilities.grouper1开始使用。它支持所有可迭代项(不仅限于序列):iteration_utilties

from iteration_utilities import grouper
seq = list(range(20))
for group in grouper(seq, 4):
    print(group)

打印:

(0, 1, 2, 3)
(4, 5, 6, 7)
(8, 9, 10, 11)
(12, 13, 14, 15)
(16, 17, 18, 19)

如果长度不是分组大小的倍数,则它还支持填充(不完整的最后一组)或截断(丢弃不完整的最后一组)最后一个:

from iteration_utilities import grouper
seq = list(range(17))
for group in grouper(seq, 4):
    print(group)
# (0, 1, 2, 3)
# (4, 5, 6, 7)
# (8, 9, 10, 11)
# (12, 13, 14, 15)
# (16,)

for group in grouper(seq, 4, fillvalue=None):
    print(group)
# (0, 1, 2, 3)
# (4, 5, 6, 7)
# (8, 9, 10, 11)
# (12, 13, 14, 15)
# (16, None, None, None)

for group in grouper(seq, 4, truncate=True):
    print(group)
# (0, 1, 2, 3)
# (4, 5, 6, 7)
# (8, 9, 10, 11)
# (12, 13, 14, 15)

基准测试

我还决定比较上述几种方法的运行时间。这是一个对数-对数图,根据大小不同的列表分为“ 10”个元素组。对于定性结果:较低意味着更快:

在此处输入图片说明

至少在此基准测试中,iteration_utilities.grouper效果最佳。其次是疯狂的方法。

基准是使用1创建的。用于运行该基准测试的代码为:simple_benchmark

import iteration_utilities
import itertools
from itertools import zip_longest

def consume_all(it):
    return iteration_utilities.consume(it, None)

import simple_benchmark
b = simple_benchmark.BenchmarkBuilder()

@b.add_function()
def grouper(l, n):
    return consume_all(iteration_utilities.grouper(l, n))

def Craz_inner(iterable, n, fillvalue=None):
    args = [iter(iterable)] * n
    return zip_longest(*args, fillvalue=fillvalue)

@b.add_function()
def Craz(iterable, n, fillvalue=None):
    return consume_all(Craz_inner(iterable, n, fillvalue))

def nosklo_inner(seq, size):
    return (seq[pos:pos + size] for pos in range(0, len(seq), size))

@b.add_function()
def nosklo(seq, size):
    return consume_all(nosklo_inner(seq, size))

def SLott_inner(ints, chunk_size):
    for i in range(0, len(ints), chunk_size):
        yield ints[i:i+chunk_size]

@b.add_function()
def SLott(ints, chunk_size):
    return consume_all(SLott_inner(ints, chunk_size))

def MarkusJarderot1_inner(iterable,size):
    it = iter(iterable)
    chunk = tuple(itertools.islice(it,size))
    while chunk:
        yield chunk
        chunk = tuple(itertools.islice(it,size))

@b.add_function()
def MarkusJarderot1(iterable,size):
    return consume_all(MarkusJarderot1_inner(iterable,size))

def MarkusJarderot2_inner(iterable,size,filler=None):
    it = itertools.chain(iterable,itertools.repeat(filler,size-1))
    chunk = tuple(itertools.islice(it,size))
    while len(chunk) == size:
        yield chunk
        chunk = tuple(itertools.islice(it,size))

@b.add_function()
def MarkusJarderot2(iterable,size):
    return consume_all(MarkusJarderot2_inner(iterable,size))

@b.add_arguments()
def argument_provider():
    for exp in range(2, 20):
        size = 2**exp
        yield size, simple_benchmark.MultiArgument([[0] * size, 10])

r = b.run()

1免责声明:我是图书馆的作者iteration_utilitiessimple_benchmark

If you don’t mind using an external package you could use iteration_utilities.grouper from iteration_utilties 1. It supports all iterables (not just sequences):

from iteration_utilities import grouper
seq = list(range(20))
for group in grouper(seq, 4):
    print(group)

which prints:

(0, 1, 2, 3)
(4, 5, 6, 7)
(8, 9, 10, 11)
(12, 13, 14, 15)
(16, 17, 18, 19)

In case the length isn’t a multiple of the groupsize it also supports filling (the incomplete last group) or truncating (discarding the incomplete last group) the last one:

from iteration_utilities import grouper
seq = list(range(17))
for group in grouper(seq, 4):
    print(group)
# (0, 1, 2, 3)
# (4, 5, 6, 7)
# (8, 9, 10, 11)
# (12, 13, 14, 15)
# (16,)

for group in grouper(seq, 4, fillvalue=None):
    print(group)
# (0, 1, 2, 3)
# (4, 5, 6, 7)
# (8, 9, 10, 11)
# (12, 13, 14, 15)
# (16, None, None, None)

for group in grouper(seq, 4, truncate=True):
    print(group)
# (0, 1, 2, 3)
# (4, 5, 6, 7)
# (8, 9, 10, 11)
# (12, 13, 14, 15)

Benchmarks

I also decided to compare the run-time of a few of the mentioned approaches. It’s a log-log plot grouping into groups of “10” elements based on a list of varying size. For qualitative results: Lower means faster:

enter image description here

At least in this benchmark the iteration_utilities.grouper performs best. Followed by the approach of Craz.

The benchmark was created with simple_benchmark1. The code used to run this benchmark was:

import iteration_utilities
import itertools
from itertools import zip_longest

def consume_all(it):
    return iteration_utilities.consume(it, None)

import simple_benchmark
b = simple_benchmark.BenchmarkBuilder()

@b.add_function()
def grouper(l, n):
    return consume_all(iteration_utilities.grouper(l, n))

def Craz_inner(iterable, n, fillvalue=None):
    args = [iter(iterable)] * n
    return zip_longest(*args, fillvalue=fillvalue)

@b.add_function()
def Craz(iterable, n, fillvalue=None):
    return consume_all(Craz_inner(iterable, n, fillvalue))

def nosklo_inner(seq, size):
    return (seq[pos:pos + size] for pos in range(0, len(seq), size))

@b.add_function()
def nosklo(seq, size):
    return consume_all(nosklo_inner(seq, size))

def SLott_inner(ints, chunk_size):
    for i in range(0, len(ints), chunk_size):
        yield ints[i:i+chunk_size]

@b.add_function()
def SLott(ints, chunk_size):
    return consume_all(SLott_inner(ints, chunk_size))

def MarkusJarderot1_inner(iterable,size):
    it = iter(iterable)
    chunk = tuple(itertools.islice(it,size))
    while chunk:
        yield chunk
        chunk = tuple(itertools.islice(it,size))

@b.add_function()
def MarkusJarderot1(iterable,size):
    return consume_all(MarkusJarderot1_inner(iterable,size))

def MarkusJarderot2_inner(iterable,size,filler=None):
    it = itertools.chain(iterable,itertools.repeat(filler,size-1))
    chunk = tuple(itertools.islice(it,size))
    while len(chunk) == size:
        yield chunk
        chunk = tuple(itertools.islice(it,size))

@b.add_function()
def MarkusJarderot2(iterable,size):
    return consume_all(MarkusJarderot2_inner(iterable,size))

@b.add_arguments()
def argument_provider():
    for exp in range(2, 20):
        size = 2**exp
        yield size, simple_benchmark.MultiArgument([[0] * size, 10])

r = b.run()

1 Disclaimer: I’m the author of the libraries iteration_utilities and simple_benchmark.


回答 9

由于没有人提到它,所以这里有一个zip()解决方案:

>>> def chunker(iterable, chunksize):
...     return zip(*[iter(iterable)]*chunksize)

仅当序列的长度始终可被块大小整除或不关心尾随的块时,它才起作用。

例:

>>> s = '1234567890'
>>> chunker(s, 3)
[('1', '2', '3'), ('4', '5', '6'), ('7', '8', '9')]
>>> chunker(s, 4)
[('1', '2', '3', '4'), ('5', '6', '7', '8')]
>>> chunker(s, 5)
[('1', '2', '3', '4', '5'), ('6', '7', '8', '9', '0')]

或使用itertools.izip返回迭代器而不是列表:

>>> from itertools import izip
>>> def chunker(iterable, chunksize):
...     return izip(*[iter(iterable)]*chunksize)

可以使用@▼ZΩΤZΙΟΥ的答案来固定填充:

>>> from itertools import chain, izip, repeat
>>> def chunker(iterable, chunksize, fillvalue=None):
...     it   = chain(iterable, repeat(fillvalue, chunksize-1))
...     args = [it] * chunksize
...     return izip(*args)

Since nobody’s mentioned it yet here’s a zip() solution:

>>> def chunker(iterable, chunksize):
...     return zip(*[iter(iterable)]*chunksize)

It works only if your sequence’s length is always divisible by the chunk size or you don’t care about a trailing chunk if it isn’t.

Example:

>>> s = '1234567890'
>>> chunker(s, 3)
[('1', '2', '3'), ('4', '5', '6'), ('7', '8', '9')]
>>> chunker(s, 4)
[('1', '2', '3', '4'), ('5', '6', '7', '8')]
>>> chunker(s, 5)
[('1', '2', '3', '4', '5'), ('6', '7', '8', '9', '0')]

Or using itertools.izip to return an iterator instead of a list:

>>> from itertools import izip
>>> def chunker(iterable, chunksize):
...     return izip(*[iter(iterable)]*chunksize)

Padding can be fixed using @ΤΖΩΤΖΙΟΥ’s answer:

>>> from itertools import chain, izip, repeat
>>> def chunker(iterable, chunksize, fillvalue=None):
...     it   = chain(iterable, repeat(fillvalue, chunksize-1))
...     args = [it] * chunksize
...     return izip(*args)

回答 10

使用map()而不是zip()可解决JF Sebastian的答案中的填充问题:

>>> def chunker(iterable, chunksize):
...   return map(None,*[iter(iterable)]*chunksize)

例:

>>> s = '1234567890'
>>> chunker(s, 3)
[('1', '2', '3'), ('4', '5', '6'), ('7', '8', '9'), ('0', None, None)]
>>> chunker(s, 4)
[('1', '2', '3', '4'), ('5', '6', '7', '8'), ('9', '0', None, None)]
>>> chunker(s, 5)
[('1', '2', '3', '4', '5'), ('6', '7', '8', '9', '0')]

Using map() instead of zip() fixes the padding issue in J.F. Sebastian’s answer:

>>> def chunker(iterable, chunksize):
...   return map(None,*[iter(iterable)]*chunksize)

Example:

>>> s = '1234567890'
>>> chunker(s, 3)
[('1', '2', '3'), ('4', '5', '6'), ('7', '8', '9'), ('0', None, None)]
>>> chunker(s, 4)
[('1', '2', '3', '4'), ('5', '6', '7', '8'), ('9', '0', None, None)]
>>> chunker(s, 5)
[('1', '2', '3', '4', '5'), ('6', '7', '8', '9', '0')]

回答 11

另一种方法是使用以下两个参数的形式iter

from itertools import islice

def group(it, size):
    it = iter(it)
    return iter(lambda: tuple(islice(it, size)), ())

可以很容易地调整它以使用填充(这类似于Markus Jarderot的回答):

from itertools import islice, chain, repeat

def group_pad(it, size, pad=None):
    it = chain(iter(it), repeat(pad))
    return iter(lambda: tuple(islice(it, size)), (pad,) * size)

这些甚至可以结合起来用于可选的填充:

_no_pad = object()
def group(it, size, pad=_no_pad):
    if pad == _no_pad:
        it = iter(it)
        sentinel = ()
    else:
        it = chain(iter(it), repeat(pad))
        sentinel = (pad,) * size
    return iter(lambda: tuple(islice(it, size)), sentinel)

Another approach would be to use the two-argument form of iter:

from itertools import islice

def group(it, size):
    it = iter(it)
    return iter(lambda: tuple(islice(it, size)), ())

This can be adapted easily to use padding (this is similar to Markus Jarderot’s answer):

from itertools import islice, chain, repeat

def group_pad(it, size, pad=None):
    it = chain(iter(it), repeat(pad))
    return iter(lambda: tuple(islice(it, size)), (pad,) * size)

These can even be combined for optional padding:

_no_pad = object()
def group(it, size, pad=_no_pad):
    if pad == _no_pad:
        it = iter(it)
        sentinel = ()
    else:
        it = chain(iter(it), repeat(pad))
        sentinel = (pad,) * size
    return iter(lambda: tuple(islice(it, size)), sentinel)

回答 12

如果列表很大,执行此操作的最高性能方法是使用生成器:

def get_chunk(iterable, chunk_size):
    result = []
    for item in iterable:
        result.append(item)
        if len(result) == chunk_size:
            yield tuple(result)
            result = []
    if len(result) > 0:
        yield tuple(result)

for x in get_chunk([1,2,3,4,5,6,7,8,9,10], 3):
    print x

(1, 2, 3)
(4, 5, 6)
(7, 8, 9)
(10,)

If the list is large, the highest-performing way to do this will be to use a generator:

def get_chunk(iterable, chunk_size):
    result = []
    for item in iterable:
        result.append(item)
        if len(result) == chunk_size:
            yield tuple(result)
            result = []
    if len(result) > 0:
        yield tuple(result)

for x in get_chunk([1,2,3,4,5,6,7,8,9,10], 3):
    print x

(1, 2, 3)
(4, 5, 6)
(7, 8, 9)
(10,)

回答 13

使用小功能和事情确实对我没有吸引力。我更喜欢只使用切片:

data = [...]
chunk_size = 10000 # or whatever
chunks = [data[i:i+chunk_size] for i in xrange(0,len(data),chunk_size)]
for chunk in chunks:
    ...

Using little functions and things really doesn’t appeal to me; I prefer to just use slices:

data = [...]
chunk_size = 10000 # or whatever
chunks = [data[i:i+chunk_size] for i in xrange(0,len(data),chunk_size)]
for chunk in chunks:
    ...

回答 14

为了避免所有转换为列表,import itertools并且:

>>> for k, g in itertools.groupby(xrange(35), lambda x: x/10):
...     list(g)

生成:

... 
0 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
1 [10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
2 [20, 21, 22, 23, 24, 25, 26, 27, 28, 29]
3 [30, 31, 32, 33, 34]
>>> 

我检查了一下groupby,它没有转换为列表或使用len所以我(认为)这将延迟每个值的解析,直到实际使用它为止。可悲的是,目前没有可用的答案似乎提供这种变化。

显然,如果您需要依次处理每个项目,请在g上嵌套for循环:

for k,g in itertools.groupby(xrange(35), lambda x: x/10):
    for i in g:
       # do what you need to do with individual items
    # now do what you need to do with the whole group

我对此的特别兴趣是需要消耗一个生成器才能将最多1000个更改批量提交给gmail API:

    messages = a_generator_which_would_not_be_smart_as_a_list
    for idx, batch in groupby(messages, lambda x: x/1000):
        batch_request = BatchHttpRequest()
        for message in batch:
            batch_request.add(self.service.users().messages().modify(userId='me', id=message['id'], body=msg_labels))
        http = httplib2.Http()
        self.credentials.authorize(http)
        batch_request.execute(http=http)

To avoid all conversions to a list import itertools and:

>>> for k, g in itertools.groupby(xrange(35), lambda x: x/10):
...     list(g)

Produces:

... 
0 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
1 [10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
2 [20, 21, 22, 23, 24, 25, 26, 27, 28, 29]
3 [30, 31, 32, 33, 34]
>>> 

I checked groupby and it doesn’t convert to list or use len so I (think) this will delay resolution of each value until it is actually used. Sadly none of the available answers (at this time) seemed to offer this variation.

Obviously if you need to handle each item in turn nest a for loop over g:

for k,g in itertools.groupby(xrange(35), lambda x: x/10):
    for i in g:
       # do what you need to do with individual items
    # now do what you need to do with the whole group

My specific interest in this was the need to consume a generator to submit changes in batches of up to 1000 to the gmail API:

    messages = a_generator_which_would_not_be_smart_as_a_list
    for idx, batch in groupby(messages, lambda x: x/1000):
        batch_request = BatchHttpRequest()
        for message in batch:
            batch_request.add(self.service.users().messages().modify(userId='me', id=message['id'], body=msg_labels))
        http = httplib2.Http()
        self.credentials.authorize(http)
        batch_request.execute(http=http)

回答 15

使用NumPy很简单:

ints = array([1, 2, 3, 4, 5, 6, 7, 8])
for int1, int2 in ints.reshape(-1, 2):
    print(int1, int2)

输出:

1 2
3 4
5 6
7 8

With NumPy it’s simple:

ints = array([1, 2, 3, 4, 5, 6, 7, 8])
for int1, int2 in ints.reshape(-1, 2):
    print(int1, int2)

output:

1 2
3 4
5 6
7 8

回答 16

def chunker(iterable, n):
    """Yield iterable in chunk sizes.

    >>> chunks = chunker('ABCDEF', n=4)
    >>> chunks.next()
    ['A', 'B', 'C', 'D']
    >>> chunks.next()
    ['E', 'F']
    """
    it = iter(iterable)
    while True:
        chunk = []
        for i in range(n):
            try:
                chunk.append(next(it))
            except StopIteration:
                yield chunk
                raise StopIteration
        yield chunk

if __name__ == '__main__':
    import doctest

    doctest.testmod()
def chunker(iterable, n):
    """Yield iterable in chunk sizes.

    >>> chunks = chunker('ABCDEF', n=4)
    >>> chunks.next()
    ['A', 'B', 'C', 'D']
    >>> chunks.next()
    ['E', 'F']
    """
    it = iter(iterable)
    while True:
        chunk = []
        for i in range(n):
            try:
                chunk.append(next(it))
            except StopIteration:
                yield chunk
                raise StopIteration
        yield chunk

if __name__ == '__main__':
    import doctest

    doctest.testmod()

回答 17

除非我错过任何事情,否则不会提及以下带有生成器表达式的简单解决方案。它假定块的大小和数量都是已知的(通常是这种情况),并且不需要填充:

def chunks(it, n, m):
    """Make an iterator over m first chunks of size n.
    """
    it = iter(it)
    # Chunks are presented as tuples.
    return (tuple(next(it) for _ in range(n)) for _ in range(m))

Unless I misses something, the following simple solution with generator expressions has not been mentioned. It assumes that both the size and the number of chunks are known (which is often the case), and that no padding is required:

def chunks(it, n, m):
    """Make an iterator over m first chunks of size n.
    """
    it = iter(it)
    # Chunks are presented as tuples.
    return (tuple(next(it) for _ in range(n)) for _ in range(m))

回答 18

在您的第二种方法中,我将通过执行以下操作进入下一组4:

ints = ints[4:]

但是,我还没有进行任何性能评估,所以我不知道哪个效率更高。

话虽如此,我通常会选择第一种方法。这不是很漂亮,但这通常是与外界交互的结果。

In your second method, I would advance to the next group of 4 by doing this:

ints = ints[4:]

However, I haven’t done any performance measurement so I don’t know which one might be more efficient.

Having said that, I would usually choose the first method. It’s not pretty, but that’s often a consequence of interfacing with the outside world.


回答 19

另一个答案,其优点是:

1)易于理解
2)可以处理任何可迭代的对象,而不仅是序列(上面的一些回答会在文件句柄上阻塞)
3)不会一次将块全部加载到内存中
4)不会对引用进行大块的长列表内存中的相同迭代器
5)列表末尾没有填充值

话虽这么说,我还没有计时,所以它可能比一些更聪明的方法要慢,并且某些优点可能与用例无关。

def chunkiter(iterable, size):
  def inneriter(first, iterator, size):
    yield first
    for _ in xrange(size - 1): 
      yield iterator.next()
  it = iter(iterable)
  while True:
    yield inneriter(it.next(), it, size)

In [2]: i = chunkiter('abcdefgh', 3)
In [3]: for ii in i:                                                
          for c in ii:
            print c,
          print ''
        ...:     
        a b c 
        d e f 
        g h 

更新:
由于内循环和外循环从同一个迭代器中提取值而造成的一些弊端:
1)继续无法在外循环中按预期方式工作-继续执行下一个项目而不是跳过一个块。但是,这似乎不是问题,因为在外循环中没有要测试的东西。
2)break不能在内部循环中按预期方式工作-控件将在迭代器中的下一个项目中再次进入内部循环。要跳过整个块,可以将内部迭代器(上面的ii)包装在一个元组中,例如for c in tuple(ii),或者设置一个标志并耗尽迭代器。

Yet another answer, the advantages of which are:

1) Easily understandable
2) Works on any iterable, not just sequences (some of the above answers will choke on filehandles)
3) Does not load the chunk into memory all at once
4) Does not make a chunk-long list of references to the same iterator in memory
5) No padding of fill values at the end of the list

That being said, I haven’t timed it so it might be slower than some of the more clever methods, and some of the advantages may be irrelevant given the use case.

def chunkiter(iterable, size):
  def inneriter(first, iterator, size):
    yield first
    for _ in xrange(size - 1): 
      yield iterator.next()
  it = iter(iterable)
  while True:
    yield inneriter(it.next(), it, size)

In [2]: i = chunkiter('abcdefgh', 3)
In [3]: for ii in i:                                                
          for c in ii:
            print c,
          print ''
        ...:     
        a b c 
        d e f 
        g h 

Update:
A couple of drawbacks due to the fact the inner and outer loops are pulling values from the same iterator:
1) continue doesn’t work as expected in the outer loop – it just continues on to the next item rather than skipping a chunk. However, this doesn’t seem like a problem as there’s nothing to test in the outer loop.
2) break doesn’t work as expected in the inner loop – control will wind up in the inner loop again with the next item in the iterator. To skip whole chunks, either wrap the inner iterator (ii above) in a tuple, e.g. for c in tuple(ii), or set a flag and exhaust the iterator.


回答 20

def group_by(iterable, size):
    """Group an iterable into lists that don't exceed the size given.

    >>> group_by([1,2,3,4,5], 2)
    [[1, 2], [3, 4], [5]]

    """
    sublist = []

    for index, item in enumerate(iterable):
        if index > 0 and index % size == 0:
            yield sublist
            sublist = []

        sublist.append(item)

    if sublist:
        yield sublist
def group_by(iterable, size):
    """Group an iterable into lists that don't exceed the size given.

    >>> group_by([1,2,3,4,5], 2)
    [[1, 2], [3, 4], [5]]

    """
    sublist = []

    for index, item in enumerate(iterable):
        if index > 0 and index % size == 0:
            yield sublist
            sublist = []

        sublist.append(item)

    if sublist:
        yield sublist

回答 21

您可以从funcy库中使用分区功能:

from funcy import partition

for a, b, c, d in partition(4, ints):
    foo += a * b * c * d

这些函数还具有迭代器版本ipartitionichunks,在这种情况下将更加高效。

您也可以查看它们的实现

You can use partition or chunks function from funcy library:

from funcy import partition

for a, b, c, d in partition(4, ints):
    foo += a * b * c * d

These functions also has iterator versions ipartition and ichunks, which will be more efficient in this case.

You can also peek at their implementation.


回答 22

关于J.F. Sebastian 这里给的解决方案:

def chunker(iterable, chunksize):
    return zip(*[iter(iterable)]*chunksize)

它很聪明,但是有一个缺点-总是返回元组。如何获取字符串呢?
当然您可以编写''.join(chunker(...)),但是无论如何都构造了临时元组。

您可以通过编写own来摆脱临时元组zip,如下所示:

class IteratorExhausted(Exception):
    pass

def translate_StopIteration(iterable, to=IteratorExhausted):
    for i in iterable:
        yield i
    raise to # StopIteration would get ignored because this is generator,
             # but custom exception can leave the generator.

def custom_zip(*iterables, reductor=tuple):
    iterators = tuple(map(translate_StopIteration, iterables))
    while True:
        try:
            yield reductor(next(i) for i in iterators)
        except IteratorExhausted: # when any of iterators get exhausted.
            break

然后

def chunker(data, size, reductor=tuple):
    return custom_zip(*[iter(data)]*size, reductor=reductor)

用法示例:

>>> for i in chunker('12345', 2):
...     print(repr(i))
...
('1', '2')
('3', '4')
>>> for i in chunker('12345', 2, ''.join):
...     print(repr(i))
...
'12'
'34'

About solution gave by J.F. Sebastian here:

def chunker(iterable, chunksize):
    return zip(*[iter(iterable)]*chunksize)

It’s clever, but has one disadvantage – always return tuple. How to get string instead?
Of course you can write ''.join(chunker(...)), but the temporary tuple is constructed anyway.

You can get rid of the temporary tuple by writing own zip, like this:

class IteratorExhausted(Exception):
    pass

def translate_StopIteration(iterable, to=IteratorExhausted):
    for i in iterable:
        yield i
    raise to # StopIteration would get ignored because this is generator,
             # but custom exception can leave the generator.

def custom_zip(*iterables, reductor=tuple):
    iterators = tuple(map(translate_StopIteration, iterables))
    while True:
        try:
            yield reductor(next(i) for i in iterators)
        except IteratorExhausted: # when any of iterators get exhausted.
            break

Then

def chunker(data, size, reductor=tuple):
    return custom_zip(*[iter(data)]*size, reductor=reductor)

Example usage:

>>> for i in chunker('12345', 2):
...     print(repr(i))
...
('1', '2')
('3', '4')
>>> for i in chunker('12345', 2, ''.join):
...     print(repr(i))
...
'12'
'34'

回答 23

我喜欢这种方法。它感觉简单而不是魔术,并且支持所有可迭代的类型,并且不需要导入。

def chunk_iter(iterable, chunk_size):
it = iter(iterable)
while True:
    chunk = tuple(next(it) for _ in range(chunk_size))
    if not chunk:
        break
    yield chunk

I like this approach. It feels simple and not magical and supports all iterable types and doesn’t require imports.

def chunk_iter(iterable, chunk_size):
it = iter(iterable)
while True:
    chunk = tuple(next(it) for _ in range(chunk_size))
    if not chunk:
        break
    yield chunk

回答 24

我从不希望填充我的数据块,因此这一要求至关重要。我发现还需要具有处理任何迭代的能力。鉴于此,我决定扩展接受的答案,https://stackoverflow.com/a/434411/1074659

如果由于需要比较和过滤填充值而不需要填充,则此方法的性能会受到轻微影响。但是,对于大块数据,此实用程序非常有效。

#!/usr/bin/env python3
from itertools import zip_longest


_UNDEFINED = object()


def chunker(iterable, chunksize, fillvalue=_UNDEFINED):
    """
    Collect data into chunks and optionally pad it.

    Performance worsens as `chunksize` approaches 1.

    Inspired by:
        https://docs.python.org/3/library/itertools.html#itertools-recipes

    """
    args = [iter(iterable)] * chunksize
    chunks = zip_longest(*args, fillvalue=fillvalue)
    yield from (
        filter(lambda val: val is not _UNDEFINED, chunk)
        if chunk[-1] is _UNDEFINED
        else chunk
        for chunk in chunks
    ) if fillvalue is _UNDEFINED else chunks

I never want my chunks padded, so that requirement is essential. I find that the ability to work on any iterable is also requirement. Given that, I decided to extend on the accepted answer, https://stackoverflow.com/a/434411/1074659.

Performance takes a slight hit in this approach if padding is not wanted due to the need to compare and filter the padded values. However, for large chunk sizes, this utility is very performant.

#!/usr/bin/env python3
from itertools import zip_longest


_UNDEFINED = object()


def chunker(iterable, chunksize, fillvalue=_UNDEFINED):
    """
    Collect data into chunks and optionally pad it.

    Performance worsens as `chunksize` approaches 1.

    Inspired by:
        https://docs.python.org/3/library/itertools.html#itertools-recipes

    """
    args = [iter(iterable)] * chunksize
    chunks = zip_longest(*args, fillvalue=fillvalue)
    yield from (
        filter(lambda val: val is not _UNDEFINED, chunk)
        if chunk[-1] is _UNDEFINED
        else chunk
        for chunk in chunks
    ) if fillvalue is _UNDEFINED else chunks

回答 25

这是一个没有导入功能的分块器,它支持生成器:

def chunks(seq, size):
    it = iter(seq)
    while True:
        ret = tuple(next(it) for _ in range(size))
        if len(ret) == size:
            yield ret
        else:
            raise StopIteration()

使用示例:

>>> def foo():
...     i = 0
...     while True:
...         i += 1
...         yield i
...
>>> c = chunks(foo(), 3)
>>> c.next()
(1, 2, 3)
>>> c.next()
(4, 5, 6)
>>> list(chunks('abcdefg', 2))
[('a', 'b'), ('c', 'd'), ('e', 'f')]

Here is a chunker without imports that supports generators:

def chunks(seq, size):
    it = iter(seq)
    while True:
        ret = tuple(next(it) for _ in range(size))
        if len(ret) == size:
            yield ret
        else:
            raise StopIteration()

Example of use:

>>> def foo():
...     i = 0
...     while True:
...         i += 1
...         yield i
...
>>> c = chunks(foo(), 3)
>>> c.next()
(1, 2, 3)
>>> c.next()
(4, 5, 6)
>>> list(chunks('abcdefg', 2))
[('a', 'b'), ('c', 'd'), ('e', 'f')]

回答 26

在Python 3.8中,您可以使用walrus运算符和itertools.islice

from itertools import islice

list_ = [i for i in range(10, 100)]

def chunker(it, size):
    iterator = iter(it)
    while chunk := list(islice(iterator, size)):
        print(chunk)
In [2]: chunker(list_, 10)                                                         
[10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
[20, 21, 22, 23, 24, 25, 26, 27, 28, 29]
[30, 31, 32, 33, 34, 35, 36, 37, 38, 39]
[40, 41, 42, 43, 44, 45, 46, 47, 48, 49]
[50, 51, 52, 53, 54, 55, 56, 57, 58, 59]
[60, 61, 62, 63, 64, 65, 66, 67, 68, 69]
[70, 71, 72, 73, 74, 75, 76, 77, 78, 79]
[80, 81, 82, 83, 84, 85, 86, 87, 88, 89]
[90, 91, 92, 93, 94, 95, 96, 97, 98, 99]

With Python 3.8 you can use the walrus operator and itertools.islice.

from itertools import islice

list_ = [i for i in range(10, 100)]

def chunker(it, size):
    iterator = iter(it)
    while chunk := list(islice(iterator, size)):
        print(chunk)
In [2]: chunker(list_, 10)                                                         
[10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
[20, 21, 22, 23, 24, 25, 26, 27, 28, 29]
[30, 31, 32, 33, 34, 35, 36, 37, 38, 39]
[40, 41, 42, 43, 44, 45, 46, 47, 48, 49]
[50, 51, 52, 53, 54, 55, 56, 57, 58, 59]
[60, 61, 62, 63, 64, 65, 66, 67, 68, 69]
[70, 71, 72, 73, 74, 75, 76, 77, 78, 79]
[80, 81, 82, 83, 84, 85, 86, 87, 88, 89]
[90, 91, 92, 93, 94, 95, 96, 97, 98, 99]


回答 27

似乎没有做到这一点的漂亮方法。 是一个包含许多方法的页面,包括:

def split_seq(seq, size):
    newseq = []
    splitsize = 1.0/size*len(seq)
    for i in range(size):
        newseq.append(seq[int(round(i*splitsize)):int(round((i+1)*splitsize))])
    return newseq

There doesn’t seem to be a pretty way to do this. Here is a page that has a number of methods, including:

def split_seq(seq, size):
    newseq = []
    splitsize = 1.0/size*len(seq)
    for i in range(size):
        newseq.append(seq[int(round(i*splitsize)):int(round((i+1)*splitsize))])
    return newseq

回答 28

如果列表大小相同,则可以将它们组合成4元组的列表zip()。例如:

# Four lists of four elements each.

l1 = range(0, 4)
l2 = range(4, 8)
l3 = range(8, 12)
l4 = range(12, 16)

for i1, i2, i3, i4 in zip(l1, l2, l3, l4):
    ...

下面是什么zip()函数生成:

>>> print l1
[0, 1, 2, 3]
>>> print l2
[4, 5, 6, 7]
>>> print l3
[8, 9, 10, 11]
>>> print l4
[12, 13, 14, 15]
>>> print zip(l1, l2, l3, l4)
[(0, 4, 8, 12), (1, 5, 9, 13), (2, 6, 10, 14), (3, 7, 11, 15)]

如果列表很大,并且您不想将它们组合成更大的列表,请使用itertools.izip(),它会生成一个迭代器,而不是列表。

from itertools import izip

for i1, i2, i3, i4 in izip(l1, l2, l3, l4):
    ...

If the lists are the same size, you can combine them into lists of 4-tuples with zip(). For example:

# Four lists of four elements each.

l1 = range(0, 4)
l2 = range(4, 8)
l3 = range(8, 12)
l4 = range(12, 16)

for i1, i2, i3, i4 in zip(l1, l2, l3, l4):
    ...

Here’s what the zip() function produces:

>>> print l1
[0, 1, 2, 3]
>>> print l2
[4, 5, 6, 7]
>>> print l3
[8, 9, 10, 11]
>>> print l4
[12, 13, 14, 15]
>>> print zip(l1, l2, l3, l4)
[(0, 4, 8, 12), (1, 5, 9, 13), (2, 6, 10, 14), (3, 7, 11, 15)]

If the lists are large, and you don’t want to combine them into a bigger list, use itertools.izip(), which produces an iterator, rather than a list.

from itertools import izip

for i1, i2, i3, i4 in izip(l1, l2, l3, l4):
    ...

回答 29

一种单行的即席解决方案,可x对大小成块的列表进行迭代4

for a, b, c, d in zip(x[0::4], x[1::4], x[2::4], x[3::4]):
    ... do something with a, b, c and d ...

One-liner, adhoc solution to iterate over a list x in chunks of size 4

for a, b, c, d in zip(x[0::4], x[1::4], x[2::4], x[3::4]):
    ... do something with a, b, c and d ...

Python 2.X中的range和xrange函数之间有什么区别?

问题:Python 2.X中的range和xrange函数之间有什么区别?

显然,xrange更快,但是我不知道为什么它更快(到目前为止,除了轶事之外,还没有证据表明它更快)或除此之外还有什么不同

for i in range(0, 20):
for i in xrange(0, 20):

Apparently xrange is faster but I have no idea why it’s faster (and no proof besides the anecdotal so far that it is faster) or what besides that is different about

for i in range(0, 20):
for i in xrange(0, 20):

回答 0

在Python 2.x中:

  • range创建一个列表,所以如果您这样做range(1, 10000000),则会在内存中创建一个包含9999999元素的列表。

  • xrange 是一个延迟计算的序列对象。

在Python 3中,range它等效于python xrange,并且必须使用来获取列表list(range(...))

In Python 2.x:

  • range creates a list, so if you do range(1, 10000000) it creates a list in memory with 9999999 elements.

  • xrange is a sequence object that evaluates lazily.

In Python 3, range does the equivalent of python’s xrange, and to get the list, you have to use list(range(...)).


回答 1

range会创建一个列表,因此,如果执行range(1, 10000000)此操作,则会在内存中创建一个包含9999999元素的列表。

xrange 是一个生成器,所以它是一个序列对象,是一个懒惰求值的对象。

的确如此,但是在Python 3中,.range()将由Python 2实现.xrange()。如果需要实际生成列表,则需要执行以下操作:

list(range(1,100))

range creates a list, so if you do range(1, 10000000) it creates a list in memory with 9999999 elements.

xrange is a generator, so it is a sequence object is a that evaluates lazily.

This is true, but in Python 3, .range() will be implemented by the Python 2 .xrange(). If you need to actually generate the list, you will need to do:

list(range(1,100))

回答 2

记住,使用该timeit模块来测试较小的代码片段更快!

$ python -m timeit 'for i in range(1000000):' ' pass'
10 loops, best of 3: 90.5 msec per loop
$ python -m timeit 'for i in xrange(1000000):' ' pass'
10 loops, best of 3: 51.1 msec per loop

就个人而言,.range()除非我处理的列表非常庞大,否则我总是使用-从时间上可以看出,对于一百万个条目的列表,额外的开销只有0.04秒。正如Corey所指出的那样,在Python 3.0中,它.xrange()将会消失,并且.range()无论如何都会为您提供良好的迭代器行为。

Remember, use the timeit module to test which of small snippets of code is faster!

$ python -m timeit 'for i in range(1000000):' ' pass'
10 loops, best of 3: 90.5 msec per loop
$ python -m timeit 'for i in xrange(1000000):' ' pass'
10 loops, best of 3: 51.1 msec per loop

Personally, I always use .range(), unless I were dealing with really huge lists — as you can see, time-wise, for a list of a million entries, the extra overhead is only 0.04 seconds. And as Corey points out, in Python 3.0 .xrange() will go away and .range() will give you nice iterator behavior anyway.


回答 3

xrange仅存储范围参数并按需生成数字。但是,Python的C实现当前将其args限制为C long:

xrange(2**32-1, 2**32+1)  # When long is 32 bits, OverflowError: Python int too large to convert to C long
range(2**32-1, 2**32+1)   # OK --> [4294967295L, 4294967296L]

请注意,在Python 3.0中仅存在,range并且其行为类似于2.x,xrange但对最小和最大端点没有限制。

xrange only stores the range params and generates the numbers on demand. However the C implementation of Python currently restricts its args to C longs:

xrange(2**32-1, 2**32+1)  # When long is 32 bits, OverflowError: Python int too large to convert to C long
range(2**32-1, 2**32+1)   # OK --> [4294967295L, 4294967296L]

Note that in Python 3.0 there is only range and it behaves like the 2.x xrange but without the limitations on minimum and maximum end points.


回答 4

xrange返回一个迭代器,一次只在内存中保留一个数字。range将整个数字列表保留在内存中。

xrange returns an iterator and only keeps one number in memory at a time. range keeps the entire list of numbers in memory.


回答 5

一定要花一些时间在图书馆参考上。您越熟悉它,就可以更快地找到此类问题的答案。关于内置对象和类型的前几章特别重要。

xrange类型的优点在于,无论xrange对象代表的范围大小如何,它始终将占用相同的内存量。没有一致的性能优势。

查找有关Python构造的快速信息的另一种方法是docstring和help-function:

print xrange.__doc__ # def doc(x): print x.__doc__ is super useful
help(xrange)

Do spend some time with the Library Reference. The more familiar you are with it, the faster you can find answers to questions like this. Especially important are the first few chapters about builtin objects and types.

The advantage of the xrange type is that an xrange object will always take the same amount of memory, no matter the size of the range it represents. There are no consistent performance advantages.

Another way to find quick information about a Python construct is the docstring and the help-function:

print xrange.__doc__ # def doc(x): print x.__doc__ is super useful
help(xrange)

回答 6

我很震惊,没有人读doc

此函数非常类似于range(),但是返回一个xrange对象而不是一个列表。这是一种不透明的序列类型,其产生的值与对应的列表相同,而实际上并没有同时存储它们。xrange()over 的优点range()是最小的(因为xrange()在要求输入值时仍必须创建值),除非在内存不足的计算机上使用了非常大的范围或从未使用过范围的所有元素时(例如当循环被使用时)。通常以break)终止。

I am shocked nobody read doc:

This function is very similar to range(), but returns an xrange object instead of a list. This is an opaque sequence type which yields the same values as the corresponding list, without actually storing them all simultaneously. The advantage of xrange() over range() is minimal (since xrange() still has to create the values when asked for them) except when a very large range is used on a memory-starved machine or when all of the range’s elements are never used (such as when the loop is usually terminated with break).


回答 7

range创建一个列表,因此如果执行range(1,10000000),它将在内存中创建一个包含10000000个元素的列表。xrange是一个生成器,因此它懒惰地求值。

这为您带来两个优点:

  1. 您可以迭代更长的列表而无需获取MemoryError
  2. 当它懒散地解析每个数字时,如果您尽早停止迭代,您将不会浪费时间创建整个列表。

range creates a list, so if you do range(1, 10000000) it creates a list in memory with 10000000 elements. xrange is a generator, so it evaluates lazily.

This brings you two advantages:

  1. You can iterate longer lists without getting a MemoryError.
  2. As it resolves each number lazily, if you stop iteration early, you won’t waste time creating the whole list.

回答 8

在这个简单的示例中,您将发现xrangeover 的优点range

import timeit

t1 = timeit.default_timer()
a = 0
for i in xrange(1, 100000000):
    pass
t2 = timeit.default_timer()

print "time taken: ", (t2-t1)  # 4.49153590202 seconds

t1 = timeit.default_timer()
a = 0
for i in range(1, 100000000):
    pass
t2 = timeit.default_timer()

print "time taken: ", (t2-t1)  # 7.04547905922 seconds

上面的示例在的情况下并没有任何实质性的改善xrange

现在range,与相比,以下情况的确非常慢xrange

import timeit

t1 = timeit.default_timer()
a = 0
for i in xrange(1, 100000000):
    if i == 10000:
        break
t2 = timeit.default_timer()

print "time taken: ", (t2-t1)  # 0.000764846801758 seconds

t1 = timeit.default_timer()
a = 0
for i in range(1, 100000000):
    if i == 10000:
        break
t2 = timeit.default_timer() 

print "time taken: ", (t2-t1)  # 2.78506207466 seconds

使用range,它已经创建了一个从0到100000000(耗时)的列表,但是它xrange是一个生成器,并且仅根据需要(即,如果继续迭代)生成数字。

在Python-3中,该range功能的实现与xrangePython-2中的相同,而xrange在Python-3中已取消了该功能。

快乐编码!

You will find the advantage of xrange over range in this simple example:

import timeit

t1 = timeit.default_timer()
a = 0
for i in xrange(1, 100000000):
    pass
t2 = timeit.default_timer()

print "time taken: ", (t2-t1)  # 4.49153590202 seconds

t1 = timeit.default_timer()
a = 0
for i in range(1, 100000000):
    pass
t2 = timeit.default_timer()

print "time taken: ", (t2-t1)  # 7.04547905922 seconds

The above example doesn’t reflect anything substantially better in case of xrange.

Now look at the following case where range is really really slow, compared to xrange.

import timeit

t1 = timeit.default_timer()
a = 0
for i in xrange(1, 100000000):
    if i == 10000:
        break
t2 = timeit.default_timer()

print "time taken: ", (t2-t1)  # 0.000764846801758 seconds

t1 = timeit.default_timer()
a = 0
for i in range(1, 100000000):
    if i == 10000:
        break
t2 = timeit.default_timer() 

print "time taken: ", (t2-t1)  # 2.78506207466 seconds

With range, it already creates a list from 0 to 100000000(time consuming), but xrange is a generator and it only generates numbers based on the need, that is, if the iteration continues.

In Python-3, the implementation of the range functionality is same as that of xrange in Python-2, while they have done away with xrange in Python-3

Happy Coding!!


回答 9

这是出于优化的原因。

range()将从头到尾创建一个值列表(在您的示例中为0 .. 20)。在很大范围内,这将成为昂贵的操作。

另一方面,xrange()更优化了。它只会在需要时(通过xrange序列对象)计算下一个值,并且不会像range()那样创建所有值的列表。

It is for optimization reasons.

range() will create a list of values from start to end (0 .. 20 in your example). This will become an expensive operation on very large ranges.

xrange() on the other hand is much more optimised. it will only compute the next value when needed (via an xrange sequence object) and does not create a list of all values like range() does.


回答 10

range(x,y)如果使用for循环,则返回x和y之间的每个数字的列表,这样range比较慢。实际上,range具有较大的Index范围。range(x.y)将打印出x和y之间所有数字的列表

xrange(x,y)返回,xrange(x,y)但如果使用for循环,则xrange速度更快。xrange索引范围较小。xrange不仅会打印出来xrange(x,y),还会保留其中的所有数字。

[In] range(1,10)
[Out] [1, 2, 3, 4, 5, 6, 7, 8, 9]
[In] xrange(1,10)
[Out] xrange(1,10)

如果使用for循环,那么它将起作用

[In] for i in range(1,10):
        print i
[Out] 1
      2
      3
      4
      5
      6
      7
      8
      9
[In] for i in xrange(1,10):
         print i
[Out] 1
      2
      3
      4
      5
      6
      7
      8
      9

尽管使用循环时并没有什么不同,但仅打印循环时也有差异!

range(x,y) returns a list of each number in between x and y if you use a for loop, then range is slower. In fact, range has a bigger Index range. range(x.y) will print out a list of all the numbers in between x and y

xrange(x,y) returns xrange(x,y) but if you used a for loop, then xrange is faster. xrange has a smaller Index range. xrange will not only print out xrange(x,y) but it will still keep all the numbers that are in it.

[In] range(1,10)
[Out] [1, 2, 3, 4, 5, 6, 7, 8, 9]
[In] xrange(1,10)
[Out] xrange(1,10)

If you use a for loop, then it would work

[In] for i in range(1,10):
        print i
[Out] 1
      2
      3
      4
      5
      6
      7
      8
      9
[In] for i in xrange(1,10):
         print i
[Out] 1
      2
      3
      4
      5
      6
      7
      8
      9

There isn’t much difference when using loops, though there is a difference when just printing it!


回答 11

range(): range(1,10)返回一个1到10个数字的列表,并将整个列表保存在内存中。

xrange():类似于range(),但不返回列表,而是返回一个对象,该对象根据需要生成范围内的数字。对于循环,这比range()快一点,并且内存效率更高。xrange()对象就像一个迭代器,并根据需要生成数字。(延迟评估)

In [1]: range(1,10)

Out[1]: [1, 2, 3, 4, 5, 6, 7, 8, 9]

In [2]: xrange(10)

Out[2]: xrange(10)

In [3]: print xrange.__doc__

xrange([start,] stop[, step]) -> xrange object

range(): range(1, 10) returns a list from 1 to 10 numbers & hold whole list in memory.

xrange(): Like range(), but instead of returning a list, returns an object that generates the numbers in the range on demand. For looping, this is lightly faster than range() and more memory efficient. xrange() object like an iterator and generates the numbers on demand.(Lazy Evaluation)

In [1]: range(1,10)

Out[1]: [1, 2, 3, 4, 5, 6, 7, 8, 9]

In [2]: xrange(10)

Out[2]: xrange(10)

In [3]: print xrange.__doc__

xrange([start,] stop[, step]) -> xrange object

回答 12

其他一些答案提到Python 3淘汰了2.x range,并将2.x重命名xrangerange。但是,除非您使用3.0或3.1(应该没有人使用),否则它实际上是一种不同的类型。

正如3.1文档所说:

范围对象几乎没有行为:它们仅支持索引,迭代和len功能。

但是,在3.2+版本中,它range是一个完整序列,它支持扩展的slice,以及所有collections.abc.Sequence与语义相同的方法list*

并且,至少在CPython和PyPy(当前仅有的两个3.2+实现)中,它还具有indexand count方法和in运算符的恒定时间实现(只要您仅将其传递为整数)。这意味着123456 in r在3.2+版本中写作是合理的,而在2.7或3.1版本中这将是一个可怕的想法。


*事实上,issubclass(xrange, collections.Sequence)回报率True在2.6-2.7 3.0-3.1和是一个错误是固定在3.2,而不是向后移植。

Some of the other answers mention that Python 3 eliminated 2.x’s range and renamed 2.x’s xrange to range. However, unless you’re using 3.0 or 3.1 (which nobody should be), it’s actually a somewhat different type.

As the 3.1 docs say:

Range objects have very little behavior: they only support indexing, iteration, and the len function.

However, in 3.2+, range is a full sequence—it supports extended slices, and all of the methods of collections.abc.Sequence with the same semantics as a list.*

And, at least in CPython and PyPy (the only two 3.2+ implementations that currently exist), it also has constant-time implementations of the index and count methods and the in operator (as long as you only pass it integers). This means writing 123456 in r is reasonable in 3.2+, while in 2.7 or 3.1 it would be a horrible idea.


* The fact that issubclass(xrange, collections.Sequence) returns True in 2.6-2.7 and 3.0-3.1 is a bug that was fixed in 3.2 and not backported.


回答 13

在python 2.x中

range(x)返回一个列表,该列表在内存中创建有x个元素。

>>> a = range(5)
>>> a
[0, 1, 2, 3, 4]

xrange(x)返回一个xrange对象,该对象是生成器obj,可按需生成数字。它们是在for循环(惰性评估)期间计算的。

对于循环,这比range()快一点,并且内存效率更高。

>>> b = xrange(5)
>>> b
xrange(5)

In python 2.x

range(x) returns a list, that is created in memory with x elements.

>>> a = range(5)
>>> a
[0, 1, 2, 3, 4]

xrange(x) returns an xrange object which is a generator obj which generates the numbers on demand. they are computed during for-loop(Lazy Evaluation).

For looping, this is slightly faster than range() and more memory efficient.

>>> b = xrange(5)
>>> b
xrange(5)

回答 14

在循环中针对xrange测试范围时(我知道我应该使用timeit,但是使用简单的列表理解示例从内存中迅速破解了它),我发现了以下内容:

import time

for x in range(1, 10):

    t = time.time()
    [v*10 for v in range(1, 10000)]
    print "range:  %.4f" % ((time.time()-t)*100)

    t = time.time()
    [v*10 for v in xrange(1, 10000)]
    print "xrange: %.4f" % ((time.time()-t)*100)

这使:

$python range_tests.py
range:  0.4273
xrange: 0.3733
range:  0.3881
xrange: 0.3507
range:  0.3712
xrange: 0.3565
range:  0.4031
xrange: 0.3558
range:  0.3714
xrange: 0.3520
range:  0.3834
xrange: 0.3546
range:  0.3717
xrange: 0.3511
range:  0.3745
xrange: 0.3523
range:  0.3858
xrange: 0.3997 <- garbage collection?

或者,在for循环中使用xrange:

range:  0.4172
xrange: 0.3701
range:  0.3840
xrange: 0.3547
range:  0.3830
xrange: 0.3862 <- garbage collection?
range:  0.4019
xrange: 0.3532
range:  0.3738
xrange: 0.3726
range:  0.3762
xrange: 0.3533
range:  0.3710
xrange: 0.3509
range:  0.3738
xrange: 0.3512
range:  0.3703
xrange: 0.3509

我的代码段测试是否正确?对较慢的xrange实例有何评论?或更好的例子:-)

When testing range against xrange in a loop (I know I should use timeit, but this was swiftly hacked up from memory using a simple list comprehension example) I found the following:

import time

for x in range(1, 10):

    t = time.time()
    [v*10 for v in range(1, 10000)]
    print "range:  %.4f" % ((time.time()-t)*100)

    t = time.time()
    [v*10 for v in xrange(1, 10000)]
    print "xrange: %.4f" % ((time.time()-t)*100)

which gives:

$python range_tests.py
range:  0.4273
xrange: 0.3733
range:  0.3881
xrange: 0.3507
range:  0.3712
xrange: 0.3565
range:  0.4031
xrange: 0.3558
range:  0.3714
xrange: 0.3520
range:  0.3834
xrange: 0.3546
range:  0.3717
xrange: 0.3511
range:  0.3745
xrange: 0.3523
range:  0.3858
xrange: 0.3997 <- garbage collection?

Or, using xrange in the for loop:

range:  0.4172
xrange: 0.3701
range:  0.3840
xrange: 0.3547
range:  0.3830
xrange: 0.3862 <- garbage collection?
range:  0.4019
xrange: 0.3532
range:  0.3738
xrange: 0.3726
range:  0.3762
xrange: 0.3533
range:  0.3710
xrange: 0.3509
range:  0.3738
xrange: 0.3512
range:  0.3703
xrange: 0.3509

Is my snippet testing properly? Any comments on the slower instance of xrange? Or a better example :-)


回答 15

python中的xrange()和range()与用户的工作原理相似,但是区别在于,当我们讨论使用这两个函数分配内存的方式时。

当我们使用range()时,我们为它生成的所有变量分配内存,因此不建议使用更大的no。生成的变量。

另一方面,xrange()一次仅生成一个特定值,并且只能与for循环一起使用以打印所需的所有值。

xrange() and range() in python works similarly as for the user , but the difference comes when we are talking about how the memory is allocated in using both the function.

When we are using range() we allocate memory for all the variables it is generating, so it is not recommended to use with larger no. of variables to be generated.

xrange() on the other hand generate only a particular value at a time and can only be used with the for loop to print all the values required.


回答 16

range生成整个列表并返回。xrange不会-它会按需生成列表中的数字。

range generates the entire list and returns it. xrange does not — it generates the numbers in the list on demand.


回答 17

xrange使用迭代器(动态生成值),range返回一个列表。

xrange uses an iterator (generates values on the fly), range returns a list.


回答 18

什么?
range在运行时返回静态列表。
xrange返回一个object(在某种情况下,它的作用类似于生成器,尽管肯定不是一个),并在需要时从中生成值。

什么时候使用?

  • xrange如果要生成一个巨大范围(例如10亿)的列表,则可以使用该选项,尤其是当您拥有像手机这样的“内存敏感系统”时。
  • 使用range,如果你想在列表几次迭代。

PS:Python 3.x的range功能== Python 2.x的xrange功能。

What?
range returns a static list at runtime.
xrange returns an object (which acts like a generator, although it’s certainly not one) from which values are generated as and when required.

When to use which?

  • Use xrange if you want to generate a list for a gigantic range, say 1 billion, especially when you have a “memory sensitive system” like a cell phone.
  • Use range if you want to iterate over the list several times.

PS: Python 3.x’s range function == Python 2.x’s xrange function.


回答 19

每个人都对此做了很大的解释。但我希望自己看到它。我使用python3。因此,我打开了资源监视器(在Windows中!),首先,首先执行以下命令:

a=0
for i in range(1,100000):
    a=a+i

然后检查“使用中”内存中的更改。这无关紧要。然后,我运行了以下代码:

for i in list(range(1,100000)):
    a=a+i

立即消耗了很大一部分内存。而且,我被说服了。您可以自己尝试。

如果您使用的是Python 2X,则在第一个代码中将“ range()”替换为“ xrange()”,将“ list(range())”替换为“ range()”。

Everyone has explained it greatly. But I wanted it to see it for myself. I use python3. So, I opened the resource monitor (in Windows!), and first, executed the following command first:

a=0
for i in range(1,100000):
    a=a+i

and then checked the change in ‘In Use’ memory. It was insignificant. Then, I ran the following code:

for i in list(range(1,100000)):
    a=a+i

And it took a big chunk of the memory for use, instantly. And, I was convinced. You can try it for yourself.

If you are using Python 2X, then replace ‘range()’ with ‘xrange()’ in the first code and ‘list(range())’ with ‘range()’.


回答 20

从帮助文档。

Python 2.7.12

>>> print range.__doc__
range(stop) -> list of integers
range(start, stop[, step]) -> list of integers

Return a list containing an arithmetic progression of integers.
range(i, j) returns [i, i+1, i+2, ..., j-1]; start (!) defaults to 0.
When step is given, it specifies the increment (or decrement).
For example, range(4) returns [0, 1, 2, 3].  The end point is omitted!
These are exactly the valid indices for a list of 4 elements.

>>> print xrange.__doc__
xrange(stop) -> xrange object
xrange(start, stop[, step]) -> xrange object

Like range(), but instead of returning a list, returns an object that
generates the numbers in the range on demand.  For looping, this is 
slightly faster than range() and more memory efficient.

Python 3.5.2

>>> print(range.__doc__)
range(stop) -> range object
range(start, stop[, step]) -> range object

Return an object that produces a sequence of integers from start (inclusive)
to stop (exclusive) by step.  range(i, j) produces i, i+1, i+2, ..., j-1.
start defaults to 0, and stop is omitted!  range(4) produces 0, 1, 2, 3.
These are exactly the valid indices for a list of 4 elements.
When step is given, it specifies the increment (or decrement).

>>> print(xrange.__doc__)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'xrange' is not defined

差异显而易见。在Python 2.x中,range返回一个列表,xrange返回一个可迭代的xrange对象。

在Python 3.x中,range成为xrangePython 2.x并被xrange删除。

From the help docs.

Python 2.7.12

>>> print range.__doc__
range(stop) -> list of integers
range(start, stop[, step]) -> list of integers

Return a list containing an arithmetic progression of integers.
range(i, j) returns [i, i+1, i+2, ..., j-1]; start (!) defaults to 0.
When step is given, it specifies the increment (or decrement).
For example, range(4) returns [0, 1, 2, 3].  The end point is omitted!
These are exactly the valid indices for a list of 4 elements.

>>> print xrange.__doc__
xrange(stop) -> xrange object
xrange(start, stop[, step]) -> xrange object

Like range(), but instead of returning a list, returns an object that
generates the numbers in the range on demand.  For looping, this is 
slightly faster than range() and more memory efficient.

Python 3.5.2

>>> print(range.__doc__)
range(stop) -> range object
range(start, stop[, step]) -> range object

Return an object that produces a sequence of integers from start (inclusive)
to stop (exclusive) by step.  range(i, j) produces i, i+1, i+2, ..., j-1.
start defaults to 0, and stop is omitted!  range(4) produces 0, 1, 2, 3.
These are exactly the valid indices for a list of 4 elements.
When step is given, it specifies the increment (or decrement).

>>> print(xrange.__doc__)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'xrange' is not defined

Difference is apparent. In Python 2.x, range returns a list, xrange returns an xrange object which is iterable.

In Python 3.x, range becomes xrange of Python 2.x, and xrange is removed.


回答 21

根据扫描/打印0-N个项目的要求,range和xrange的工作方式如下。

range()-在内存中创建一个新列表,并将整个0到N个项目(总共N + 1个)打印出来。xrange()-创建一个迭代器实例,该实例扫描项目并仅将当前遇到的项目保留在内存中,因此始终使用相同数量的内存。

如果所需元素只是在列表的开头,那么它可以节省大量时间和内存。

On a requirement for scanning/printing of 0-N items , range and xrange works as follows.

range() – creates a new list in the memory and takes the whole 0 to N items(totally N+1) and prints them. xrange() – creates a iterator instance that scans through the items and keeps only the current encountered item into the memory , hence utilising same amount of memory all the time.

In case the required element is somewhat at the beginning of the list only then it saves a good amount of time and memory.


回答 22

Range返回一个列表,xrange返回一个xrange对象,无论范围大小如何,该对象都占用相同的内存,因为在这种情况下,每次迭代仅生成一个元素并且可用,而在使用range的情况下,所有元素一次生成,并且在内存中可用。

Range returns a list while xrange returns an xrange object which takes the same memory irrespective of the range size,as in this case,only one element is generated and available per iteration whereas in case of using range, all the elements are generated at once and are available in the memory.


回答 23

对于较小的参数差减小range(..)/ xrange(..)

$ python -m timeit "for i in xrange(10111):" " for k in range(100):" "  pass"
10 loops, best of 3: 59.4 msec per loop

$ python -m timeit "for i in xrange(10111):" " for k in xrange(100):" "  pass"
10 loops, best of 3: 46.9 msec per loop

在这种情况下,xrange(100)效率仅提高约20%。

The difference decreases for smaller arguments to range(..) / xrange(..):

$ python -m timeit "for i in xrange(10111):" " for k in range(100):" "  pass"
10 loops, best of 3: 59.4 msec per loop

$ python -m timeit "for i in xrange(10111):" " for k in xrange(100):" "  pass"
10 loops, best of 3: 46.9 msec per loop

In this case xrange(100) is only about 20% more efficient.


回答 24

range:-range会立即填充所有内容,这意味着范围的每个数字都会占用内存。

xrange:-xrange类似于生成器,当您想要数字的范围但不希望将它们存储时(例如当您要在for loop.so中使用时),它将出现在图片中,这样可以提高内存效率。

range :-range will populate everything at once.which means every number of the range will occupy the memory.

xrange :-xrange is something like generator ,it will comes into picture when you want the range of numbers but you dont want them to be stored,like when you want to use in for loop.so memory efficient.


回答 25

此外,如果这样做list(xrange(...))等同于range(...)

所以 list很慢。

xrange真的没有完全完成序列

这就是为什么它不是列表,而是xrange对象

Additionally, if do list(xrange(...)) will be equivalent to range(...).

So list is slow.

Also xrange really doesn’t fully finish the sequence

So that’s why its not a list, it’s a xrange object


回答 26

range() 在Python中 2.x

此函数本质range()上是Python中可用的旧函数,它2.x返回list包含指定范围内的元素的对象的实例。

但是,这种实现在初始化带有一系列数字的列表时效率太低。例如,for i in range(1000000)就内存和时间使用而言,要执行的命令非常昂贵,因为它需要将此列表存储到内存中。


range()在Python 3.xxrange()Python中2.x

Python 3.x引入了更新的实现range()(而更新的实现已经可以2.x通过Python 通过xrange()功能)。

range()漏洞利用一种称为惰性评估的策略较新的实现未在范围内创建大量元素,而是引入了class range,这是一个轻量级的对象,代表给定范围内的所需元素,而无需将其显式存储在内存中(这听起来像是生成器,但是惰性求值的概念是不同)。


例如,请考虑以下内容:

# Python 2.x
>>> a = range(10)
>>> type(a)
<type 'list'>
>>> b = xrange(10)
>>> type(b)
<type 'xrange'>

# Python 3.x
>>> a = range(10)
>>> type(a)
<class 'range'>

range() in Python 2.x

This function is essentially the old range() function that was available in Python 2.x and returns an instance of a list object that contains the elements in the specified range.

However, this implementation is too inefficient when it comes to initialise a list with a range of numbers. For example, for i in range(1000000) would be a very expensive command to execute, both in terms of memory and time usage as it requires the storage of this list into the memory.


range() in Python 3.x and xrange() in Python 2.x

Python 3.x introduced a newer implementation of range() (while the newer implementation was already available in Python 2.x through the xrange() function).

The range() exploits a strategy known as lazy evaluation. Instead of creating a huge list of elements in range, the newer implementation introduces the class range, a lightweight object that represents the required elements in the given range, without storing them explicitly in memory (this might sound like generators but the concept of lazy evaluation is different).


As an example, consider the following:

# Python 2.x
>>> a = range(10)
>>> type(a)
<type 'list'>
>>> b = xrange(10)
>>> type(b)
<type 'xrange'>

and

# Python 3.x
>>> a = range(10)
>>> type(a)
<class 'range'>

回答 27

看到这个帖子以查找range和xrange之间的区别:

报价:

range返回确切的结果:一系列连续的整数,定义的长度以0开头xrange。但是,将返回“ xrange object”,其作用类似于迭代器

See this post to find difference between range and xrange:

To quote:

range returns exactly what you think: a list of consecutive integers, of a defined length beginning with 0. xrange, however, returns an “xrange object”, which acts a great deal like an iterator