python带参数装饰器 函数名_python 全栈开发,Day11(函数名应用,闭包,装饰器初识,带参数以及带返回值的装饰器)...
一、函數名應用
函數名是什么?函數名是函數的名字,本質:變量,特殊的變量。
函數名(),執行此函數。
python 規范寫法
1. #后面加一個空格,再寫內容,就沒有波浪線了。
2.一行代碼寫完,下面一行的的內容要空2行,
3.逗號2個邊的內容要有空格。
如果是不規范的寫法,Pycharm編輯器,會有灰色的波浪線顯示。
1.單獨打印函數名
def func1():
print(666)
print(func1)
執行輸出:
打印的是一個函數內存地址
2.函數名的賦值
def func2():
print(666)
f = func2
print(f())
執行輸出:
666
None
3.函數名可以作為容器類數據的元素
下面的函數都要執行
def f1():
print(111)
def f2():
print(222)
def f3():
print(333)
def f4():
print(444)
寫4個執行代碼就可以了
f1()
f2()
f3()
f4()
如果是100個呢?
使用for循環批量執行函數
def f1():
print(111)
def f2():
print(222)
def f3():
print(333)
def f4():
print(444)
l1 = []
for i in range(1, 5):
l1.append('f' + str(i))
for i in l1:
eval(i)()
執行輸出:
111
222
333
444
4.函數名可以作為參數
def f1():
print(666)
def f2(x):
x()
f2(f1)
執行輸出:
666
分析如下:
def f1():
print(666)
def f2(x): # x = f1
x() # f1()
f2(f1) #將f1函數作為變量傳參給f2,x()表示執行函數f1(),輸出666
5.函數名可以作為函數的返回值。
def f11(x):
return x #將返回值傳給調用者f11(5),此時ret = 5
ret = f11(5)
print(ret)
執行輸出:
5
def f1():
print(666)
def f2(x):
return x
f2(f1)()
執行輸出:
666
代碼分析:
def f1():
print(666)
def f2(x): # x = f1
return x #將f1返回給函數調用者f2(f1),此時就相當于 f1 = f2(f1)
f2(f1)() #將f1函數作為變量傳參給f2,return之后f2(f1)等于f1,f1()就表示執行f1函數,輸出666
f1是一個特殊變量,加()就可以執行了
第一類對象( first-class object)指
1.可在運行期創建
2.可用作函數參數或返回值
3.可存入變量的實體
*不明白?那就記住一句話,就當普通變量用
函數名就是第一類對象
加括號,就可以執行了。
二、閉包
閉包函數:
內部函數包含對外部作用域而非全劇作用域變量的引用,該內部函數稱為閉包函數
有如下代碼,請補全代碼,在函數外部執行inner()函數
def wrapper():
def inner():
print(666)
第一種寫法
def wrapper():
def inner():
print(666)
inner()
wrapper()
執行輸出:
666
第二種寫法
def wrapper():
def inner():
print(666)
return inner #將inner函數返回給調用者ret,此時ret = inner
ret = wrapper()
ret() #執行inner函數
執行輸出:
666
ret = wrapper()
ret()
等同于
wrapper()()
return inner之后,inner由臨時空間,轉換為全局空間了。
因為ret賦值了
閉包舉例
def wrapper():
name = '老男孩'
def inner():
print(name)
inner()
wrapper()
執行輸出:
老男孩
如何判斷它是否是一個閉包函數呢??內層函數名.__closure__? cell 就是=閉包
def wrapper():
name = '老男孩'
def inner():
print(name)
inner()
print(inner.__closure__)
wrapper()
執行輸出:
老男孩
(,)
出現了cell,就表示它是一個閉包函數
name = '老男還'
def wrapper():
def inner():
print(name)
inner()
print(inner.__closure__)
wrapper()
執行輸出:
老男還
None
返回值為None 表示它不是閉包
因為name是一個全局變量
如果函數調用了外層變量而非全局變量,那么它就是閉包
name = '老男還'
def wrapper2():
name1 = 'alex'
def inner():
print(name)
print(name1)
inner()
print(inner.__closure__)
wrapper2()
執行輸出:
老男還
alex
(,)
只要引用了外層變量至少一次,非全局的,它就是閉包
面試題:
下面的函數,是一個閉包嗎?
name = '老男孩'
def wraaper2(n):
# n = '老男孩' 相當于
def inner():
print(n)
inner()
print(inner.__closure__) # None
wraaper2(name)
它也是一個閉包
雖然wraaper2傳了一個全局變量,但是在函數wraaper2內部,inner引用了外層變量,相當于在函數inner外層定義了n = '老男孩',所以inner是一個閉包函數
閉包的好處
當函數開始執行時,如果遇到了閉包,他有一個機制,他會永遠開辟一個內存空間,將必包中的變量等值放入其中,不會隨著函數的執行完畢而消失。
舉一個例子
from urllib.request import urlopen
content = urlopen('http://www.xiaohua100.cn/index.html').read().decode('utf-8')
print(content)
執行輸出,是一堆網頁源代碼。
爬3次
from urllib.request import urlopen
content1 = urlopen('http://www.xiaohua100.cn/index.html').read().decode('utf-8')
content2 = urlopen('http://www.xiaohua100.cn/index.html').read().decode('utf-8')
content3 = urlopen('http://www.xiaohua100.cn/index.html').read().decode('utf-8')
內存開了3次,很占用內存
把它封裝成閉包
from urllib.request import urlopen
def index():
url = "http://www.xiaohua100.cn/index.html"
def get():
return urlopen(url).read()
return get
xiaohua = index()
content = xiaohua()
print(content)
這個例子,只有第一遍,是從網站抓取的。
之后的執行,直接從內存中加載,節省內存空間
三、裝飾器初識
裝飾器本質上就是一個python函數,他可以讓其他函數在不需要做任何代碼變動的前提下,增加額外的功能,裝飾器的返回值也是一個函數對象。
裝飾器的應用場景:比如插入日志,性能測試,事務處理,緩存等等場景。
現在我有一個需求,我想讓你測試這個函數的執行時間,在不改變這個函數代碼的情況下:
先寫一個雛形
import time
def func1():
print('in func1')
time.sleep(1) # 模擬程序邏輯
start = time.time()
func1()
print(time.time() - start)
執行輸出:
in func1
1.0001070499420166
封裝成函數
傳一個參數,函數名,就可以查看函數的執行時間了
import time
def func1():
print('in func1')
time.sleep(1) # 模擬程序邏輯
def timmer(f):
start_time = time.time()
f()
end_time = time.time()
print('此函數的執行時間為{}'.format(end_time - start_time))
timmer(func1)
執行輸出:
in func1
此函數的執行時間為1.0002098083496094
原來100個函數,都是調用了func1(),現在測試函數,需要timeer(func1)。如果想測試所有調用地方的執行時間,那么需要修改100個函數位置,改成timeer(func1),太麻煩了。
不能更改原來的調用方式,同時需要加一個函數執行時間的功能。怎么辦呢?
要無限接近于原來的調用方法,慢慢來
先來講一個變量賦值的例子
a = 1
b = a
a = 2 #a重新賦值了,原來a的值不存在了。但是b還是等于原來a的值,也就是1
print(a,b)
執行輸出2,1
1.更改執行方式
import time
def func1():
print('in func1')
time.sleep(1) # 模擬程序邏輯
def timmer(f):
start_time = time.time()
f()
end_time = time.time()
print('此函數的執行時間為{}'.format(end_time - start_time))
f1 = func1 #定義f1等于func1函數
func1 = timmer #定義func1等于timmer,等式計算右邊的。此時func1被覆蓋了,和原來的沒有任何關系,f1還是等于原來的func1
func1(f1) #此時相當于執行 timmer(老的func1)
執行輸出:
in func1
此函數的執行時間為1.0001263618469238
在精簡一步
import time
def func1():
print('in func1')
time.sleep(1) # 模擬程序邏輯
def timmer(f): # f = func1
def inner():
start_time = time.time()
f() #執行func1()
end_time = time.time()
print('此函數的執行時間為{}'.format(end_time - start_time))
return inner #將inner函數返回給函數調用者timmer(func1)
a = timmer(func1) #等式計算右邊的,將func1函數傳給timmer函數
a() #此時相當于執行了inner函數
執行輸出:
in func1
此函數的執行時間為1.000577449798584
最終版本
import time
def timmer(f): # 2接收參數.f = func1
def inner():
start_time = time.time() #5.進入inner函數
f() #6.執行f(),也就是原來的func1函數。雖然func1被覆蓋了,但是之前的值還存在。請參考上面a,b賦值的例子
end_time = time.time() #10 獲取當前時間
print('此函數的執行時間為{}'.format(end_time - start_time)) #11.輸出差值
return inner #3.將inner函數返回給函數調用者timmer(func1),此時程序結束,繼續執行func1()
def func1(): #7.進入函數
print('in func1') # 8.打印輸出
time.sleep(1) # 9.等待1秒
func1 = timmer(func1) #1.等式計算右邊的,將func1函數傳給timmer函數,此時func1被覆蓋了,原來func1的不存在了。
func1() #4.這里的func1是全新的func1,就是上面的賦值,此時相當于執行 inner函數
代碼從上至下執行,先加載函數變量timmer和func1。在執行上文說的第1步以及后面的,請看數字。
執行輸出:
in func1
此函數的執行時間為1.0009608268737793
但是加這一步,還是不太方便。那么python給這種情況,加了一個語法糖@
語法糖指那些沒有給計算機語言添加新功能,而只是對人類來說更"甜蜜"的語法
import time
def timmer(f): # f = func1
def inner():
start_time = time.time()
f() #執行func1()
end_time = time.time()
print('此函數的執行時間為{}'.format(end_time - start_time))
return inner #將inner函數返回給函數調用者timmer(func1)
#語法糖@
@timmer #相當于 func1 = timmer(func1) #這一步就已經覆蓋了
def func1():
print('in func1')
time.sleep(1) # 模擬程序邏輯
func1() #相當于執行了inner()函數
想測試誰,前面加@裝飾器函數,即可。
裝飾器利用return制造了一個假象,func1()執行,其實是執行了inner()
func1()已經把原來的func1()給覆蓋了
這個裝飾器,還不夠完整,函數不能傳參數
先來舉個例子
def func2(a1,b1): #4.執行函數,接收位置參數a1=1,b1=2
print(a1,b1) #5. 輸出1,2
def func3(a,b): #2. 接收參數,a=1,b=2
func2(a,b) #3. 執行函數func2(1,2)
func3(1,2) #1.傳位置參數1,2
執行輸出:
1 2
func3執行一遍后,將a,b的值,傳給a1,b1了
上面裝飾器的例子,func1,要傳2個參數a,b
import time
def timmer(f):
def inner(a,b):
start_time = time.time()
f(a,b)
end_time = time.time()
print('此函數的執行時間為{}'.format(end_time - start_time))
return inner
@timmer
def func1(a,b):
print('in func1 {}{}'.format(a,b))
time.sleep(1) # 模擬程序邏輯
func1(1,2)
執行輸出:
in func1 12
此函數的執行時間為1.0006024837493896
如果有多個參數呢?改成動態參數
import time
def timmer(f):
def inner(*args,**kwargs):
start_time = time.time()
f(*args,**kwargs)
end_time = time.time()
print('此函數的執行時間為{}'.format(end_time - start_time))
return inner
@timmer
def func1(*args,**kwargs):
print('in func1 {}{}'.format(args,kwargs))
time.sleep(1) # 模擬程序邏輯
func1(1,2,a='3',b=4)
執行輸出:
in func1 (1, 2){'b': 4, 'a': '3'}
此函數的執行時間為1.000101089477539
面試題,手寫一個裝飾器
寫裝飾器,約定俗成,函數名為wrapper
第一步
def wrapper(func):
def inner():
func()
return inner
第二步
def wrapper(func):
def inner(*args,**kwargs):
func(*args,**kwargs)
return inner
第三步
def wrapper(func):
def inner(*args,**kwargs):
'''被裝飾函數之前'''
ret = func(*args,**kwargs)
'''被裝飾函數之后'''
return ret
return inner
完整的
def wrapper(func):
def inner(*args,**kwargs):
'''被裝飾函數之前'''
ret = func(*args,**kwargs)
'''被裝飾函數之后'''
return ret
return inner
@wrapper
def func(*args,**kwargs):
print(args,kwargs)
return 666
print(func())
作業:
使用函數完成用戶登錄和注冊功能
#先讓用戶選擇,是登陸還是注冊
#選擇序號完畢之后,運行相應的程序,
#驗證成功之后,可以讓其繼續選擇,登陸還是注冊,還可以選擇退出。
1.先準備函數的雛形
def registered_username(username):
'''
#判斷注冊用戶名是否存在
:param username: 用戶名
:return: True 存在 False 不存在
'''
def write_file(username,password):
'''
#寫入用戶列表文件
:param username: 用戶名
:param password: 密碼
:return: True 寫入成功 False 寫入失敗
'''
def username_password(username,password):
'''
#判斷用戶名和密碼是否匹配
:param username: 用戶名
:param password: 密碼
:return: True 匹配成功 False 匹配失敗
'''
def register(*args,**kwargs):
'''
注冊邏輯
:return:
'''
pass
def login(*args,**kwargs):
'''
登錄邏輯
:return:
'''
pass
def user_menu(*args,**kwargs):
'''
# 用戶菜單
'''
pass
2.先寫菜單
def user_menu(*args,**kwargs):
'''
# 用戶菜單
'''
# 判斷文件是否存在,不存在創建文件
file_exists()
# 循環
while True:
# 打印菜單
menu = ['注冊', '登錄', '退出']
print('bbs系統'.center(25, '#'))
for i in range(len(menu)):
print('{}\t{}'.format(i + 1, menu[i]))
print(''.center(27, '#'))
number = input('請選擇序號: ').strip()
if number == '1':
# 執行注冊程序
register()
elif number == '2':
# 執行登錄程序
login()
elif number == '3':
exit()
else:
print('輸入錯誤,請重新輸入!')
3.寫判斷用戶列表文件是否存在
def file_exists(*args,**kwargs):
'''
# 判斷用戶列表文件是否存在
:return: True 存在 False 不存在
'''
# 判斷文件是否存在
if os.path.exists(file_name):
return True
else:
with open(file_name, encoding='utf-8', mode='w') as mk:
mk.write('張三 123')
return False
4.再寫注冊邏輯
def register(*args,**kwargs):
'''
注冊邏輯
:return:
'''
while True:
username = input('請輸入注冊的用戶名,或輸入q返回菜單:').strip()
if username == '':
print('用戶名為空,請重新輸入!')
elif username.upper() == 'Q':
break
else:
# 執行判斷用戶名函數
result = registered_username(username)
if result:
password = input('請輸入您的注冊的密碼:').strip()
# 判斷密碼
if password == '':
print('密碼為空,請重新輸入!')
else:
# 執行寫入用戶列表文件函數
result = write_file(username, password)
if result:
print('注冊成功!,您的用戶名為: {}\n倒計時2秒返回菜單!'.format(username))
time.sleep(2)
user_menu()
else:
print('注冊失敗!請重試')
else:
print('用戶名已經存在,請重新輸入!')
5.判斷注冊用戶名是否可用
def registered_username(username):
'''
#判斷注冊用戶名是否可用
:param username: 用戶名
:return: True 可用(用戶不存在) False 不可用(用戶已存在)
'''
# 純用戶名列表,不包含密碼
user_list = []
with open(file_name, encoding='utf-8') as f1:
for i in f1:
# 去空格,以空格切割,轉換為列表
li = i.strip().split() # [張三,123]
# 將用戶名追加到列表中
user_list.append(li[0])
# 判斷用戶名是否存在列表中
if username in user_list:
# 返回False
return False
else:
return True
6.寫入用戶列表文件
def write_file(username,password):
'''
#寫入用戶列表文件
:param username: 用戶名
:param password: 密碼
:return: True 寫入成功 False 寫入失敗
'''
with open(file_name, encoding='utf-8', mode='a') as f2:
f2.write('\n{} {}'.format(username, password))
return True
7.登錄邏輯
def login(count=0,max=3):
'''
登錄邏輯
:param count: 初始失敗次數
:param max: 最大失敗次數
:return:
'''
while count < max:
count += 1
username = input('請輸入用戶名:').strip()
password = input('請輸入密碼:').strip()
# 執行驗證用戶名和密碼函數
result = username_password(username,password)
if result:
print('登陸成功\n倒計時1秒返回菜單!')
time.sleep(1)
user_menu()
break
else:
print('用戶名或密碼錯誤,還剩余{}次機會!'.format(max - count))
# 返回主菜單
user_menu()
8.判斷用戶名和密碼是否匹配
def username_password(username,password):
'''
#判斷用戶名和密碼是否匹配
:param username: 用戶名
:param password: 密碼
:return: True 匹配成功 False 匹配失敗
'''
# print(username,password)
with open(file_name, encoding='utf-8', mode='r') as f3:
for i in f3:
# print(i)
# 去空格,以空格切割,轉換為列表
li = i.strip().split() # [張三,123]
# 判斷用戶名和密碼是否匹配
if username == li[0] and password == li[1]:
result = True
# 當找到匹配時,跳出循環
break
else:
result = False
# 當整個用戶列表遍歷完成之后,再return
return result
最終完整代碼如下:
# -*- coding: utf-8 -*-
import time,os
#文件名
file_name = 'user_list.txt'
def file_exists(*args,**kwargs):
'''
# 判斷用戶列表文件是否存在
:return: True 存在 False 不存在
'''
# 判斷文件是否存在
if os.path.exists(file_name):
return True
else:
with open(file_name, encoding='utf-8', mode='w') as mk:
mk.write('張三 123')
return False
def registered_username(username):
'''
#判斷注冊用戶名是否可用
:param username: 用戶名
:return: True 可用(用戶不存在) False 不可用(用戶已存在)
'''
# 純用戶名列表,不包含密碼
user_list = []
with open(file_name, encoding='utf-8') as f1:
for i in f1:
# 去空格,以空格切割,轉換為列表
li = i.strip().split() # [張三,123]
# 將用戶名追加到列表中
user_list.append(li[0])
# 判斷用戶名是否存在列表中
if username in user_list:
# 返回False
return False
else:
return True
def write_file(username,password):
'''
#寫入用戶列表文件
:param username: 用戶名
:param password: 密碼
:return: True 寫入成功 False 寫入失敗
'''
with open(file_name, encoding='utf-8', mode='a') as f2:
f2.write('\n{} {}'.format(username, password))
return True
def username_password(username,password):
'''
#判斷用戶名和密碼是否匹配
:param username: 用戶名
:param password: 密碼
:return: True 匹配成功 False 匹配失敗
'''
# print(username,password)
with open(file_name, encoding='utf-8', mode='r') as f3:
for i in f3:
# print(i)
# 去空格,以空格切割,轉換為列表
li = i.strip().split() # [張三,123]
# 判斷用戶名和密碼是否匹配
if username == li[0] and password == li[1]:
result = True
# 當找到匹配時,跳出循環
break
else:
result = False
# 當整個用戶列表遍歷完成之后,再return
return result
def register(*args,**kwargs):
'''
注冊邏輯
:return:
'''
while True:
username = input('請輸入注冊的用戶名,或輸入q返回菜單:').strip()
if username == '':
print('用戶名為空,請重新輸入!')
elif username.upper() == 'Q':
break
else:
# 執行判斷用戶名函數
result = registered_username(username)
if result:
password = input('請輸入您的注冊的密碼:').strip()
# 判斷密碼
if password == '':
print('密碼為空,請重新輸入!')
else:
# 執行寫入用戶列表文件函數
result = write_file(username, password)
if result:
print('注冊成功!,您的用戶名為: {}\n倒計時2秒返回菜單!'.format(username))
time.sleep(2)
user_menu()
else:
print('注冊失敗!請重試')
else:
print('用戶名已經存在,請重新輸入!')
def login(count=0,max=3):
'''
登錄邏輯
:param count: 初始失敗次數
:param max: 最大失敗次數
:return:
'''
while count < max:
count += 1
username = input('請輸入用戶名:').strip()
password = input('請輸入密碼:').strip()
# 執行驗證用戶名和密碼函數
result = username_password(username,password)
if result:
print('登陸成功\n倒計時1秒返回菜單!')
time.sleep(1)
user_menu()
break
else:
print('用戶名或密碼錯誤,還剩余{}次機會!'.format(max - count))
# 返回主菜單
user_menu()
def user_menu(*args,**kwargs):
'''
# 用戶菜單
'''
# 判斷文件是否存在,不存在創建文件
file_exists()
# 循環
while True:
# 打印菜單
menu = ['注冊', '登錄', '退出']
print('bbs系統'.center(25, '#'))
for i in range(len(menu)):
print('{}\t{}'.format(i + 1, menu[i]))
print(''.center(27, '#'))
number = input('請選擇序號: ').strip()
if number == '1':
# 執行注冊程序
register()
elif number == '2':
# 執行登錄程序
login()
elif number == '3':
exit()
else:
print('輸入錯誤,請重新輸入!')
# 執行菜單
user_menu()
執行輸出:
總結
以上是生活随笔為你收集整理的python带参数装饰器 函数名_python 全栈开发,Day11(函数名应用,闭包,装饰器初识,带参数以及带返回值的装饰器)...的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: iOS 17支持哪些机型iOS 17支持
- 下一篇: ps4手柄 安卓手机(ps4手柄 安卓)