狠狠操夜夜甜|人妻在线中文字幕亚洲无码不卡av|一区二区欧美亚洲|日躁夜躁狠狠躁2001|亚洲,超碰,欧美|18AV成人电影|午夜成人免费在线|婷婷激情网深爱五月|色欲综合成人在线|在线美女搞黄大片

中企動(dòng)力 > 頭條 > python中類

網(wǎng)站性能檢測(cè)評(píng)分

注:本網(wǎng)站頁(yè)面html檢測(cè)工具掃描網(wǎng)站中存在的基本問(wèn)題,僅供參考。

python中類

看完這篇文章還不懂Python中的閉包,請(qǐng)拍死小編 推廣視頻課程

img

阿巍

關(guān)注

Python作為一門(mén)編程語(yǔ)言,被昵稱為“膠水語(yǔ)言”,更被熱愛(ài)它的程序員譽(yù)為“最美麗的”編程語(yǔ)言。從云端、客戶端,到物聯(lián)網(wǎng)終端,python應(yīng)用無(wú)處不在。

Python中的閉包不是一個(gè)一說(shuō)就能明白的概念,但是隨著你往學(xué)習(xí)的深入,無(wú)論如何你都需要去了解這么一個(gè)東西。

閉包的概念

我們嘗試從概念上去理解一下閉包。

在一些語(yǔ)言中,在函數(shù)中可以(嵌套)定義另一個(gè)函數(shù)時(shí),如果內(nèi)部的函數(shù)引用了外部的函數(shù)的變量,則可能產(chǎn)生閉包。閉包可以用來(lái)在一個(gè)函數(shù)與一組“私有”變量之間創(chuàng)建關(guān)聯(lián)關(guān)系。在給定函數(shù)被多次調(diào)用的過(guò)程中,這些私有變量能夠保持其持久性?!?維基百科

用比較容易懂的人話說(shuō),就是當(dāng)某個(gè)函數(shù)被當(dāng)成對(duì)象返回時(shí),夾帶了外部變量,就形成了一個(gè)閉包??蠢印?/p>

支持將函數(shù)當(dāng)成對(duì)象使用的編程語(yǔ)言,一般都支持閉包。比如Python, JavaScript。

如何理解閉包

閉包存在有什么意義呢?為什么需要閉包?

我個(gè)人認(rèn)為,閉包存在的意義就是它夾帶了外部變量(私貨),如果它不夾帶私貨,它和普通的函數(shù)就沒(méi)有任何區(qū)別。同一個(gè)的函數(shù)夾帶了不同的私貨,就實(shí)現(xiàn)了不同的功能。其實(shí)你也可以這么理解,閉包和面向接口編程的概念很像,可以把閉包理解成輕量級(jí)的接口封裝。

接口定義了一套對(duì)方法簽名的約束規(guī)則。

在這個(gè)例子里,我們想要一個(gè)給content加tag的功能,但是具體的tag_name是什么樣子的要根據(jù)實(shí)際需求來(lái)定,對(duì)外部調(diào)用的接口已經(jīng)確定,就是add_tag(content)。如果按照面向接口方式實(shí)現(xiàn),我們會(huì)先把a(bǔ)dd_tag寫(xiě)成接口,指定其參數(shù)和返回類型,然后分別去實(shí)現(xiàn)a和b的add_tag。

但是在閉包的概念中,

add_tag

就是一個(gè)函數(shù),它需要

tag_name

content

兩個(gè)參數(shù),只不過(guò)

tag_name

這個(gè)參數(shù)是打包帶走的。所以一開(kāi)始時(shí)就可以告訴我怎么打包,然后帶走就行。

上面的例子不太生動(dòng),其實(shí)在我們生活和工作中,閉包的概念也很常見(jiàn)。比如說(shuō)手機(jī)撥號(hào),你只關(guān)心電話打給誰(shuí),而不會(huì)去糾結(jié)每個(gè)品牌的手機(jī)是怎么實(shí)現(xiàn)的,用到了哪些模塊。再比如去餐館吃飯,你只要付錢(qián)就可以享受到服務(wù),你并不知道那桌飯菜用了多少地溝油。這些都可以看成閉包,返回來(lái)的是一些功能或者服務(wù)(打電話,用餐),但是這些功能使用了外部變量(天線,地溝油等等)。

你也可以把一個(gè)類實(shí)例看成閉包,當(dāng)你在構(gòu)造這個(gè)類時(shí),使用了不同的參數(shù),這些參數(shù)就是閉包里的包,這個(gè)類對(duì)外提供的方法就是閉包的功能。但是類遠(yuǎn)遠(yuǎn)大于閉包,因?yàn)殚]包只是一個(gè)可以執(zhí)行的函數(shù),但是類實(shí)例則有可能提供很多方法。

何時(shí)使用閉包

其實(shí)閉包在Python中很常見(jiàn),只不過(guò)你沒(méi)特別注意這就是一個(gè)閉包。比如Python中的裝飾器Decorator,假如你需要寫(xiě)一個(gè)帶參數(shù)的裝飾器,那么一般都會(huì)生成閉包。

為什么?因?yàn)镻ython的裝飾器是一個(gè)固定的函數(shù)接口。它要求你的裝飾器函數(shù)(或裝飾器類)必須返回這樣一種接口,接受一個(gè)函數(shù)并返回一個(gè)函數(shù):

那么如果你的裝飾器如果帶參數(shù)呢?那么你就需要在原來(lái)的裝飾器上再包一層,用于接收這些參數(shù)。這些參數(shù)(私貨)傳遞到內(nèi)層的裝飾器里后,閉包就形成了。所以說(shuō)當(dāng)你的裝飾器需要自定義參數(shù)時(shí),一般都會(huì)形成閉包。(類裝飾器例外)

# 不用@的寫(xiě)法如下# hello = html_tag('b')(hello)# html_tag('b') 是一個(gè)閉包,它接受一個(gè)函數(shù),并返回一個(gè)函數(shù)print hello() # Hello Toby!print hello('world') # Hello world!

再深入一點(diǎn)

其實(shí)也不必太深入,理解這上面的概念,很多看起來(lái)頭疼的代碼也不過(guò)如此。

下面讓我們來(lái)了解一下閉包的包到底長(zhǎng)什么樣子。其實(shí)閉包函數(shù)相對(duì)與普通函數(shù)會(huì)多出一個(gè)

__closure__

的屬性,里面定義了一個(gè)元組用于存放所有的

cell

對(duì)象,每個(gè)

cell

對(duì)象一一保存了這個(gè)閉包中所有的外部變量。

原理就是這么簡(jiǎn)單。

一步一步帶你理解 Python 中的淺復(fù)制與深復(fù)制 公司視頻課程

img

心酸

關(guān)注

Python 中的賦值語(yǔ)句不會(huì)對(duì)對(duì)象進(jìn)行拷貝,僅僅是將變量名指向?qū)ο蟆?duì)于不可修改的對(duì)象來(lái)說(shuō),這種機(jī)制不會(huì)影響我們?nèi)粘5氖褂?。但是,?duì)于可修改的對(duì)象,你偶爾可能需要對(duì)該對(duì)象做一個(gè)真正的復(fù)制。何為真正的復(fù)制?就是修改拷貝來(lái)的對(duì)象不會(huì)影響原來(lái)的對(duì)象。

Python 中內(nèi)置的可修改的集合類對(duì)象,比如列表、字典、集合等,可以直接使用對(duì)應(yīng)的工廠方法進(jìn)行拷貝。

需要注意的是,對(duì)于復(fù)合類型的對(duì)象,比如列表、字典、集合等,復(fù)制有淺復(fù)制與深復(fù)制兩種類型。

淺復(fù)制意味著新建一個(gè)對(duì)象,但是其子元素仍然指向的對(duì)應(yīng)原對(duì)象的子對(duì)象。也就是說(shuō),這只會(huì)對(duì)原對(duì)象進(jìn)行一層的拷貝,而不會(huì)遞歸的對(duì)子對(duì)象也進(jìn)行拷貝。

深復(fù)制則會(huì)遞歸的對(duì)子對(duì)象進(jìn)行拷貝。

如果上面的看不懂,沒(méi)關(guān)系,我們通過(guò)一個(gè)個(gè)例子來(lái)搞清楚。

淺復(fù)制

現(xiàn)在下面這個(gè)列表,我們使用 list() 方法對(duì)它進(jìn)行復(fù)制

這意味著 xs 和 ys 是兩個(gè)不同的對(duì)象。我們可以看看它們的值

為了證明 xs 與 ys 確實(shí)不同,我們可以做個(gè)小實(shí)驗(yàn)。添加一個(gè)子列表到 xs 中,然后看會(huì)不會(huì)影響 ys 。

