python lambda/fn是否可以代表任意调用方生成?
更新:示例现在列出了所需的结果(以下黑体) 我发现自己编写了很多函数来搜索某些数据,我想让调用方在找到匹配项时指定行为:它们可能会打印出某些内容或将其添加到其中一个数据结构中,但也非常希望能够有选择地返回找到的数据以供进一步传输、存储或处理 例子 首次客户端使用:python lambda/fn是否可以代表任意调用方生成?,python,lambda,generator,yield,Python,Lambda,Generator,Yield,更新:示例现在列出了所需的结果(以下黑体) 我发现自己编写了很多函数来搜索某些数据,我想让调用方在找到匹配项时指定行为:它们可能会打印出某些内容或将其添加到其中一个数据结构中,但也非常希望能够有选择地返回找到的数据以供进一步传输、存储或处理 例子 首次客户端使用: def my_visitor(x): # client visitor functions (also often use lambdas) if x > 3: yield x / 2 #>
def my_visitor(x): # client visitor functions (also often use lambdas)
if x > 3:
yield x / 2 #>>> WANT TO DO SOMETHING LIKE THIS <<<#
results = find_stuff(my_visitor) # client usage
def print_repr_visitor(x):
print repr(x)
find_stuff(print_repr_visitor) # alternative usage
应打印1 2 3 4 5 6(单独的行),但不产生任何结果
但是,yield
并没有在“results”中创建生成器(至少在我一直坚持使用的python 2.6.6中)
我试过的 我一直在整理这个,经常是这样
def find_stuff(visitor):
for x in (1, 2, 3, 4, 5):
val = visitor(x)
if val is not None:
yield val
…或者有时,当访问者参数列表输入太多次会让人感到痛苦时
def find_stuff(visitor):
for x in (1, 2, 3, 4, 5):
val = visitor(x)
if val == 'yield':
yield x
elif val is not None:
yield val
议题/问题
这些“解决方案”不仅笨拙——需要“查找”例程的显式内置支持——它们从访问者可以返回给顶级调用者的结果集中删除了哨兵值
在简洁、直观、灵活、优雅等方面是否有更好的选择
谢谢 在Python 3中,可以使用
yield from
从子生成器中生成项:
def find_stuff(visitor):
for x in (1, 2, 3, 4, 5):
yield from visitor(x)
sentinel = object()
def my_scalar_visitor(x):
if x > 3:
return x / 2
else:
return sentinel
def find_stuff_scalar(scalar_visitor):
search_list=(1,2,3,4,5,6)
return (x for x in (scalar_visitor(y) for y in search_list) if x != sentinel)
list(find_stuff_scalar(my_scalar_visitor))
[0x2, 0x2, 0x3]
在Python 2中,必须循环子生成器。这需要更多的代码,不能处理一些边缘情况,但通常已经足够好了:
def find_stuff(visitor):
for x in (1, 2, 3, 4, 5):
for item in visitor(x):
yield item
边缘情况是尝试
发送
值或向子生成器抛出
异常。如果您没有使用协同程序功能,您可能不需要担心它们。如果理解正确,可能您需要这样的功能:
def find_stuff(visitor):
for x in [1, 2, 3, 4, 5]:
match, val = visitor(x)
if match:
yield val
def my_visitor(x):
if x > 4:
return True, x/2
else:
return False, None
def my_visitor2(x):
if x > 3:
yield x / 2
elif x > 1:
yield x
yield x*2
yield x-3
In [83]: list(find_stuff(my_visitor2))
[0x2, 0x4, -0x1, 0x3, 0x6, 0x0, 0x2, 0x2, 0x3]
也就是说,让访问者返回两件事:要生成的值(如果有)和指示是否生成该值的布尔值。这样就可以产生任何价值
您问题的标题似乎暗示您希望
my_visitor
以某种方式决定find_stuff
是否会在每次迭代中产生一个值,但您实际上并没有在问题中描述这一点。无论如何,这是不可能的。生成器可以调用另一个函数来决定要生成什么,但被调用的函数无法神奇地使其调用者生成或不生成;这个决定必须在调用者内部做出(find_stuff
)
但从你的问题来看,我不明白为什么这是个问题。你说你提出的解决方案“笨拙——需要“查找”例程的显式内置支持”,但我看不出这有多笨拙。它只是一个API
find_stuff
显然必须有“内置支持”来完成它应该做的事情,而且访问者必须知道返回什么来与呼叫方进行通信。您不能期望能够编写一个my_visitor
函数,该函数可以与任何人可能想到的任何查找例程一起工作;整个系统必须定义一个API,描述如何编写find\u stuff
可以使用的访问者。所以,你只需要拿出一个API,访问者必须遵循。我上面的示例是一个简单的API,但很难从您的问题中看出您在寻找什么。我通过一些调查,在python 2.6中找到了解决方案。这有点奇怪,但它确实起作用了
from itertools import chain
def my_visitor(x):
if x > 3:
yield x / 2
def find_stuff(visitor):
search_list = (1,2,3,4,5,6)
return (x for x in chain.from_iterable(visitor(x) for x in search_list))
find_stuff(my_visitor)
<generator object <genexpr> at 0x0000000047825558>
list(find_stuff(my_visitor))
[0x2, 0x2, 0x3]
让每次访问都不返回任何值、单个值或一组值,它们都会进入结果
不过,您也可以将其调整为标量值。最好的方法是使用嵌套生成器:
def find_stuff(visitor):
for x in (1, 2, 3, 4, 5):
yield from visitor(x)
sentinel = object()
def my_scalar_visitor(x):
if x > 3:
return x / 2
else:
return sentinel
def find_stuff_scalar(scalar_visitor):
search_list=(1,2,3,4,5,6)
return (x for x in (scalar_visitor(y) for y in search_list) if x != sentinel)
list(find_stuff_scalar(my_scalar_visitor))
[0x2, 0x2, 0x3]
解决了问题给出的问题,但在我看来,generator-in-a-generator方法在这种特定情况下过于复杂,限制了客户端使用代码的选项
您需要遍历一些结构,应用一些函数,并产生结果。您的代码允许这样做,但是您将Python已经拥有的两个优秀的独立支持(遍历和映射)合并在一起,没有额外的好处
您的遍历函数可以简单地遍历:
def traverse_stuff():
for x in (1, 2, 3, 4, 5, 6):
yield x
当我们想要消费时,您或您的客户可以使用列表理解,组合词,如map
和filter
,或者对于循环使用简单的:
[x / 2 for x in traverse_stuff() if x > 3]
map(lambda x: x / 2, filter(lambda x: x > 3, traverse_stuff())
for value in traverse_stuff():
print(value)
以这种方式拆分代码使其更具可组合性(您的客户端不限于访问者模式/生成器),对于其他Python开发人员更直观,对于只需要使用部分结构的情况,性能更高(例如,当您只需要从树中查找n个节点时,当您只想查找结构中满足条件的第一个值时,&c)。我不太明白您的要求。您能举一个示例说明如何使用该函数吗你想要的结果是什么?我想我不明白为什么你不总是在你的find\u stuff
函数中产生visitor(x)
的结果。你可以使用object()
创建一个唯一的哨兵值,如果这是问题的话。生成器函数返回的生成器对象不是None
,因此,我不确定如果val不是None
,那么有什么意义。是的,更多的信息会很有帮助……考虑到您的限制,我看不到任何方法可以绕过您发布的内容。我阅读这篇文章是想了解的ate在generator/iterable上运行,其中一些调用更改状态但不返回值,其他调用返回值,有时这是查找元素,有时是后处理数据?似乎任何更优雅的解决方案都将是非常特定于域的……简化的一种方法是,如果my_visitor
想要返回查找值(即if val=='yield'
术语),而不是返回哨兵,它应该只返回x
@BrenBarn:我添加了一个示例/结果