溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

Python設(shè)計(jì)方法有哪些

發(fā)布時(shí)間:2021-11-20 11:14:35 來源:億速云 閱讀:129 作者:iii 欄目:編程語言

這篇文章主要介紹“Python設(shè)計(jì)方法有哪些”,在日常操作中,相信很多人在Python設(shè)計(jì)方法有哪些問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對(duì)大家解答”Python設(shè)計(jì)方法有哪些”的疑惑有所幫助!接下來,請(qǐng)跟著小編一起來學(xué)習(xí)吧!

15. 為什么 CPython 不使用更傳統(tǒng)的垃圾回收方案?

首先,這不是 C 標(biāo)準(zhǔn)特性,因此不能移植。(是的,我們知道 Boehm GC 庫。它包含了 大多數(shù) 常見平臺(tái)(但不是所有平臺(tái))的匯編代碼,盡管它基本上是透明的,但也不是完全透明的; 要讓 Python 使用它,需要使用補(bǔ)丁。)

當(dāng) Python 嵌入到其他應(yīng)用程序中時(shí),傳統(tǒng)的 GC 也成為一個(gè)問題。在獨(dú)立的 Python 中,可以用 GC 庫提供的版本替換標(biāo)準(zhǔn)的 malloc()和 free(),嵌入 Python 的應(yīng)用程序可能希望用 它自己 替代 malloc()和 free(),而可能不需要 Python 的?,F(xiàn)在,CPython 可以正確地實(shí)現(xiàn) malloc()和 free()。

16. CPython 退出時(shí)為什么不釋放所有內(nèi)存?

當(dāng) Python 退出時(shí),從全局命名空間或 Python 模塊引用的對(duì)象并不總是被釋放。如果存在循環(huán)引用,則可能發(fā)生這種情況 C 庫分配的某些內(nèi)存也是不可能釋放的(例如像 Purify 這樣的工具會(huì)抱怨這些內(nèi)容)。但是,Python 在退出時(shí)清理內(nèi)存并嘗試銷毀每個(gè)對(duì)象。

如果要強(qiáng)制 Python 在釋放時(shí)刪除某些內(nèi)容,請(qǐng)使用 atexit 模塊運(yùn)行一個(gè)函數(shù),強(qiáng)制刪除這些內(nèi)容。

17. 為什么有單獨(dú)的元組和列表數(shù)據(jù)類型?

雖然列表和元組在許多方面是相似的,但它們的使用方式通常是完全不同的。可以認(rèn)為元組類似于 Pascal 記錄或 C 結(jié)構(gòu);它們是相關(guān)數(shù)據(jù)的小集合,可以是不同類型的數(shù)據(jù),可以作為一個(gè)組進(jìn)行操作。例如,笛卡爾坐標(biāo)適當(dāng)?shù)乇硎緸閮蓚€(gè)或三個(gè)數(shù)字的元組。

另一方面,列表更像其他語言中的數(shù)組。它們傾向于持有不同數(shù)量的對(duì)象,所有對(duì)象都具有相同的類型,并且逐個(gè)操作。例如, os.listdir('.') 返回表示當(dāng)前目錄中的文件的字符串列表。如果向目錄中添加了一兩個(gè)文件,對(duì)此輸出進(jìn)行操作的函數(shù)通常不會(huì)中斷。

元組是不可變的,這意味著一旦創(chuàng)建了元組,就不能用新值替換它的任何元素。列表是可變的,這意味著您始終可以更改列表的元素。只有不變?cè)乜梢杂米髯值涞?key,因此只能將元組和非列表用作 key。

18. 列表如何在 CPython 中實(shí)現(xiàn)?

CPython 的列表實(shí)際上是可變長度的數(shù)組,而不是 lisp 風(fēng)格的鏈表。該實(shí)現(xiàn)使用對(duì)其他對(duì)象的引用的連續(xù)數(shù)組,并在列表頭結(jié)構(gòu)中保留指向該數(shù)組和數(shù)組長度的指針。