與預(yù)期相同,如果只是修改原對(duì)象這一層,確實(shí)不對(duì)對(duì) ys 造成影響。

但是由于是淺復(fù)制,ys 中的子元素并非是 xs 子元素的拷貝,僅僅是 xs 子對(duì)象的引用。因此,如果你修改 xs 中的子元素,ys 中的子元素同樣會(huì)被修改。

看起來(lái)我們只是修改了 xs 中的子元素,實(shí)則 ys 中的子元素也被修改了,這是由于淺復(fù)制的緣故。

現(xiàn)在我們大概清楚了淺復(fù)制與深復(fù)制的區(qū)別,但是還有兩個(gè)問(wèn)題待解決:

一是,對(duì)于內(nèi)置的集合類對(duì)象,我們?cè)趺催M(jìn)行深復(fù)制?

二是,對(duì)于任意的類,又該如何進(jìn)行淺復(fù)制與深復(fù)制?

答案是標(biāo)準(zhǔn)庫(kù)中的 copy 模塊,其提供了簡(jiǎn)單的方法對(duì)對(duì)象進(jìn)行淺復(fù)制或深復(fù)制。

深復(fù)制

這次我們?nèi)匀皇褂蒙厦娴睦樱煌氖?,我們使?copy 模塊中的 deepcopy() 方法來(lái)進(jìn)行復(fù)制。

還是先看看各自的值

這次,因?yàn)?zs 對(duì) xs 進(jìn)行了深復(fù)制,即不僅拷貝了 xs 本身,還對(duì)它的子對(duì)象都進(jìn)行了遞歸的拷貝,所以,如果我們?cè)俅涡薷?xs 的子元素時(shí),并不會(huì)對(duì) zs 造成影響。我們來(lái)試一試

果然,與預(yù)期一致。

順便說(shuō)下,使用 copy 模塊中的 copy() 方法可以對(duì)對(duì)象進(jìn)行淺復(fù)制。平時(shí)開(kāi)發(fā)需要淺復(fù)制的時(shí)候,你可以使用該方法,但如果碰到內(nèi)置的集合類對(duì)象,比如列表、字典、集合等的時(shí)候,使用對(duì)應(yīng)的工廠方法如 list() ,dict() ,set() 等更 Pythonic 一些。

對(duì)任意對(duì)象進(jìn)行復(fù)制

現(xiàn)在有一個(gè)點(diǎn)類 Point ,如下

其中,__repr__ 方法可以讓我們更方便的查看對(duì)象的信息。需要注意的是,f'...' 這種格式化字符串的方式在 Python 3.6 才支持,如果是 Python 2 或者 3.6 以前的版本可以使用 '...' % (v1[, v2, ...]) 的方式。

現(xiàn)在我們創(chuàng)建一個(gè)點(diǎn)對(duì)象并進(jìn)行淺復(fù)制

查看兩個(gè)對(duì)象的信息

和預(yù)期一致。

需要注意的是,因?yàn)轭惖膬蓚€(gè)成員 x 和 y 都是簡(jiǎn)單類型,這里是整型,所以這里淺復(fù)制與深復(fù)制沒(méi)有任何差別。后面我會(huì)擴(kuò)展這個(gè)例子。

現(xiàn)在我要定義一個(gè)矩形類,其類成員將會(huì)使用到上面的點(diǎn)類。

首先我們先試一下淺復(fù)制

看下各自的信息

記得我們上面修改列表的淺復(fù)制與深復(fù)制的例子嗎?這里,我要類似的實(shí)驗(yàn)。我們修改 rect 的成員,不出意外的話,srect 也會(huì)改變。

果然。

下面,我會(huì)進(jìn)行深復(fù)制,然后再類似的修改

這次,深復(fù)制出來(lái)的 drect 對(duì)象與 srect 才能說(shuō)是“真正不同”的對(duì)象。

copy 模塊中還有其它很多用法,比如定義對(duì)象的 __copy__() 和 __deepcopy__() 方法可以自定義對(duì)象的淺復(fù)制與深復(fù)制行為等。這不是本文的重心,有興趣的可以去看相應(yīng)的文檔 https://docs.python.org/3/library/copy.html 。

小結(jié)

* 淺復(fù)制不會(huì)克隆子對(duì)象,所以,復(fù)制出來(lái)的對(duì)象和原對(duì)象并非完全不想關(guān)。

* 深復(fù)制會(huì)遞歸的克隆子對(duì)象,所以,復(fù)制出來(lái)的對(duì)象和原對(duì)象完全不相關(guān),但是深復(fù)制比淺復(fù)制會(huì)慢一些。

* 使用 copy 模塊你可以復(fù)制任何類,不管是淺復(fù)制還是深復(fù)制。

鞏固

從網(wǎng)上找了組圖片(侵刪),讓大家加深下認(rèn)識(shí)

首先是賦值

然后是淺復(fù)制

最后是深復(fù)制

分享 Python 3.6 中常用的幾個(gè)新特性 營(yíng)銷視頻課程

img

安吉拉

關(guān)注

Python 3.6 發(fā)布已經(jīng)有段時(shí)間了,不少新特性應(yīng)該被很多開(kāi)發(fā)者用在了日常的開(kāi)發(fā)中,這篇文章主要介紹下自己在開(kāi)發(fā)中常用到的幾個(gè)。

增加數(shù)字可讀性

在定義數(shù)字的時(shí)候,有一個(gè)語(yǔ)法特性可以讓數(shù)字可讀性增強(qiáng),即你在數(shù)字之間可以按照自己的喜好添加一個(gè)下滑線,就像這樣

這樣書(shū)寫(xiě)方式與之前的并無(wú)區(qū)別

語(yǔ)義其實(shí)是一樣的,也就是定義了一個(gè)值為 100000 的變量。

當(dāng)然,對(duì)于十六進(jìn)制與二進(jìn)制也是支持的

這個(gè)小修改也恰好體現(xiàn)了 Python 之禪的 Readability counts (可讀性很重要)這一點(diǎn)吧。

詳情請(qǐng)看 PEP 515 。

新的格式化字符串的方法

Python 3.6 中添加了另一種格式化字符串的方法,這個(gè)方法允許你在字符串中直接嵌入表達(dá)式。下面有兩個(gè)例子,可以看看感受一下

另外,和之前的 str.format() 格式化字符串的方法類似,也可以用特定的格式說(shuō)明符說(shuō)明格式化的方式。比如我們想用十六進(jìn)制的形式格式化一個(gè)十進(jìn)制的數(shù),可以這樣

相比于之前的 '...' % (val1[, val2, ...]) 以及 str.format() ,這種方式不管是書(shū)寫(xiě)上還是可讀性上,感覺(jué)都方便了不少,所以如果你在 Python 3.6 中做開(kāi)發(fā)的話,我十分推薦這種方式。

詳情請(qǐng)看 PEP 498 。

變量的類型注解

從 Python 3.5 開(kāi)始,你可以給函數(shù)添加類型注解

在 Python 3.6 中,你也可以給獨(dú)立的變量作類型注解,就像給函數(shù)中形式參數(shù)作注解差不多

需要注意的是,Python 的類型注解并不會(huì)在運(yùn)行中執(zhí)行相應(yīng)的檢查

也就是說(shuō),如果你定義了一個(gè)整形的變量,而你卻賦值給它一個(gè)字符串,Python 并不會(huì)報(bào)錯(cuò),對(duì)于函數(shù)的參數(shù)以及返回值的注解也是這樣的。個(gè)人覺(jué)得這更像是編程時(shí)的一個(gè)輔助措施。

