[Python]函數(shù)與函數(shù)編程

1. 函數(shù)

使用def語(yǔ)句可定義函數(shù):

創(chuàng)新互聯(lián)堅(jiān)持“要么做到,要么別承諾”的工作理念,服務(wù)領(lǐng)域包括:成都做網(wǎng)站、成都網(wǎng)站設(shè)計(jì)、企業(yè)官網(wǎng)、英文網(wǎng)站、手機(jī)端網(wǎng)站、網(wǎng)站推廣等服務(wù),滿足客戶于互聯(lián)網(wǎng)時(shí)代的新余網(wǎng)站設(shè)計(jì)、移動(dòng)媒體設(shè)計(jì)的需求,幫助企業(yè)找到有效的互聯(lián)網(wǎng)解決方案。努力成為您成熟可靠的網(wǎng)絡(luò)建設(shè)合作伙伴!

def add(x, y):
    return x + y

函數(shù)體就是在調(diào)用函數(shù)時(shí)所執(zhí)行的一系列語(yǔ)句。調(diào)用函數(shù)的方法是在函數(shù)名稱后面加上參數(shù)。參數(shù)的順序必須與函數(shù)定義匹配,否則會(huì)引發(fā)TypeError異常。可以為函數(shù)的參數(shù)設(shè)置默認(rèn)值,例如:

def split(line, delimiter=','):
    statements

如果給最后一個(gè)參數(shù)名加上星號(hào)"*",函數(shù)就可以接受任意數(shù)量的參數(shù):

def fprintf(file, fmt, *args):
    file.write(fmt % args)
fprintf(out, "%d %s %f", 42, "hello world", 3.45)

在這個(gè)例子中,所有余下的參數(shù)都作為一個(gè)元組放入args變量。要把元組args當(dāng)作參數(shù)傳遞給函數(shù),可以在函數(shù)調(diào)用中使用*args語(yǔ)法。例如:

def printf(fmt, *args):
    fprintf(sys.stdout, fmt, *args)

提供函數(shù)參數(shù)還有一種方式,即顯示地命名每個(gè)參數(shù)并為其指定一個(gè)值,這稱為關(guān)鍵字參數(shù),例如:

def foo(w, x, y, z):
    statements
foo(x=3, y=22, w='hello', z=[1, 2])

使用關(guān)鍵字參數(shù)時(shí),參數(shù)的順序無(wú)關(guān)緊要。但除非提供了默認(rèn)值,否則必須顯式地命名所有必需的函數(shù)參數(shù)。位置參數(shù)和關(guān)鍵字參數(shù)可以同時(shí)使用,前提是所有位置參數(shù)必須先出現(xiàn),給所有非可選參數(shù)提供值,例如:

foo('hello', 3, z=[1, 2], y=22)

如果函數(shù)定義的最后一個(gè)參數(shù)以"**"開(kāi)頭,可以把所有額外的關(guān)鍵字參數(shù)都放入一個(gè)字典中,并把這個(gè)字典傳遞給參數(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ù)和可變長(zhǎng)度參數(shù)列表可以一起使用,只要"**"參數(shù)出現(xiàn)在最后即可,例如:

def spam(*args, **kwargs):
    statements

?

2. 參數(shù)傳遞與返回值

調(diào)用函數(shù)時(shí),函數(shù)參數(shù)僅僅是引用傳入對(duì)象的名稱。參數(shù)傳遞的基本語(yǔ)義和其他編程語(yǔ)言中已知的方式不完全相同,如“按值傳遞”和“按引用傳遞”。比如傳遞不可變的值,參數(shù)看起來(lái)實(shí)際是按值傳遞的,如果傳遞的是可變對(duì)象(如列表或字典)給函數(shù),然后再修改此可變對(duì)象,這些改動(dòng)將反映在原始對(duì)象中。例如:

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語(yǔ)句從函數(shù)返回一個(gè)值。如果沒(méi)有指定任何值或者省略return語(yǔ)句,就會(huì)返回None對(duì)象。如果返回值有多個(gè),可以把它們放在一個(gè)元組中,例如:

def factor(a):
    d = 2
    while (d <= (a / 2)):
        if ((a / d) * d == a):
            return ((a / d), d)
        d = d + 1
    return (a, 1)

?

