Библиотека Python разработчика. Книги по программированию на Python.
next(x) returns the new value from the x iterator unless an exception is raised. If this is StopIteration, it means the iterator is exhausted and can supply no more values. If a generator is iterated, it automatically raises StopIteration upon the end of the body:
>>> def one_two():
... yield 1
... yield 2
...
>>> i = one_two()
>>> next(i)
1
>>> next(i)
2
>>> next(i)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
StopIteration is automatically handled by tools that calls next for you:
>>> list(one_two())
[1, 2]
The problem is, any unexpected StopIteration that is raised within a generator causes it to stop silently instead of actually raising an exception:
def one_two():
yield 1
yield 2
def one_two_repeat(n):
for _ in range(n):
i = one_two()
yield next(i)
yield next(i)
yield next(i)
print(list(one_two_repeat(3)))
The last yield here is a mistake: StopIteration is raised and makes list(...) to stop the iteration. The result is [1, 2], surprisingly.
However, that was changed in Python 3.7. Such foreign StopIteration is now replaced with RuntimeError:
Traceback (most recent call last):
File "test.py", line 10, in one_two_repeat
yield next(i)
StopIteration
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "test.py", line 12, in <module>
print(list(one_two_repeat(3)))
RuntimeError: generator raised StopIteration
You can enable the same behavior since python3.5 by from __future__ import generator_stop.stdout instead of providing some API that is usable within a program (returning a string, for example).
Instead of refactoring such code you may use the contextlib.redirect_stdout context manager that allows temporary redirecting stdout to any custom file-like object. In conjuncture with io.StringIO, it allows capturing output to a variable.
from contextlib import redirect_stdout
from io import StringIO
s = StringIO()
with redirect_stdout(s):
print(42)
print(s.getvalue())
There is also contextlib.redirect_stderr available for redirecting sys.stderr.list can be such a container:
In : lst = [1, 2, 3]
In : lst.pop()
Out: 3
In : lst
Out: [1, 2]
In : lst[:0] = [4] # push
In : lst
Out: [4, 1, 2]
However, using list doesn't only look eerie (look at that push), but also is quite inefficient.
In : lst = [0] * 10_000_000
In : %timeit lst[:0] = [1]
9.5 ms ± 111 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
In : %timeit lst.pop()
84.3 ns ± 4.01 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)
As you can see, on my machine pop is 100 times faster than “push”. This is how list works: elements can be easily added to or removed from the end of the list, but to remove the first element, Python needs to create a new list from scratch.
What you really want to use for this problem is collections.deque. It's designed to be used as a queue:
In : d = deque([1] * 100_000_000)
In : %timeit d.popleft()
65 ns ± 0.436 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)json module has a command line interface that can be useful to prettify JSON by python alone. The module for this is called json.tool and is meant to be called like this:
$ echo '{"a": [], "b": "c"}' | python -m json.tool
{
"a": [],
"b": "c"
}def edges(lst):
return lst[0], lst[-1]
first, last = edges([1, 2, 3])
assert first == 1
assert last == 3
In truth, lst[0], lst[-1] is a simple tuple. It's returned as usual and then unpacked to first and last:
result = edges([1, 2, 3])
assert isinstance(result, tuple)
first, last = result
assert first == 1
assert last == 3
Usually, you don't care about it at all. However, all these things come to the surface when you use type hints. You have to define the function return value as tuple:
def edges(lst) -> Tuple[int, int]:
return lst[0], lst[-1]
Calling that function is even harder. You may think that you can do something like this:
first: int, last: int = edges([1, 2, 3])
Or at least this:
first, last: Tuple[int, int] = edges([1, 2, 3])
But both ways are incorrect. This is the only reasonable thing you can do to annotate these variables:
first: int
last: int
first, last = edges([1, 2, 3])