新聞中心
編程常見問題
一般問題
python 有沒有提供帶有斷點(diǎn)、單步調(diào)試等功能的源碼級(jí)調(diào)試器?
有的。

成都創(chuàng)新互聯(lián)致力于網(wǎng)站制作、成都做網(wǎng)站,成都網(wǎng)站設(shè)計(jì),集團(tuán)網(wǎng)站建設(shè)等服務(wù)標(biāo)準(zhǔn)化,推過標(biāo)準(zhǔn)化降低中小企業(yè)的建站的成本,并持續(xù)提升建站的定制化服務(wù)水平進(jìn)行質(zhì)量交付,讓企業(yè)網(wǎng)站從市場(chǎng)競(jìng)爭(zhēng)中脫穎而出。 選擇成都創(chuàng)新互聯(lián),就選擇了安全、穩(wěn)定、美觀的網(wǎng)站建設(shè)服務(wù)!
以下介紹了一些 Python 的調(diào)試器,用內(nèi)置函數(shù) breakpoint() 即可切入這些調(diào)試器中。
pdb 模塊是一個(gè)簡(jiǎn)單但是夠用的控制臺(tái)模式 Python 調(diào)試器。 它是標(biāo)準(zhǔn) Python 庫的一部分,并且 已收錄于庫參考手冊(cè)。 你也可以通過使用 pdb 代碼作為樣例來編寫你自己的調(diào)試器。
The IDLE interactive development environment, which is part of the standard Python distribution (normally available as Tools/scripts/idle3), includes a graphical debugger.
PythonWin 是一種 Python IDE,其中包含了一個(gè)基于 pdb 的 GUI 調(diào)試器。PythonWin 的調(diào)試器會(huì)為斷點(diǎn)著色,并提供了相當(dāng)多的超酷特性,例如調(diào)試非 PythonWin 程序等。PythonWin 是 pywin32 項(xiàng)目的組成部分,也是 ActivePython 發(fā)行版的組成部分。
Eric is an IDE built on PyQt and the Scintilla editing component.
trepan3k 是一個(gè)類似 gdb 的調(diào)試器。
Visual Studio Code 是包含了調(diào)試工具的 IDE,并集成了版本控制軟件。
有許多商業(yè) Python IDE 都包含了圖形化調(diào)試器。包括:
Wing IDE
Komodo IDE
PyCharm
是否有能幫助尋找漏洞或執(zhí)行靜態(tài)分析的工具?
有的。
Pylint and Pyflakes do basic checking that will help you catch bugs sooner.
靜態(tài)類型檢查器,例如 Mypy 、 Pyre 和 Pytype 可以檢查Python源代碼中的類型提示。
如何由 Python 腳本創(chuàng)建能獨(dú)立運(yùn)行的二進(jìn)制程序?
如果只是想要一個(gè)獨(dú)立的程序,以便用戶不必預(yù)先安裝 Python 即可下載和運(yùn)行它,則不需要將 Python 編譯成 C 代碼。有許多工具可以檢測(cè)程序所需的模塊,并將這些模塊與 Python 二進(jìn)制程序捆綁在一起生成單個(gè)可執(zhí)行文件。
One is to use the freeze tool, which is included in the Python source tree as Tools/freeze. It converts Python byte code to C arrays; with a C compiler you can embed all your modules into a new program, which is then linked with the standard Python modules.
它的工作原理是遞歸掃描源代碼,獲取兩種格式的 import 語句,并在標(biāo)準(zhǔn) Python 路徑和源碼目錄(用于內(nèi)置模塊)檢索這些模塊。然后,把這些模塊的 Python 字節(jié)碼轉(zhuǎn)換為 C 代碼(可以利用 marshal 模塊轉(zhuǎn)換為代碼對(duì)象的數(shù)組初始化器),并創(chuàng)建一個(gè)定制的配置文件,該文件僅包含程序?qū)嶋H用到的內(nèi)置模塊。然后,編譯生成的 C 代碼并將其與 Python 解釋器的其余部分鏈接,形成一個(gè)自給自足的二進(jìn)制文件,其功能與 Python 腳本代碼完全相同。
下列包可以用于幫助創(chuàng)建控制臺(tái)和 GUI 的可執(zhí)行文件:
Nuitka (跨平臺(tái))
PyInstaller (Cross-platform)
PyOxidizer (跨平臺(tái))
cx_Freeze (跨平臺(tái))
py2app (僅限 macOS)
py2exe (Windows only)
是否有 Python 編碼標(biāo)準(zhǔn)或風(fēng)格指南?
有的。 標(biāo)準(zhǔn)庫模塊所要求的編碼風(fēng)格記錄于 PEP 8 之中。
語言核心內(nèi)容
變量明明有值,為什么還會(huì)出現(xiàn) UnboundLocalError?
It can be a surprise to get the UnboundLocalError in previously working code when it is modified by adding an assignment statement somewhere in the body of a function.
以下代碼:
>>> x = 10>>> def bar():... print(x)...>>> bar()10
正常工作,但是以下代碼
>>> x = 10>>> def foo():... print(x)... x += 1
results in an UnboundLocalError:
>>> foo()Traceback (most recent call last):...UnboundLocalError: local variable 'x' referenced before assignment
原因就是,當(dāng)對(duì)某作用域內(nèi)的變量進(jìn)行賦值時(shí),該變量將成為該作用域內(nèi)的局部變量,并覆蓋外部作用域中的同名變量。由于 foo 的最后一條語句為 x 分配了一個(gè)新值,編譯器會(huì)將其識(shí)別為局部變量。因此,前面的 print(x) 試圖輸出未初始化的局部變量,就會(huì)引發(fā)錯(cuò)誤。
在上面的示例中,可以將外部作用域的變量聲明為全局變量以便訪問:
>>> x = 10>>> def foobar():... global x... print(x)... x += 1...>>> foobar()10
與類和實(shí)例變量貌似但不一樣,其實(shí)以上是在修改外部作用域的變量值,為了提示這一點(diǎn),這里需要顯式聲明一下。
>>> print(x)11
你可以使用 nonlocal 關(guān)鍵字在嵌套作用域中執(zhí)行類似的操作:
>>> def foo():... x = 10... def bar():... nonlocal x... print(x)... x += 1... bar()... print(x)...>>> foo()1011
Python 的局部變量和全局變量有哪些規(guī)則?
函數(shù)內(nèi)部只作引用的 Python 變量隱式視為全局變量。如果在函數(shù)內(nèi)部任何位置為變量賦值,則除非明確聲明為全局變量,否則均將其視為局部變量。
起初盡管有點(diǎn)令人驚訝,不過考慮片刻即可釋然。一方面,已分配的變量要求加上 global 可以防止意外的副作用發(fā)生。另一方面,如果所有全局引用都要加上 global ,那處處都得用上 global 了。那么每次對(duì)內(nèi)置函數(shù)或?qū)肽K中的組件進(jìn)行引用時(shí),都得聲明為全局變量。這種雜亂會(huì)破壞 global 聲明用于警示副作用的有效性。
為什么在循環(huán)中定義的參數(shù)各異的 lambda 都返回相同的結(jié)果?
假設(shè)用 for 循環(huán)來定義幾個(gè)取值各異的 lambda(即便是普通函數(shù)也一樣):
>>> squares = []>>> for x in range(5):... squares.append(lambda: x**2)
以上會(huì)得到一個(gè)包含5個(gè) lambda 函數(shù)的列表,這些函數(shù)將計(jì)算 x**2。大家或許期望,調(diào)用這些函數(shù)會(huì)分別返回 0 、1 、 4 、 9 和 16。然而,真的試過就會(huì)發(fā)現(xiàn),他們都會(huì)返回 16 :
>>> squares[2]()16>>> squares[4]()16
這是因?yàn)?x 不是 lambda 函數(shù)的內(nèi)部變量,而是定義于外部作用域中的,并且 x 是在調(diào)用 lambda 時(shí)訪問的——而不是在定義時(shí)訪問。循環(huán)結(jié)束時(shí) x 的值是 4 ,所以此時(shí)所有的函數(shù)都將返回 4**2 ,即 16 。通過改變 x 的值并查看 lambda 的結(jié)果變化,也可以驗(yàn)證這一點(diǎn)。
>>> x = 8>>> squares[2]()64
為了避免發(fā)生上述情況,需要將值保存在 lambda 局部變量,以使其不依賴于全局 x 的值:
>>> squares = []>>> for x in range(5):... squares.append(lambda n=x: n**2)
以上 n=x 創(chuàng)建了一個(gè)新的 lambda 本地變量 n,并在定義 lambda 時(shí)計(jì)算其值,使其與循環(huán)當(dāng)前時(shí)點(diǎn)的 x 值相同。這意味著 n 的值在第 1 個(gè) lambda 中為 0 ,在第 2 個(gè) lambda 中為 1 ,在第 3 個(gè)中為 2,依此類推。因此現(xiàn)在每個(gè) lambda 都會(huì)返回正確結(jié)果:
>>> squares[2]()4>>> squares[4]()16
請(qǐng)注意,上述表現(xiàn)并不是 lambda 所特有的,常規(guī)的函數(shù)也同樣適用。
如何跨模塊共享全局變量?
在單個(gè)程序中跨模塊共享信息的規(guī)范方法是創(chuàng)建一個(gè)特殊模塊(通常稱為 config 或 cfg)。只需在應(yīng)用程序的所有模塊中導(dǎo)入該 config 模塊;然后該模塊就可當(dāng)作全局名稱使用了。因?yàn)槊總€(gè)模塊只有一個(gè)實(shí)例,所以對(duì)該模塊對(duì)象所做的任何更改將會(huì)在所有地方得以體現(xiàn)。 例如:
config.py:
x = 0 # Default value of the 'x' configuration setting
mod.py:
import configconfig.x = 1
main.py:
import configimport modprint(config.x)
Note that using a module is also the basis for implementing the singleton design pattern, for the same reason.
導(dǎo)入模塊的“最佳實(shí)踐”是什么?
通常請(qǐng)勿使用 from modulename import * 。因?yàn)檫@會(huì)擾亂 importer 的命名空間,且會(huì)造成未定義名稱更難以被 Linter 檢查出來。
請(qǐng)?jiān)诖a文件的首部就導(dǎo)入模塊。這樣代碼所需的模塊就一目了然了,也不用考慮模塊名是否在作用域內(nèi)的問題。每行導(dǎo)入一個(gè)模塊則增刪起來會(huì)比較容易,每行導(dǎo)入多個(gè)模塊則更節(jié)省屏幕空間。
按如下順序?qū)肽K就是一種好做法:
standard library modules — e.g. sys, os, argparse, re
third-party library modules (anything installed in Python’s site-packages directory) — e.g.
dateutil,requests,PIL.Imagelocally developed modules
為了避免循環(huán)導(dǎo)入引發(fā)的問題,有時(shí)需要將模塊導(dǎo)入語句移入函數(shù)或類的內(nèi)部。Gordon McMillan 的說法如下:
當(dāng)兩個(gè)模塊都采用 “import
” 的導(dǎo)入形式時(shí),循環(huán)導(dǎo)入是沒有問題的。但如果第 2 個(gè)模塊想從第 1 個(gè)模塊中取出一個(gè)名稱(”from module import name”)并且導(dǎo)入處于代碼的最頂層,那導(dǎo)入就會(huì)失敗。原因是第 1 個(gè)模塊中的名稱還不可用,這時(shí)第 1 個(gè)模塊正忙于導(dǎo)入第 2 個(gè)模塊呢。
如果只是在一個(gè)函數(shù)中用到第 2 個(gè)模塊,那這時(shí)將導(dǎo)入語句移入該函數(shù)內(nèi)部即可。當(dāng)調(diào)用到導(dǎo)入語句時(shí),第 1 個(gè)模塊將已經(jīng)完成初始化,第 2 個(gè)模塊就可以進(jìn)行導(dǎo)入了。
如果某些模塊是平臺(tái)相關(guān)的,可能還需要把導(dǎo)入語句移出最頂級(jí)代碼。這種情況下,甚至有可能無法導(dǎo)入文件首部的所有模塊。于是在對(duì)應(yīng)的平臺(tái)相關(guān)代碼中導(dǎo)入正確的模塊,就是一種不錯(cuò)的選擇。
只有為了避免循環(huán)導(dǎo)入問題,或有必要減少模塊初始化時(shí)間時(shí),才把導(dǎo)入語句移入類似函數(shù)定義內(nèi)部的局部作用域。如果根據(jù)程序的執(zhí)行方式,許多導(dǎo)入操作不是必需的,那么這種技術(shù)尤其有用。如果模塊僅在某個(gè)函數(shù)中用到,可能還要將導(dǎo)入操作移入該函數(shù)內(nèi)部。請(qǐng)注意,因?yàn)槟K有一次初始化過程,所以第一次加載模塊的代價(jià)可能會(huì)比較高,但多次加載幾乎沒有什么花費(fèi),代價(jià)只是進(jìn)行幾次字典檢索而已。即使模塊名超出了作用域,模塊在 sys.modules 中也是可用的。
為什么對(duì)象之間會(huì)共享默認(rèn)值?
新手程序員常常中招這類 Bug。請(qǐng)看以下函數(shù):
def foo(mydict={}): # Danger: shared reference to one dict for all calls... compute something ...mydict[key] = valuereturn mydict
第一次調(diào)用此函數(shù)時(shí), mydict 中只有一個(gè)數(shù)據(jù)項(xiàng)。第二次調(diào)用 mydict 則會(huì)包含兩個(gè)數(shù)據(jù)項(xiàng),因?yàn)?foo() 開始執(zhí)行時(shí), mydict 中已經(jīng)帶有一個(gè)數(shù)據(jù)項(xiàng)了。
大家往往希望,函數(shù)調(diào)用會(huì)為默認(rèn)值創(chuàng)建新的對(duì)象。但事實(shí)并非如此。默認(rèn)值只會(huì)在函數(shù)定義時(shí)創(chuàng)建一次。如果對(duì)象發(fā)生改變,就如上例中的字典那樣,則后續(xù)調(diào)用該函數(shù)時(shí)將會(huì)引用這個(gè)改動(dòng)的對(duì)象。
按照定義,不可變對(duì)象改動(dòng)起來是安全的,諸如數(shù)字、字符串、元組和 None 之類。而可變對(duì)象的改動(dòng)則可能引起困惑,例如字典、列表和類實(shí)例等。
因此,不把可變對(duì)象用作默認(rèn)值是一種良好的編程做法。而應(yīng)采用 None 作為默認(rèn)值,然后在函數(shù)中檢查參數(shù)是否為 None 并新建列表、字典或其他對(duì)象。例如,代碼不應(yīng)如下所示:
def foo(mydict={}):...
而應(yīng)這么寫:
def foo(mydict=None):if mydict is None:mydict = {} # create a new dict for local namespace
參數(shù)默認(rèn)值的特性有時(shí)會(huì)很有用處。 如果有個(gè)函數(shù)的計(jì)算過程會(huì)比較耗時(shí),有一種常見技巧是將每次函數(shù)調(diào)用的參數(shù)和結(jié)果緩存起來,并在同樣的值被再次請(qǐng)求時(shí)返回緩存的值。這種技巧被稱為“memoize”,實(shí)現(xiàn)代碼可如下所示:
# Callers can only provide two parameters and optionally pass _cache by keyworddef expensive(arg1, arg2, *, _cache={}):if (arg1, arg2) in _cache:return _cache[(arg1, arg2)]# Calculate the valueresult = ... expensive computation ..._cache[(arg1, arg2)] = result # Store result in the cachereturn result
也可以不用參數(shù)默認(rèn)值來實(shí)現(xiàn),而是采用全局的字典變量;這取決于個(gè)人偏好。
如何將可選參數(shù)或關(guān)鍵字參數(shù)從一個(gè)函數(shù)傳遞到另一個(gè)函數(shù)?
請(qǐng)利用函數(shù)參數(shù)列表中的標(biāo)識(shí)符 * 和 ** 歸集實(shí)參;結(jié)果會(huì)是元組形式的位置實(shí)參和字典形式的關(guān)鍵字實(shí)參。然后就可利用 * 和 ** 在調(diào)用其他函數(shù)時(shí)傳入這些實(shí)參:
def f(x, *args, **kwargs):...kwargs['width'] = '14.3c'...g(x, *args, **kwargs)
形參和實(shí)參之間有什么區(qū)別?
Parameters are defined by the names that appear in a function definition, whereas arguments are the values actually passed to a function when calling it. Parameters define what kind of arguments a function can accept. For example, given the function definition:
def func(foo, bar=None, **kwargs):pass
foo 、 bar 和 kwargs 是 func 的形參。 不過在調(diào)用 func 時(shí),例如:
func(42, bar=314, extra=somevar)
42 、 314 和 somevar 則是實(shí)參。
為什么修改列表 ‘y’ 也會(huì)更改列表 ‘x’?
如果代碼編寫如下:
>>> x = []>>> y = x>>> y.append(10)>>> y[10]>>> x[10]
或許大家很想知道,為什么在 y 中添加一個(gè)元素時(shí), x 也會(huì)改變。
產(chǎn)生這種結(jié)果有兩個(gè)因素:
變量只是指向?qū)ο蟮囊粋€(gè)名稱。執(zhí)行
y = x并不會(huì)創(chuàng)建列表的副本——而只是創(chuàng)建了一個(gè)新變量y,并指向x所指的同一對(duì)象。這就意味著只存在一個(gè)列表對(duì)象,x和y都是對(duì)它的引用。列表屬于 mutable 對(duì)象,這意味著它的內(nèi)容是可以修改的。
在調(diào)用 append() 之后,該可變對(duì)象的內(nèi)容由 [] 變?yōu)?[10]。由于 x 和 y 這兩個(gè)變量引用了同一對(duì)象,因此用其中任意一個(gè)名稱所訪問到的都是修改后的值 [10]。
如果把賦給 x 的對(duì)象換成一個(gè)不可變對(duì)象:
>>> x = 5 # ints are immutable>>> y = x>>> x = x + 1 # 5 can't be mutated, we are creating a new object here>>> x6>>> y5
可見這時(shí) x 和 y 就不再相等了。因?yàn)檎麛?shù)是 immutable 對(duì)象,在執(zhí)行 x = x + 1 時(shí),并不會(huì)修改整數(shù)對(duì)象 5,給它加上 1;而是創(chuàng)建了一個(gè)新的對(duì)象(整數(shù)對(duì)象 6 )并將其賦給 x (也就是改變了 x 所指向的對(duì)象)。在賦值完成后,就有了兩個(gè)對(duì)象(整數(shù)對(duì)象 6 和 5 )和分別指向他倆的兩個(gè)變量( x 現(xiàn)在指向 6 而 y 仍然指向 5 )。
Some operations (for example y.append(10) and y.sort()) mutate the object, whereas superficially similar operations (for example y = y + [10] and sorted(y)) create a new object. In general in Python (and in all cases in the standard library) a method that mutates an object will return None to help avoid getting the two types of operations confused. So if you mistakenly write y.sort() thinking it will give you a sorted copy of y, you’ll instead end up with None, which will likely cause your program to generate an easily diagnosed error.
不過還存在一類操作,用不同的類型執(zhí)行相同的操作有時(shí)會(huì)發(fā)生不同的行為:即增量賦值運(yùn)算符。例如,+= 會(huì)修改列表,但不會(huì)修改元組或整數(shù)(a_list += [1, 2, 3] 與 a_list.extend([1, 2, 3]) 同樣都會(huì)改變 a_list,而 some_tuple += (1, 2, 3) 和 some_int += 1 則會(huì)創(chuàng)建新的對(duì)象)。
換而言之:
對(duì)于一個(gè)可變對(duì)象( list 、 dict 、 set 等等),可以利用某些特定的操作進(jìn)行修改,所有引用它的變量都會(huì)反映出改動(dòng)情況。
對(duì)于一個(gè)不可變對(duì)象( str 、 int 、 tuple 等),所有引用它的變量都會(huì)給出相同的值,但所有改變其值的操作都將返回一個(gè)新的對(duì)象。
如要知道兩個(gè)變量是否指向同一個(gè)對(duì)象,可以利用 is 運(yùn)算符或內(nèi)置函數(shù) id()。
如何編寫帶有輸出參數(shù)的函數(shù)(按照引用調(diào)用)?
請(qǐng)記住,Python 中的實(shí)參是通過賦值傳遞的。由于賦值只是創(chuàng)建了對(duì)象的引用,所以調(diào)用方和被調(diào)用方的參數(shù)名都不存在別名,本質(zhì)上也就不存在按引用調(diào)用的方式。通過以下幾種方式,可以得到所需的效果。
返回一個(gè)元組:
>>> def func1(a, b):... a = 'new-value' # a and b are local names... b = b + 1 # assigned to new objects... return a, b # return new values...>>> x, y = 'old-value', 99>>> func1(x, y)('new-value', 100)
這差不多是最明晰的解決方案了。
使用全局變量。這不是線程安全的方案,不推薦使用。
傳遞一個(gè)可變(即可原地修改的) 對(duì)象:
>>> def func2(a):... a[0] = 'new-value' # 'a' references a mutable list... a[1] = a[1] + 1 # changes a shared object...>>> args = ['old-value', 99]>>> func2(args)>>> args['new-value', 100]
傳入一個(gè)接收可變對(duì)象的字典:
>>> def func3(args):... args['a'] = 'new-value' # args is a mutable dictionary... args['b'] = args['b'] + 1 # change it in-place...>>> args = {'a': 'old-value', 'b': 99}>>> func3(args)>>> args{'a': 'new-value', 'b': 100}
或者把值用類實(shí)例封裝起來:
>>> class Namespace:... def __init__(self, /, **args):... for key, value in args.items():... setattr(self, key, value)...>>> def func4(args):... args.a = 'new-value' # args is a mutable Namespace... args.b = args.b + 1 # change object in-place...>>> args = Namespace(a='old-value', b=99)>>> func4(args)>>> vars(args){'a': 'new-value', 'b': 100}
沒有什么理由要把問題搞得這么復(fù)雜。
最佳選擇就是返回一個(gè)包含多個(gè)結(jié)果值的元組。
如何在 Python 中創(chuàng)建高階函數(shù)?
有兩種選擇:嵌套作用域、可調(diào)用對(duì)象。假定需要定義 linear(a,b) ,其返回結(jié)果是一個(gè)計(jì)算出 a*x+b 的函數(shù) f(x)。 采用嵌套作用域的方案如下:
def linear(a, b):def result(x):return a * x + breturn result
或者可采用可調(diào)用對(duì)象:
class linear:def __init__(self, a, b):self.a, self.b = a, bdef __call__(self, x):return self.a * x + self.b
采用這兩種方案時(shí):
taxes = linear(0.3, 2)
都會(huì)得到一個(gè)可調(diào)用對(duì)象,可實(shí)現(xiàn) taxes(10e6) == 0.3 * 10e6 + 2 。
可調(diào)用對(duì)象的方案有個(gè)缺點(diǎn),就是速度稍慢且生成的代碼略長(zhǎng)。不過值得注意的是,同一組可調(diào)用對(duì)象能夠通過繼承來共享簽名(類聲明):
class exponential(linear):# __init__ inheriteddef __call__(self, x):return self.a * (x ** self.b)
對(duì)象可以為多個(gè)方法的運(yùn)行狀態(tài)進(jìn)行封裝:
class counter:value = 0def set(self, x):self.value = xdef up(self):self.value = self.value + 1def down(self):self.value = self.value - 1count = counter()inc, dec, reset = count.up, count.down, count.set
以上 inc() 、 dec() 和 reset() 的表現(xiàn),就如同共享了同一計(jì)數(shù)變量一樣。
如何復(fù)制 Python 對(duì)象?
一般情況下,用 copy.copy() 或 copy.deepcopy() 基本就可以了。并不是所有對(duì)象都支持復(fù)制,但多數(shù)是可以的。
某些對(duì)象可以用更簡(jiǎn)便的方法進(jìn)行復(fù)制。比如字典對(duì)象就提供了 copy() 方法:
newdict = olddict.copy()
序列可以用切片操作進(jìn)行復(fù)制:
new_l = l[:]
如何找到對(duì)象的方法或?qū)傩裕?/h3>
For an instance x of a user-defined class, dir(x) returns an alphabetized list of the names containing the instance attributes and methods and attributes defined by its class.
如何用代碼獲取對(duì)象的名稱?
一般而言這是無法實(shí)現(xiàn)的,因?yàn)閷?duì)象并不存在真正的名稱。賦值本質(zhì)上是把某個(gè)名稱綁定到某個(gè)值上;def 和 class 語句同樣如此,只是值換成了某個(gè)可調(diào)用對(duì)象。比如以下代碼:
>>> class A:... pass...>>> B = A>>> a = B()>>> b = a>>> print(b)<__main__.A object at 0x16D07CC>>>> print(a)<__main__.A object at 0x16D07CC>
Arguably the class has a name: even though it is bound to two names and invoked through the name B the created instance is still reported as an instance of class A. However, it is impossible to say whether the instance’s name is a or b, since both names are bound to the same value.
代碼一般沒有必要去“知曉”某個(gè)值的名稱。通常這種需求預(yù)示著還是改變方案為好,除非真的是要編寫內(nèi)審程序。
在 comp.lang.python 中,F(xiàn)redrik Lundh 在回答這樣的問題時(shí)曾經(jīng)給出過一個(gè)絕佳的類比:
這就像要知道家門口的那只貓的名字一樣:貓(對(duì)象)自己不會(huì)說出它的名字,它根本就不在乎自己叫什么——所以唯一方法就是問一遍你所有的鄰居(命名空間),這是不是他們家的貓(對(duì)象)……
……并且如果你發(fā)現(xiàn)它有很多名字或根本沒有名字,那也不必驚訝!
逗號(hào)運(yùn)算符的優(yōu)先級(jí)是什么?
逗號(hào)不是 Python 的運(yùn)算符。 請(qǐng)看以下例子:
>>> "a" in "b", "a"(False, 'a')
由于逗號(hào)不是運(yùn)算符,而只是表達(dá)式之間的分隔符,因此上述代碼就相當(dāng)于:
("a" in "b"), "a"
而不是:
"a" in ("b", "a")
對(duì)于各種賦值運(yùn)算符( = 、 += 等)來說同樣如此。他們并不是真正的運(yùn)算符,而只是賦值語句中的語法分隔符。
是否提供等價(jià)于 C 語言 “?:” 三目運(yùn)算符的東西?
有的。語法如下:
[on_true] if [expression] else [on_false]x, y = 50, 25small = x if x < y else y
在 Python 2.5 引入上述語法之前,通常的做法是使用邏輯運(yùn)算符:
[expression] and [on_true] or [on_false]
然而這種做法并不保險(xiǎn),因?yàn)楫?dāng) on_true 為布爾值“假”時(shí),結(jié)果將會(huì)出錯(cuò)。所以肯定還是采用 ... if ... else ... 形式為妙。
是否可以用 Python 編寫讓人眼暈的單行程序?
Yes. Usually this is done by nesting lambda within lambda. See the following three examples, slightly adapted from Ulf Bartelt:
from functools import reduce# Primes < 1000print(list(filter(None,map(lambda y:y*reduce(lambda x,y:x*y!=0,map(lambda x,y=y:y%x,range(2,int(pow(y,0.5)+1))),1),range(2,1000)))))# First 10 Fibonacci numbersprint(list(map(lambda x,f=lambda x,f:(f(x-1,f)+f(x-2,f)) if x>1 else 1:f(x,f), range(10))))# Mandelbrot setprint((lambda Ru,Ro,Iu,Io,IM,Sx,Sy:reduce(lambda x,y:x+'\n'+y,map(lambda y,Iu=Iu,Io=Io,Ru=Ru,Ro=Ro,Sy=Sy,L=lambda yc,Iu=Iu,Io=Io,Ru=Ru,Ro=Ro,i=IM,Sx=Sx,Sy=Sy:reduce(lambda x,y:x+y,map(lambda x,xc=Ru,yc=yc,Ru=Ru,Ro=Ro,i=i,Sx=Sx,F=lambda xc,yc,x,y,k,f=lambda xc,yc,x,y,k,f:(k<=0)or (x*x+y*y>=4.0) or 1+f(xc,yc,x*x-y*y+xc,2.0*x*y+yc,k-1,f):f(xc,yc,x,y,k,f):chr(64+F(Ru+x*(Ro-Ru)/Sx,yc,0,0,i)),range(Sx))):L(Iu+y*(Io-Iu)/Sy),range(Sy))))(-2.1, 0.7, -1.2, 1.2, 30, 80, 24))# \___ ___/ \___ ___/ | | |__ lines on screen# V V | |______ columns on screen# | | |__________ maximum of "iterations"# | |_________________ range on y axis# |____________________________ range on x axis
請(qǐng)不要在家里嘗試,騷年!
函數(shù)形參列表中的斜杠(/)是什么意思?
A slash in the argument list of a function denotes that the parameters prior to it are positional-only. Positional-only parameters are the ones without an externally usable name. Upon calling a function that accepts positional-only parameters, arguments are mapped to parameters based solely on their position. For example, divmod() is a function that accepts positional-only parameters. Its documentation looks like this:
>>> help(divmod)Help on built-in function divmod in module builtins:divmod(x, y, /)Return the tuple (x//y, x%y). Invariant: div*y + mod == x.
形參列表尾部的斜杠說明,兩個(gè)形參都是僅限位置形參。因此,用關(guān)鍵字參數(shù)調(diào)用 divmod() 將會(huì)引發(fā)錯(cuò)誤:
>>> divmod(x=3, y=4)Traceback (most recent call last):File "", line 1, in TypeError: divmod() takes no keyword arguments
數(shù)字和字符串
如何給出十六進(jìn)制和八進(jìn)制整數(shù)?
要給出八進(jìn)制數(shù),需在八進(jìn)制數(shù)值前面加上一個(gè)零和一個(gè)小寫或大寫字母 “o” 作為前綴。例如,要將變量 “a” 設(shè)為八進(jìn)制的 “10” (十進(jìn)制的 8),寫法如下:
>>> a = 0o10>>> a8
十六進(jìn)制數(shù)也很簡(jiǎn)單。只要在十六進(jìn)制數(shù)前面加上一個(gè)零和一個(gè)小寫或大寫的字母 “x”。十六進(jìn)制數(shù)中的字母可以為大寫或小寫。比如在 Python 解釋器中輸入:
>>> a = 0xa5>>> a165>>> b = 0XB2>>> b178
為什么 -22 // 10 會(huì)返回 -3 ?
這主要是為了讓 i % j 的正負(fù)與 j 一致,如果期望如此,且期望如下等式成立:
i == (i // j) * j + (i % j)
那么整除就必須返回向下取整的結(jié)果。C 語言同樣要求保持這種一致性,于是編譯器在截?cái)?i // j 的結(jié)果時(shí)需要讓 i % j 的正負(fù)與 i 一致。
對(duì)于 i % j 來說 j 為負(fù)值的應(yīng)用場(chǎng)景實(shí)際上是非常少的。 而 j 為正值的情況則非常多,并且實(shí)際上在所有情況下讓 i % j 的結(jié)果為 >= 0 會(huì)更有用處。 如果現(xiàn)在時(shí)間為 10 時(shí),那么 200 小時(shí)前應(yīng)是幾時(shí)? -190 % 12 == 2 是有用處的;-190 % 12 == -10 則是會(huì)導(dǎo)致意外的漏洞。
我如何獲得 int 字面屬性而不是 SyntaxError ?
Trying to lookup an int literal attribute in the normal manner gives a SyntaxError because the period is seen as a decimal point:
>>> 1.__class__File "", line 1 1.__class__^SyntaxError: invalid decimal literal
解決辦法是用空格或括號(hào)將字詞與句號(hào)分開。
>>> 1 .__class__>>> (1).__class__
如何將字符串轉(zhuǎn)換為數(shù)字?
對(duì)于整數(shù),可使用內(nèi)置的 int() 類型構(gòu)造器,例如 int('144') == 144。 類似地,可使用 float() 轉(zhuǎn)換為浮點(diǎn)數(shù),例如 float('144') == 144.0。
默認(rèn)情況下,這些操作會(huì)將數(shù)字按十進(jìn)制來解讀,因此 int('0144') == 144 為真值,而 int('0x144') 會(huì)引發(fā) ValueError。 int(string, base) 接受第二個(gè)可選參數(shù)指定轉(zhuǎn)換的基數(shù),例如 int( '0x144', 16) == 324。 如果指定基數(shù)為 0,則按 Python 規(guī)則解讀數(shù)字:前綴 ‘0o’ 表示八進(jìn)制,而 ‘0x’ 表示十六進(jìn)制。
如果只是想把字符串轉(zhuǎn)為數(shù)字,請(qǐng)不要使用內(nèi)置函數(shù) eval()。 eval() 的速度慢很多且存在安全風(fēng)險(xiǎn):別人可能會(huì)傳入帶有不良副作用的 Python 表達(dá)式。比如可能會(huì)傳入 __import__('os').system("rm -rf $HOME") ,這會(huì)把 home 目錄給刪了。
eval() 還有把數(shù)字解析為 Python 表達(dá)式的后果,因此如 eval('09') 將會(huì)導(dǎo)致語法錯(cuò)誤,因?yàn)?Python 不允許十進(jìn)制數(shù)帶有前導(dǎo) ‘0’(’0’ 除外)。
如何將數(shù)字轉(zhuǎn)換為字符串?
To convert, e.g., the number 144 to the string '144', use the built-in type constructor str(). If you want a hexadecimal or octal representation, use the built-in functions hex() or oct(). For fancy formatting, see the 格式字符串字面值 and 格式字符串語法 sections, e.g. "{:04d}".format(144) yields '0144' and "{:.3f}".format(1.0/3.0) yields '0.333'.
如何修改字符串?
無法修改,因?yàn)樽址遣豢勺儗?duì)象。 在大多數(shù)情況下,只要將各個(gè)部分組合起來構(gòu)造出一個(gè)新字符串即可。如果需要一個(gè)能原地修改 Unicode 數(shù)據(jù)的對(duì)象,可以試試 io.StringIO 對(duì)象或 array 模塊:
>>> import io>>> s = "Hello, world">>> sio = io.StringIO(s)>>> sio.getvalue()'Hello, world'>>> sio.seek(7)7>>> sio.write("there!")6>>> sio.getvalue()'Hello, there!'>>> import array>>> a = array.array('u', s)>>> print(a)array('u', 'Hello, world')>>> a[0] = 'y'>>> print(a)array('u', 'yello, world')>>> a.tounicode()'yello, world'
如何使用字符串調(diào)用函數(shù)/方法?
有多種技巧可供選擇。
最好的做法是采用一個(gè)字典,將字符串映射為函數(shù)。其主要優(yōu)勢(shì)就是字符串不必與函數(shù)名一樣。這也是用來模擬 case 結(jié)構(gòu)的主要技巧:
def a():passdef b():passdispatch = {'go':
當(dāng)前題目:創(chuàng)新互聯(lián)Python教程:編程常見問題
URL網(wǎng)址:http://www.dlmjj.cn/article/dpjsjhi.html


咨詢
建站咨詢