如果你打算嚴(yán)格執(zhí)行注解的要求,你可以使用 python/mypy 工具進(jìn)行檢查。(使用方法請(qǐng)看 https://github/python/mypy )

詳情請(qǐng)看 PEP 526 。

其它值得注意的變化

新增加的模塊中有一個(gè)叫做 secrets ,可以較為方便的提供密碼學(xué)安全的隨機(jī)串,我之前的一篇文章《玩轉(zhuǎn) Python 中的隨機(jī)數(shù)》的后面部分對(duì)這個(gè)模塊進(jìn)行了一些簡(jiǎn)單的說(shuō)明,有興趣的朋友可以去看一下。

Python 3.6 中不僅僅是新特性的添加,也有性能的提升,比如字典類型相比之前更快,并且節(jié)省了 20% 到 25% 的空間。

當(dāng)然,這個(gè)版本帶來(lái)的變化肯定不止上面這么一些,我例舉出來(lái)的都是一些常用的點(diǎn)。如果想看全面的說(shuō)明,可以去官網(wǎng)( https://docs.python.org/3.6/whatsnew/3.6.html )看看。

上面的 PEP 515 、PEP 498 、PEP 526 的具體信息都可以在上面的地址中看到。

Python代碼風(fēng)格:PEP8規(guī)則 筆記 公司視頻課程

img

幼蓉

關(guān)注

Python程序設(shè)計(jì)的代碼風(fēng)格應(yīng)該遵循PEP8規(guī)則:

一、代碼布局

1、縮進(jìn):

每級(jí)縮進(jìn)4個(gè)空格(不用Tab,更不空格Tab混用)

1、續(xù)行應(yīng)該與其包裹元素對(duì)齊,要么使用圓括號(hào)、方括號(hào)和花括號(hào)內(nèi)的隱式行連接來(lái)垂直對(duì)齊,要么使用懸掛式縮進(jìn)對(duì)齊。當(dāng)使用懸掛縮進(jìn)時(shí),應(yīng)該考慮到第一行不應(yīng)該有參數(shù),以及使用縮進(jìn)以區(qū)分自己是續(xù)行。

2、縮進(jìn)4個(gè)空格的規(guī)則對(duì)于續(xù)行是可選的。

3、當(dāng) if 語(yǔ)句的條件部分長(zhǎng)到需要換行寫(xiě)的時(shí)候,注意可以在兩個(gè)字符關(guān)鍵字的連接處(比如 if ),增加一個(gè)空格,再增加一個(gè)左括號(hào)來(lái)創(chuàng)造一個(gè)4空格縮進(jìn)的多行條件。這會(huì)與 if 語(yǔ)句內(nèi)同樣使用4空格縮進(jìn)的代碼產(chǎn)生視覺(jué)沖突。PEP沒(méi)有明確指明要如何區(qū)分i發(fā)的條件代碼和內(nèi)嵌代碼。可使用的選項(xiàng)包括但不限于下面幾種情況:

4、(可以參考下面關(guān)于是否在二進(jìn)制運(yùn)算符之前或之后截?cái)嗟挠懻摚?/p>

在多行結(jié)構(gòu)中的大括號(hào)/中括號(hào)/小括號(hào)的右括號(hào)可以與內(nèi)容對(duì)齊單獨(dú)起一行作為最后一行的第一個(gè)字符,如:

或者也可以與多行結(jié)構(gòu)的第一行第一個(gè)字符對(duì)齊,如:

2、Tab還是空格?

空格是被首先推薦的縮進(jìn)方式。

Tab應(yīng)該只在現(xiàn)有代碼已經(jīng)使用tab進(jìn)行縮進(jìn)的情況下使用,以便和現(xiàn)有代碼保持一致。

Python 3不允許再同一個(gè)代碼塊中Tab和空格混合使用。

混合使用制表符和空格縮進(jìn)的Python2代碼應(yīng)該統(tǒng)一轉(zhuǎn)成空格。

使用命令行運(yùn)行Python 2時(shí),使用-t選項(xiàng),會(huì)出現(xiàn)非法混用tab和空格的警告。當(dāng)使用-tt選項(xiàng)時(shí),這些警告會(huì)變成錯(cuò)誤。強(qiáng)烈推薦使用這些選項(xiàng)!

3、最大行長(zhǎng)

每行最大長(zhǎng)度79個(gè)字符。

對(duì)于連續(xù)大段的文字(比如文檔字符串(docstring)或注釋),每行應(yīng)該被限制在72個(gè)字符長(zhǎng)度內(nèi)。

Python標(biāo)準(zhǔn)庫(kù)比較傳統(tǒng),將行長(zhǎng)限制在79個(gè)字符以內(nèi)(文檔字符串/注釋為72個(gè)字符)。

一種推薦的換行方式是利用Python圓括號(hào)、方括號(hào)和花括號(hào)中的隱式續(xù)行。長(zhǎng)行可以通過(guò)在括號(hào)內(nèi)換行來(lái)分成多行。應(yīng)該最好加上反斜杠來(lái)區(qū)別續(xù)行。

有時(shí)續(xù)行只能使用反斜杠才。例如,較長(zhǎng)的多個(gè) with 語(yǔ)句不能采用隱式續(xù)行,只能接受反斜杠表示換行:

另一個(gè)這樣的例子是assert語(yǔ)句。要確保續(xù)行的縮進(jìn)適當(dāng)。

在二元運(yùn)算符之前應(yīng)該換行嗎?

遵循數(shù)學(xué)的傳統(tǒng)能產(chǎn)出更多可讀性高的代碼:

4、空行

頂層函數(shù)和類的定義,前后用兩個(gè)空行隔開(kāi)。

類里的方法定義用一個(gè)空行隔開(kāi)。

相關(guān)的功能組可以用額外的空行(盡量少地)隔開(kāi)。一堆相關(guān)的單行代碼之間的空白行可以省略(例如,一組虛擬實(shí)現(xiàn) dummy implementations)。

在函數(shù)中使用空行來(lái)區(qū)分邏輯段(盡量少地)。

Python接受control-L(即^L)換頁(yè)符作為空格;許多工具把這些字符當(dāng)作頁(yè)面分隔符,所以你可以在文件中使用它們來(lái)分隔相關(guān)段落。請(qǐng)注意,一些編輯器和基于Web的代碼閱讀器可能無(wú)法識(shí)別control-L為換頁(yè),將在其位置顯示另一個(gè)字形。

5、源文件編碼

Python核心發(fā)布版本中的代碼總是以UTF-8格式編碼(或者在Python2中用ASCII編碼)。

使用ASCII(Python 2)或者UTF-8(Python 3)的文件不應(yīng)該添加編碼聲明。

在標(biāo)準(zhǔn)庫(kù)中,只有用作測(cè)試目的,或者注釋或文檔字符串需要提及作者名字而不得不使用非ASCII字符時(shí),才能使用非默認(rèn)的編碼。否則,在字符串文字中包括非ASCII數(shù)據(jù)時(shí),推薦使用\x, \u, U或N等轉(zhuǎn)義符。

對(duì)于Python 3.0及其以后的版本中,標(biāo)準(zhǔn)庫(kù)遵循以下原則(參見(jiàn)PEP 3131):Python標(biāo)準(zhǔn)庫(kù)中的所有標(biāo)識(shí)符都必須只采用ASCII編碼的標(biāo)識(shí)符,在可行的條件下也應(yīng)當(dāng)使用英文詞(很多情況下,使用的縮寫(xiě)和技術(shù)術(shù)語(yǔ)詞都不是英文)。此外,字符串文字和注釋?xiě)?yīng)該只包括ASCII編碼。只有兩種例外:

(a) 測(cè)試情況下為了測(cè)試非ASCII編碼的特性

(b) 作者名字。作者名字不是由拉丁字母組成的也必須提供一個(gè)拉丁音譯名。

鼓勵(lì)面向全球的開(kāi)源項(xiàng)目都采用類似的原則。

6、導(dǎo)入

1、imports應(yīng)該分行寫(xiě),而不是都寫(xiě)在一行,例如:

這樣寫(xiě)也是可以的:

導(dǎo)入(import)始終在文件的頂部,在模塊注釋和文檔字符串之后,在模塊全局變量和常量之前。

導(dǎo)入順序如下:

imports應(yīng)該按照下面的順序分組來(lái)寫(xiě):

1、標(biāo)準(zhǔn)庫(kù)imports

2、相關(guān)第三方imports

3、本地應(yīng)用/庫(kù)的特定imports

不同組的imports之前用空格隔開(kāi)。

將任何相關(guān)的 __all__ 說(shuō)明(specification)放在imports之后。

推薦使用絕對(duì)(absolute)imports,因?yàn)檫@樣通常更易讀,在import系統(tǒng)沒(méi)有正確配置的情況下,也會(huì)有更好的表現(xiàn)(或者至少會(huì)給出錯(cuò)誤信息):

在絕對(duì)路徑比較長(zhǎng)的情況下,也可以使用相對(duì)導(dǎo)入:

Python 3中已經(jīng)禁止隱式的相對(duì)導(dǎo)入。

導(dǎo)入類的方法,通常可以這樣寫(xiě):

如果和本地命名的拼寫(xiě)產(chǎn)生了沖突,應(yīng)當(dāng)使用絕對(duì)導(dǎo)入:

禁止使用通配符導(dǎo)入。

通配符導(dǎo)入(from import *)應(yīng)該避免,因?yàn)樗磺宄臻g有哪些名稱存,混淆讀者和許多自動(dòng)化的工具。唯一的例外是重新發(fā)布對(duì)外的API時(shí)可以考慮使用。

7、模塊中前后具有雙下劃線的變量名

像__all__ , __author__ , __version__ 等這樣的模塊中的變量名(也就是名字里有兩個(gè)前綴下劃線和兩個(gè)后綴下劃線),應(yīng)該放在文檔字符串的后面,以及除from __future__ 之外的import表達(dá)式前面。Python要求將來(lái)在模塊中的導(dǎo)入,必須出現(xiàn)在除文檔字符串之外的其他代碼之前。

