日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程语言 > python >内容正文

python

Python 的 51 个秘密曝光,Github 获 2 万星

發(fā)布時(shí)間:2024/9/15 python 44 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Python 的 51 个秘密曝光,Github 获 2 万星 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

點(diǎn)擊上方小詹學(xué)Python”,選擇“置頂或者星標(biāo)”

第一時(shí)間收到精彩推送!


Python, 是一個(gè)設(shè)計(jì)優(yōu)美的解釋型高級(jí)語言, 它提供了很多能讓程序員感到舒適的功能特性. 但有的時(shí)候, Python 的一些輸出結(jié)果對(duì)于初學(xué)者來說似乎并不是那么一目了然.

一個(gè)解析51項(xiàng)堪稱是"秘密"的Python特性項(xiàng)目,在GitHub上徹底火了。

英文原版已經(jīng)拿到了近15000星,中文翻譯版也獲得了7600+星。



這個(gè)有趣的項(xiàng)目意在收集 Python 中那些難以理解和反人類直覺的例子以及鮮為人知的功能特性, 并嘗試討論這些現(xiàn)象背后真正的原理!

雖然下面的有些例子并不一定會(huì)讓你覺得 WTFs, 但它們依然有可能會(huì)告訴你一些你所不知道的 Python 有趣特性. 我覺得這是一種學(xué)習(xí)編程語言內(nèi)部原理的好辦法, 而且我相信你也會(huì)從中獲得樂趣!

如果您是一位經(jīng)驗(yàn)比較豐富的 Python 程序員, 你可以嘗試挑戰(zhàn)看是否能一次就找到例子的正確答案. 你可能對(duì)其中的一些例子已經(jīng)比較熟悉了, 那這也許能喚起你當(dāng)年踩這些坑時(shí)的甜蜜回憶?

這個(gè)項(xiàng)目的中文版全文大約2萬字,干貨多的快要溢出來了,大家可以先看一下目錄。

示例結(jié)構(gòu)

所有示例的結(jié)構(gòu)都如下所示:

我個(gè)人建議, 最好依次閱讀下面的示例, 并對(duì)每個(gè)示例:

  • 仔細(xì)閱讀設(shè)置例子最開始的代碼. 如果您是一位經(jīng)驗(yàn)豐富的 Python 程序員, 那么大多數(shù)時(shí)候您都能成功預(yù)期到后面的結(jié)果.

  • 閱讀輸出結(jié)果,

    • 如果不知道, 深呼吸然后閱讀說明 (如果你還是看不明白, 別沉默! 可以在這提個(gè) issue).

    • 如果知道, 給自己點(diǎn)獎(jiǎng)勵(lì), 然后去看下一個(gè)例子.

    • 確認(rèn)結(jié)果是否如你所料.

    • 確認(rèn)你是否知道這背后的原理.

PS: 你也可以在命令行閱讀 WTFpython. 我們有 pypi 包 和 npm 包(支持代碼高亮).(譯: 這兩個(gè)都是英文版的)

示例

Strings can be tricky sometimes/微妙的字符串?

1、

>>> a = "some_string">>> id(a)140420665652016>>> id("some" + "_" + "string") # 注意兩個(gè)的id值是相同的.140420665652016

2、

>>> a = "wtf">>> b = "wtf">>> a is bTrue>>> a = "wtf!">>> b = "wtf!">>> a is bFalse>>> a, b = "wtf!", "wtf!">>> a is b # 僅適用于3.7版本以下, 3.7以后的返回結(jié)果為False.True

3、

>>> 'a' * 20 is 'aaaaaaaaaaaaaaaaaaaa'True>>> 'a' * 21 is 'aaaaaaaaaaaaaaaaaaaaa'False