3. 作用域規(guī)則

每次執(zhí)行一個(gè)函數(shù)時(shí),就會(huì)創(chuàng)建新的局部命名空間。該命名空間代表一個(gè)局部環(huán)境,其中包含函數(shù)參數(shù)的名稱和在函數(shù)體內(nèi)賦值的變量名稱。解析這些名稱時(shí),解釋器將首先搜索局部命名空間。如果沒(méi)有找到匹配的名稱,它就會(huì)搜索全局命名空間。如果在全局命名空間中也找不到匹配值,最終會(huì)檢查內(nèi)置命名空間。如果仍然找不到,就會(huì)引發(fā)NameError異常。
命名空間的特性之一是在函數(shù)中對(duì)全局變量的操作,例如:

a = 42
def foo():
    a = 13
foo() # a仍然是42

執(zhí)行這段代碼時(shí),盡量在函數(shù)foo中修改了變量a的值,但最終a仍然是42.在函數(shù)中對(duì)變量進(jìn)行賦值時(shí),這些變量始終綁定到該函數(shù)的局部命名空間中,因此函數(shù)體中的變量a引用的是一個(gè)包含值13的全新對(duì)象,而不是外部的變量。使用global語(yǔ)句可以改變這種行為,例如:

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ù)中的變量,即解析名稱時(shí)首先檢查局部作用域,而后由內(nèi)向外一層層檢查外部嵌套函數(shù)定義的作用域。如果找不到匹配,最后將搜索全局命名空間和內(nèi)置命名空間??梢允褂胣onlocal語(yǔ)句綁定外部變量,例如:

def countdown(start):
    n = start
    def display():
        print('T-minus %d' % n)
    def decrement():
        nonlocal n
        n -= 1
    while n > 0:
        display()
        decrement()

nonlocal聲明不會(huì)把名稱綁定到任意函數(shù)中定義的局部變量,而是搜索當(dāng)前調(diào)用棧中的下一層函數(shù)定義,即動(dòng)態(tài)作用域。例如:

i = 0
def foo():
    i = i + 1 # UnboundLocalError異常

盡管有一個(gè)全局變量i,但它不會(huì)給局部變量i提供值。函數(shù)定義時(shí)就確定了變量是局部的還是全局的,而且在函數(shù)中不能突然改變它們的作用域。

?

4. 函數(shù)對(duì)象與閉包

函數(shù)在Python中是第一類對(duì)象。即可以把它們當(dāng)作參數(shù)傳遞給其他函數(shù),放在數(shù)據(jù)結(jié)構(gòu)中,以及作為函數(shù)的返回結(jié)果。例如:

def callf(func):
    return func()