比如:

二、字符串引號(hào)

Python中單引號(hào)字符串和雙引號(hào)字符串都是相同的。注意盡量避免在字符串中的反斜杠以提高可讀性。

根據(jù)PEP 257, 三個(gè)引號(hào)都使用雙引號(hào)。

三、表達(dá)式和語(yǔ)句中的空格

在下列情況下,避免使用無(wú)關(guān)的空格:

1、緊跟在小括號(hào),中括號(hào)或者大括號(hào)后。

2、緊貼在逗號(hào)、分號(hào)或者冒號(hào)之前。

3、然而,冒號(hào)在切片中就像二元運(yùn)算符,在兩邊應(yīng)該有相同數(shù)量的空格(把它當(dāng)做優(yōu)先級(jí)最低的操作符)。在擴(kuò)展的切片操作中,所有的冒號(hào)必須有相同的間距。例外情況:當(dāng)一個(gè)切片參數(shù)被省略時(shí),空格就被省略了。

4、緊貼在函數(shù)參數(shù)的左括號(hào)之前。

5、緊貼索引或者切片的左括號(hào)之前。

6、為了和另一個(gè)賦值語(yǔ)句對(duì)齊,在賦值運(yùn)算符附件加多個(gè)空格。

其他建議

1、避免在尾部添加空格。因?yàn)槲膊康目崭裢ǔ6伎床灰?jiàn),會(huì)產(chǎn)生混亂:比如,一個(gè)反斜杠后面跟一個(gè)空格的換行符,不算續(xù)行標(biāo)記。有些編輯器不會(huì)保留尾空格,并且很多項(xiàng)目(像CPython)在pre-commit的掛鉤調(diào)用中會(huì)過(guò)濾掉尾空格。

總是在二元運(yùn)算符兩邊加一個(gè)空格:賦值(=),增量賦值(+=,-=),比較(==,,!=,,=,in,not,in,is,is not),布爾(and, or, not)。

如果使用具有不同優(yōu)先級(jí)的運(yùn)算符,請(qǐng)考慮在具有最低優(yōu)先級(jí)的運(yùn)算符周圍添加空格。有時(shí)需要通過(guò)自己來(lái)判斷;但是,不要使用一個(gè)以上的空格,并且在二元運(yùn)算符的兩邊使用相同數(shù)量的空格。

2、在指定函數(shù) 關(guān)鍵字參數(shù) 或者 默認(rèn)參數(shù) 值的時(shí)候,不要在=附近加上空格。

3、功能型注釋?xiě)?yīng)該使用冒號(hào)的一般性規(guī)則,并且在使用 ->的時(shí)候要在兩邊加空格。(參考下面的功能注釋得到能夠多信息)

4、當(dāng)給有類型備注的參數(shù)賦值的時(shí)候,在=兩邊添加空格(僅針對(duì)那種有類型備注和默認(rèn)值的參數(shù))。

5、復(fù)合語(yǔ)句(同一行中的多個(gè)語(yǔ)句)通常是不允許的。

6、雖然有時(shí)候?qū)⑿〉拇a塊和 if/for/while 放在同一行沒(méi)什么問(wèn)題,多行語(yǔ)句塊的情況不要這樣用,同樣也要避免代碼行太長(zhǎng)!

四、注釋

與代碼自相矛盾的注釋比沒(méi)注釋更差。修改代碼時(shí)要優(yōu)先更新注釋!

注釋是完整的句子。如果注釋是斷句,首字母應(yīng)該大寫(xiě),除非它是小寫(xiě)字母開(kāi)頭的標(biāo)識(shí)符(永遠(yuǎn)不要修改標(biāo)識(shí)符的大小寫(xiě))。

如果注釋很短,可以省略末尾的句號(hào)。注釋塊通常由一個(gè)或多個(gè)段落組成。段落由完整的句子構(gòu)成且每個(gè)句子應(yīng)該以點(diǎn)號(hào)(后面要有兩個(gè)空格)結(jié)束,并注意斷詞和空格。

非英語(yǔ)國(guó)家的程序員請(qǐng)用英語(yǔ)書(shū)寫(xiě)你的注釋,除非你120%確信代碼永遠(yuǎn)不會(huì)被不懂你的語(yǔ)言的人閱讀。

1、注釋塊

注釋塊通常應(yīng)用在代碼前,并和這些代碼有同樣的縮進(jìn)。每行以 '# '(除非它是注釋內(nèi)的縮進(jìn)文本,注意#后面有空格)。

注釋塊內(nèi)的段落用僅包含單個(gè) '#' 的行分割。

2、行內(nèi)注釋

慎用行內(nèi)注釋(Inline Comments) 節(jié)儉使用行內(nèi)注釋。 行內(nèi)注釋是和語(yǔ)句在同一行,至少用兩個(gè)空格和語(yǔ)句分開(kāi)。行內(nèi)注釋不是必需的,重復(fù)羅嗦會(huì)使人分心。

不推薦:

但是有時(shí),很有必要:

加了以后對(duì)理解代碼很有幫助的情況下,關(guān)鍵處才加。

3、文檔字符串

文檔字符串的標(biāo)準(zhǔn)參見(jiàn):PEP 257。

為所有公共模塊、函數(shù)、類和方法書(shū)寫(xiě)文檔字符串。非公開(kāi)方法不一定有文檔字符串,建議有注釋(出現(xiàn)在 def 行之后)來(lái)描述這個(gè)方法做什么。

更多參考:PEP 257 文檔字符串約定。注意結(jié)尾的 """ 應(yīng)該單獨(dú)成行,例如:

單行的文檔字符串,結(jié)尾的 """ 在同一行。

4、版本標(biāo)簽

如果你必須在源文件中包含git、Subversion、CVS或RCS crud信息,放置在模塊的文檔字符串之后,任何其他代碼之前,上下各用一個(gè)空行:

五、命名規(guī)范

Python庫(kù)的命名約定有點(diǎn)混亂,不可能完全一致。但依然有些普遍推薦的命名規(guī)范的。新的模塊和包 (包括第三方的框架) 應(yīng)該遵循這些標(biāo)準(zhǔn)。對(duì)不同風(fēng)格的已有的庫(kù),建議保持內(nèi)部的一致性。

1、最重要的原則

用戶可見(jiàn)的API命名應(yīng)遵循使用約定而不是實(shí)現(xiàn)。

2、命名風(fēng)格

以下是常見(jiàn)的命名方式:

b(單個(gè)小寫(xiě)字母)

B(單個(gè)大寫(xiě)字母)

lowercase (小寫(xiě)字母)

lower_case_with_underscores (使用下劃線分隔的小寫(xiě)字母)

UPPERCASE( 大寫(xiě)字母)

UPPER_CASE_WITH_UNDERSCORES (使用下劃線分隔的大寫(xiě)字母)

CapitalizedWords(首字母大寫(xiě)的單詞串或駝峰縮寫(xiě))

注意: 使用大寫(xiě)縮寫(xiě)時(shí),縮寫(xiě)使用大寫(xiě)字母更好。故 HTTPServerError 比 HttpServerError 更好。

mixedCase(不同于首字母大寫(xiě),第一個(gè)單詞的首字母小寫(xiě))

Capitalized_Words_With_Underscores(帶下劃線,首字母大寫(xiě),巨丑無(wú)比)

還有一種風(fēng)格使用短前綴分組名字。這在Python中不常用, 但出于完整性提一下。例如,os.stat()返回的元組有st_mode, st_size, st_mtime等等這樣的名字(與POSIX系統(tǒng)調(diào)用結(jié)構(gòu)體一致)。

X11庫(kù)的所有公開(kāi)函數(shù)以X開(kāi)頭, Python中通常認(rèn)為是不必要的,因?yàn)閷傩院头椒袑?duì)象作前綴,而函數(shù)名有模塊名為前綴。

下面講述首尾有下劃線的情況:

_single_leading_underscore:(單前置下劃線): 弱內(nèi)部使用標(biāo)志。 例如"from M import " 不會(huì)導(dǎo)入以下劃線開(kāi)頭的對(duì)象。

single_trailing_underscore_(單后置下劃線): 用于避免與 Python關(guān)鍵詞的沖突。 例如:

__double_leading_underscore(雙前置下劃線): 當(dāng)用于命名類屬性,會(huì)觸發(fā)名字重整。 (在類FooBar中,__boo變成 _FooBar__boo)。

__double_leading_and_trailing_underscore__(雙前后下劃線):用戶名字空間的魔法對(duì)象或?qū)傩浴@?__init__ , __import__ or __file__,不要自己發(fā)明這樣的名字。

3、命名約定規(guī)范

