一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

腳本之家,腳本語言編程技術及教程分享平臺!
分類導航

Python|VBS|Ruby|Lua|perl|VBA|Golang|PowerShell|Erlang|autoit|Dos|bat|

服務器之家 - 腳本之家 - Python - Python 函數裝飾器應用教程

Python 函數裝飾器應用教程

2022-03-11 10:05無風聽海 Python

函數裝飾器是Python提供的一種增強函數功能的標記函數,本文將帶大家深入學習一下Python 函數裝飾器,感興趣的同學跟隨小編一起學習吧

一、什么是函數裝飾器

1.函數裝飾器是Python提供的一種增強函數功能的標記函數;

2.裝飾器是可調用的函數對象,其參數是另一個函數(被裝飾的函數);

我們可以使用修飾器來封裝某個函數,從而讓程序在執行這個函數之前與執行完這個函數之后,分別運行某些代碼。這意味著,調用者傳給函數的參數值、函數返回給調用者的值,以及函數拋出的異常,都可以由修飾器訪問并修改。這是個很有用的機制,能夠確保用戶以正確的方式使用函數,也能夠用來調試程序或實現函數注冊功能,此外還有許多用途。

二、函數裝飾器的執行時機

函數裝飾器在被裝飾函數編譯解析之后直接執行,裝飾器是按照從上到下執行的;

函數裝飾器內部定義的返回函數依附在裝飾器的執行環境中;

函數裝飾器每次執行都會生成新的返回函數;

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import sys
 
def dec1(m):
    print(f'{sys._getframe().f_code.co_name} is execute, arg {m.__name__}')
    def newm1():
        print(f'{sys._getframe().f_code.co_name}')
 
    return newm1;
 
@dec1
def m1():
    print(f'{sys._getframe().f_code.co_name}')
 
@dec1
def m11():
    print(f'{sys._getframe().f_code.co_name}')
 
if __name__ == '__main__':
    print(m1)
    print(m11)
    print(f'm1==m11:{m1==m11}')
    
# dec1 is execute, arg m1
# dec1 is execute, arg m11
# <function dec1.<locals>.newm1 at 0x7fdfa97d9160>
# <function dec1.<locals>.newm1 at 0x7fdfa97d91f0>
# m1==m11:False

三、變量作用域

Python中將變量聲明和賦值操作合一,很容易導致函數局部變量覆蓋函數外的變量

?
1
2
3
4
5
6
7
b=6
def f():
    print(b)
 
f()
 
# 6
?
1
2
3
4
5
6
7
8
b=6
def f():
    print(b)
    b = 9
 
f()
 
# UnboundLocalError: local variable 'b' referenced before assignment

通過生成的字節碼可以看到兩者對變量b的處理的差異,前者直接LOAD_GLOBAL,后者是LOAD_FAST,但是給b賦值卻在print之后導致報錯;

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from dis import dis
 
b=6
def f():
    print(b)
    # b = 9
 
dis(f)
 
 # 5           0 LOAD_GLOBAL              0 (print)
 #              2 LOAD_GLOBAL              1 (b)
 #              4 CALL_FUNCTION            1
 #              6 POP_TOP
 #              8 LOAD_CONST               0 (None)
 #             10 RETURN_VALUE
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
from dis import dis
 
b=6
def f():
    print(b)
    b = 9
    
#  5          0 LOAD_GLOBAL              0 (print)
#             2 LOAD_FAST                0 (b)
#             4 CALL_FUNCTION            1
#             6 POP_TOP
 
#  6          8 LOAD_CONST               1 (9)
#             10 STORE_FAST               0 (b)
#             12 LOAD_CONST               0 (None)
#             14 RETURN_VALUE

可以使用global來強制聲明b是全局變量,然后就可以重新賦值了;

?
1
2
3
4
5
6
7
8
9
b=6
def f():
    global b
    print(b)
    b = 9
 
f()
 
# 6

四、閉包

閉包是是指可以訪問非在函數體內定義的非全局變量的函數;

通過函數的__code__及__closure__可以看到局部變量和自由變量及閉包的情況;

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
def makesum():
    sum = [0]
 
    def s(val):
        sum[0] += val
        return sum[0]
 
    return s
 
 
 
s = makesum()
print(s(1))
print(s(2))
print(s.__code__.co_varnames)
print(s.__code__.co_freevars)
print(s.__closure__)
print(s.__closure__[0].cell_contents)
 
 
# 1
# 3
# ('val',)
# ('sum',)
# (<cell at 0x7f63321f1b20: list object at 0x7f63321e8a00>,)
# [3]

基于三中Python變量作用域的緣故,上邊的sum只能使用列表對象,python提供的nonlocal關鍵字可以直接使用int類型的變量;

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
def makesum():
    sum = 0
 
    def s(val):
        nonlocal sum
        sum += val
        return sum
 
    return s
 
s = makesum()
print(s(1))
print(s(2))
print(s.__code__.co_varnames)
print(s.__code__.co_freevars)
print(s.__closure__)
print(s.__closure__[0].cell_contents)
 
 
# 1
# 3
# ('val',)
# ('sum',)
# (<cell at 0x7f73e6a4ab20: int object at 0x7f73e6b47970>,)
# 3

五、保留函數的元數據

函數裝飾器默認會使用返回的函數完全取代被裝飾的函數,這樣可能會導致序列化或者開發工具智能提示的問題;可以使用functools.wraps來保留原始函數的標準屬性(name、module、__annotations__等);

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
import functools
 
def dec(func):
    def real():
        '''this is real function'''
        pass
    return real
 
def wrapsdec(func):
    @functools.wraps(func)
    def real():
        '''this is real function'''
        pass
    return real
 
