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