我有一个对象的树形结构。我需要遍历叶子中的所有项目(“值”)。为此我正在使用如下所示的生成器方法:
class Node(object):
def __init__(self):
self.items = [Leaf(1), Leaf(2), Leaf(3)]
def values(self):
for item in self.items:
for value in item.values():
yield value
class Leaf(object):
def __init__(self, value):
self.value = value
def values(self):
for i in range(2):
yield self.value
n = Node()
for value in n.values():
print(value)
打印:
1
1
2
2
3
3
现在,Leaf
返回的值将取决于外部参数。我正在考虑使用协同程序将此参数传递给叶节点:
import itertools
class Node2(object):
def __init__(self):
self.items = [Leaf2(1), Leaf2(2), Leaf2(3)]
def values(self):
parameter = yield
for item in self.items:
item_values = item.values()
next(item_values) # advance to first yield
try:
while True:
parameter = (yield item_values.send(parameter))
except StopIteration:
pass
class Leaf2(object):
def __init__(self, value):
self.value = value
def values(self):
parameter = yield
try:
for i in range(2):
parameter = (yield '{}{}'.format(self.value, parameter))
except StopIteration:
pass
n2 = Node2()
values2 = n2.values()
next(values2) # advance to first yield
try:
for i in itertools.count(ord('A')):
print(values2.send(chr(i)))
except StopIteration:
pass
这段代码很不错,但它确实有效。它打印:
1A
1B
2C
2D
3E
3F
但这个解决方案存在问题。我正在广泛使用itertools.tee
(和chain
)来轻松保存迭代器的状态,以防我需要回溯。我希望这些也适用于协同程序,但唉,没有这样的运气。
我正在考虑的一些替代解决方案:
第一种选择似乎最具吸引力。但也许有更好的选择?
某些上下文:我在RinohType中使用此构造,其中树由MixedStyledText
(节点)和SingleStyledText
(叶)对象组成。 spans()
方法会生成SingleStyledText
个实例。后者可以取决于外部参数。例如,它们被呈现的页码。这些目前被视为特例。
答案 0 :(得分:1)
在可能的范围内,我喜欢让函数返回简单的数据结构。在这种情况下,
RinohType
的情况下,产生一个SingleStyledTextConfig
对象或namedtuple
)。与以前一样,此实现将与itertools
很好地协作,因为它只是将数据转换为其他数据。RinohType
的情况下,SingleStyledText
对象)。在更高级别:您建议的解决方案(据我了解您的简化版本)同时尝试做太多事情。它正在尝试配置对象创建,调整配置,并在一步中基于 配置创建对象。您的实现将更简单,使用itertools
更好,并且如果您将这些问题分开,则更容易测试。
有关此类思维的更详细处理,请参阅Gary Bernhardt's Boundaries talk和Brandon Rhodes' talk on the Clean Architecture in Python(当然还有他们在会谈中提到的资源)。
答案 1 :(得分:0)
这是第二个选项的开始
from types import GeneratorType
def gen_wrapper(func):
def _inner(*args):
try:
if args:
if isinstance(args[0], GeneratorType):
func.gen = getattr(func, 'gen', args[0])
func.recall = next(func.gen)
try:
return func.recall
except AttributeError:
func.recall = next(func.gen)
return func.recall
except StopIteration:
pass
return _inner
@gen_wrapper
def Gen_recall(*args):
pass
答案 2 :(得分:0)
我不确定我是否正确理解了这个问题。 Leaf2需要进行计算吗? 如果没有,那么你可以这样做:
import itertools
class Node2(object):
def __init__(self):
self.items = [Leaf2(1), Leaf2(2), Leaf2(3)]
def values(self):
for item in self.items:
for value in item.values():
yield value
class Leaf2(object):
def __init__(self, value):
self.value = value
def values(self):
for i in range(2):
yield self.value
def process(i, parameter):
return '{}{}'.format(i, parameter)
n = Node2()
for value, parameter in zip(n.values(), itertools.count(ord('A'))):
print(process(value, chr(parameter)))
如果Leaf2进行处理非常重要,你可以做
class Leaf2:
def values(self):
for i in range(2):
yield self
def process(self, parameter):
pass
所以在主要的你可以做
n = Node2()
for node, parameter in zip(n.values(), itertools.count(ord('A'))):
print(node.process(chr(parameter)))