Appendix#
This section covers Python language features that may be new to some readers. They are used by bluesky but not unique Wherever possible, we to bluesky.
A Primer on yield
and yield from
#
This is a very brief primer on the Python syntax yield
and yield from
,
a feature of the core language that we will use extensively.
A Python function returns once:
In [1]: def f():
...: return 1
...:
In [2]: f()
Out[2]: 1
A Python generator is like a function with multiple exit points. Calling a
generator produces an iterator that yields one value at a time. After
each yield
statement, its execution is suspended.
In [3]: def f():
...: yield 1
...: yield 2
...:
We can exhaust the generator (i.e., get all its values) by calling list()
.
In [4]: list(f())
Out[4]: [1, 2]
We can get one value at a time by calling next()
In [5]: it = f()
In [6]: next(it)
Out[6]: 1
In [7]: next(it)
Out[7]: 2
or by looping through the values.
In [8]: for val in f():
...: print(val)
...:
1
2
To examine what is happening when, we can add prints.
In [9]: def verbose_f():
...: print("before 1")
...: yield 1
...: print("before 2")
...: yield 2
...:
In [10]: it = verbose_f()
In [11]: next(it)
before 1
Out[11]: 1
In [12]: next(it)
before 2
Out[12]: 2
Notice that execution is suspended after the first yield statement. The
second print
is not run until we resume execution by requesting a second
value. This is a useful feature of generators: they can express “lazy”
execution.
Generators can delegate to other generators using yield from
. This is
syntax we commonly use to combine plans.
In [13]: def double_f():
....: yield from f()
....: yield from f()
....:
The above is equivalent to:
In [14]: def double_f():
....: for val in f():
....: yield val
....: for val in f():
....: yield val
....:
The yield from
syntax is just more succinct.
In [15]: list(double_f())
Out[15]: [1, 2, 1, 2]