Par*_*xis 23 python recursion function generator
我目前正在编写一个项目,该项目需要使用返回其自身迭代器的方法的第三方代码,这是我的代码中的示例:
def generate():
for x in obj.children():
for y in x.children():
for z in y.children():
yield z.thing
Run Code Online (Sandbox Code Playgroud)
目前,这简直使我的代码变得混乱,并且在3个级别之后变得难以阅读.理想情况下,我会让它做这样的事情:
x = recursive(obj, method="children", repeat=3).thing
Run Code Online (Sandbox Code Playgroud)
是否有内置的方法在Python中执行此操作?
cs9*_*s95 24
从python3.3开始,您可以使用yield from语法生成整个生成器表达式.
因此,您可以稍微修改一下您的函数,以获取一些参数:
def generate(obj, n):
if n == 1:
for x in obj.children():
yield x.thing
else:
for x in obj.children():
yield from generate(x, n - 1)
Run Code Online (Sandbox Code Playgroud)
该yield from表达式将产生递归调用的整个生成器表达式.
像这样调用你的函数:
x = generate(obj, 3)
Run Code Online (Sandbox Code Playgroud)
请注意,这会返回一个生成器x.things.
根据您的特定要求,这是一个更通用的版本,使用getattr任意属性.
def generate(obj, iterable_attr, attr_to_yield, n):
if n == 1:
for x in getattr(obj, iterable_attr):
yield getattr(x, attr_to_yield)
else:
for x in getattr(obj, iterable_attr):
yield from generate(x, iterable_attr, attr_to_yield, n - 1)
Run Code Online (Sandbox Code Playgroud)
现在,将您的功能称为:
x = generate(obj, 'children', 'thing', 3)
Run Code Online (Sandbox Code Playgroud)
yield from上面的例子很好,但我严重怀疑需要的级别/深度参数.适用于任何树的更简单/更通用的解决方案:
class Node(object):
def __init__(self, thing, children=None):
self.thing = thing
self._children = children
def children(self):
return self._children if self._children else []
def generate(node):
if node.thing:
yield node.thing
for child in node.children():
yield from generate(child)
node = Node('mr.', [Node('derek', [Node('curtis')]), Node('anderson')])
print(list(generate(node)))
Run Code Online (Sandbox Code Playgroud)
返回:
$ python3 test.py
['mr.', 'derek', 'curtis', 'anderson']
Run Code Online (Sandbox Code Playgroud)
请注意,这将返回当前节点thing之前的任何子节点.(IE它在走的路上表达自己.)如果你更喜欢它在回来的路上表达自己,交换if和for声明.(DFS与BFS)但在你的情况下可能无关紧要(我怀疑一个节点有一个thing或一个孩子,从来没有).
如果使用Python 2.7,您需要保留自己的可迭代堆栈并进行循环:
from operator import methodcaller
def recursive(obj, iterater, yielder, depth):
iterate = methodcaller(iterater)
xs = [iterate(obj)]
while xs:
try:
x = xs[-1].next()
if len(xs) != depth:
xs.append(iterate(x))
else:
yield getattr(x, yielder)
except StopIteration:
xs.pop()
Run Code Online (Sandbox Code Playgroud)
这是一个来自可迭代函数的更通用的递归ichain的特殊情况:
def recursive_ichain(iterable_tree):
xs = [iter(iterable_tree)]
while [xs]:
try:
x = xs[-1].next()
if isinstance(x, collections.Iterable):
xs.append(iter(x))
else:
yield x
except StopIteration:
xs.pop()
Run Code Online (Sandbox Code Playgroud)
还有一些测试对象:
class Thing(object):
def __init__(self, thing):
self.thing = thing
class Parent(object):
def __init__(self, *kids):
self.kids = kids
def children(self):
return iter(self.kids)
test_obj = Parent(
Parent(
Parent(Thing('one'), Thing('two'), Thing('three')),
Parent(Thing('four')),
Parent(Thing('five'), Thing('six')),
),
Parent(
Parent(Thing('seven'), Thing('eight')),
Parent(),
Parent(Thing('nine'), Thing('ten')),
)
)
Run Code Online (Sandbox Code Playgroud)
并测试它:
>>>for t in recursive(test_obj, 'children', 'thing', 3):
>>> print t
one
two
three
four
five
six
seven
eight
nine
ten
Run Code Online (Sandbox Code Playgroud)
Personnaly我倾向于更改yield getattr(x, yielder)to yield x来访问leaf对象本身并显式访问该东西.即
for leaf in recursive(test_obj, 'children', 3):
print leaf.thing
Run Code Online (Sandbox Code Playgroud)