這使得索引列表 a[i] 的操作成本與列表的大小或索引的值無關(guān)。

當(dāng)添加或插入項(xiàng)時(shí),將調(diào)整引用數(shù)組的大小。并采用了一些巧妙的方法來提高重復(fù)添加項(xiàng)的性能; 當(dāng)數(shù)組必須增長時(shí),會(huì)分配一些額外的空間,以便在接下來的幾次中不需要實(shí)際調(diào)整大小。

19. 字典如何在 CPython 中實(shí)現(xiàn)?

CPython 的字典實(shí)現(xiàn)為可調(diào)整大小的哈希表。與 B-樹相比,這在大多數(shù)情況下為查找(目前最常見的操作)提供了更好的性能,并且實(shí)現(xiàn)更簡單。

字典的工作方式是使用 hash() 內(nèi)置函數(shù)計(jì)算字典中存儲(chǔ)的每個(gè)鍵的 hash 代碼。hash 代碼根據(jù)鍵和每個(gè)進(jìn)程的種子而變化很大;例如,"Python" 的 hash 值為-539294296,而"python"(一個(gè)按位不同的字符串)的 hash 值為 1142331976。然后,hash 代碼用于計(jì)算內(nèi)部數(shù)組中將存儲(chǔ)該值的位置。假設(shè)您存儲(chǔ)的鍵都具有不同的 hash 值,這意味著字典需要恒定的時(shí)間 -- O(1),用 Big-O 表示法 -- 來檢索一個(gè)鍵。

20. 為什么字典 key 必須是不可變的?

字典的哈希表實(shí)現(xiàn)使用從鍵值計(jì)算的哈希值來查找鍵。如果鍵是可變對(duì)象,則其值可能會(huì)發(fā)生變化,因此其哈希值也會(huì)發(fā)生變化。但是,由于無論誰更改鍵對(duì)象都無法判斷它是否被用作字典鍵值,因此無法在字典中修改條目。然后,當(dāng)你嘗試在字典中查找相同的對(duì)象時(shí),將無法找到它,因?yàn)槠涔V挡煌?。如果你嘗試查找舊值,也不會(huì)找到它,因?yàn)樵谠摴1碇姓业降膶?duì)象的值會(huì)有所不同。

如果你想要一個(gè)用列表索引的字典,只需先將列表轉(zhuǎn)換為元組;用函數(shù) tuple(L)創(chuàng)建一個(gè)元組,其條目與列表 L相同。元組是不可變的,因此可以用作字典鍵。

已經(jīng)提出的一些不可接受的解決方案:

  • 哈希按其地址(對(duì)象 ID)列出。這不起作用,因?yàn)槿绻銟?gòu)造一個(gè)具有相同值的新列表,它將無法找到;例如:

mydict = {[1, 2]: '12'}
print(mydict[[1, 2]])
  • 會(huì)引發(fā)一個(gè) KeyError 異常,因?yàn)榈诙兄惺褂玫?[1, 2] 的 id 與第一行中的 id 不同。換句話說,應(yīng)該使用 == 來比較字典鍵,而不是使用is 。

  • 使用列表作為鍵時(shí)進(jìn)行復(fù)制。這沒有用的,因?yàn)樽鳛榭勺儗?duì)象的列表可以包含對(duì)自身的引用,然后復(fù)制代碼將進(jìn)入無限循環(huán)。

  • 允許列表作為鍵,但告訴用戶不要修改它們。當(dāng)你意外忘記或修改列表時(shí),這將產(chǎn)生程序中的一類難以跟蹤的錯(cuò)誤。它還使一個(gè)重要的字典不變量無效:d.keys() 中的每個(gè)值都可用作字典的鍵。

  • 將列表用作字典鍵后,應(yīng)標(biāo)記為其只讀。問題是,它不僅僅是可以改變其值的頂級(jí)對(duì)象;你可以使用包含列表作為鍵的元組。將任何內(nèi)容作為鍵關(guān)聯(lián)到字典中都需要將從那里可到達(dá)的所有對(duì)象標(biāo)記為只讀 —— 并且自引用對(duì)象可能會(huì)導(dǎo)致無限循環(huán)。