避免采用的名字:

決不要用字符'l'(小寫(xiě)字母el),'O'(大寫(xiě)字母oh),或 'I'(大寫(xiě)字母eye) 作為單個(gè)字符的變量名。一些字體中,這些字符不能與數(shù)字1和0區(qū)別。用'L' 代替'l'時(shí)。

包和模塊名:

模塊名要簡(jiǎn)短,全部用小寫(xiě)字母,可使用下劃線以提高可讀性。包名和模塊名類似,但不推薦使用下劃線。

模塊名對(duì)應(yīng)到文件名,有些文件系統(tǒng)不區(qū)分大小寫(xiě)且截短長(zhǎng)名字,在 Unix上不是問(wèn)題,但當(dāng)把代碼遷移到 Mac、Windows 或 DOS 上時(shí),就可能是個(gè)問(wèn)題。當(dāng)然隨著系統(tǒng)的演進(jìn),這個(gè)問(wèn)題已經(jīng)不是經(jīng)常出現(xiàn)。

另外有些模塊底層用C或C++ 書(shū)寫(xiě),并有對(duì)應(yīng)的高層Python模塊,C/C++模塊名有一個(gè)前置下劃線 (如:_socket)。

類名:

遵循CapWord。

接口需要文檔化并且可以調(diào)用時(shí),可能使用函數(shù)的命名規(guī)則。

注意大部分內(nèi)置的名字是單個(gè)單詞(或兩個(gè)),CapWord只適用于異常名稱和內(nèi)置的常量。

異常名:

如果確實(shí)是錯(cuò)誤,需要在類名添加后綴 "Error"。

全局變量名:

變量盡量只用于模塊內(nèi)部,約定類似函數(shù)。

對(duì)設(shè)計(jì)為通過(guò) "from M import " 來(lái)使用的模塊,應(yīng)采用 __all__ 機(jī)制來(lái)防止導(dǎo)入全局變量;或者為全局變量加一個(gè)前置下劃線。

函數(shù)名:

函數(shù)名應(yīng)該為小寫(xiě),必要時(shí)可用下劃線分隔單詞以增加可讀性。 mixedCase(混合大小寫(xiě))僅被允許用于兼容性考慮(如: threading.py)。

函數(shù)和方法的參數(shù):

實(shí)例方法第一個(gè)參數(shù)是 'self'。

類方法第一個(gè)參數(shù)是 'cls'。

如果函數(shù)的參數(shù)名與保留關(guān)鍵字沖突,通常在參數(shù)名后加一個(gè)下劃線。

方法名和實(shí)例變量:

同函數(shù)命名規(guī)則。

非公開(kāi)方法和實(shí)例變量增加一個(gè)前置下劃線。

為避免與子類命名沖突,采用兩個(gè)前置下劃線來(lái)觸發(fā)重整。類Foo屬性名為_(kāi)_a, 不能以 Foo.__a訪問(wèn)。(執(zhí)著的用戶還是可以通過(guò)Foo._Foo__a。) 通常雙前置下劃線僅被用來(lái)避免與基類的屬性發(fā)生命名沖突。

常量:

<...

Python中不可變類型和可變類型詳解 企業(yè)視頻課程

img

潘樂(lè)巧

關(guān)注

Python中不可變類型和可變類型詳解

1. 什么是不可變類型

變量對(duì)應(yīng)的值中的數(shù)據(jù)是不能被修改,如果修改就會(huì)生成一個(gè)新的值從而分配新的內(nèi)存空間。

不可變類型:

數(shù)字(int,long,float)

布爾(bool)

字符串(string)

元組(tuple)

案例一:

結(jié)果:

兩個(gè)不同的數(shù)字

案例二:

結(jié)果:

兩個(gè)不同的數(shù)字

2. 什么是不可變類型

變量對(duì)應(yīng)的值中的數(shù)據(jù)可以被修改,但內(nèi)存地址保持不變。

不可變類型:

列表(list)

字典(dict)

案例一:

結(jié)果:

兩個(gè)相同的數(shù)字

['劉備', '關(guān)羽', '張飛', '趙云']

案例二:

結(jié)果:

兩個(gè)相同的數(shù)字

{'name': '劉備', 'age': 20}

3. 為什么生成以上兩種情況

因?yàn)閜ython中的值是通過(guò)引用(地址值)傳遞的,不可變類型的值一旦被修改后會(huì)創(chuàng)建一個(gè)內(nèi)存空間并且生成新的地址值,可變類型的值會(huì)在原內(nèi)存空間中被修改。

4. 不可變類型執(zhí)行過(guò)程和內(nèi)存分析

5. 不可變類型執(zhí)行過(guò)程和內(nèi)存分析

感謝源碼時(shí)代教學(xué)講師提供此文章!

本文為原創(chuàng)文章,轉(zhuǎn)載請(qǐng)注明出處!

Python程序員最常犯的10個(gè)錯(cuò)誤,你中招了嗎? 互聯(lián)網(wǎng)視頻課程

img

Phoebe

關(guān)注

大數(shù)據(jù)文摘作品

編譯:什錦甜、Gao Ning、小魚(yú)

Python簡(jiǎn)介

Python是一種具有動(dòng)態(tài)語(yǔ)義的、面向?qū)ο蟮慕忉屝透呒?jí)編程語(yǔ)言。因其內(nèi)置了高級(jí)數(shù)據(jù)結(jié)構(gòu),并支持動(dòng)態(tài)類型和動(dòng)態(tài)綁定,使用Python進(jìn)行快速應(yīng)用程序開(kāi)發(fā)十分便利。同時(shí)作為一門(mén)腳本語(yǔ)言,它兼容部分現(xiàn)有的組件和服務(wù)。Python還支持模塊和各種庫(kù)的擴(kuò)展,有助于實(shí)現(xiàn)模塊化編程和提高代碼復(fù)用率。

關(guān)于本文

剛接觸這門(mén)語(yǔ)言的新手可能會(huì)對(duì)Python簡(jiǎn)潔靈活的語(yǔ)法有些不適應(yīng),或是低估了Python強(qiáng)大的性能。鑒于此,本文列出了Python開(kāi)發(fā)人員常犯的10個(gè)小錯(cuò)誤,資深程序猿也難免會(huì)中招哦。

本文供Python高級(jí)開(kāi)發(fā)人員參考,Python小白可以參考下面這篇文章:

http://onlamp/pub/a/python/2004/02/05/learn_python.html

常見(jiàn)錯(cuò)誤1:濫用表達(dá)式作為函數(shù)參數(shù)的默認(rèn)值

Python允許開(kāi)發(fā)者指定函數(shù)參數(shù)的默認(rèn)值,這也是Python的一大特色,但當(dāng)默認(rèn)值可變時(shí),可能會(huì)給開(kāi)發(fā)者帶來(lái)一些困擾。例如下面定義的函數(shù):

>>> def foo(bar=[]): # bar is optional and defaults to [] if not specified... bar.append("baz") # but this line could be problematic, as we'll see...... return bar

看出bug了嗎?那就是在每次調(diào)用函數(shù)前沒(méi)有對(duì)可變參數(shù)進(jìn)行賦值,而認(rèn)為該參數(shù)就是默認(rèn)值。比如上面的代碼,有人可能期望在反復(fù)調(diào)用foo()時(shí)返回'baz',以為每次調(diào)用foo()時(shí),bar的值都為[],即一個(gè)空列表。

但是,讓我們來(lái)看看代碼運(yùn)行結(jié)果:

>>> foo()["baz"]>>> foo()["baz", "baz"]>>> foo()["baz", "baz", "baz"]

嗯?為什么每次調(diào)用foo()后會(huì)不斷把"baz"添加到已有的列表,而不是新建一個(gè)新列表呢?答案就是,函數(shù)參數(shù)的默認(rèn)值僅在定義函數(shù)時(shí)執(zhí)行一次。因此,僅在第一次定義foo()時(shí),bar初始化為默認(rèn)值(即空列表),此后,每次調(diào)用foo()函數(shù)時(shí),參數(shù)bar都是第一次初始化時(shí)生成的列表。

常見(jiàn)的解決方案:

>>> def foo(bar=None):... if bar is None: # or if not bar:... bar = []... bar.append("baz")... return bar...>>> foo()["baz"]>>> foo()["baz"]>>>foo()["baz"]

常見(jiàn)錯(cuò)誤2:錯(cuò)誤地使用類變量

代碼示例:

>>> class A(object):... x = 1...>>> class B(A):... pass...>>> class C(A):... pass...>>> print A.x, B.x, C.x1 1 1

運(yùn)行結(jié)果沒(méi)問(wèn)題。

>>> B.x = 2>>> print A.x, B.x, C.x1 2 1

結(jié)果也正確。

>>> A.x = 3>>> print A.x, B.x, C.x3 2 3

什么鬼?我們只改變了A.x.,為什么C.x 也變了?

在Python中,類變量是以字典形式進(jìn)行內(nèi)部處理,遵循方法解析順序(Method Resolution Order ,MRO)。因此,在上述代碼中,因?yàn)樵陬怌中沒(méi)有找到屬性x,它就會(huì)從父類中查找x的值(盡管Python支持多重繼承,但上述代碼只存在一個(gè)父類A)。換句話說(shuō),C沒(méi)有獨(dú)立于類A的屬于自己的x。因此,C.x實(shí)際上指的是A.x。除非處理得當(dāng),否則就會(huì)導(dǎo)致Python出現(xiàn)錯(cuò)誤。

如果想更深入了解Python的類特性,請(qǐng)戳:

https://toptal/python/python-class-attributes-an-overly-thorough-guide

常見(jiàn)錯(cuò)誤3:錯(cuò)誤指定異常代碼塊的參數(shù)

假設(shè)你有如下代碼:

>>> try:... l = ["a", "b"]... int(l[2])... except ValueError, IndexError: # To catch both exceptions, right?... pass...Traceback (most recent call last):File "", line 3, in IndexError: list index out of range

這里的問(wèn)題是except語(yǔ)句不接受以這種方式指定的異常列表。在Python2.x中,except Exception語(yǔ)句中變量e可用來(lái)把異常信息綁定到第二個(gè)可選參數(shù)上,以便進(jìn)一步查看異常的情況。因此,在上述代碼中,except語(yǔ)句并沒(méi)有捕捉到IndexError異常;而是將出現(xiàn)的異常綁定到了參數(shù)IndexError中。

想在一個(gè)except語(yǔ)句同時(shí)捕捉到多個(gè)異常的正確方式是,將第一個(gè)參數(shù)指定為元組,并將要捕捉的異常類型都寫(xiě)入該元組中。為了方便起見(jiàn),可以使用as關(guān)鍵字,Python 2 和Python 3都支持這種語(yǔ)法格式:

>>> try:... l = ["a", "b"]... int(l[2])... except (ValueError, IndexError) as e: ... pass...>>>

常見(jiàn)錯(cuò)誤4:錯(cuò)誤理解Python中變量的作用域

Python變量作用域遵循LEGB規(guī)則,LEGB是Local,Enclosing,Global,Builtin的縮寫(xiě),分別代表本地作用域、封閉作用域、全局作用域和內(nèi)置作用域,這個(gè)規(guī)則看起來(lái)一目了然。事實(shí)上,Python的這種工作方式較為獨(dú)特,會(huì)導(dǎo)致一些編程錯(cuò)誤,例如:

>>> x = 10>>> def foo():... x += 1... print x...>>> foo()Traceback (most recent call last):File "", line 1, in File "", line 2, in fooUnboundLocalError: local variable 'x' referenced before assignment

問(wèn)題出在哪?

上面的錯(cuò)誤是因?yàn)樵谧饔糜騼?nèi)對(duì)變量賦值時(shí),Python自動(dòng)將該變量視為該作用域的本地變量,并對(duì)外部定義的同名變量進(jìn)行了屏蔽。因此,原本正確的代碼,在某個(gè)函數(shù)內(nèi)部添加了一個(gè)賦值語(yǔ)句后,卻意外收到了UnboundLocalError的報(bào)錯(cuò)信息。

關(guān)于UnboundLocalError更多內(nèi)容請(qǐng)戳:

https://docs.python.org/2/faq/programming.html#why-am-i-getting-an-unboundlocalerror-when-the-variable-has-a-value

在使用列表時(shí),Python程序員更容易掉入此類陷阱,例如:

>>> lst = [1, 2, 3]>>> def foo1():... lst.append(5) # This works ok......>>> foo1()>>> lst[1, 2, 3, 5]>>> lst = [1, 2, 3]>>> def foo2():... lst += [5] # ... but this bombs!...>>> foo2()Traceback (most recent call last):File "", line 1, in File "", line 2, in fooUnboundLocalError: local variable 'lst' referenced before assignment

奇怪,為什么foo1正常運(yùn)行,而foo2崩潰了呢?

原因和上一個(gè)案例中出現(xiàn)的問(wèn)題相似,但這里的錯(cuò)誤更加細(xì)微。函數(shù)foo1沒(méi)有對(duì)變量lst進(jìn)行賦值操作,而函數(shù)foo2有賦值操作。

首先, lst += [5]是lst = lst + [5]的縮寫(xiě)形式,在函數(shù)foo2中試圖對(duì)變量lst進(jìn)行賦值操作(Python將變量lst默認(rèn)為本地作用域的變量)。但是,lst += [5]語(yǔ)句是對(duì)lst變量自身進(jìn)行的賦值操作(此時(shí)變量lst的作用域是函數(shù)foo2),但是在函數(shù)foo2中還未聲明該變量,所以就報(bào)錯(cuò)啦!

常見(jiàn)錯(cuò)誤5:在遍歷列表時(shí)修改列表

下面代碼中的錯(cuò)誤很明顯:

>>> odd = lambda x : bool(x % 2)>>> numbers = [n for n in range(10)]>>> for i in range(len(numbers)):... if odd(numbers[i]):... del numbers[i] # BAD: Deleting item from a list while iterating over it...Traceback (most recent call last):File "", line 2, in IndexError: list index out of range

有經(jīng)驗(yàn)的程序員都知道,在Python中遍歷列表或數(shù)組時(shí)不應(yīng)該刪除該列表(數(shù)組)中的元素。雖然上面代碼的錯(cuò)誤很明顯,但是在編寫(xiě)復(fù)雜代碼時(shí),資深程序員也難免會(huì)犯此類錯(cuò)誤。

幸好Python集成了大量經(jīng)典的編程范式,如果運(yùn)用得當(dāng),可以大大簡(jiǎn)化代碼并提高編程效率。簡(jiǎn)單的代碼會(huì)降低出現(xiàn)上述bug的幾率。列表解析式(list comprehensions)就是利器之一,它將完美避開(kāi)上述bug,解決方案如下:

>>> odd = lambda x : bool(x % 2)>>> numbers = [n for n in range(10)]>>> numbers[:] = [n for n in numbers if not odd(n)] # ahh, the beauty of it all>>> numbers[0, 2, 4, 6, 8]

更多有關(guān)列表解析式的詳細(xì)內(nèi)容,請(qǐng)戳:https://docs.python.org/2/tutorial/datastructures.html#tut-listcomps

常見(jiàn)錯(cuò)誤6:不理解Python閉包中的變量綁定

代碼示例:

>>> def create_multipliers():... return [lambda x : i * x for i in range(5)]>>> for multiplier in create_multipliers():... print multiplier(2)...

你以為運(yùn)行結(jié)果會(huì)是:

02468

但實(shí)際輸出結(jié)果是:8

8888

驚不驚喜!

這種情況是由于Python延遲綁定(late binding)機(jī)制造成的,也就是說(shuō)只有在內(nèi)部函數(shù)被調(diào)用時(shí)才會(huì)搜索閉包中變量的值。所以在上述代碼中,每次調(diào)用create_multipliers()函數(shù)中的return函數(shù)時(shí),會(huì)在附近作用域中查詢變量i的值。(此時(shí),return中循環(huán)已結(jié)束,所以i值為4)。

常見(jiàn)解決方案:

>>> def create_multipliers():... return [lambda x, i=i : i * x for i in range(5)]...>>> for multiplier in create_multipliers():... print multiplier(2)...02468

沒(méi)錯(cuò)!我們利用了匿名函數(shù)lambda的默認(rèn)參數(shù)來(lái)生成結(jié)果序列。有人覺(jué)得這種用法很簡(jiǎn)潔,有人會(huì)說(shuō)它很巧妙,還有人會(huì)覺(jué)得晦澀難懂。如果你是Python開(kāi)發(fā)人員,那么深刻理解上述語(yǔ)法對(duì)你而言非常重要。

常見(jiàn)錯(cuò)誤7:模塊之間出現(xiàn)循環(huán)依賴

假設(shè)你有兩個(gè)文件,分別是a.py和b.py,兩者相互導(dǎo)入,如下所示:

a.py模塊中的代碼:

import bdef f():return b.xprint f()

b.py模塊中的代碼:

import ax = 1def g():print a.f()

首先,我們嘗試導(dǎo)入a.py:

>>> import a1

運(yùn)行結(jié)果正確!這似乎有點(diǎn)出人意料,因?yàn)槲覀冊(cè)谶@里進(jìn)行循環(huán)導(dǎo)入,應(yīng)該會(huì)報(bào)錯(cuò)呀!

