python设计_设计和历史常见问题
Guido van Rossum 認(rèn)為使用縮進(jìn)進(jìn)行分組非常優(yōu)雅,并且大大提高了普通Python程序的清晰度。大多數(shù)人在一段時間后就學(xué)會并喜歡上這個功能。
由于沒有開始/結(jié)束括號,因此解析器感知的分組與人類讀者之間不會存在分歧。偶爾C程序員會遇到像這樣的代碼片段:
if (x <= y)
x++;
y--;
z++;
如果條件為真,則只執(zhí)行 x++ 語句,但縮進(jìn)會使你認(rèn)為情況并非如此。即使是經(jīng)驗(yàn)豐富的C程序員有時會長時間盯著它,想知道為什么即使 x > y , y 也在減少。
因?yàn)闆]有開始/結(jié)束括號,所以Python不太容易發(fā)生編碼式?jīng)_突。在C中,括號可以放到許多不同的位置。如果您習(xí)慣于閱讀和編寫使用一種風(fēng)格的代碼,那么在閱讀(或被要求編寫)另一種風(fēng)格時,您至少會感到有些不安。
許多編碼風(fēng)格將開始/結(jié)束括號單獨(dú)放在一行上。這使得程序相當(dāng)長,浪費(fèi)了寶貴的屏幕空間,使得更難以對程序進(jìn)行全面的了解。理想情況下,函數(shù)應(yīng)該適合一個屏幕(例如,20--30行)。 20行Python可以完成比20行C更多的工作。這不僅僅是由于缺少開始/結(jié)束括號 -- 缺少聲明和高級數(shù)據(jù)類型也是其中的原因 -- 但縮進(jìn)基于語法肯定有幫助。
用戶經(jīng)常對這樣的結(jié)果感到驚訝:
>>>1.2 - 1.0
0.19999999999999996
并且認(rèn)為這是 Python中的一個 bug。其實(shí)不是這樣。這與 Python 關(guān)系不大,而與底層平臺如何處理浮點(diǎn)數(shù)字關(guān)系更大。
CPython 中的 float 類型使用C語言的 double 類型進(jìn)行存儲。 float 對象的值是以固定的精度(通常為 53 位)存儲的二進(jìn)制浮點(diǎn)數(shù),由于 Python 使用 C 操作,而后者依賴于處理器中的硬件實(shí)現(xiàn)來執(zhí)行浮點(diǎn)運(yùn)算。 這意味著就浮點(diǎn)運(yùn)算而言,Python 的行為類似于許多流行的語言,包括 C 和 Java。
許多可以輕松地用十進(jìn)制表示的數(shù)字不能用二進(jìn)制浮點(diǎn)表示。例如,在輸入以下語句后:
>>>x = 1.2
為 x 存儲的值是與十進(jìn)制的值 1.2 (非常接近) 的近似值,但不完全等于它。 在典型的機(jī)器上,實(shí)際存儲的值是:
1.0011001100110011001100110011001100110011001100110011 (binary)
它對應(yīng)于十進(jìn)制數(shù)值:
1.1999999999999999555910790149937383830547332763671875 (decimal)
典型的 53 位精度為 Python 浮點(diǎn)數(shù)提供了 15-16 位小數(shù)的精度。
要獲得更完整的解釋,請參閱 Python 教程中的 浮點(diǎn)算術(shù) 一章。
有幾個優(yōu)點(diǎn)。
一個是性能:知道字符串是不可變的,意味著我們可以在創(chuàng)建時為它分配空間,并且存儲需求是固定不變的。這也是元組和列表之間區(qū)別的原因之一。
另一個優(yōu)點(diǎn)是,Python 中的字符串被視為與數(shù)字一樣“基本”。 任何動作都不會將值 8 更改為其他值,在 Python 中,任何動作都不會將字符串 "8" 更改為其他值。
這個想法借鑒了 Modula-3 語言。 出于多種原因它被證明是非常有用的。
首先,更明顯的顯示出,使用的是方法或?qū)嵗龑傩远皇蔷植孔兞俊?閱讀 self.x 或 self.meth() 可以清楚地表明,即使您不知道類的定義,也會使用實(shí)例變量或方法。在 C++ 中,可以通過缺少局部變量聲明來判斷(假設(shè)全局變量很少見或容易識別) —— 但是在 Python 中沒有局部變量聲明,所以必須查找類定義才能確定。 一些 C++ 和 Java 編碼標(biāo)準(zhǔn)要求實(shí)例屬性具有 m_ 前綴,因此這種顯式性在這些語言中仍然有用。
其次,這意味著如果要顯式引用或從特定類調(diào)用該方法,不需要特殊語法。 在 C++ 中,如果你想使用在派生類中重寫基類中的方法,你必須使用 :: 運(yùn)算符 -- 在 Python 中你可以編寫 baseclass.methodname(self, )。 這對于 __init__() 方法非常有用,特別是在派生類方法想要擴(kuò)展同名的基類方法,而必須以某種方式調(diào)用基類方法時。
最后,它解決了變量賦值的語法問題:為了 Python 中的局部變量(根據(jù)定義!)在函數(shù)體中賦值的那些變量(并且沒有明確聲明為全局)賦值,就必須以某種方式告訴解釋器一個賦值是為了分配一個實(shí)例變量而不是一個局部變量,它最好是通過語法實(shí)現(xiàn)的(出于效率原因)。 C++ 通過聲明來做到這一點(diǎn),但是 Python 沒有聲明,僅僅為了這個目的而引入它們會很可惜。 使用顯式的 self.var 很好地解決了這個問題。 類似地,對于使用實(shí)例變量,必須編寫 self.var 意味著對方法內(nèi)部的非限定名稱的引用不必搜索實(shí)例的目錄。 換句話說,局部變量和實(shí)例變量存在于兩個不同的命名空間中,您需要告訴 Python 使用哪個命名空間。
自 Python 3.8 開始,你能做到的!
賦值表達(dá)式使用海象運(yùn)算符 := 在表達(dá)式中為變量賦值:
while chunk := fp.read(200):
print(chunk)
請參閱 PEP 572 了解詳情。
正如Guido所說:
(a) 對于某些操作,前綴表示法比后綴更容易閱讀 -- 前綴(和中綴!)運(yùn)算在數(shù)學(xué)中有著悠久的傳統(tǒng),就像在視覺上幫助數(shù)學(xué)家思考問題的記法。比較一下我們將 x*(a+b) 這樣的公式改寫為 x*a+x*b 的容易程度,以及使用原始OO符號做相同事情的笨拙程度。
(b) 當(dāng)讀到寫有l(wèi)en(X)的代碼時,就知道它要求的是某件東西的長度。這告訴我們兩件事:結(jié)果是一個整數(shù),參數(shù)是某種容器。相反,當(dāng)閱讀x.len()時,必須已經(jīng)知道x是某種實(shí)現(xiàn)接口的容器,或者是從具有標(biāo)準(zhǔn)len()的類繼承的容器。當(dāng)沒有實(shí)現(xiàn)映射的類有g(shù)et()或key()方法,或者不是文件的類有write()方法時,我們偶爾會感到困惑。
從Python 1.6開始,字符串變得更像其他標(biāo)準(zhǔn)類型,當(dāng)添加方法時,這些方法提供的功能與始終使用String模塊的函數(shù)時提供的功能相同。這些新方法中的大多數(shù)已被廣泛接受,但似乎讓一些程序員感到不舒服的一種方法是:
", ".join(['1', '2', '4', '8', '16'])
結(jié)果如下:
"1, 2, 4, 8, 16"
反對這種用法有兩個常見的論點(diǎn)。
第一條是這樣的:“使用字符串文本(String Constant)的方法看起來真的很難看”,答案是也許吧,但是字符串文本只是一個固定值。如果在綁定到字符串的名稱上允許使用這些方法,則沒有邏輯上的理由使其在文字上不可用。
第二個異議通常是這樣的:“我實(shí)際上是在告訴序列使用字符串常量將其成員連接在一起”。遺憾的是并非如此。出于某種原因,把 split() 作為一個字符串方法似乎要容易得多,因?yàn)樵谶@種情況下,很容易看到:
"1, 2, 4, 8, 16".split(", ")
是對字符串文本的指令,用于返回由給定分隔符分隔的子字符串(或在默認(rèn)情況下,返回任意空格)。
join() 是字符串方法,因?yàn)樵谑褂迷摲椒〞r,您告訴分隔符字符串去迭代一個字符串序列,并在相鄰元素之間插入自身。此方法的參數(shù)可以是任何遵循序列規(guī)則的對象,包括您自己定義的任何新的類。對于字節(jié)和字節(jié)數(shù)組對象也有類似的方法。
如果沒有引發(fā)異常,則try/except塊的效率極高。實(shí)際上捕獲異常是昂貴的。在2.0之前的Python版本中,通常使用這個習(xí)慣用法:
try:
value = mydict[key]
except KeyError:
mydict[key] = getvalue(key)
value = mydict[key]
只有當(dāng)你期望dict在任何時候都有key時,這才有意義。如果不是這樣的話,你就是應(yīng)該這樣編碼:
if key in mydict:
value = mydict[key]
else:
value = mydict[key] = getvalue(key)
對于這種特定的情況,您還可以使用 value = dict.setdefault(key, getvalue(key)),但前提是調(diào)用 getvalue() 足夠便宜,因?yàn)樵谒星闆r下都會對其進(jìn)行評估。
你可以通過一系列 if... elif... elif... else.輕松完成這項(xiàng)工作。對于switch語句語法已經(jīng)有了一些建議,但尚未就是否以及如何進(jìn)行范圍測試達(dá)成共識。有關(guān)完整的詳細(xì)信息和當(dāng)前狀態(tài),請參閱 PEP 275 。
對于需要從大量可能性中進(jìn)行選擇的情況,可以創(chuàng)建一個字典,將case 值映射到要調(diào)用的函數(shù)。例如:
def function_1(...):
...
functions = {'a': function_1,
'b': function_2,
'c': self.method_1, ...}
func = functions[value]
func()
對于對象調(diào)用方法,可以通過使用 getattr() 內(nèi)置檢索具有特定名稱的方法來進(jìn)一步簡化:
def visit_a(self, ...):
...
...
def dispatch(self, value):
method_name = 'visit_' + str(value)
method = getattr(self, method_name)
method()
建議對方法名使用前綴,例如本例中的 visit_ 。如果沒有這樣的前綴,如果值來自不受信任的源,攻擊者將能夠調(diào)用對象上的任何方法。
答案1: 不幸的是,解釋器為每個Python堆棧幀推送至少一個C堆棧幀。此外,擴(kuò)展可以隨時回調(diào)Python。因此,一個完整的線程實(shí)現(xiàn)需要對C的線程支持。
答案2: 幸運(yùn)的是, Stackless Python 有一個完全重新設(shè)計(jì)的解釋器循環(huán),可以避免C堆棧。
Python的 lambda表達(dá)式不能包含語句,因?yàn)镻ython的語法框架不能處理嵌套在表達(dá)式內(nèi)部的語句。然而,在Python中,這并不是一個嚴(yán)重的問題。與其他語言中添加功能的lambda表單不同,Python的 lambdas只是一種速記符號,如果您懶得定義函數(shù)的話。
函數(shù)已經(jīng)是Python中的第一類對象,可以在本地范圍內(nèi)聲明。 因此,使用lambda而不是本地定義的函數(shù)的唯一優(yōu)點(diǎn)是你不需要為函數(shù)創(chuàng)建一個名稱 -- 這只是一個分配了函數(shù)對象(與lambda表達(dá)式生成的對象類型完全相同)的局部變量!
Cython 將帶有可選注釋的Python修改版本編譯到C擴(kuò)展中。 Nuitka 是一個將Python編譯成 C++ 代碼的新興編譯器,旨在支持完整的Python語言。要編譯成Java,可以考慮 VOC 。
Python 內(nèi)存管理的細(xì)節(jié)取決于實(shí)現(xiàn)。 Python 的標(biāo)準(zhǔn)實(shí)現(xiàn) CPython 使用引用計(jì)數(shù)來檢測不可訪問的對象,并使用另一種機(jī)制來收集引用循環(huán),定期執(zhí)行循環(huán)檢測算法來查找不可訪問的循環(huán)并刪除所涉及的對象。 gc 模塊提供了執(zhí)行垃圾回收、獲取調(diào)試統(tǒng)計(jì)信息和優(yōu)化收集器參數(shù)的函數(shù)。
但是,其他實(shí)現(xiàn)(如 Jython 或 PyPy ),)可以依賴不同的機(jī)制,如完全的垃圾回收器?。如果你的Python代碼依賴于引用計(jì)數(shù)實(shí)現(xiàn)的行為,則這種差異可能會導(dǎo)致一些微妙的移植問題。
在一些Python實(shí)現(xiàn)中,以下代碼(在CPython中工作的很好)可能會耗盡文件描述符:
for file in very_long_list_of_files:
f = open(file)
c = f.read(1)
實(shí)際上,使用CPython的引用計(jì)數(shù)和析構(gòu)函數(shù)方案, 每個新賦值的 f 都會關(guān)閉前一個文件。然而,對于傳統(tǒng)的GC,這些文件對象只能以不同的時間間隔(可能很長的時間間隔)被收集(和關(guān)閉)。
如果要編寫可用于任何python實(shí)現(xiàn)的代碼,則應(yīng)顯式關(guān)閉該文件或使用 with 語句;無論內(nèi)存管理方案如何,這都有效:
for file in very_long_list_of_files:
with open(file) as f:
c = f.read(1)
首先,這不是C標(biāo)準(zhǔn)特性,因此不能移植。(是的,我們知道Boehm GC庫。它包含了 大多數(shù) 常見平臺(但不是所有平臺)的匯編代碼,盡管它基本上是透明的,但也不是完全透明的; 要讓Python使用它,需要使用補(bǔ)丁。)
當(dāng)Python嵌入到其他應(yīng)用程序中時,傳統(tǒng)的GC也成為一個問題。在獨(dú)立的Python中,可以用GC庫提供的版本替換標(biāo)準(zhǔn)的malloc()和free(),嵌入Python的應(yīng)用程序可能希望用 它自己 替代malloc()和free(),而可能不需要Python的?,F(xiàn)在,CPython可以正確地實(shí)現(xiàn)malloc()和free()。
當(dāng)Python退出時,從全局命名空間或Python模塊引用的對象并不總是被釋放。 如果存在循環(huán)引用,則可能發(fā)生這種情況 C庫分配的某些內(nèi)存也是不可能釋放的(例如像Purify這樣的工具會抱怨這些內(nèi)容)。 但是,Python在退出時清理內(nèi)存并嘗試銷毀每個對象。
如果要強(qiáng)制 Python 在釋放時刪除某些內(nèi)容,請使用 atexit 模塊運(yùn)行一個函數(shù),強(qiáng)制刪除這些內(nèi)容。
雖然列表和元組在許多方面是相似的,但它們的使用方式通常是完全不同的??梢哉J(rèn)為元組類似于Pascal記錄或C結(jié)構(gòu);它們是相關(guān)數(shù)據(jù)的小集合,可以是不同類型的數(shù)據(jù),可以作為一個組進(jìn)行操作。例如,笛卡爾坐標(biāo)適當(dāng)?shù)乇硎緸閮蓚€或三個數(shù)字的元組。
另一方面,列表更像其他語言中的數(shù)組。它們傾向于持有不同數(shù)量的對象,所有對象都具有相同的類型,并且逐個操作。例如, os.listdir('.') 返回表示當(dāng)前目錄中的文件的字符串列表。如果向目錄中添加了一兩個文件,對此輸出進(jìn)行操作的函數(shù)通常不會中斷。
元組是不可變的,這意味著一旦創(chuàng)建了元組,就不能用新值替換它的任何元素。列表是可變的,這意味著您始終可以更改列表的元素。只有不變元素可以用作字典的key,因此只能將元組和非列表用作key。
CPython的列表實(shí)際上是可變長度的數(shù)組,而不是lisp風(fēng)格的鏈表。該實(shí)現(xiàn)使用對其他對象的引用的連續(xù)數(shù)組,并在列表頭結(jié)構(gòu)中保留指向該數(shù)組和數(shù)組長度的指針。
這使得索引列表 a[i] 的操作成本與列表的大小或索引的值無關(guān)。
當(dāng)添加或插入項(xiàng)時,將調(diào)整引用數(shù)組的大小。并采用了一些巧妙的方法來提高重復(fù)添加項(xiàng)的性能; 當(dāng)數(shù)組必須增長時,會分配一些額外的空間,以便在接下來的幾次中不需要實(shí)際調(diào)整大小。
CPython的字典實(shí)現(xiàn)為可調(diào)整大小的哈希表。與B-樹相比,這在大多數(shù)情況下為查找(目前最常見的操作)提供了更好的性能,并且實(shí)現(xiàn)更簡單。
字典的工作方式是使用 hash() 內(nèi)置函數(shù)計(jì)算字典中存儲的每個鍵的hash代碼。hash代碼根據(jù)鍵和每個進(jìn)程的種子而變化很大;例如,"Python" 的hash值為-539294296,而"python"(一個按位不同的字符串)的hash值為1142331976。然后,hash代碼用于計(jì)算內(nèi)部數(shù)組中將存儲該值的位置。假設(shè)您存儲的鍵都具有不同的hash值,這意味著字典需要恒定的時間 -- O(1),用Big-O表示法 -- 來檢索一個鍵。
字典的哈希表實(shí)現(xiàn)使用從鍵值計(jì)算的哈希值來查找鍵。如果鍵是可變對象,則其值可能會發(fā)生變化,因此其哈希值也會發(fā)生變化。但是,由于無論誰更改鍵對象都無法判斷它是否被用作字典鍵值,因此無法在字典中修改條目。然后,當(dāng)你嘗試在字典中查找相同的對象時,將無法找到它,因?yàn)槠涔V挡煌?。如果你嘗試查找舊值,也不會找到它,因?yàn)樵谠摴1碇姓业降膶ο蟮闹禃兴煌?/p>
如果你想要一個用列表索引的字典,只需先將列表轉(zhuǎn)換為元組;用函數(shù) tuple(L) 創(chuàng)建一個元組,其條目與列表 L 相同。 元組是不可變的,因此可以用作字典鍵。
已經(jīng)提出的一些不可接受的解決方案:
哈希按其地址(對象ID)列出。這不起作用,因?yàn)槿绻銟?gòu)造一個具有相同值的新列表,它將無法找到;例如:
mydict = {[1, 2]: '12'}
print(mydict[[1, 2]])
會引發(fā)一個 KeyError 異常,因?yàn)榈诙兄惺褂玫?[1, 2] 的 id 與第一行中的 id 不同。換句話說,應(yīng)該使用 == 來比較字典鍵,而不是使用 is 。
使用列表作為鍵時進(jìn)行復(fù)制。這沒有用的,因?yàn)樽鳛榭勺儗ο蟮牧斜砜梢园瑢ψ陨淼囊?#xff0c;然后復(fù)制代碼將進(jìn)入無限循環(huán)。
允許列表作為鍵,但告訴用戶不要修改它們。當(dāng)你意外忘記或修改列表時,這將產(chǎn)生程序中的一類難以跟蹤的錯誤。它還使一個重要的字典不變量無效: d.keys() 中的每個值都可用作字典的鍵。
將列表用作字典鍵后,應(yīng)標(biāo)記為其只讀。問題是,它不僅僅是可以改變其值的頂級對象;你可以使用包含列表作為鍵的元組。將任何內(nèi)容作為鍵關(guān)聯(lián)到字典中都需要將從那里可到達(dá)的所有對象標(biāo)記為只讀 —— 并且自引用對象可能會導(dǎo)致無限循環(huán)。
如果需要,可以使用以下方法來解決這個問題,但使用它需要你自擔(dān)風(fēng)險:你可以將一個可變結(jié)構(gòu)包裝在一個類實(shí)例中,該實(shí)例同時具有 __eq__() 和 __hash__() 方法。然后,你必須確保駐留在字典(或其他基于 hash 的結(jié)構(gòu))中的所有此類包裝器對象的哈希值在對象位于字典(或其他結(jié)構(gòu))中時保持固定。:
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__() ),無論對象是否在字典中。 如果你不能滿足這些限制,字典和其他基于 hash 的結(jié)構(gòu)將會出錯。
對于 ListWrapper ,只要包裝器對象在字典中,包裝列表就不能更改以避免異常。除非你準(zhǔn)備好認(rèn)真考慮需求以及不正確地滿足這些需求的后果,否則不要這樣做。請留意。
在性能很重要的情況下,僅僅為了排序而復(fù)制一份列表將是一種浪費(fèi)。因此, list.sort() 對列表進(jìn)行了適當(dāng)?shù)呐判颉榱颂嵝涯@一事實(shí),它不會返回已排序的列表。這樣,當(dāng)您需要排序的副本,但也需要保留未排序的版本時,就不會意外地覆蓋列表。
如果要返回新列表,請使用內(nèi)置 sorted() 函數(shù)。此函數(shù)從提供的可迭代列表中創(chuàng)建新列表,對其進(jìn)行排序并返回。例如,下面是如何迭代遍歷字典并按keys排序:
for key in sorted(mydict):
... # do whatever with mydict[key]...
由C++和Java等語言提供的模塊接口規(guī)范描述了模塊的方法和函數(shù)的原型。許多人認(rèn)為接口規(guī)范的編譯時強(qiáng)制執(zhí)行有助于構(gòu)建大型程序。
Python 2.6添加了一個 abc 模塊,允許定義抽象基類 (ABCs)。然后可以使用 isinstance() 和 issubclass() 來檢查實(shí)例或類是否實(shí)現(xiàn)了特定的ABC。 collections.abc 模塊定義了一組有用的ABCs 例如 Iterable , Container , 和 MutableMapping
對于 Python,接口規(guī)范的許多好處可以通過組件的適當(dāng)測試規(guī)程來獲得。
一個好的模塊測試套件既可以提供回歸測試,也可以作為模塊接口規(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í)上,它可能會更好,因?yàn)榻涌谝?guī)范不能測試程序的某些屬性。例如, append() 方法將向一些內(nèi)部列表的末尾添加新元素;接口規(guī)范不能測試您的 append() 實(shí)現(xiàn)是否能夠正確執(zhí)行此操作,但是在測試套件中檢查這個屬性是很簡單的。
編寫測試套件非常有用,您可能希望設(shè)計(jì)代碼時著眼于使其易于測試。一種日益流行的技術(shù)是面向測試的開發(fā),它要求在編寫任何實(shí)際代碼之前,首先編寫測試套件的各個部分。當(dāng)然,Python允許您草率行事,根本不編寫測試用例。
可以使用異常捕獲來提供 “goto結(jié)構(gòu)” ,甚至可以跨函數(shù)調(diào)用工作的 。許多人認(rèn)為異常捕獲可以方便地模擬C,Fortran和其他語言的 "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)慎使用。
更準(zhǔn)確地說,它們不能以奇數(shù)個反斜杠結(jié)束:結(jié)尾處的不成對反斜杠會轉(zhuǎn)義結(jié)束引號字符,留下未結(jié)束的字符串。
原始字符串的設(shè)計(jì)是為了方便想要執(zhí)行自己的反斜杠轉(zhuǎn)義處理的處理器(主要是正則表達(dá)式引擎)創(chuàng)建輸入。此類處理器將不匹配的尾隨反斜杠視為錯誤,因此原始字符串不允許這樣做。反過來,允許通過使用引號字符轉(zhuǎn)義反斜杠轉(zhuǎn)義字符串。當(dāng)r-string用于它們的預(yù)期目的時,這些規(guī)則工作的很好。
如果您正在嘗試構(gòu)建Windows路徑名,請注意所有Windows系統(tǒng)調(diào)用都使用正斜杠:
f = open("/mydir/file.txt") # works fine!
如果您正在嘗試為DOS命令構(gòu)建路徑名,請嘗試以下示例
dir = r"\this\is\my\dos\dir" "\\"
dir = r"\this\is\my\dos\dir\ "[:-1]
dir = "\\this\\is\\my\\dos\\dir\\"
Python有一個 '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) -- 編譯器 總是 在編譯時知道每個變量的作用域。
Python使用動態(tài)類型。事先不可能知道在運(yùn)行時引用哪個屬性??梢詣討B(tài)地在對象中添加或刪除成員屬性。這使得無法通過簡單的閱讀就知道引用的是什么屬性:局部屬性、全局屬性還是成員屬性?
例如,采用以下不完整的代碼段:
def foo(a):
with a:
print(x)
該代碼段假設(shè) "a" 必須有一個名為 "x" 的成員屬性。然而,Python中并沒有告訴解釋器這一點(diǎn)。假設(shè) "a" 是整數(shù),會發(fā)生什么?如果有一個名為 "x" 的全局變量,它是否會在with塊中使用?如您所見,Python的動態(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)镻ython在運(yùn)行時解析名稱綁定,而第二個版本只需要執(zhí)行一次解析。
冒號主要用于增強(qiáng)可讀性(ABC語言實(shí)驗(yàn)的結(jié)果之一)。考慮一下這個:
if a == b
print(a)
與
if a == b:
print(a)
注意第二種方法稍微容易一些。請進(jìn)一步注意,在這個FAQ解答的示例中,冒號是如何設(shè)置的;這是英語中的標(biāo)準(zhǔn)用法。
另一個次要原因是冒號使帶有語法突出顯示的編輯器更容易工作;他們可以尋找冒號來決定何時需要增加縮進(jìn),而不必對程序文本進(jìn)行更精細(xì)的解析。
Python 允許您在列表,元組和字典的末尾添加一個尾隨逗號:
[1, 2, 3,]
('a', 'b', 'c',)
d = {
"A": [1, 5],
"B": [6, 7], # last trailing comma is optional but good style
}
有幾個理由允許這樣做。
如果列表,元組或字典的字面值分布在多行中,則更容易添加更多元素,因?yàn)椴槐赜涀≡谏弦恍兄刑砑佣禾?。這些行也可以重新排序,而不會產(chǎn)生語法錯誤。
不小心省略逗號會導(dǎo)致難以診斷的錯誤。例如:
x = [
"fee",
"fie"
"foo",
"fum"
]
這個列表看起來有四個元素,但實(shí)際上包含三個 : "fee", "fiefoo" 和 "fum" 。總是加上逗號可以避免這個錯誤的來源。
允許尾隨逗號也可以使編程代碼更容易生成。
總結(jié)
以上是生活随笔為你收集整理的python设计_设计和历史常见问题的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: typec四线焊接图_实物图+电气图纸讲
- 下一篇: zigbee绑定 使用_遇见-果加智能锁