把函數(shù)當(dāng)作數(shù)據(jù)處理時(shí),它將顯式地?cái)y帶與定義該函數(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中也定義了一個(gè)變量x,變際調(diào)用的是與helloworld()函數(shù)相同的環(huán)境中定義的值。將組成函數(shù)的語(yǔ)句和這些語(yǔ)句的執(zhí)行環(huán)境打包在一起時(shí),得到的對(duì)象稱為閉包。事實(shí)上所有函數(shù)都擁有一個(gè)指向了定義該函數(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

?

5. 裝飾器

裝飾器是一個(gè)函數(shù),其主要用途是包裝另一個(gè)函數(shù)或類。這種包裝的首要目的是透明地修改或增強(qiáng)被包裝對(duì)象的行為。表示裝飾器的語(yǔ)法是特殊符號(hào)"@",例如:

@trace
def square(x):
    return x * x

上面的代碼可以簡(jiǎn)化為:

def square(x):
    return x * x
square = trace(square)

現(xiàn)在考慮trace的實(shí)現(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)建了寫(xiě)有一些調(diào)試輸出的包裝器函數(shù),然后調(diào)用了原始函數(shù)對(duì)象。因此如果調(diào)用square()函數(shù),看到的將是包裝器中write()方法的輸出。
使用裝飾器時(shí),它們必須出現(xiàn)在函數(shù)或類定義之前的單獨(dú)行上??梢酝瑫r(shí)使用多個(gè)裝飾器,例如:

@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ù),裝飾器的語(yǔ)義如下所示:

def handle_button(msg):
    ...
temp = eventhandler('BUTTON')
handle_button = temp(handle_button)

對(duì)于類裝飾器,應(yīng)該讓裝飾器函數(shù)始終返回類對(duì)象作為結(jié)果。需要使用原始類定義的代碼可能要直接引用類成員。
?

6. 生成器與yield

函數(shù)使用yield關(guān)鍵字可以定義生成器對(duì)象。生成器是一個(gè)函數(shù),它生成一個(gè)值的序列,以便在迭代中使用,例如:

def countdown(n):
    while n > 0:
        yield n
        n -=1
    return

如果調(diào)用該函數(shù),其中的代碼不會(huì)開(kāi)始執(zhí)行,它會(huì)返回一個(gè)生成器對(duì)象,該對(duì)象在__next__()被調(diào)用,例如:

c = countdown(10)
c.__next__()

調(diào)用__next__()時(shí),生成器函數(shù)將不斷執(zhí)行語(yǔ)句,直到遇到y(tǒng)ield語(yǔ)句為止。通常不會(huì)在生成器上直接調(diào)用__next__()方法,而是在for語(yǔ)句、sum()或一些使用序列的其他操作中使用,例如:

for n in countdown(10):
    statements
a = sum(countdown(10))

生成器函數(shù)完成的標(biāo)志是返回或引發(fā)StopIteration異常,這標(biāo)志著迭代的結(jié)束。如果生成器沒(méi)有全部完成,并且不再使用,可以調(diào)用close()方法,雖然通常情況下可以不必調(diào)用,例如:

c = countdown(10)
c.__next__()
c.close()
c.__next__() # 拋出異常

在生成器函數(shù)內(nèi)部,在yield語(yǔ)句上出現(xiàn)GeneratorExit異常時(shí)就會(huì)調(diào)用close()方法??梢赃x擇獲取這個(gè)異常,例如:

def countdown(n):
    try:
        while n > 0:
            yield n
            n -= 1
    except GeneratorExit:
        print("Only made it to %d" % n)

?

7. 協(xié)程與yield表達(dá)式

在函數(shù)內(nèi),yield語(yǔ)句還可以用作出現(xiàn)在賦值運(yùn)算符右邊的表達(dá)式,例如:

def receiver():
    while True:
        n = (yield)
        print("Got %s" % n)

以這種方式使用yield語(yǔ)句的函數(shù)稱為協(xié)程,它的執(zhí)行是為了響應(yīng)發(fā)送給它的值。它的行為也類似于生成器,例如:

r = receiver()
r.__next__()
r.send(1)
r.send(2)

在協(xié)程中需要首先調(diào)用__next__()這件事很容易被忘記,可以用一個(gè)自動(dòng)完成該步驟的裝飾器來(lái)包裝協(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é)程的運(yùn)行一般是無(wú)限期的,除非它被顯式關(guān)閉或者自己退出。使用close()可以關(guān)閉輸入值的流,例如:

r.close()
r.send() # 拋出異常

關(guān)閉后如果繼續(xù)給協(xié)程發(fā)送值,就會(huì)引發(fā)StopIteration異常,close()操作將在協(xié)程內(nèi)部引發(fā)GeneratorExit異常。
?

8. 列表包含

函數(shù)的常用操作是將函數(shù)應(yīng)用給一個(gè)列表的所有項(xiàng),并使用結(jié)果創(chuàng)建一個(gè)新列表。這種操作很常見(jiàn),因此出現(xiàn)了叫做列表推導(dǎo)的運(yùn)算符,例如:

nums = [1, 2, 3, 4, 5]
squares = [n * n for n in nums]

列表推導(dǎo)的一般語(yǔ)法如下:

[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]

?

9. 生成器表達(dá)式

生成器表達(dá)式是一個(gè)對(duì)象,它執(zhí)行的計(jì)算與列表包含相同,但會(huì)迭代地生成結(jié)果,語(yǔ)法與列表包含相同,除了用圓括號(hào)代替方括號(hào),如下:

(expression for item1 in iterable1 if condition1
                                        for item2 in iterable2 if condition2
                                        ...
                                        for itemN in iterableN if conditionN)

生成器表達(dá)式實(shí)際上不創(chuàng)建列表或者立即對(duì)圓括號(hào)內(nèi)的表達(dá)式求值,它創(chuàng)建一個(gè)通過(guò)迭代并按照需要生成值的生成器對(duì)象,例如:

a  = [1, 2, 3, 4]
b = (10 * i for i in a)
print(b.__next__())
print(b.__next__())

使用列表推導(dǎo)時(shí),Python實(shí)際上創(chuàng)建了包含結(jié)果數(shù)據(jù)的列表。而使用生成器表達(dá)式時(shí),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)