答案是,在Python中如果僅存在一個(gè)循環(huán)導(dǎo)入,程序不會(huì)報(bào)錯(cuò)。如果一個(gè)模塊已經(jīng)被導(dǎo)入,Python會(huì)自動(dòng)識(shí)別而不會(huì)再次導(dǎo)入。但是如果每個(gè)模塊試圖訪問(wèn)其他模塊不同位置的函數(shù)或變量時(shí),那么Error又雙叒叕出現(xiàn)了。

回到上面的示例中,當(dāng)導(dǎo)入a.py模塊時(shí),程序可以正常導(dǎo)入b.py模塊,因?yàn)榇藭r(shí)b.py模塊未訪問(wèn)a.py中定義任何的變量或函數(shù)。b.py模塊僅引用了a.py模中的a.f()函數(shù)。調(diào)用的a.f()函數(shù)隸屬于g()函數(shù),而a.py或b.py模塊中并沒(méi)有調(diào)用g()函數(shù)。所以程序沒(méi)有報(bào)錯(cuò)。

但是,如果我們?cè)谖磳?dǎo)入a.py模塊之前先導(dǎo)入b.py模塊,結(jié)果會(huì)怎樣?

>>> import bTraceback (most recent call last):File "", line 1, in File "b.py", line 1, in import a File "a.py", line 6, in print f() File "a.py", line 4, in f return b.xAttributeError: 'module' object has no attribute 'x'

報(bào)錯(cuò)了!問(wèn)題在于,在導(dǎo)入b.py的過(guò)程中,它試圖導(dǎo)入a.py模塊,而a.py模塊會(huì)調(diào)用f()函數(shù),f()函數(shù)又試圖訪問(wèn)b.x變量。但此時(shí),還未對(duì)變量b.x進(jìn)行定義,所以出現(xiàn)了AttributeError異常。

稍微修改下b.py,即在g()函數(shù)內(nèi)部導(dǎo)入a.py就可以解決上述問(wèn)題。

修改后的b.py:

x = 1def g():

import a # This will be evaluated only when g() is calledprint a.f()

現(xiàn)在我們?cè)賹?dǎo)入b.py模塊,就不會(huì)報(bào)錯(cuò)啦!

>>> import b>>> b.g()1 # Printed a first time since module 'a' calls 'print f()' at the end1 # Printed a second time, this one is our call to 'g'

常見(jiàn)錯(cuò)誤8:文件命名與Python標(biāo)準(zhǔn)庫(kù)模塊的名稱沖突

Python的優(yōu)勢(shì)之一就是其集成了豐富的標(biāo)準(zhǔn)庫(kù)。正因?yàn)槿绱?,稍不留神就?huì)在為自己的文件命名時(shí)與Python自帶標(biāo)準(zhǔn)庫(kù)模塊重名。例如,如果你的代碼中有一個(gè)名為email.py的模塊,恰好就和Python標(biāo)準(zhǔn)庫(kù)中email.py模塊重名了。)

上述問(wèn)題比較復(fù)雜。舉個(gè)例子,在導(dǎo)入模塊A的時(shí)候,假如該模塊A試圖導(dǎo)入Python標(biāo)準(zhǔn)庫(kù)中的模塊B,但你已經(jīng)定義了一個(gè)同名模塊B,模塊A會(huì)錯(cuò)誤導(dǎo)入你自定義的模塊B,而不是Python標(biāo)準(zhǔn)庫(kù)中的模塊B。這種錯(cuò)誤很糟糕,因?yàn)槌绦騿T很難察覺(jué)到是因?yàn)槊麤_突而導(dǎo)致的。

因此,Python程序員要注意避免與Python標(biāo)準(zhǔn)庫(kù)模塊的命名沖突。畢竟,修改自己模塊的名稱比修改標(biāo)準(zhǔn)庫(kù)的名稱要容易的多!當(dāng)然你也可以寫(xiě)一份Python改善建議書(shū)(Python Enhancement Proposal,PEP)提議修改標(biāo)準(zhǔn)庫(kù)的名稱。

常見(jiàn)錯(cuò)誤9:不熟悉Python2和Python3之間的差異

先來(lái)看看foo.py文件中的代碼:

import sysdef bar(i):if i == 1: raise KeyError(1) if i == 2: raise ValueError(2)def bad(): e = None try: bar(int(sys.argv[1])) except KeyError as e: print('key error') except ValueError as e: print('value error') print(e)bad()

在Python 2中,上述代碼運(yùn)行正常

$ python foo.py 1key error1$ python foo.py 2value error2

但是在Python 3中運(yùn)行時(shí):

$ python3 foo.py 1key errorTraceback (most recent call last):File "foo.py", line 19, in bad() File "foo.py", line 17, in bad print(e)UnboundLocalError: local variable 'e' referenced before assignment

什么情況?原來(lái),在Python 3中,在except代碼塊作用域外無(wú)法訪問(wèn)異常對(duì)象。(原因是,Python 3會(huì)將內(nèi)存堆棧中的循環(huán)引用進(jìn)行保留,直到垃圾回收...

Python中集合(set)類型的詳細(xì)解釋及操作 行業(yè)視頻課程

img

薛雅香

關(guān)注

一、集合(set)類型的含義:

Set是一個(gè)無(wú)序不重復(fù)元素集,與列表和元組不同,集合是無(wú)序的,無(wú)法通過(guò)數(shù)字進(jìn)行索引。

注意:下面所舉例子在python3.6,IDE為pycharm2016.1中通過(guò)。

創(chuàng)建集合:用set()函數(shù),或直接賦值。

例子:

x=set('Nike MM')

y=set(['w','a','m','a'])

print(x)

print(y)

輸出:

{'M', 'N', 'e', 'k', ' ', 'i'}

{'w', 'm', 'a'}

可以看到,在輸出中,是用一對(duì){}號(hào)包住,里面重復(fù)的元素被去除。

再看一個(gè)例子:

s={'11','22','33'}

print(s)

print(type(s))

s={}

{'33', '11', '22'}

在定義不,不能用s={},這關(guān)創(chuàng)建的實(shí)際上是一個(gè)字典類型。

二、有關(guān)集合的操作:

1.增加操作

a=set('python')

a.add('why')

print(a)

b=set('python')

b.update('why')

print(b)

{'n', 'p', 'y', 'h', 'o', 't', 'why'}

{'n', 'p', 'y', 'h', 'o', 'w', 't'}

可能看到:add是單個(gè)元素的添加,并沒(méi)有把元素再分拆為單個(gè)字符。Update是批量的增加,增加的元素如果是一個(gè)字符串(實(shí)際上,在Python中字符串也是一個(gè)系列),是作為一個(gè)系列增加的。在輸出結(jié)果中,兩個(gè)函數(shù)都是無(wú)序的,并且無(wú)重復(fù),也非添加到尾部。

2.刪除操作(remove,discard,pop)

例子1:

a=set('abcdefghijk')

a.remove('a')

a.remove('w')

輸出 :

Traceback (most recent call last):

{'h', 'k', 'e', 'd', 'g', 'c', 'f', 'i', 'b', 'j'}

File "D:/python/temp3.py", line 4, in

KeyError: 'w'

例子2:

a.discard('a')

a.discard('w')

{'f', 'h', 'd', 'e', 'b', 'k', 'i', 'j', 'c', 'g'}

例子3:

b=a.pop()

print(b,type(b))

{'k', 'd', 'h', 'c', 'b', 'j', 'g', 'i', 'e', 'f'}

a

從以上例子可以看到,remove方法刪除指定無(wú)素,如果要?jiǎng)h除的元素的不在集合中,則報(bào)錯(cuò);discard方法刪除指定元素,如果要?jiǎng)h除物元素不在集合中,則不報(bào)錯(cuò),pop方法刪除任意元素,并可將這個(gè)元素賦值給一個(gè)變量,但集合并沒(méi)有把這個(gè)元素移除。

3.清空(clear)

例子:

a.clear()

set()

4.交集&,并集|,差集-,對(duì)稱差集^,子集(被包含)<=,父集(包含)>=

a=set(['a','b','c','d','e','f'])

b=set(('d','e','f','g','h','i'))

d=set('def')

print('交集:',a.intersection(b))

print('交集:',a & b)

print('并集:',a.union(b))

print('并集:',a | b)

print('差集:',a.difference(b))

print('差集:',a-b)

#對(duì)稱差集:

#把兩個(gè)集合中相同的元素去掉,然后

#兩個(gè)集合中剩下的元素組成一個(gè)新的集合

print('對(duì)稱差集:',a.symmetric_difference(b) )

print('對(duì)稱差集:',a ^ b )

print('子集:',a.issubset(d) )

print('子集:',a<=d )

print('父集:',a.issuperset(d) )

print('父集:',a>=d )

交集: {'f', 'e', 'd'}