?說明:

  • 這些行為是由于 Cpython 在編譯優(yōu)化時(shí), 某些情況下會(huì)嘗試使用已經(jīng)存在的不可變對(duì)象而不是每次都創(chuàng)建一個(gè)新對(duì)象. (這種行為被稱作字符串的駐留[string interning])

  • 發(fā)生駐留之后, 許多變量可能指向內(nèi)存中的相同字符串對(duì)象. (從而節(jié)省內(nèi)存)

  • 在上面的代碼中, 字符串是隱式駐留的. 何時(shí)發(fā)生隱式駐留則取決于具體的實(shí)現(xiàn). 這里有一些方法可以用來猜測(cè)字符串是否會(huì)被駐留:

    • 所有長(zhǎng)度為 0 和長(zhǎng)度為 1 的字符串都被駐留.

    • 字符串在編譯時(shí)被實(shí)現(xiàn) ('wtf'?將被駐留, 但是?''.join(['w', 't', 'f']?將不會(huì)被駐留)

    • 字符串中只包含字母,數(shù)字或下劃線時(shí)將會(huì)駐留. 所以?'wtf!'?由于包含?!?而未被駐留. 可以在這里找到 CPython 對(duì)此規(guī)則的實(shí)現(xiàn).


  • 當(dāng)在同一行將?a?和?b?的值設(shè)置為?"wtf!"?的時(shí)候, Python 解釋器會(huì)創(chuàng)建一個(gè)新對(duì)象, 然后同時(shí)引用第二個(gè)變量(譯: 僅適用于3.7以下, 詳細(xì)情況請(qǐng)看這里). 如果你在不同的行上進(jìn)行賦值操作, 它就不會(huì)“知道”已經(jīng)有一個(gè)?wtf!?對(duì)象 (因?yàn)?"wtf!"?不是按照上面提到的方式被隱式駐留的). 它是一種編譯器優(yōu)化, 特別適用于交互式環(huán)境.

  • 常量折疊(constant folding) 是 Python 中的一種?窺孔優(yōu)化(peephole optimization)?技術(shù). 這意味著在編譯時(shí)表達(dá)式?'a'*20會(huì)被替換為?'aaaaaaaaaaaaaaaaaaaa'?以減少運(yùn)行時(shí)的時(shí)鐘周期. 只有長(zhǎng)度小于 20 的字符串才會(huì)發(fā)生常量折疊. (為啥? 想象一下由于表達(dá)式?'a'*10**10?而生成的?.pyc?文件的大小). 相關(guān)的源碼實(shí)現(xiàn)

https://github.com/python/cpython/blob/3.6/Python/peephole.c#L288

Time for some hash brownies!/是時(shí)候來點(diǎn)蛋糕了!

  • hash brownie指一種含有大麻成分的蛋糕, 所以這里是句雙關(guān)

1、

some_dict = {}some_dict[5.5] = "Ruby"some_dict[5.0] = "JavaScript"some_dict[5] = "Python"

Output:

>>> some_dict[5.5]"Ruby">>> some_dict[5.0]"Python">>> some_dict[5]"Python"

"Python" 消除了 "JavaScript" 的存在?

?說明:

  • Python 字典通過檢查鍵值是否相等和比較哈希值來確定兩個(gè)鍵是否相同.

  • 具有相同值的不可變對(duì)象在Python中始終具有相同的哈希值.

>>> 5 == 5.0True>>> hash(5) == hash(5.0)True
  • 注意:?具有不同值的對(duì)象也可能具有相同的哈希值(哈希沖突).

  • 當(dāng)執(zhí)行?some_dict[5] = "Python"?語句時(shí), 因?yàn)镻ython將?5?和?5.0?識(shí)別為?some_dict?的同一個(gè)鍵, 所以已有值 "JavaScript" 就被 "Python" 覆蓋了.

  • 這個(gè) StackOverflow的?回答?漂亮的解釋了這背后的基本原理.

https://stackoverflow.com/questions/32209155/why-can-a-floating-point-dictionary-key-overwrite-an-integer-key-with-the-same-v

Return return everywhere!/到處返回!

def some_func(): try: return 'from_try' finally: return 'from_finally'

Output:

>>> some_func()'from_finally'

?說明:

  • 當(dāng)在 "try...finally" 語句的?try?中執(zhí)行?return,?break?或?continue?后,?finally?子句依然會(huì)執(zhí)行.

  • 函數(shù)的返回值由最后執(zhí)行的?return?語句決定. 由于?finally?子句一定會(huì)執(zhí)行, 所以?finally?子句中的?return?將始終是最后執(zhí)行的語句.

?Deep down, we're all the same./本質(zhì)上,我們都一樣.?

class WTF: pass

Output:

>>> WTF() == WTF() # 兩個(gè)不同的對(duì)象應(yīng)該不相等False>>> WTF() is WTF() # 也不相同F(xiàn)alse>>> hash(WTF()) == hash(WTF()) # 哈希值也應(yīng)該不同True>>> id(WTF()) == id(WTF())True

?說明:

  • 當(dāng)調(diào)用?id?函數(shù)時(shí), Python 創(chuàng)建了一個(gè)?WTF?類的對(duì)象并傳給?id?函數(shù). 然后?id?函數(shù)獲取其id值 (也就是內(nèi)存地址), 然后丟棄該對(duì)象. 該對(duì)象就被銷毀了.

  • 當(dāng)我們連續(xù)兩次進(jìn)行這個(gè)操作時(shí), Python會(huì)將相同的內(nèi)存地址分配給第二個(gè)對(duì)象. 因?yàn)?(在CPython中)?id?函數(shù)使用對(duì)象的內(nèi)存地址作為對(duì)象的id值, 所以兩個(gè)對(duì)象的id值是相同的.

  • 綜上, 對(duì)象的id值僅僅在對(duì)象的生命周期內(nèi)唯一. 在對(duì)象被銷毀之后, 或被創(chuàng)建之前, 其他對(duì)象可以具有相同的id值.

  • 那為什么?is?操作的結(jié)果為?False?呢? 讓我們看看這段代碼.

