make_index 这个函数就是简单的做一个倒排索引,将出现了某种语言的文章与该语言关联起来,返回的结果是一个列表,列表中每个元素是一个 tuple 。
from collections import namedtuple
WikipediaArticle = namedtuple("WikipediaArticle", ["title", "text"])
def make_index(langs, articles):
result = []
for lang in langs:
# 创建包含该 lang 的文章的生成器
article_gen = (article for article in articles if article.text.find(lang) >= 0)
result.append((lang, article_gen))
return result
if __name__ == '__main__':
articles = [
WikipediaArticle('1', "Groovy is pretty interesting, and so is Erlang"),
WikipediaArticle('2', "Scala and Java run on the JVM"),
WikipediaArticle('3', "Scala is not purely functional"),
WikipediaArticle('4', "The cool kids like Haskell more than Java"),
WikipediaArticle('5', "Java is for enterprise developers")
]
langs = ["Scala", "Java", "Groovy", "Haskell", "Erlang"]
for item in make_index(langs, articles):
print(item[0], list(item[1])
然后跑出来的结果是:
Scala [WikipediaArticle(title='1', text='Groovy is pretty interesting, and so is Erlang')]
Java [WikipediaArticle(title='1', text='Groovy is pretty interesting, and so is Erlang')]
Groovy [WikipediaArticle(title='1', text='Groovy is pretty interesting, and so is Erlang')]
Haskell [WikipediaArticle(title='1', text='Groovy is pretty interesting, and so is Erlang')]
Erlang [WikipediaArticle(title='1', text='Groovy is pretty interesting, and so is Erlang')]
很明显这结果有问题,取得都是第一条数据, 但是如果在函数内部做一点修改,会得到以下结果:
# result.append((lang, article_gen)) 这一句改成 result.append((lang, list(article_gen)))
('Scala', [WikipediaArticle(title='2', text='Scala and Java run on the JVM'), WikipediaArticle(title='3', text='Scala is not purely functional')])
('Java', [WikipediaArticle(title='2', text='Scala and Java run on the JVM'), WikipediaArticle(title='4', text='The cool kids like Haskell more than Java'), WikipediaArticle(title='5', text='Java is for enterprise developers')])
('Groovy', [WikipediaArticle(title='1', text='Groovy is pretty interesting, and so is Erlang')])
('Haskell', [WikipediaArticle(title='4', text='The cool kids like Haskell more than Java')])
('Erlang', [WikipediaArticle(title='1', text='Groovy is pretty interesting, and so is Erlang')])
感觉好奇怪,为什么会这样,求解, 在函数里面解开生成器和在函数外面解开有什么区别吗?
我把return换成yield之后,又能得到预期的结果
def make_index(langs, articles):
for lang in langs:
article_gen = (article for article in articles if article.text.find(lang) >= 0)
yield lang, article_gen
for item in make_index(langs, articles):
print(item, list(item[1]))
输出:
('Scala', <generator object make_index.<locals>.<genexpr> at 0x00EFBDB0>) [WikipediaArticle(title='2', text='Scala and Java run on the JVM'), WikipediaArticle(title='3', text='Scala is not purely functional')]
('Java', <generator object make_index.<locals>.<genexpr> at 0x00F091E0>) [WikipediaArticle(title='2', text='Scala and Java run on the JVM'), WikipediaArticle(title='4', text='The cool kids like Haskell more than Java'), WikipediaArticle(title='5', text='Java is for enterprise developers')]
('Groovy', <generator object make_index.<locals>.<genexpr> at 0x00EFBDB0>) [WikipediaArticle(title='1', text='Groovy is pretty interesting, and so is Erlang')]
('Haskell', <generator object make_index.<locals>.<genexpr> at 0x00F091E0>) [WikipediaArticle(title='4', text='The cool kids like Haskell more than Java')]
('Erlang', <generator object make_index.<locals>.<genexpr> at 0x00EFBDB0>) [WikipediaArticle(title='1', text='Groovy is pretty interesting, and so is Erlang')]
问题解决了,大概就是一个和 https://docs.python.org/3.6/faq/programming.html#why-do-lambdas-defined-in-a-loop-with-different-values-all-return-the-same-result 类似的问题,for循环结束后,迭代器中绑定的lang值都执行了langs这个列表中最后一个元素(Erlang), 所以导致所有迭代器的结果都是Erlang的结果。
解决这个问题的办法可以参照@siteshen说的。
def make_index(langs, articles):
res = []
for lang in langs:
res.append((lang, (lambda arg: (article for article in articles if article.text.find(arg) >= 0))(lang)))
return res
感谢各位大神解惑!~
1
a87150 2017-03-16 11:50:13 +08:00 1
|
2
aragakiiyui OP @a87150 大哥,你有认真看我的描述吗,我看你发的链接是教程,完全没解答问题呢。希望能给建设性的指教。
|
3
bxb100 2017-03-16 12:04:05 +08:00
兄弟
``` [article for article in articles if article.text.find(lang) >= 0] ``` |
4
boluoshu 2017-03-16 12:08:21 +08:00
居然不是震惊....
|
5
bxb100 2017-03-16 12:12:19 +08:00
不过输出成这样有点奇怪
按理说不该是<generator object...>吗 觉得该是 print 的锅 |
6
mink 2017-03-16 12:15:44 +08:00
为什么我试了一下没问题呢
``` WikipediaArticle(title='2', text='Scala and Java run on the JVM') WikipediaArticle(title='2', text='Scala and Java run on the JVM') WikipediaArticle(title='1', text='Groovy is pretty interesting, and so is Erlang') WikipediaArticle(title='4', text='The cool kids like Haskell more than Java') WikipediaArticle(title='1', text='Groovy is pretty interesting, and so is Erlang') ``` |
8
mink 2017-03-16 12:22:19 +08:00
@bxb100 还是有问题, 我测试错了
```python article_gen = (article for article in articles if article.text.find(lang) >= 0) print(lang, next(article_gen)) result.append((lang, article_gen)) ``` 我实在里面试了一下, 最后输出的还是跟楼主发的一样。 |
9
a87150 2017-03-16 12:27:09 +08:00
@aragakiiyui 这么短一个程序有混用 tab 和空格,少括弧,先生成 tuple 又莫明其妙转换成 list 这么多错误,所以叫你学好基础知识。
|
10
misaka19000 2017-03-16 12:27:46 +08:00 via Android
楼主明天就来 UC 上班,待遇从优
|
11
bxb100 2017-03-16 12:29:53 +08:00
25 87 SETUP_LOOP 50 (to 140)
90 LOAD_GLOBAL 1 (make_index) 93 LOAD_FAST 1 (langs) 96 LOAD_FAST 0 (articles) 99 CALL_FUNCTION 2 (2 positional, 0 keyword pair) 102 GET_ITER >> 103 FOR_ITER 33 (to 139) 106 STORE_FAST 2 (item) 26 109 LOAD_GLOBAL 2 (print) 112 LOAD_FAST 2 (item) 115 LOAD_CONST 16 (0) 118 BINARY_SUBSCR 119 LOAD_GLOBAL 3 (list) 122 LOAD_FAST 2 (item) 125 LOAD_CONST 17 (1) 128 BINARY_SUBSCR 129 CALL_FUNCTION 1 (1 positional, 0 keyword pair) 132 CALL_FUNCTION 2 (2 positional, 0 keyword pair) 135 POP_TOP 136 JUMP_ABSOLUTE 103 >> 139 POP_BLOCK >> 140 LOAD_CONST 0 (None) 143 RETURN_VALUE |
12
bxb100 2017-03-16 12:31:08 +08:00
寄存器的锅?求大神解释.....
|
13
imn1 2017-03-16 12:33:16 +08:00
生成器只能读取一次,自己想想逻辑哪里有问题吧
提示: list(<generator object>) in for |
14
ArieShout 2017-03-16 12:42:29 +08:00 via iPhone 2
局部变量 lang 被所有生成器表达式捕获并共享,表达式被遍历输出的时候才会延迟获取 lang 的值,此时循环已经结束, lang 获取的是最语言列表的最后一个值
|
15
siteshen 2017-03-16 12:44:56 +08:00 1
原因: generator 在取 next 时才去执行的代码,执行代码时 lang 的值是最后一次的值,可以改成这样看看效果:
article_gen = ((lang, article) for article in articles if article.text.find(lang) >= 0) 这里返回的 lang 就是最后一个值。 暂时没想到更好的在循环里生成 generator 的办法,我会避免使用。参照之前经典的 js 面试题改了下代码: article_gen = (lambda l: (article for article in articles if article.text.find(l) >= 0))(lang) |
16
aragakiiyui OP @bxb100 我用的不是列表解析啊,用的是生成器表达式,如果只是 print 生成器,就是 generator objec , 但是我 print 的时候把他转成列表了。
所有我还是不明白为什么,转成列表之后,就只剩一个元素了。 |
17
aragakiiyui OP @a87150 不好意思,可能是我 markdown 没写好,但是我代码都是 pep8 检测了的,不可能会有什么语法上的错误或者多括号少括号的问题,至于你说的生成 tuple 和 list ,我觉得你可能没看懂代码。转成 list 的原因是要把生成器的元素释放出来。
|
18
zhy0216 2017-03-16 13:00:15 +08:00
原因是变量的作用域问题
gen_generator = lambda lang, articles: (article for article in articles if article.text.find(lang) >= 0) in loop: article_gen = gen_generator(lang, articles) |
19
aragakiiyui OP @imn1 可是我的结果是五个 ( String , generator ) tuple :
('Scala', <generator object make_index.<locals>.<genexpr> at 0x0061BDB0>) ('Java', <generator object make_index.<locals>.<genexpr> at 0x006291E0>) ('Groovy', <generator object make_index.<locals>.<genexpr> at 0x0061BDB0>) ('Haskell', <generator object make_index.<locals>.<genexpr> at 0x006291E0>) ('Erlang', <generator object make_index.<locals>.<genexpr> at 0x0061BDB0>) 然后对每个 tuple 的生成器利用 list 函数,为啥返回的结果是一毛一样的。我就是这点存在疑惑。 |
20
oisc 2017-03-16 13:05:08 +08:00
你 article 还是当年的 article , lang 已经不是当年的 lang 了。
|
21
a87150 2017-03-16 13:09:10 +08:00
|
22
aragakiiyui OP |
23
a87150 2017-03-16 13:46:09 +08:00
|
24
zhy0216 2017-03-16 14:19:48 +08:00
@aragakiiyui
Finding closure with closures : |
25
aragakiiyui OP @a87150 你是这里找到的吧 # result.append((lang, article_gen)) 这一句改成 result.append((lang, list(article_gen)))
这里是为了说明问题所做的比较啊大哥....关注点也是清奇。 所以请好好审题。明确别人问的是什么。然后丢合适的链接。要么就不答,要么就认真答。大家都是奔着解决问题去的。你这种回答态度很让人不舒服。 还好其他 v 友读懂了题目,给我上了一课。 |
26
falseen 2017-03-16 16:07:18 +08:00 via Android 1
|
27
aragakiiyui OP |
28
WKPlus 2017-03-17 09:59:26 +08:00
这是闭包的一个容易误解的地方嘛,等你把 generator 转成 list 的时候, generator 的代码开始执行,这时 lang 的值是 Erlang
|