您好,登錄后才能下訂單哦!
使用def語句可定義函數(shù):
def add(x, y):
return x + y
函數(shù)體就是在調(diào)用函數(shù)時所執(zhí)行的一系列語句。調(diào)用函數(shù)的方法是在函數(shù)名稱后面加上參數(shù)。參數(shù)的順序必須與函數(shù)定義匹配,否則會引發(fā)TypeError異常??梢詾楹瘮?shù)的參數(shù)設(shè)置默認值,例如:
def split(line, delimiter=','):
statements
如果給最后一個參數(shù)名加上星號"*",函數(shù)就可以接受任意數(shù)量的參數(shù):
def fprintf(file, fmt, *args):
file.write(fmt % args)
fprintf(out, "%d %s %f", 42, "hello world", 3.45)
在這個例子中,所有余下的參數(shù)都作為一個元組放入args變量。要把元組args當作參數(shù)傳遞給函數(shù),可以在函數(shù)調(diào)用中使用*args語法。例如:
def printf(fmt, *args):
fprintf(sys.stdout, fmt, *args)
提供函數(shù)參數(shù)還有一種方式,即顯示地命名每個參數(shù)并為其指定一個值,這稱為關(guān)鍵字參數(shù),例如:
def foo(w, x, y, z):
statements
foo(x=3, y=22, w='hello', z=[1, 2])
使用關(guān)鍵字參數(shù)時,參數(shù)的順序無關(guān)緊要。但除非提供了默認值,否則必須顯式地命名所有必需的函數(shù)參數(shù)。位置參數(shù)和關(guān)鍵字參數(shù)可以同時使用,前提是所有位置參數(shù)必須先出現(xiàn),給所有非可選參數(shù)提供值,例如:
foo('hello', 3, z=[1, 2], y=22)
如果函數(shù)定義的最后一個參數(shù)以"**"開頭,可以把所有額外的關(guān)鍵字參數(shù)都放入一個字典中,并把這個字典傳遞給參數(shù)。例如:
def make_table(data, **params):
fgcolor = params.pop("fgcolor", "black")
bgcolor = params.pop("bgcolor", "white")
width = params.pop("width", None)
if params:
raise TypeError("Unsupported configuration options %s" % list(params))
make_table(items, fgcolor="black", bgcolor="white", border=1, border, cellpoadding=10, width=400)
關(guān)鍵字參數(shù)和可變長度參數(shù)列表可以一起使用,只要"**"參數(shù)出現(xiàn)在最后即可,例如:
def spam(*args, **kwargs):
statements
?
調(diào)用函數(shù)時,函數(shù)參數(shù)僅僅是引用傳入對象的名稱。參數(shù)傳遞的基本語義和其他編程語言中已知的方式不完全相同,如“按值傳遞”和“按引用傳遞”。比如傳遞不可變的值,參數(shù)看起來實際是按值傳遞的,如果傳遞的是可變對象(如列表或字典)給函數(shù),然后再修改此可變對象,這些改動將反映在原始對象中。例如:
a = [1, 2, 3, 4, 5]
def square(items):
for i, x in enumerate(items):
items[i] = x * x
square(a) # a = [1, 4, 9, 16, 25]
return語句從函數(shù)返回一個值。如果沒有指定任何值或者省略return語句,就會返回None對象。如果返回值有多個,可以把它們放在一個元組中,例如:
def factor(a):
d = 2
while (d <= (a / 2)):
if ((a / d) * d == a):
return ((a / d), d)
d = d + 1
return (a, 1)
?
每次執(zhí)行一個函數(shù)時,就會創(chuàng)建新的局部命名空間。該命名空間代表一個局部環(huán)境,其中包含函數(shù)參數(shù)的名稱和在函數(shù)體內(nèi)賦值的變量名稱。解析這些名稱時,解釋器將首先搜索局部命名空間。如果沒有找到匹配的名稱,它就會搜索全局命名空間。如果在全局命名空間中也找不到匹配值,最終會檢查內(nèi)置命名空間。如果仍然找不到,就會引發(fā)NameError異常。
命名空間的特性之一是在函數(shù)中對全局變量的操作,例如:
a = 42
def foo():
a = 13
foo() # a仍然是42
執(zhí)行這段代碼時,盡量在函數(shù)foo中修改了變量a的值,但最終a仍然是42.在函數(shù)中對變量進行賦值時,這些變量始終綁定到該函數(shù)的局部命名空間中,因此函數(shù)體中的變量a引用的是一個包含值13的全新對象,而不是外部的變量。使用global語句可以改變這種行為,例如:
a = 42
def foo():
global a
a = 13
foo() # a的值已變13
Python支持嵌套的函數(shù)定義,例如:
def countdown(start):
n = start
def display():
print('T-minus %d' % n)
while n > 0:
display()
n -= 1
使用靜態(tài)作用域綁定嵌套函數(shù)中的變量,即解析名稱時首先檢查局部作用域,而后由內(nèi)向外一層層檢查外部嵌套函數(shù)定義的作用域。如果找不到匹配,最后將搜索全局命名空間和內(nèi)置命名空間??梢允褂胣onlocal語句綁定外部變量,例如:
def countdown(start):
n = start
def display():
print('T-minus %d' % n)
def decrement():
nonlocal n
n -= 1
while n > 0:
display()
decrement()
nonlocal聲明不會把名稱綁定到任意函數(shù)中定義的局部變量,而是搜索當前調(diào)用棧中的下一層函數(shù)定義,即動態(tài)作用域。例如:
i = 0
def foo():
i = i + 1 # UnboundLocalError異常
盡管有一個全局變量i,但它不會給局部變量i提供值。函數(shù)定義時就確定了變量是局部的還是全局的,而且在函數(shù)中不能突然改變它們的作用域。
?
函數(shù)在Python中是第一類對象。即可以把它們當作參數(shù)傳遞給其他函數(shù),放在數(shù)據(jù)結(jié)構(gòu)中,以及作為函數(shù)的返回結(jié)果。例如:
def callf(func):
return func()
把函數(shù)當作數(shù)據(jù)處理時,它將顯式地攜帶與定義該函數(shù)的周圍環(huán)境相關(guān)的信息。這將影響到函數(shù)中自由變量的綁定方式。例如:
# foo.py
x = 42
def callf(func):
return func()
# main.py
import foo
x = 37
def helloworld():
reutrn "x is %d" % x
foo.callf(helloworld) # x is 37
在上例中,即使foo.py中也定義了一個變量x,變際調(diào)用的是與helloworld()函數(shù)相同的環(huán)境中定義的值。將組成函數(shù)的語句和這些語句的執(zhí)行環(huán)境打包在一起時,得到的對象稱為閉包。事實上所有函數(shù)都擁有一個指向了定義該函數(shù)的全局命名空間的__globals__屬性。例如:
def page(url):
def get():
return urlopen(url).read()
return get
python = page("http://www.python.org")
jython = page("http://www.jython.org")
pydata = python() # 獲取http://www.python.org
jydata = jython() # 獲取http://www.jython.org
?
裝飾器是一個函數(shù),其主要用途是包裝另一個函數(shù)或類。這種包裝的首要目的是透明地修改或增強被包裝對象的行為。表示裝飾器的語法是特殊符號"@",例如:
@trace
def square(x):
return x * x
上面的代碼可以簡化為:
def square(x):
return x * x
square = trace(square)
現(xiàn)在考慮trace的實現(xiàn):
enable_tracing = True
if enable_tracing:
debug_log = open("debug.log", "w")
def trace(func):
if enable_tracing:
def callf(*args, **kwargs):
debug_log.write("Calling %s: %s, %s\n" % (func.__name__, args, kwargs))
r = func(*args, **kwargs)
debug_log.write("%s returned %s\n" % (func.__name__, r))
return r
return callf
else:
return func
這段代碼中,trace()創(chuàng)建了寫有一些調(diào)試輸出的包裝器函數(shù),然后調(diào)用了原始函數(shù)對象。因此如果調(diào)用square()函數(shù),看到的將是包裝器中write()方法的輸出。
使用裝飾器時,它們必須出現(xiàn)在函數(shù)或類定義之前的單獨行上。可以同時使用多個裝飾器,例如:
@foo
@bar
@spam
def grok(x):
pass\
grok = foo(bar(spam(grok)))
裝飾器也可以接受參數(shù),例如:
@eventhandler('BUTTON')
def handle_button(msg):
...
@eventhandler('RESET')
def handle_reset(msg):
...
如果提供參數(shù),裝飾器的語義如下所示:
def handle_button(msg):
...
temp = eventhandler('BUTTON')
handle_button = temp(handle_button)
對于類裝飾器,應(yīng)該讓裝飾器函數(shù)始終返回類對象作為結(jié)果。需要使用原始類定義的代碼可能要直接引用類成員。
?
函數(shù)使用yield關(guān)鍵字可以定義生成器對象。生成器是一個函數(shù),它生成一個值的序列,以便在迭代中使用,例如:
def countdown(n):
while n > 0:
yield n
n -=1
return
如果調(diào)用該函數(shù),其中的代碼不會開始執(zhí)行,它會返回一個生成器對象,該對象在__next__()被調(diào)用,例如:
c = countdown(10)
c.__next__()
調(diào)用__next__()時,生成器函數(shù)將不斷執(zhí)行語句,直到遇到y(tǒng)ield語句為止。通常不會在生成器上直接調(diào)用__next__()方法,而是在for語句、sum()或一些使用序列的其他操作中使用,例如:
for n in countdown(10):
statements
a = sum(countdown(10))
生成器函數(shù)完成的標志是返回或引發(fā)StopIteration異常,這標志著迭代的結(jié)束。如果生成器沒有全部完成,并且不再使用,可以調(diào)用close()方法,雖然通常情況下可以不必調(diào)用,例如:
c = countdown(10)
c.__next__()
c.close()
c.__next__() # 拋出異常
在生成器函數(shù)內(nèi)部,在yield語句上出現(xiàn)GeneratorExit異常時就會調(diào)用close()方法??梢赃x擇獲取這個異常,例如:
def countdown(n):
try:
while n > 0:
yield n
n -= 1
except GeneratorExit:
print("Only made it to %d" % n)
?
在函數(shù)內(nèi),yield語句還可以用作出現(xiàn)在賦值運算符右邊的表達式,例如:
def receiver():
while True:
n = (yield)
print("Got %s" % n)
以這種方式使用yield語句的函數(shù)稱為協(xié)程,它的執(zhí)行是為了響應(yīng)發(fā)送給它的值。它的行為也類似于生成器,例如:
r = receiver()
r.__next__()
r.send(1)
r.send(2)
在協(xié)程中需要首先調(diào)用__next__()這件事很容易被忘記,可以用一個自動完成該步驟的裝飾器來包裝協(xié)程,例如:
def coroutine(func):
def start(*args, **kwargs):
g = func(*args, **kwargs)
g.next()
return g
return start
@coroutine
def receiver():
while True:
n = (yield)
print("Got %s" % n)
r = receiver()
r.send("Hello World")
協(xié)程的運行一般是無限期的,除非它被顯式關(guān)閉或者自己退出。使用close()可以關(guān)閉輸入值的流,例如:
r.close()
r.send() # 拋出異常
關(guān)閉后如果繼續(xù)給協(xié)程發(fā)送值,就會引發(fā)StopIteration異常,close()操作將在協(xié)程內(nèi)部引發(fā)GeneratorExit異常。
?
函數(shù)的常用操作是將函數(shù)應(yīng)用給一個列表的所有項,并使用結(jié)果創(chuàng)建一個新列表。這種操作很常見,因此出現(xiàn)了叫做列表推導(dǎo)的運算符,例如:
nums = [1, 2, 3, 4, 5]
squares = [n * n for n in nums]
列表推導(dǎo)的一般語法如下:
[expression for item1 in iterable1 if condition1
for item2 in iterable2 if condition2
...
for itemN in iterableN if conditionN]
下面給出一些例子:
a = [-3, 5, 2, -10, 7, 8]
b = 'abc'
c = [2 * s for s in a] # c = [-6, 10, 4, -20, 14, 16]
d = [s for s in a if s >= 0] # d = [5, 2, 7, 8]
e= [(x, y) for x in a
for y in b
if x > 0]
# e = [(5, 'a'), (5, 'b'), (5, 'c'),
(2, 'a'), (2, 'b'), (2, 'c'),
(7, 'a'), (7, 'b'), (7, 'c'),
(8, 'a'), (8, 'b'), (8, 'c')]
f = [(1, 2), (3, 4), (5, 6)]
g = [math.sqrt(x * x + y * y) for x, y in f] # g = [2.23606797749979, 5.0, 7.810249675906654]
?
生成器表達式是一個對象,它執(zhí)行的計算與列表包含相同,但會迭代地生成結(jié)果,語法與列表包含相同,除了用圓括號代替方括號,如下:
(expression for item1 in iterable1 if condition1
for item2 in iterable2 if condition2
...
for itemN in iterableN if conditionN)
生成器表達式實際上不創(chuàng)建列表或者立即對圓括號內(nèi)的表達式求值,它創(chuàng)建一個通過迭代并按照需要生成值的生成器對象,例如:
a = [1, 2, 3, 4]
b = (10 * i for i in a)
print(b.__next__())
print(b.__next__())
使用列表推導(dǎo)時,Python實際上創(chuàng)建了包含結(jié)果數(shù)據(jù)的列表。而使用生成器表達式時,Python創(chuàng)建的是只知道如何按照需要生成數(shù)據(jù)的生成器。在某些應(yīng)用中,可能影響性能和內(nèi)存使用,例如:
f = open("data.txt")
lines = (t.strip() for t in f)
comments = (t for t in lines if t[0] == '#')
for c in comments:
print(c)
生成器表達式不會創(chuàng)建序列形式的對象,不能對它進行索引。但是,使用內(nèi)置的list()函數(shù)可以將生成器表達式轉(zhuǎn)換為列表,例如:
clist = list(comments)
?
使用lambda語句可以創(chuàng)建表達式形式的匿名函數(shù):
lambda args: expression
args是以逗號分隔的參數(shù)列表,而expression是用到這些參數(shù)的表達式,例如:
a = lambda x, y: x + y
r = a(2, 3)
使用lambda語句定義的代碼必須是合法的表達式。lambda語句中不能出現(xiàn)多條語句和其他非表達式語句,比如for或while。
?
通常,函數(shù)的第一條語句會使用文檔字符串,用于描述函數(shù)的用途,例如:
def factorial(n):
"""Computes n factorial. For examples:
>>> factorial(6)
120
"""
if n <= 1: return 1
else: return n* factorial(n-1)
文檔字符串保存在函數(shù)的__doc__屬性中,IDE通常使用該函數(shù)提供交互式幫助。如果需要使用裝飾器,可能會破壞與文檔字符串相關(guān)的幫助功能,例如:
def wrap(func):
call(*args, **kwargs):
return func(*args, **kwargs)
return call
@wrap
def factorial(n):
"""Computes n factorial."""
如果查目的地以上函數(shù)的幫助,可能會看到一個相當奇怪的內(nèi)容,解決方法是編寫可以傳遞函數(shù)名稱和文檔字符串的裝飾器函數(shù),例如:
def wrap(func):
call(*args, **kwargs):
return func(*args, **kwargs)
call.__doc__ = func.__doc__
call.__name__ = func.__name__
return call
因為這是一個常見問題,所以functools模塊提供了函數(shù)wraps,用于自動復(fù)制這些屬性,例如:
from functools import wraps
def wrap(func):
@wrap(func)
call(*args, **kwargs):
return func(*args, **kwargs)
return call
?
可以給函數(shù)添加任意屬性,例如:
def foo():
statements
foo.secure = 1
foo.private = 1
函數(shù)屬性保存在函數(shù)的__dict__屬性中,__dic__屬性是一個字典。和文檔字符串一樣,也要注意混合使用函數(shù)屬性和裝飾器的問題。如果使用裝飾器包裝函數(shù),實際上是由裝飾器函數(shù)而非原始函數(shù)來訪問屬性。
?
eval(str [, globals [, locals]])函數(shù)執(zhí)行一個表達式字符串并返回結(jié)果,例如:
a = eval('3 * math.sin(3.5 + x) + 7.2')
相似地,exec(str [, globals [, locals]])函數(shù)執(zhí)行一個包含任意Python代碼的字符串。例如:
a = [3, 5, 10, 13]
exec("for i in a: print(i)")
這兩個函數(shù)都會在調(diào)用者的命名空間中執(zhí)行。eval()和exec()函數(shù)可以接受一個或兩個可選的映射對象,分別用作代碼執(zhí)行的全局和局部命名空間,例如:
globals = {'x': 7, 'y': 10, 'birds': ['Parrot', 'Swallow', 'Albatross']}
locals = {}
a = eval("3 * x + 4 * y", globals, locals)
exec("fro b in birds: print(b)", globals, locals)
compile(str, filename, kind)函數(shù)將字符串編譯為字節(jié)碼,其中str是包含要編譯代碼的字符串,而filename是定義該字符串的文件,kind參數(shù)指定了要編譯代碼的類型。single表示一條語句,exec代表一組語句,而eval代表一個表達式。例如:
s = "for i inrange(0, 10): print(i)"
c = compile(s, '', 'exec')
exec(c)
s2 = "3 * x + 4 * y"
c2 = compile(s2, '', 'eval')
result = eval(c2)
免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。