如果需要,可以使用以下方法來解決這個(gè)問題,但使用它需要你自擔(dān)風(fēng)險(xiǎn):你可以將一個(gè)可變結(jié)構(gòu)包裝在一個(gè)類實(shí)例中,該實(shí)例同時(shí)具有 __eq__() 和 __hash__() 方法。然后,你必須確保駐留在字典(或其他基于 hash 的結(jié)構(gòu))中的所有此類包裝器對(duì)象的哈希值在對(duì)象位于字典(或其他結(jié)構(gòu))中時(shí)保持固定。

class ListWrapper:
 def __init__(self, the_list):
 self.the_list = the_list
 def __eq__(self, other):
 return self.the_list == other.the_list
 def __hash__(self):
 l = self.the_list
 result = 98767 - len(l)*555
 for i, el in enumerate(l):
 try:
 result = result + (hash(el) % 9999999) * 1001 + i
 except Exception:
 result = (result % 7777777) + i * 333
 return result

注意,哈希計(jì)算由于列表的某些成員可能不可用以及算術(shù)溢出的可能性而變得復(fù)雜。

此外,必須始終如此,如果 o1 == o2 (即 o1.__eq__(o2) is True )則 hash(o1) == hash(o2)``(即``o1.__hash__() == o2.__hash__() ),無論對(duì)象是否在字典中。如果你不能滿足這些限制,字典和其他基于 hash 的結(jié)構(gòu)將會(huì)出錯(cuò)。

對(duì)于 ListWrapper ,只要包裝器對(duì)象在字典中,包裝列表就不能更改以避免異常。除非你準(zhǔn)備好認(rèn)真考慮需求以及不正確地滿足這些需求的后果,否則不要這樣做。請(qǐng)留意。

21. 為什么 list.sort() 沒有返回排序列表?

在性能很重要的情況下,僅僅為了排序而復(fù)制一份列表將是一種浪費(fèi)。因此, list.sort() 對(duì)列表進(jìn)行了適當(dāng)?shù)呐判颉榱颂嵝涯@一事實(shí),它不會(huì)返回已排序的列表。這樣,當(dāng)您需要排序的副本,但也需要保留未排序的版本時(shí),就不會(huì)意外地覆蓋列表。

如果要返回新列表,請(qǐng)使用內(nèi)置 sorted() 函數(shù)。此函數(shù)從提供的可迭代列表中創(chuàng)建新列表,對(duì)其進(jìn)行排序并返回。例如,下面是如何迭代遍歷字典并按 keys 排序:

for key in sorted(mydict):
 ... # do whatever with mydict[key]...

22. 如何在 Python 中指定和實(shí)施接口規(guī)范?

由 C++和 Java 等語言提供的模塊接口規(guī)范描述了模塊的方法和函數(shù)的原型。許多人認(rèn)為接口規(guī)范的編譯時(shí)強(qiáng)制執(zhí)行有助于構(gòu)建大型程序。

Python 2.6 添加了一個(gè) abc 模塊,允許定義抽象基類 (ABCs)。然后可以使用isinstance() 和 issubclass() 來檢查實(shí)例或類是否實(shí)現(xiàn)了特定的 ABC。collections.abc 模塊定義了一組有用的 ABCs 例如 Iterable , Container , 和 MutableMapping

對(duì)于 Python,通過對(duì)組件進(jìn)行適當(dāng)?shù)臏y試規(guī)程,可以獲得接口規(guī)范的許多好處。還有一個(gè)工具 PyChecker,可用于查找由于子類化引起的問題。

一個(gè)好的模塊測試套件既可以提供回歸測試,也可以作為模塊接口規(guī)范和一組示例。許多 Python 模塊可以作為腳本運(yùn)行,以提供簡單的“自我測試”。即使是使用復(fù)雜外部接口的模塊,也常常可以使用外部接口的簡單“樁代碼(stub)”模擬進(jìn)行隔離測試??梢允褂?doctest 和 unittest 模塊或第三方測試框架來構(gòu)造詳盡的測試套件,以運(yùn)行模塊中的每一行代碼。

