问题描述
通过其电池的Python的succint语法允许冗长的代码行被表示为可读的一行。考虑下面的例子 ====================== ============================== | (3):
范围(3)中的b
:|在范围(3)中c为
:|
print(a,b,c),|
- - - - - - - - - - - - - - - - - - |产品中的e
(范围(3),repeat = 3):|
打印e,|
============================================== ====== | (3):
(a,3)中的b
:| (b,3)中c为
:|
print(a,b,c),|
- - - - - - - - - - - - - - - - - - |
for combination_with_replacement(range(3),3):|
打印e,|
============================================== ====== | (3):
(a + 1,3)中的b
:|
在范围(b + 1,3)中的c:|
print(a,b,c),|
- - - - - - - - - - - - - - - - - - |
用于组合(范围(3),3):|
打印e,|
============================================== ====== | (3):
范围(3)中的b
:|在范围(3)中c为
:|
如果len(set([a,b,c]))== 3:|
print(a,b,c),|
- - - - - - - - - - - - - - - - - - |
表示排列中的e(范围(3)):|
打印e,|
============================================== ====== |
最后,我得到了一个深层嵌套的依赖循环,我试图简洁地表达,
循环的结构如下(b)中
for b(a):
for c(b):
foo(a,b,c)
可以用等价的itertools表示法来表示这样的结构吗?
没有确切的 itertools
解决方案,但是 itertools
函数的简单组合就足够了:
def chain_imap_accumulate(seq,f):
def acc_f(x):
for f in(x [ 1)):
yield x +(n,)
返回chain.from_iterable(imap(acc_f,seq))
dec accumulative_product(* generators):
头,尾巴=发电机[0],发电机[1:]
头部= imap(元组,head())
返回reduce(chain_imap_accumulate,tail,head)
快速测试。定义:
来自itertools import chain,imap,izip
chain_ = chain.from_iterable
$ b $ (b)
产生'A'
产生'B'
def B(x):
产生int(x,16)$ b $ (x)+ 1
$ b $ def C(x):
yield str(x)+'Z'
yield str(x)+'Y'
结果:
$ b $
>>> (A,B,C))
[('A',10,'10Z'),('A',10,'10Y'),
('A',11 ,'11Z'),('A',11,'11Y'),
('B',11,'11Z'),('B',11,'11Y'),
('B',12,'12Z'),('B',12,'12Y')]
几乎所有的复杂性都来自于输入的积累,正如上面代码的一个快速推导所显示的那样。 ( c
)值可以使用一对嵌套的 itertools
构造来生成:
>>> list(chain_(imap(C,chain_(imap(B,(A()))))))
['10Z','10Y','11Z','11Y','11Z' 11Y','12Z','12Y']
这可以概括为减少
。要处理 reduce
, chain_imap
不能使用标准 imap
参数顺序。它必须被交换:
$ pre $ def $ chain $ index $ ,seq))
这给出了相同的结果:
>>> list(reduce(chain_imap,[B,C],A()))
['10Z','10Y','11Z','11Y','11Z','11Y','12Z', '12Y']
最后的任务是累积初始值,以便您可以访问 a
, b
和 c
。这需要一点思路才能得到正确的结果,但是实现相当简单 - 我们只需要将 f
转换为忽略所有输入值而不是最后一个的函数,添加新的值到完整的输入:
$ p $ def $ chain $
for f(x [-1]):
yield x +(n,)
return chain.from_iterable(imap(acc_f,seq))
这要求第一个输入包装在元组中,所以我们映射 A
元组
:
>>> list(reduce(chain_imap_accumulate,[B,C],imap(tuple,A())))
[('A',10,'10Z'),('A',10,'10Y') ('A',11,'11Z'),('A',11,'11Y'),
('B',11,'11Z'),('B' 11,'11Y'),
('B',12,'12Z'),('B',12,'12Y')]
为了清晰起见,重写上面的代码,并在结果的顶部输入代码。
顺便说一句, chain_imap_accumulate
可以使用genex更简洁一点。这可以与一个简短版本的 accumulative_product
结合得非常紧凑(如果你对这种事感兴趣的话)。这也正好消除了itertools的依赖:
$ b $ pre $ def $ chain $ map $
return(x + (n,)for x in seq for n in f(x [-1]))
def accumulative_product2(* gens):
return reduce(chain_map_accumulate,gens [1:], (tuple(x)for gens [0]()))
Python's succint syntax through its batteries allows verbose code line to be expressed in readable one liners. Consider the following examples
====================================================|
for a in range(3): |
for b in range(3): |
for c in range(3): |
print (a,b,c), |
- - - - - - - - - - - - - - - - - -|
for e in product(range(3), repeat=3): |
print e, |
====================================================|
for a in range(3): |
for b in range(a , 3): |
for c in range(b , 3): |
print (a,b,c), |
- - - - - - - - - - - - - - - - - -|
for e in combinations_with_replacement(range(3), 3):|
print e, |
====================================================|
for a in range(3): |
for b in range(a + 1, 3): |
for c in range(b + 1, 3): |
print (a,b,c), |
- - - - - - - - - - - - - - - - - -|
for e in combinations(range(3), 3): |
print e, |
====================================================|
for a in range(3): |
for b in range(3): |
for c in range(3): |
if len(set([a,b,c])) == 3: |
print (a,b,c), |
- - - - - - - - - - - - - - - - - -|
for e in permutations(range(3)): |
print e, |
====================================================|
Of Late I ended up with a deep nested dependent Loop I was trying to express succinctly but failed
The structure of the loop would be as follows
for a in A():
for b in B(a):
for c in C(b):
foo(a,b,c)
Can such structure be expressed in an equivalent itertools notation?
There's no exact itertools
solution, but a simple combination of itertools
functions will suffice:
def chain_imap_accumulate(seq, f):
def acc_f(x):
for n in f(x[-1]):
yield x + (n,)
return chain.from_iterable(imap(acc_f, seq))
def accumulative_product(*generators):
head, tail = generators[0], generators[1:]
head = imap(tuple, head())
return reduce(chain_imap_accumulate, tail, head)
A quick test. Definitions:
from itertools import chain, imap, izip
chain_ = chain.from_iterable
def A():
yield 'A'
yield 'B'
def B(x):
yield int(x, 16)
yield int(x, 16) + 1
def C(x):
yield str(x) + 'Z'
yield str(x) + 'Y'
And the result:
>>> list(accumulative_product(A, B, C))
[('A', 10, '10Z'), ('A', 10, '10Y'),
('A', 11, '11Z'), ('A', 11, '11Y'),
('B', 11, '11Z'), ('B', 11, '11Y'),
('B', 12, '12Z'), ('B', 12, '12Y')]
Almost all the complexity comes from the accumulation of inputs, as a quick "derivation" of the above code shows. The final (c
) values can be generated using just a couple of nested itertools
constructs:
>>> list(chain_(imap(C, chain_(imap(B, (A()))))))
['10Z', '10Y', '11Z', '11Y', '11Z', '11Y', '12Z', '12Y']
This can be generalized with reduce
. To work with reduce
, chain_imap
can't use the standard imap
argument order. It has to be swapped:
def chain_imap(seq, f):
return chain.from_iterable(imap(f, seq))
This gives the same results:
>>> list(reduce(chain_imap, [B, C], A()))
['10Z', '10Y', '11Z', '11Y', '11Z', '11Y', '12Z', '12Y']
The final task is accumulating the initial values, so that you have access to a
, b
, and c
. This takes a bit of thought to get right, but the implementation is fairly simple -- we just have to convert f
into a function that ignores all input values but the last, and appends new values to the full input:
def chain_imap_accumulate(seq, f):
def acc_f(x):
for n in f(x[-1]):
yield x + (n,)
return chain.from_iterable(imap(acc_f, seq))
This requires that the first inputs be wrapped in tuples, so we map A
with tuple
:
>>> list(reduce(chain_imap_accumulate, [B, C], imap(tuple, A())))
[('A', 10, '10Z'), ('A', 10, '10Y'),
('A', 11, '11Z'), ('A', 11, '11Y'),
('B', 11, '11Z'), ('B', 11, '11Y'),
('B', 12, '12Z'), ('B', 12, '12Y')]
Rewrite the above for clarity, and the code at the top of this answer results.
By the way, chain_imap_accumulate
can be rewritten a bit more tersely using a genex. This can be combined with a shorter version of accumulative_product
for a very compact definition (if you're interested in that kind of thing). This also happens to eliminate the itertools dependency entirely:
def chain_map_accumulate(seq, f):
return (x + (n,) for x in seq for n in f(x[-1]))
def accumulative_product2(*gens):
return reduce(chain_map_accumulate, gens[1:], (tuple(x) for x in gens[0]()))
这篇关于带有Itertools的等效嵌套循环结构的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!