生成器表達(dá)式不會(huì)創(chuàng)建序列形式的對(duì)象,不能對(duì)它進(jìn)行索引。但是,使用內(nèi)置的list()函數(shù)可以將生成器表達(dá)式轉(zhuǎn)換為列表,例如:

clist = list(comments)

?

10. lambda運(yùn)算符

使用lambda語(yǔ)句可以創(chuàng)建表達(dá)式形式的匿名函數(shù):

lambda args: expression

args是以逗號(hào)分隔的參數(shù)列表,而expression是用到這些參數(shù)的表達(dá)式,例如:

a = lambda x, y: x + y
r = a(2, 3)

使用lambda語(yǔ)句定義的代碼必須是合法的表達(dá)式。lambda語(yǔ)句中不能出現(xiàn)多條語(yǔ)句和其他非表達(dá)式語(yǔ)句,比如for或while。
?

11. 文檔字符串

通常,函數(shù)的第一條語(yǔ)句會(huì)使用文檔字符串,用于描述函數(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ù)提供交互式幫助。如果需要使用裝飾器,可能會(huì)破壞與文檔字符串相關(guān)的幫助功能,例如:

def wrap(func):
    call(*args, **kwargs):
        return func(*args, **kwargs)
    return call
@wrap
def factorial(n):
    """Computes n factorial."""

如果查目的地以上函數(shù)的幫助,可能會(huì)看到一個(gè)相當(dāng)奇怪的內(nèi)容,解決方法是編寫(xiě)可以傳遞函數(shù)名稱和文檔字符串的裝飾器函數(shù),例如:

def wrap(func):
    call(*args, **kwargs):
        return func(*args, **kwargs)
    call.__doc__ =  func.__doc__
    call.__name__ = func.__name__
    return call

因?yàn)檫@是一個(gè)常見(jiàn)問(wèn)題,所以functools模塊提供了函數(shù)wraps,用于自動(dòng)復(fù)制這些屬性,例如:

from functools import wraps
def wrap(func):
    @wrap(func)
    call(*args, **kwargs):
        return func(*args, **kwargs)
    return call

?

12. 函數(shù)屬性

可以給函數(shù)添加任意屬性,例如:

def foo():
    statements
foo.secure = 1
foo.private = 1

函數(shù)屬性保存在函數(shù)的__dict__屬性中,__dic__屬性是一個(gè)字典。和文檔字符串一樣,也要注意混合使用函數(shù)屬性和裝飾器的問(wèn)題。如果使用裝飾器包裝函數(shù),實(shí)際上是由裝飾器函數(shù)而非原始函數(shù)來(lái)訪問(wèn)屬性。
?

13. eval()、exec()和compile()函數(shù)

eval(str [, globals [, locals]])函數(shù)執(zhí)行一個(gè)表達(dá)式字符串并返回結(jié)果,例如:

a = eval('3 * math.sin(3.5 + x) + 7.2')

相似地,exec(str [, globals [, locals]])函數(shù)執(zhí)行一個(gè)包含任意Python代碼的字符串。例如:

a = [3, 5, 10, 13]
exec("for i in a: print(i)")

這兩個(gè)函數(shù)都會(huì)在調(diào)用者的命名空間中執(zhí)行。eval()和exec()函數(shù)可以接受一個(gè)或兩個(gè)可選的映射對(duì)象,分別用作代碼執(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表示一條語(yǔ)句,exec代表一組語(yǔ)句,而eval代表一個(gè)表達(dá)式。例如:

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)

文章題目:[Python]函數(shù)與函數(shù)編程
瀏覽路徑:http://muchs.cn/article6/gcedig.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供手機(jī)網(wǎng)站建設(shè)、響應(yīng)式網(wǎng)站商城網(wǎng)站、App設(shè)計(jì)、外貿(mào)建站軟件開(kāi)發(fā)

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)

成都做網(wǎng)站