詭異的 list comprehension 展開

問題描述

存在一些文件名類似 2016-07-16_161.res 2016-07-18_161.res 的文件弥鹦,文件內(nèi)容如下:

less 2016-07-16_161.res
1.34.0.68
1.34.1.37
1.34.1.121
1.34.5.87
1.34.5.182
1.34.6.72
1.34.6.245
1.34.9.149
1.34.11.74
1.34.13.161
...

希望通過 Spark 統(tǒng)計(jì)出存活超過 n 天的 IP落君,并可以同時(shí)看到該 IP 分別在哪天存活。

因此需要先將這些文件轉(zhuǎn)換成 Pair RDD,其中的每一項(xiàng)以 IP 為 key脖咐,以日期為 value断箫。

編寫 Spark 腳本如下:

rdds = [sc.textFile(f).map(lambda x: (x, f.split('_')[0])) for f in glob('*.res')]

預(yù)期得到結(jié)果:

rdds[0].first()
(u'210.240.117.126', '2016-07-16')

rdds[1].first()
(u'210.240.117.126', '2016-07-18')

但實(shí)際得到的結(jié)果為:

rdds[0].first()
(u'210.240.117.126', '2016-07-18')

rdds[1].first()
(u'210.240.117.126', '2016-07-18')

即所有的 RDD 中每一項(xiàng)的 value 都為同一值瓜浸。

問題定位

看到這個(gè)現(xiàn)象基本把可能出問題的點(diǎn)鎖定在了 map(lambda x: (x, f.split('_')[0])) 附近。

查找了 pyspark 中 map 函數(shù)的實(shí)現(xiàn)比原,也沒發(fā)現(xiàn)有什么不妥的地方插佛。rdd.py 中 RDD::map() 的實(shí)現(xiàn)如下:

    def map(self, f, preservesPartitioning=False):
        """
        Return a new RDD by applying a function to each element of this RDD.
        >>> rdd = sc.parallelize(["b", "a", "c"])
        >>> sorted(rdd.map(lambda x: (x, 1)).collect())
        [('a', 1), ('b', 1), ('c', 1)]
        """
        def func(_, iterator):
            return map(f, iterator)
        return self.mapPartitionsWithIndex(func, preservesPartitioning)

另一個(gè)值得懷疑的點(diǎn)就是 lambda 的行為是不是真的符合預(yù)期,但是一直只是懷疑量窘,沒有找到有效的方法來驗(yàn)證自己的猜測(cè)雇寇。直到看了 @張通 轉(zhuǎn)發(fā)的 《Python 中的 lambda 和「真正的」lambda 有什么區(qū)別?》[1]蚌铜,才確認(rèn)并搞清楚問題發(fā)生的根本原因锨侯。

問題發(fā)生的根本原因在于 Python 中的 lambda 在實(shí)現(xiàn)上存在缺陷[2],導(dǎo)致 Spark 腳本中傳入 map 函數(shù)的 lambda 表達(dá)式共享了同一個(gè)變量 f冬殃,從而導(dǎo)致了上述問題的發(fā)生囚痴。

舉一反三

目前大概可以確定,Python 實(shí)現(xiàn)的 lambda 表達(dá)式中的變量可能并不像正常的函數(shù)那樣具有獨(dú)立的作用域审葬。

以下述代碼為例:

test = [lambda x: x+i for i in range(10)]
print test[0](1)
print test[9](1)

這段代碼并不會(huì)如預(yù)期的輸出 110深滚,而是會(huì)輸出 1010。使用 dis 模塊分析列表中的任意一個(gè) lambda 表達(dá)式得到如下結(jié)果涣觉。

In [3]: dis.dis(test[0])
  1           0 LOAD_FAST                0 (x)
              3 LOAD_GLOBAL              0 (i)
              6 BINARY_ADD
              7 RETURN_VALUE

從上述 Python bytecode 中可以看出 lambda 表達(dá)式中的變量 i 的確沒有一個(gè)獨(dú)立的作用域痴荐,而是使用了相對(duì)全局的作用域,而此時(shí)該作用域中的變量 i 已經(jīng)變成了 9官册,因此得到了上述結(jié)果生兆。