適當(dāng)?shù)臏y試規(guī)程可以幫助在 Python 中構(gòu)建大型的、復(fù)雜的應(yīng)用程序以及接口規(guī)范。事實(shí)上,它可能會(huì)更好,因?yàn)榻涌谝?guī)范不能測試程序的某些屬性。例如,append() 方法將向一些內(nèi)部列表的末尾添加新元素;接口規(guī)范不能測試您的 append() 實(shí)現(xiàn)是否能夠正確執(zhí)行此操作,但是在測試套件中檢查這個(gè)屬性是很簡單的。

編寫測試套件非常有用,您可能希望設(shè)計(jì)代碼時(shí)著眼于使其易于測試。一種日益流行的技術(shù)是面向測試的開發(fā),它要求在編寫任何實(shí)際代碼之前,首先編寫測試套件的各個(gè)部分。當(dāng)然,Python 允許您草率行事,根本不編寫測試用例。

23. 為什么沒有 goto?

可以使用異常捕獲來提供 “goto 結(jié)構(gòu)” ,甚至可以跨函數(shù)調(diào)用工作的 。許多人認(rèn)為異常捕獲可以方便地模擬 C,F(xiàn)ortran 和其他語言的 "go" 或 "goto" 結(jié)構(gòu)的所有合理用法。例如:

class label(Exception): pass # declare a label
try:
 ...
 if condition: raise label() # goto label
 ...
except label: # where to goto
 pass
...

但是不允許你跳到循環(huán)的中間,這通常被認(rèn)為是濫用 goto。謹(jǐn)慎使用。

24. 為什么原始字符串(r-strings)不能以反斜杠結(jié)尾?

更準(zhǔn)確地說,它們不能以奇數(shù)個(gè)反斜杠結(jié)束:結(jié)尾處的不成對(duì)反斜杠會(huì)轉(zhuǎn)義結(jié)束引號(hào)字符,留下未結(jié)束的字符串。

原始字符串的設(shè)計(jì)是為了方便想要執(zhí)行自己的反斜杠轉(zhuǎn)義處理的處理器(主要是正則表達(dá)式引擎)創(chuàng)建輸入。此類處理器將不匹配的尾隨反斜杠視為錯(cuò)誤,因此原始字符串不允許這樣做。反過來,允許通過使用引號(hào)字符轉(zhuǎn)義反斜杠轉(zhuǎn)義字符串。當(dāng) r-string 用于它們的預(yù)期目的時(shí),這些規(guī)則工作的很好。

如果您正在嘗試構(gòu)建 Windows 路徑名,請(qǐng)注意所有 Windows 系統(tǒng)調(diào)用都使用正斜杠:

f = open("/mydir/file.txt") # works fine!

如果您正在嘗試為 DOS 命令構(gòu)建路徑名,請(qǐng)嘗試以下示例

dir = r"\this\is\my\dos\dir" "\\"
dir = r"\this\is\my\dos\dir\ "[:-1]
dir = "\\this\\is\\my\\dos\\dir\\"

25. 為什么 Python 沒有屬性賦值的“with”語句?

Python 有一個(gè) 'with' 語句,它封裝了塊的執(zhí)行,在塊的入口和出口調(diào)用代碼。有些語言的結(jié)構(gòu)是這樣的:

with obj:
 a = 1 # equivalent to obj.a = 1
 total = total + 1 # obj.total = obj.total + 1

在 Python 中,這樣的結(jié)構(gòu)是不明確的。

其他語言,如 ObjectPascal、Delphi 和 C++ 使用靜態(tài)類型,因此可以毫不含糊地知道分配給什么成員。這是靜態(tài)類型的要點(diǎn) -- 編譯器 總是 在編譯時(shí)知道每個(gè)變量的作用域。