并集: {'c', 'e', 'd', 'b', 'f', 'a', 'g', 'i', 'h'}

差集: {'a', 'c', 'b'}

對(duì)稱差集: {'a', 'c', 'g', 'b', 'i', 'h'}

子集: False

父集: True

5.集合的其它一些操作

#如果a和d沒(méi)有交集,返回True,有則返回False

print(a.isdisjoint(d) ) 輸出:False

print(a

print(a>d) 輸出:True

print(a!=b) 輸出:True

print(a.copy()) 輸出:{'f', 'e', 'b', 'a', 'd', 'c'} #復(fù)制一個(gè)集合

print('a' in a) 輸出:True #測(cè)試元素是否在集合中

print('a' not in a) 輸出:False #測(cè)試元素是否不在集合中

print(len(a)) 輸出:6 #返回集合的長(zhǎng)度

6.集合計(jì)算:

(1)

#從a中減去a和b的交集,即從a集合中刪除和b集合中相同的元素

a.difference_update(b) 即等于:a=a-b 或a-=b

print(a) 輸出:{'a', 'b', 'c'}

(2)

#修改a集合,僅僅保持a與b的交集,如果沒(méi)有交集,則a變?yōu)榭占蟬et()

a.intersection_update(b) 即等于:a=a&b 或a&=b

print(a )

輸出:{'e', 'd', 'f'}

(3)

#a集合中增加‘在b集合中除去a和b交集剩下的元素’

a.symmetric_difference_update(b) 即等于:a=a^b 或 a^=b

print(a) 輸出:{'i', 'g', 'a', 'c', 'b', 'h'}

一步一步帶你理解 Python 中的淺復(fù)制與深復(fù)制 營(yíng)銷視頻課程

img

Tiffany

關(guān)注

Python 中的賦值語(yǔ)句不會(huì)對(duì)對(duì)象進(jìn)行拷貝,僅僅是將變量名指向?qū)ο蟆?duì)于不可修改的對(duì)象來(lái)說(shuō),這種機(jī)制不會(huì)影響我們?nèi)粘5氖褂谩5?,?duì)于可修改的對(duì)象,你偶爾可能需要對(duì)該對(duì)象做一個(gè)真正的復(fù)制。何為真正的復(fù)制?就是修改拷貝來(lái)的對(duì)象不會(huì)影響原來(lái)的對(duì)象。

Python 中內(nèi)置的可修改的集合類對(duì)象,比如列表、字典、集合等,可以直接使用對(duì)應(yīng)的工廠方法進(jìn)行拷貝。

需要注意的是,對(duì)于復(fù)合類型的對(duì)象,比如列表、字典、集合等,復(fù)制有淺復(fù)制與深復(fù)制兩種類型。

淺復(fù)制意味著新建一個(gè)對(duì)象,但是其子元素仍然指向的對(duì)應(yīng)原對(duì)象的子對(duì)象。也就是說(shuō),這只會(huì)對(duì)原對(duì)象進(jìn)行一層的拷貝,而不會(huì)遞歸的對(duì)子對(duì)象也進(jìn)行拷貝。

深復(fù)制則會(huì)遞歸的對(duì)子對(duì)象進(jìn)行拷貝。

如果上面的看不懂,沒(méi)關(guān)系,我們通過(guò)一個(gè)個(gè)例子來(lái)搞清楚。

淺復(fù)制

現(xiàn)在下面這個(gè)列表,我們使用 list() 方法對(duì)它進(jìn)行復(fù)制

這意味著 xs 和 ys 是兩個(gè)不同的對(duì)象。我們可以看看它們的值

為了證明 xs 與 ys 確實(shí)不同,我們可以做個(gè)小實(shí)驗(yàn)。添加一個(gè)子列表到 xs 中,然后看會(huì)不會(huì)影響 ys 。

與預(yù)期相同,如果只是修改原對(duì)象這一層,確實(shí)不對(duì)對(duì) ys 造成影響。

但是由于是淺復(fù)制,ys 中的子元素并非是 xs 子元素的拷貝,僅僅是 xs 子對(duì)象的引用。因此,如果你修改 xs 中的子元素,ys 中的子元素同樣會(huì)被修改。

看起來(lái)我們只是修改了 xs 中的子元素,實(shí)則 ys 中的子元素也被修改了,這是由于淺復(fù)制的緣故。

現(xiàn)在我們大概清楚了淺復(fù)制與深復(fù)制的區(qū)別,但是還有兩個(gè)問(wèn)題待解決:

一是,對(duì)于內(nèi)置的集合類對(duì)象,我們?cè)趺催M(jìn)行深復(fù)制?

二是,對(duì)于任意的類,又該如何進(jìn)行淺復(fù)制與深復(fù)制?

答案是標(biāo)準(zhǔn)庫(kù)中的 copy 模塊,其提供了簡(jiǎn)單的方法對(duì)對(duì)象進(jìn)行淺復(fù)制或深復(fù)制。

深復(fù)制

這次我們?nèi)匀皇褂蒙厦娴睦?,不同的是,我們使?copy 模塊中的 deepcopy() 方法來(lái)進(jìn)行復(fù)制。

還是先看看各自的值

這次,因?yàn)?zs 對(duì) xs 進(jìn)行了深復(fù)制,即不僅拷貝了 xs 本身,還對(duì)它的子對(duì)象都進(jìn)行了遞歸的拷貝,所以,如果我們?cè)俅涡薷?xs 的子元素時(shí),并不會(huì)對(duì) zs 造成影響。我們來(lái)試一試

果然,與預(yù)期一致。

順便說(shuō)下,使用 copy 模塊中的 copy() 方法可以對(duì)對(duì)象進(jìn)行淺復(fù)制。平時(shí)開(kāi)發(fā)需要淺復(fù)制的時(shí)候,你可以使用該方法,但如果碰到內(nèi)置的集合類對(duì)象,比如列表、字典、集合等的時(shí)候,使用對(duì)應(yīng)的工廠方法如 list() ,dict() ,set() 等更 Pythonic 一些。

對(duì)任意對(duì)象進(jìn)行復(fù)制

現(xiàn)在有一個(gè)點(diǎn)類 Point ,如下

其中,__repr__ 方法可以讓我們更方便的查看對(duì)象的信息。需要注意的是,f'...' 這種格式化字符串的方式在 Python 3.6 才支持,如果是 Python 2 或者 3.6 以前的版本可以使用 '...' % (v1[, v2, ...]) 的方式。

現(xiàn)在我們創(chuàng)建一個(gè)點(diǎn)對(duì)象并進(jìn)行淺復(fù)制

查看兩個(gè)對(duì)象的信息

和預(yù)期一致。

需要注意的是,因?yàn)轭惖膬蓚€(gè)成員 x 和 y 都是簡(jiǎn)單類型,這里是整型,所以這里淺復(fù)制與深復(fù)制沒(méi)有任何差別。后面我會(huì)擴(kuò)展這個(gè)例子。

現(xiàn)在我要定義一個(gè)矩形類,其類成員將會(huì)使用到上面的點(diǎn)類。

首先我們先試一下淺復(fù)制

看下各自的信息

記得我們上面修改列表的淺復(fù)制與深復(fù)制的例子嗎?這里,我要類似的實(shí)驗(yàn)。我們修改 rect 的成員,不出意外的話,srect 也會(huì)改變。

果然。

下面,我會(huì)進(jìn)行深復(fù)制,然后再類似的修改

這次,深復(fù)制出來(lái)的 drect 對(duì)象與 srect 才能說(shuō)是“真正不同”的對(duì)象。

copy 模塊中還有其它很多用法,比如定義對(duì)象的 __copy__() 和 __deepcopy__() 方法可以自定義對(duì)象的淺復(fù)制與深復(fù)制行為等。這不是本文的重心,有興趣的可以去看相應(yīng)的文檔 https://docs.python.org/3/library/copy.html 。

小結(jié)

* 淺復(fù)制不會(huì)克隆子對(duì)象,所以,復(fù)制出來(lái)的對(duì)象和原對(duì)象并非完全不想關(guān)。

* 深復(fù)制會(huì)遞歸的克隆子對(duì)象,所以,復(fù)制出來(lái)的對(duì)象和原對(duì)象完全不相關(guān),但是深復(fù)制比淺復(fù)制會(huì)慢一些。

* 使用 copy 模塊你可以復(fù)制任何類,不管是淺復(fù)制還是深復(fù)制。

鞏固

從網(wǎng)上找了組圖片(侵刪),讓大家加深下認(rèn)識(shí)

首先是賦值

然后是淺復(fù)制

最后是深復(fù)制

img

在線咨詢

建站在線咨詢

img

微信咨詢

掃一掃添加
動(dòng)力姐姐微信

img
img

TOP