更近一步,list comprehension 中的變量的作用域又是怎樣的呢攀隔?是僅僅作用于 list comprehension 內(nèi)部皂贩,還是也會(huì)影響到外部呢?

實(shí)測(cè)代碼如下:

In [13]: p = 100

In [14]: a = [p for p in range(10)]

In [15]: print a
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

In [16]: print p
9

可見 list comprehension 中的變量也會(huì)對(duì)其外的變量產(chǎn)生影響昆汹,即 list comprehension 中的變量也不具有獨(dú)立的作用域明刷。所以,雖然 list comprehension 具有執(zhí)行效率高和可讀性強(qiáng)等優(yōu)點(diǎn)满粗,在實(shí)際的編碼中也需要多注意這些副作用辈末,防止被坑。

問題解決

下面兩個(gè)方法均可解決該問題:

def gen_rdd(f):
    return sc.textFile(f).map(lambda x: (x, f.split('_')[0]))
    
rdds = [gen_rdd(f) for f in glob('*.res')]
rdds = map(
    lambda f: sc.textFile(f).map(lambda x: (x, f.split('_')[0])), glob('*.res')
)

推薦使用第二種方式映皆。

參考鏈接


  1. http://www.zhihu.com/question/22819202/answer/113794339?from=timeline&isappinstalled=0 ?

  2. http://www.yinwang.org/blog-cn/2013/03/26/lisp-dead-alive ?

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末挤聘,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子捅彻,更是在濱河造成了極大的恐慌组去,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,013評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件步淹,死亡現(xiàn)場(chǎng)離奇詭異从隆,居然都是意外死亡诚撵,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,205評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門键闺,熙熙樓的掌柜王于貴愁眉苦臉地迎上來寿烟,“玉大人,你說我怎么就攤上這事辛燥∩肝洌” “怎么了?”我有些...
    開封第一講書人閱讀 152,370評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵挎塌,是天一觀的道長(zhǎng)徘六。 經(jīng)常有香客問我,道長(zhǎng)勃蜘,這世上最難降的妖魔是什么硕噩? 我笑而不...
    開封第一講書人閱讀 55,168評(píng)論 1 278
  • 正文 為了忘掉前任,我火速辦了婚禮缭贡,結(jié)果婚禮上炉擅,老公的妹妹穿的比我還像新娘。我一直安慰自己阳惹,他們只是感情好谍失,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,153評(píng)論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著莹汤,像睡著了一般快鱼。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上纲岭,一...
    開封第一講書人閱讀 48,954評(píng)論 1 283
  • 那天抹竹,我揣著相機(jī)與錄音,去河邊找鬼止潮。 笑死窃判,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的喇闸。 我是一名探鬼主播袄琳,決...
    沈念sama閱讀 38,271評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼燃乍!你這毒婦竟也來了唆樊?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,916評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤刻蟹,失蹤者是張志新(化名)和其女友劉穎逗旁,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體舆瘪,經(jīng)...
    沈念sama閱讀 43,382評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡痢艺,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,877評(píng)論 2 323
  • 正文 我和宋清朗相戀三年仓洼,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片堤舒。...
    茶點(diǎn)故事閱讀 37,989評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖哺呜,靈堂內(nèi)的尸體忽然破棺而出舌缤,到底是詐尸還是另有隱情,我是刑警寧澤某残,帶...
    沈念sama閱讀 33,624評(píng)論 4 322
  • 正文 年R本政府宣布国撵,位于F島的核電站,受9級(jí)特大地震影響玻墅,放射性物質(zhì)發(fā)生泄漏介牙。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,209評(píng)論 3 307
  • 文/蒙蒙 一澳厢、第九天 我趴在偏房一處隱蔽的房頂上張望环础。 院中可真熱鬧,春花似錦剩拢、人聲如沸线得。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,199評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽贯钩。三九已至,卻和暖如春办素,著一層夾襖步出監(jiān)牢的瞬間角雷,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,418評(píng)論 1 260
  • 我被黑心中介騙來泰國(guó)打工性穿, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留勺三,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,401評(píng)論 2 352
  • 正文 我出身青樓季二,卻偏偏與公主長(zhǎng)得像檩咱,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子胯舷,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,700評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容