class WTF(object): def __init__(self): print("I") def __del__(self): print("D")

Output:

>>> WTF() is WTF()IIDDFalse>>> id(WTF()) == id(WTF())IDIDTrue

????????正如你所看到的, 對(duì)象銷毀的順序是造成所有不同之處的原因.

For what?/為什么?

some_string = "wtf"some_dict = {}for i, some_dict[i] in enumerate(some_string): pass

Output:

>>> some_dict # 創(chuàng)建了索引字典.{0: 'w', 1: 't', 2: 'f'}

?說明:

  • Python 語法?中對(duì)?for?的定義是:

    for_stmt: 'for' exprlist 'in' testlist ':' suite ['else' ':' suite]

    其中?exprlist?指分配目標(biāo). 這意味著對(duì)可迭代對(duì)象中的每一項(xiàng)都會(huì)執(zhí)行類似?{exprlist} = {next_value}?的操作.

    一個(gè)有趣的例子說明了這一點(diǎn):

    for i in range(4): print(i) i = 10

    Output:

    0123

    你可曾覺得這個(gè)循環(huán)只會(huì)運(yùn)行一次?

    ?說明:

    • 由于循環(huán)在Python中工作方式, 賦值語句?i = 10?并不會(huì)影響迭代循環(huán), 在每次迭代開始之前, 迭代器(這里指?range(4)) 生成的下一個(gè)元素就被解包并賦值給目標(biāo)列表的變量(這里指?i)了.

  • 在每一次的迭代中,?enumerate(some_string)?函數(shù)就生成一個(gè)新值?i?(計(jì)數(shù)器增加) 并從?some_string?中獲取一個(gè)字符. 然后將字典?some_dict?鍵?i?(剛剛分配的) 的值設(shè)為該字符. 本例中循環(huán)的展開可以簡(jiǎn)化為:

>>> i, some_dict[i] = (0, 'w')>>> i, some_dict[i] = (1, 't')>>> i, some_dict[i] = (2, 'f')>>> some_dict

?Evaluation time discrepancy/執(zhí)行時(shí)機(jī)差異

1、

array = [1, 8, 15]g = (x for x in array if array.count(x) > 0)array = [2, 8, 22]

Output:

>>> print(list(g))[8]

2、

array_1 = [1,2,3,4]g1 = (x for x in array_1)array_1 = [1,2,3,4,5]array_2 = [1,2,3,4]g2 = (x for x in array_2)array_2[:] = [1,2,3,4,5]

Output:

>>> print(list(g1))[1,2,3,4]>>> print(list(g2))[1,2,3,4,5]

?說明:

  • 在生成器表達(dá)式中,?in?子句在聲明時(shí)執(zhí)行, 而條件子句則是在運(yùn)行時(shí)執(zhí)行.

  • 所以在運(yùn)行前,?array?已經(jīng)被重新賦值為?[2, 8, 22], 因此對(duì)于之前的?1,?8?和?15, 只有?count(8)?的結(jié)果是大于?0的, 所以生成器只會(huì)生成?8.

  • 第二部分中?g1?和?g2?的輸出差異則是由于變量?array_1?和?array_2?被重新賦值的方式導(dǎo)致的.

  • 在第一種情況下,?array_1?被綁定到新對(duì)象?[1,2,3,4,5], 因?yàn)?in?子句是在聲明時(shí)被執(zhí)行的, 所以它仍然引用舊對(duì)象?[1,2,3,4](并沒有被銷毀).

  • 在第二種情況下, 對(duì)?array_2?的切片賦值將相同的舊對(duì)象?[1,2,3,4]?原地更新為?[1,2,3,4,5]. 因此?g2?和?array_2?仍然引用同一個(gè)對(duì)象(這個(gè)對(duì)象現(xiàn)在已經(jīng)更新為?[1,2,3,4,5]).


本文內(nèi)容來自中文版項(xiàng)目,項(xiàng)目全文2萬多字,以及海量代碼。

因?yàn)槠?#xff0c;本文就只為大家展示這6個(gè)案例了,更多案例大家可以在項(xiàng)目中查看。

英文版項(xiàng)目名稱:wtfpython

鏈接:https://github.com/satwikkansal/wtfpython

中文項(xiàng)目名稱:wtfpython-cn

鏈接:https://github.com/leisurelicht/wtfpython-cn

猜你想讀(點(diǎn)擊標(biāo)題即可跳轉(zhuǎn))

NB,用這一篇文章帶你了解什么是爬蟲?

與50位技術(shù)專家面對(duì)面20年技術(shù)見證,附贈(zèng)技術(shù)全景圖

總結(jié)

以上是生活随笔為你收集整理的Python 的 51 个秘密曝光,Github 获 2 万星的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。