@dec
def max(nums):
    '''this is max function'''
    pass
 
@wrapsdec
def sum(nums):
    '''this is sum function'''
 
print(max)
print(max.__name__)
print(max.__doc__)
print(help(max))
print()
print(sum)
print(sum.__name__)
print(sum.__doc__)
print(help(sum))
 
 
# <function dec.<locals>.real at 0x7f1bfd4241f0>
# real
# this is real function
# Help on function real in module __main__:
#
# real()
#     this is real function
#
# None
#
# <function sum at 0x7f1bfd424280>
# sum
# this is sum function
# Help on function sum in module __main__:
#
# sum(nums)
#     this is sum function
#
# None

六、支持關鍵字參數、位置參數

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
def dec(func):
    def real(*args, **kwargs):
        result = func(*args, **kwargs)
        return result
 
    return real
 
@dec
def sum(*nums, **kwargs):
    s = 0
    for n in nums:
        s = s + n
 
    for a in kwargs.values():
        s = s + a
    return s
 
print(sum(1,2,3,first=1))

七、使用lru_cache緩存函數執行結果

lru_cache內部使用函數的參數作為key,使用dict進行緩存執行結果,減少重復計算;

默認支持緩存128條記錄,超過后采用LRU方式丟棄多余記錄;

需要注意執行中不要改變參數,否則會影響字典key的有效性;

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
from functools import lru_cache
 
@lru_cache()
def fibonacci(n):
    print(f'fibonacci({n})')
    if n<2:
        return n
    return fibonacci(n-1) + fibonacci(n-2)
 
print(fibonacci(6))
 
# fibonacci(6)
# fibonacci(5)
# fibonacci(4)
# fibonacci(3)
# fibonacci(2)
# fibonacci(1)
# fibonacci(0)
# 8

八、使用singledispatch實現泛型函數

python不支持方法或者函數的重載,我們無法單獨定義不同參數類型的同名函數;

singledispatch提供了這樣一種能力,其通過注冊具體的參數類型和關聯的函數;

我們可以在自己的模塊定義自己的類型,并實現自己的自定義函數;

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
import math
import numbers
from functools import singledispatch
 
 
 
@singledispatch
def absall(obj):
    return abs(obj)
 
@absall.register(numbers.Number)
def numabs(num):
    return abs(num)
 
@absall.register(numbers.Complex)
def cabs(c):
    return math.sqrt(c.real*c.real + c.imag* c.imag)
 
class Line:
 
    def __init__(self, startx, starty, endx, endy):
        self.startx = startx
        self.starty = starty
        self.endx = endx
        self.endy = endy
 
@absall.register(Line)
def lineabs(line):
    y =line.endy - line.starty
    x = line.endx - line.startx
    return math.sqrt(x*x + y*y)
 
print(absall(-1.1))
print(absall(3+4j))
 
l = Line(1,2,4,6)
print(absall(l))
 
# 1.1
# 5.0
# 5.0

九、通過參數控制函數裝飾器的行為

函數裝飾器本身不支持傳遞參數,解析源代碼的時候,python會將被裝飾的函數對象作為第一個參數傳遞給裝飾器;

我們可以通過在函數裝飾器外再嵌套一個函數工廠來承載裝飾特定函數的時候設置的參數;

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
def accesscontrol(checkuser=True, updatesession=True):
 
    def dec(func):
        def checkuserf():
            print('check user')
            return True
 
        def updatesessionf():
            print('update session')
            return True
 
        def access():
            if checkuser:
                checkuserf()
 
            if updatesession:
               updatesessionf()
 
            func()
 
        return access
 
    return dec
 
@accesscontrol()
def pushitem():
    print('pushitem')
    return True
 
@accesscontrol(checkuser=False)
def getitem():
    print('getitem')
    return True
 
# pushitem()
# print()
# getitem()
#
# check user
# update session
# pushitem
#
# update session
# getitem

以上就是Python 函數裝飾器應用教程的詳細內容,更多關于Python 函數裝飾器的資料請關注服務器之家其它相關文章!

原文鏈接:https://www.cnblogs.com/wufengtinghai/p/15631730.html

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 国产亚洲精品视频中文字幕 | 亚洲精品第五页中文字幕 | 操一炮| bdsm中国精品调教 | xx顶级欧美熟妞xxhd | 果冻传媒天美传媒乌鸦传媒 | 出a级黑粗大硬长爽猛视频 吃胸膜奶视频456 | 成人高清视频在线观看 | 国产在线精品一区二区高清不卡 | 玩两个少妇女邻居 | 欧美一区二区三区免费高 | 国产精品亚洲综合第一区 | 男同桌脱我奶罩吸我奶作文 | 日韩欧美一区二区在线观看 | 日本人成年视频在线观看 | 午夜国产 | 91精品91久久久久久 | 好姑娘完整版在线观看中文 | www.日本在线播放 | 国产高清好大好夹受不了了 | 国产草草视频 | 日韩欧美一区黑人vs日本人 | 小浪妇奶真大水多 | zoomkool最新版 | 精品国产免费观看一区高清 | 天天快乐高清在线观看 | 太大了轻点阿受不了小说h 四色6677最新永久网站 | 欧美专区综合 | 免费在线观看视频 | 九九成人免费视频 | 日韩黄色录像 | 午夜影院免费看 | 三极黄色 | 亚洲精品午夜久久aaa级久久久 | 日韩在线一区二区三区 | 99在线视频精品费观看视 | 国产免费又粗又猛又爽视频国产 | 门房秦大爷小说 | 免费被黄网站在观看 | 日韩大片在线播放 | 成全视频在线观看免费 |