Python 使用動(dòng)態(tài)類型。事先不可能知道在運(yùn)行時(shí)引用哪個(gè)屬性??梢詣?dòng)態(tài)地在對(duì)象中添加或刪除成員屬性。這使得無法通過簡單的閱讀就知道引用的是什么屬性:局部屬性、全局屬性還是成員屬性?

例如,采用以下不完整的代碼段:

def foo(a):
 with a:
 print(x)

該代碼段假設(shè) "a" 必須有一個(gè)名為 "x" 的成員屬性。然而,Python 中并沒有告訴解釋器這一點(diǎn)。假設(shè) "a" 是整數(shù),會(huì)發(fā)生什么?如果有一個(gè)名為 "x" 的全局變量,它是否會(huì)在 with 塊中使用?如您所見,Python 的動(dòng)態(tài)特性使得這樣的選擇更加困難。

然而,Python 可以通過賦值輕松實(shí)現(xiàn) "with" 和類似語言特性(減少代碼量)的主要好處。代替:

function(args).mydict[index][index].a = 21
function(args).mydict[index][index].b = 42
function(args).mydict[index][index].c = 63

寫成這樣:

ref = function(args).mydict[index][index]
ref.a = 21
ref.b = 42
ref.c = 63

這也具有提高執(zhí)行速度的副作用,因?yàn)?Python 在運(yùn)行時(shí)解析名稱綁定,而第二個(gè)版本只需要執(zhí)行一次解析。

26. 為什么 if/while/def/class 語句需要冒號(hào)?

冒號(hào)主要用于增強(qiáng)可讀性(ABC 語言實(shí)驗(yàn)的結(jié)果之一)。考慮一下這個(gè):

if a == b
 print(a)

if a == b:
 print(a)

注意第二種方法稍微容易一些。請(qǐng)進(jìn)一步注意,在這個(gè) FAQ 解答的示例中,冒號(hào)是如何設(shè)置的;這是英語中的標(biāo)準(zhǔn)用法。

另一個(gè)次要原因是冒號(hào)使帶有語法突出顯示的編輯器更容易工作;他們可以尋找冒號(hào)來決定何時(shí)需要增加縮進(jìn),而不必對(duì)程序文本進(jìn)行更精細(xì)的解析。

27. 為什么 Python 在列表和元組的末尾允許使用逗號(hào)?

Python 允許您在列表,元組和字典的末尾添加一個(gè)尾隨逗號(hào):

[1, 2, 3,]
('a', 'b', 'c',)
d = {
 "A": [1, 5],
 "B": [6, 7], # last trailing comma is optional but good style
}

有幾個(gè)理由允許這樣做。

如果列表,元組或字典的字面值分布在多行中,則更容易添加更多元素,因?yàn)椴槐赜涀≡谏弦恍兄刑砑佣禾?hào)。這些行也可以重新排序,而不會(huì)產(chǎn)生語法錯(cuò)誤。

不小心省略逗號(hào)會(huì)導(dǎo)致難以診斷的錯(cuò)誤。例如:

x = [
 "fee",
 "fie"
 "foo",
 "fum"
]

這個(gè)列表看起來有四個(gè)元素,但實(shí)際上包含三個(gè) : "fee", "fiefoo" 和 "fum" ??偸羌由隙禾?hào)可以避免這個(gè)錯(cuò)誤的來源。

允許尾隨逗號(hào)也可以使編程代碼更容易生成。

到此,關(guān)于“Python設(shè)計(jì)方法有哪些”的學(xué)習(xí)就結(jié)束了,希望能夠解決大家的疑惑。理論與實(shí)踐的搭配能更好的幫助大家學(xué)習(xí),快去試試吧!若想繼續(xù)學(xué)習(xí)更多相關(guān)知識(shí),請(qǐng)繼續(xù)關(guān)注億速云網(wǎng)站,小編會(huì)繼續(xù)努力為大家?guī)砀鄬?shí)用的文章!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請(qǐng)聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI