python--高級語法

来源:https://www.cnblogs.com/wushaofan/archive/2023/02/26/17156647.html
-Advertisement-
Play Games

1、變數 推薦 駝峰體:AgeOfOldboy = 73 下劃線:age_of_oldboy = 73 # 變數的小高級: age1 = 18 age2 = age1 age3 = age2 age2 = 12 print(age1,age2,age3) 18 12 18 Process finis ...


1、變數

  • 推薦
    • 駝峰體:AgeOfOldboy = 73
    • 下劃線:age_of_oldboy = 73
# 變數的小高級:
age1 = 18
age2 = age1
age3 = age2
age2 = 12

print(age1,age2,age3)
18 12 18

Process finished with exit code 0
#********************************************************************************************************
# 變數的小高級:
age1 = 18
age2 = 12
age2 = age1
age3 = age2

print(age1,age2,age3)
18 18 18

Process finished with exit code 0

解析:首先從上到下執行,

2、常量

  • 常量:一直不變的量。python中沒有真正的常量,為了應和其他語言的口味,全部大寫的變數稱之為常量。
  • 將變數全部大寫,放在文件的最上面。
# 常量
# 約定俗成不能改變 
NAME = '太白' 
print(NAME)
太白

Process finished with exit code 0

3.註釋(重點

  • 單行註釋: #
  • 多行註釋: '''被註釋內容''' """被註釋內容"""

3、流程式控制制語句if

基本結構:

if 條件: 
	結果 

1、單獨if

  • 如果條件成立,就執行if語句中的代碼,如果條件不成立,就不執行if語句中的代碼。
#單獨if
if 2 < 1:
    print(111)

if 2 > 1:
    print(222)

222

Process finished with exit code 0

2、if else 二選一

# if else 二選一

age = input('請輸入年齡:')
if int(age) > 18:
    print('恭喜你,成年了')
else:
    print('小屁孩兒')
    
請輸入年齡:1
小屁孩兒

Process finished with exit code 0

3、if elif elif .... 多選一

  • 語句測成立後就列印該語句中的代碼,下麵的判斷語句就不執行了
num = int(input('猜點數:'))

if num == 1:
    print("晚上請你吃飯")
elif num == 3:
    print("一起溜達")
elif num==2:
    print("請你大寶劍")

4、if elif elif .... else 多選一

  • 當所有條件都不滿足,就執行else中的代碼、有一個成立了,就不會執行else中的代碼
num = int(input('猜點數:'))

if num == 1:
    print("晚上請你吃飯")
elif num == 3:
    print("一起溜達")
elif num==2:
    print("請你大寶劍")
else:
    print("太笨了")

5、嵌套的if

#思路 -----然後將pass改為對應的用戶名、密碼的條件判斷
if your_code == code:
   pass
else:
    print("驗證碼錯誤")
username=input("請輸入用戶名:")
password=input("請輸入密碼")
code= 'qwer'
your_code=input("請輸入驗證碼:")

if your_code == code:
    if username == 'mike' and password == '123456':
        print('登錄成功')
    else:
        print('賬號或者密碼錯誤')
else:
    print("驗證碼錯誤")

4、while 迴圈

1,基本結構:

while:無限迴圈 for :有限迴圈

while 條件:
    迴圈體
   

2,基本原理:

3,迴圈如何終止?

1,改變條件。

flag = True
while flag:
    print('狼的誘惑')
    print('我們不一樣')
    print('月亮之上')
    flag = False
    print('廬州月')
    print('人間')
#加入計數器,使其達到條件後退出迴圈
i=0
while True:
    print('狼的誘惑')
    print('我們不一樣')
    print('月亮之上')
    print('廬州月')
    print('人間')
    i=i+1
    if i==2:
        break

2,break

  • 退出迴圈
while True:
    print('狼的誘惑')
    print('我們不一樣')
    print('月亮之上')
    print('廬州月')
    print('人間')
    
    break

3,系統命令(今天不講)

4,continue

  • continue : 退出本次迴圈,繼續下一次迴圈
# continue : 退出本次迴圈,繼續下一次迴圈
i=0
while True:
    print('狼的誘惑')
    i=i+1
    if i==2:
        continue
    print('月亮之上')
    if i == 3:
        break
        
狼的誘惑  #i=1
月亮之上

狼的誘惑 #i=2  continue退出本次迴圈  只列印了狼的誘惑

狼的誘惑  #i=3
月亮之上

Process finished with exit code 0
# while else: while 迴圈如果被break打斷,則不執行else語句。
count = 1
while count < 5:
    print(count)
    if count == 2:
        break
    count = count + 1
else:
    print(666)

優化登錄的代碼;

#有三次錯誤輸入的機會
count=1
while count<4:
    username=input("請輸入用戶名:")
    password=input("請輸入密碼")
    code= 'qwer'
    your_code=input("請輸入驗證碼:")

    if your_code == code:
        if username == 'mike' and password == '123456':
            print('登錄成功')
        else:
            print('賬號或者密碼錯誤')
    else:
        print("驗證碼錯誤")
    count=count+1

練習題:列印1~ 100 所有的數字

#第一種方案
count=1
while True:
    print(count)
    count=count+1
    if count==101:
        break
        
#第二種方案
count = 1
flag = True
while flag:
    print(count)
    count = count + 1
    if count == 101:
        flag = False

#第三種方案        
count = 1
while count < 101:
    print(count)
    count = count + 1

練習題:1 + 2 + ..... 100 的最終結果

# 1 + 2 + 3 + ...... 100  的最終結果:

s = 0
count = 1
while count < 101:
    s = s + count
    count = count + 1
print(s)

day2:

1、代碼塊(重點)

  • 代碼塊:我們所有的代碼都需要依賴代碼塊執行。
  • 一個模塊,一個函數,一個類,一個文件等都是一個代碼塊。。
  • 而作為交互方式輸入的每個命令都是一個代碼塊。

而對於一個文件中的兩個函數,也分別是兩個不同的代碼塊:

2、代碼塊的緩存機制

  1. 兩個機制: 同一個代碼塊下,有一個機制。不同的代碼塊下,遵循另一個機制。

  2. 同一個代碼塊下的緩存機制。

    • 前提條件:同一個代碼塊內。

    • 機制內容:pass

      機制內容:Python在執行同一個代碼塊的初始化對象的命令時,會檢查是否其值是否已經存在,如果存在,會將其重用。換句話說:執行同一個代碼塊時,遇到初始化對象的命令時,他會將初始化的這個變數與值存儲在一個字典中,在遇到新的變數時,會先在字典中查詢記錄,如果有同樣的記錄那麼它會重覆使用這個字典中的之前的這個值。所以在你給出的例子中,文件執行時(同一個代碼塊)會把i1、i2兩個變數指向同一個對象,滿足緩存機制則他們在記憶體中只存在一個,即:id相同
      
    • 適用的對象: int bool str

    • 具體細則:所有的數字,bool,幾乎所有的字元串。

    • 優點:提升性能,節省記憶體。

  3. 不同代碼塊下的緩存機制: 小數據池。

    • 前提條件:不同代碼塊內。

    • 機制內容:pass

      Python自動將-5~256的整數進行了緩存,當你將這些整數賦值給變數時,並不會重新創建對象,而是使用已經創建好的緩存對象。
      
      python會將一定規則的字元串在字元串駐留池中,創建一份,當你將這些字元串賦值給變數時,並不會重新創建對象, 而是使用在字元串駐留池中創建好的對象。
      
        其實,無論是緩存還是字元串駐留池,都是python做的一個優化,就是將~5-256的整數,和一定規則的字元串,放在一個‘池’(容器,或者字典)中,無論程式中那些變數指向這些範圍內的整數或者字元串,那麼他直接在這個‘池’中引用,言外之意,就是記憶體中之創建一個
      
    • 適用的對象: int bool str

    • 具體細則:-5~256數字,bool,滿足規則的字元串。

    • 優點:提升性能,節省記憶體。

總結:

  1. 面試題考。
  2. 回答的時候一定要分清楚:同一個代碼塊下適用一個緩存機制。不同的代碼塊下適用另一個緩存機制(小數據池)
  3. 小數據池:數字的範圍是-5~256.
  4. 緩存機制的優點:提升性能,節省記憶體。

3、深淺copy(面試會考)

1,先看賦值運算

  • 對於賦值運算來說,a1與a2指向的是同一個記憶體地址,所以他們是完全一樣的,在舉個例子,比如張三李四合租在一起,那麼對於客廳來說,他們是公用的,張三可以用,李四也可以用,但是突然有一天張三把客廳的的電視換成投影了,那麼李四使用客廳時,想看電視沒有了,而是投影了,對吧?a1,a2指向的是同一個列表,任何一個變數對列表進行改變,剩下那個變數在使用列表之後,這個列表就是發生改變之後的列表。
#賦值運算
a1=[1,2,3,[22,33]]
a2=a1

a1.append(666)
print(a1,id(a1))
print(a2,id(a2))

[1, 2, 3, [22, 33], 666] 1768812233480
[1, 2, 3, [22, 33], 666] 1768812233480

Process finished with exit code 0

2,淺拷貝copy。

  • 對於淺copy來說,只是在記憶體中重新創建了開闢了一個空間存放一個新列表,但是新列表中的元素與原列表中的元素是公用的。

所以a1 和a2的ID不同,但是內容ID相同

#淺copy

#同一代碼塊下:
a1=[1,2,3,[22,33]]
a2=a1.copy()

a1.append(666)
print(a1,id(a1))
print(a2,id(a2))

[1, 2, 3, [22, 33], 666] 1321060254472
[1, 2, 3, [22, 33]] 	 1321059431112

Process finished with exit code 0

#*********************************************
# 不同代碼塊下:
a1=[1,2,3,[22,33]]
a2=a1.copy()

a1[-1].append(666)
print(a1,id(a1))
print(a2,id(a2))
#列印a1 a2中小列表的存儲ID
print(id(a1[-1]))
print(id(a2[-1]))

[1, 2, 3, [22, 33, 666]] 2332545303304
[1, 2, 3, [22, 33, 666]] 2332544479944
2332545286088
2332545286088

Process finished with exit code 0

思考1:a2 淺拷貝a1後,a1列表中的小列表添加元素後,a2的小列表為啥也添加?

---因為a1 a2  是兩個不同的大列表,但是列表裡邊的元素都是公用一個,所以a1的小列表添加666  a2的小列表也添加666

思考2:如果使a1[0]=90,此時a2[0]是否等於90?  為什麼a2[0]不變?
因為之前的a1[0],a1[1],a1[3]是1,2,3  是字元串,是不可變的,我改變的不是a1[0]本身,我只是改變了a1這個列表第一個槽位的記憶體關係
a1=[1,2,3,[22,33]]
a2=a1.copy()

a1[0]=90
print(a1)
print(a2)

[90, 2, 3, [22, 33]]
[1, 2, 3, [22, 33]]

Process finished with exit code 0

3,深拷貝deepcopy

  • 深copy則會在記憶體中開闢新空間,將原列表以及列表裡邊的可變的數據類型重新創建一份,不可變的數據類型則沿用之前的(即公用一個)
#深copy
import copy
a1=[1,2,3,[22,33]]
a2=copy.deepcopy(a1)

print(a1,id(a1))
print(a2,id(a2))

[1, 2, 3, [22, 33]] 1512146813512
[1, 2, 3, [22, 33]] 1512146814664

Process finished with exit code 0

示例:a1[-1].append(666)

#深copy
import copy
a1=[1,2,3,[22,33]]
a2=copy.deepcopy(a1)

a1[-1].append(666)
print(a1)
print(a2)

[1, 2, 3, [22, 33, 666]]
[1, 2, 3, [22, 33]]

Process finished with exit code 0

4,深淺拷貝面試題

#面試題:
# 考察的內容是:切邊是深拷貝還是淺拷貝?--淺拷貝
a1=[1,2,3,[22,33]]
a2=a1[:]
a1[-1].append(666)
print(a1)
print(a2)

# 淺copy: list dict: 嵌套的可變的數據類型是同一個。
# 深copy: list dict: 嵌套的可變的數據類型不是同一個 。
def eat(a,b,c,d):
    print('我請你吃:{},{},{},{}'.format(a,b,c,d))

eat('蒸羊羔', '蒸熊掌', '蒸鹿邑','燒花鴨')

# 急需要一種形參,可以接受所有的實參。
# 萬能參數: *args, 約定俗稱:args,
# 函數定義時,*代表聚合。 他將所有的位置參數聚合成一個元組,賦值給了 args。

def eat(*args):
    print(args)
    print('我請你吃:{},{},{},{}'.format(*args))

eat('蒸羊羔', '蒸熊掌', '蒸鹿邑','燒花鴨')

day3:

1、形參角度:(重點)

萬能參數: *args

  • *的魔性用法-------在函數的定義時,表示聚合 ;在函數的調用時,表示打散或解包。
def eat(a,b,c,d):
    print('我請你吃:{},{},{},{}'.format(a,b,c,d))

eat('蒸羊羔', '蒸熊掌', '蒸鹿邑','燒花鴨')

# 急需要一種形參,可以接受所有的實參。
# 萬能參數: *args, 約定俗稱:args,
# 函數定義時,*代表聚合。 他將所有的位置參數聚合成一個元組,賦值給了 args。

def eat(*args):
    print(args)
    print('我請你吃:{},{},{},{}'.format(*args))

eat('蒸羊羔', '蒸熊掌', '蒸鹿邑','燒花鴨')

我請你吃:蒸羊羔,蒸熊掌,蒸鹿邑,燒花鴨
('蒸羊羔', '蒸熊掌', '蒸鹿邑', '燒花鴨')
我請你吃:蒸羊羔,蒸熊掌,蒸鹿邑,燒花鴨

Process finished with exit code 0
  • 練習題:寫一個函數:計算你傳入函數的所有的數字的和。
#練習題:寫一個函數:計算你傳入函數的所有的數字的和。
# tu1=(1,2,3,4,5,6,7)
# count=0
# for i in tu1:
#     count=count+i
# print(count)

def func(*args):
    count = 0
    for i in args:
        count = count + i
    return count

print(func(1,2,3,4,5,6,7))

28

Process finished with exit code 0

萬能參數:**kwargs

# **kwargs
# 函數的定義時: ** 將所有的關鍵字參數聚合到一個字典中,將這個字典賦值給了kwargs.
def func(**kwargs):
    print(kwargs)

func(name='alex',age=73,sex='laddyboy')

{'name': 'alex', 'age': 73, 'sex': 'laddyboy'}

Process finished with exit code 0
# * **在函數的調用時,*代表打散---也可以說 解包
def func(*args):
    print(args)

func([1,2,3],[22,33])
func(*[1,2,3],[22,33])
func(*[1,2,3],*[22,33])

([1, 2, 3], [22, 33])
(1, 2, 3, [22, 33])
(1, 2, 3, 22, 33)

Process finished with exit code 0
def func(*args,**kwargs):
    print(args)
    print(kwargs)

func({'name': '太白'},{'age': 18})
print('*********')
func(**{'name': '太白'},**{'age': 18})

({'name': '太白'}, {'age': 18})
{}
*********
()
{'name': '太白', 'age': 18}

Process finished with exit code 0

形參角度的參數的順序

(位置參數,預設參數,萬能參數)

  • 思考?------*args的位置?
    • 必須放在位置參數後面,預設參數前面(如果預設參數放在*args 前面,那麼調用的時候,預設參數的值會被改變)
#形參角度的參數的順序(位置參數,預設參數,萬能參數)
# *args的位置?----必須放在位置參數後面,預設參數前面
def func(a,b,*args,sex='男'):
    print(a,b)
    print(sex)
    print(args)

func(1,2,3,4)

1 2
男
(3, 4)

Process finished with exit code 0
  • 思考?----**kwargs的位置?
    • 要放在預設參數後面,
#**kwargs的位置?
def func(a,b,*args,sex='男',**kwargs):
    print(a,b)
    print(sex)
    print(args)
    print(kwargs)

func(1,2,3,4,age=12)

1 2
男
(3, 4)
{'age': 12}

Process finished with exit code 0

僅限關鍵字參數(瞭解)

  • 放在*args和**kwargs的位置之間,類似於預設參數
def func(a,b,*args,sex='男',c,**kwargs):
    print(a,b)
    print(sex)
    print(args)
    print(c)
    print(kwargs)

func(1,2,3,4,age=12,c='666')

1 2
男
(3, 4)
666
{'age': 12}

Process finished with exit code 0

形參角度最終的順序:

  • 位置參數,*args,預設參數,僅限關鍵字參數,**kwargs

函數的嵌套(高階函數)

# 例1:---思考列印結果和順序
def func1():
    print('in func1')
    print(3)

def func2():
    print('in func2')
    print(4)

func1()
print(1)
func2()
print(2)

in func1
3
1
in func2
4
2

Process finished with exit code 0
# 例2:---思考列印結果和順序
def func1():
    print('in func1')
    print(3)

def func2():
    print('in func2')
    func1()
    print(4)

print(1)
func2()
print(2)

1
in func2
in func1
3
4
2

Process finished with exit code 0

# 例3:---思考列印結果和順序
def fun2():
    print(2)

    def fun3():
        print(6)

    print(4)
    fun3()
    print(8)

print(3)
fun2()
print(5)

3
2
4
6
8
5

Process finished with exit code 0

day11:

1、補充:預設參數的陷阱

  • 陷阱只針對於預設參數是可變的數據類型:
    • 結論:如果你的預設參數指向的是可變的數據類型,那麼你無論調用多少次這個預設參數,都是同一個。
# 陷阱只針對於預設參數是可變的數據類型:
def func(name,alist=[]):
    alist.append(name)
    return alist

ret1=func("mike")
print(ret1,id(ret1))     #列印結果 ['mike']

ret2=func('太白金星')
print(ret2,id(ret2))

['mike'] 2579283148744
['mike', '太白金星'] 2579283148744

Process finished with exit code 0
  • 圖形講解

面試題1:

def func(a,list=[]):
    list.append(a)
    return list

print(func(10))
print(func(20,[]))
print(func(100))

[10]
[20]
[10, 100]

Process finished with exit code 0

#等同於如下:
l1 = []
l1.append(10)
print(l1)
l2 = []
l2.append(20)
print(l2)
l1.append(100)
print(l1)

面試題2:

  • 面試題2是 3個執行完才列印,面試題1是執行一個列印一次
def func(a,list=[]):
    list.append(a)
    return list


ret1=func(10)
ret2=(func(20,[]))
ret3=(func(100))
print(ret1)
print(ret2)
print(ret3)

[10, 100]
[20]
[10, 100]

Process finished with exit code 0

2、補充:局部作用域的坑:

global:

1, 在局部作用域聲明一個全局變數。

​ 註意:要先調用函數,這樣函數里的全局變數才可以被引用

#global
#1, 在局部作用域聲明一個全局變數。
name = 'alex'

def func():
    global name
    name = '太白金星'
    print(name)
func()
print(name)

太白金星
太白金星

Process finished with exit code 0
  • 如果先調用局部變數中的全局變數,會報錯-----要先調用函數
def func():
    global name
    name = '太白金星'
    print(name)

print(name)
func()

NameError: name 'name' is not defined

Process finished with exit code 1

2.修改一個全局變數

  • 原來這樣寫會報錯

  • 加入global 就可以完成引用全局變數並且修改全局變數
#2. 修改一個全局變數

count=1
def func():
    global count
    count=count+1

print(count)
func()
print(count)

1
2

Process finished with exit code 0

nonlocal

1.不能夠操作全局變數

count = 1
def func():
    nonlocal count
    count += 1
func()

SyntaxError: no binding for nonlocal 'count' found

Process finished with exit code 1

2.局部作用域:內層函數對外層函數的局部變數進行修改

# 2. 局部作用域:內層函數對外層函數的局部變數進行修改。

def wrapper():
    count = 1
    def inner():
        nonlocal count
        count += 1
    print(count)
    inner()
    print(count)
wrapper()

3、函數名的運用

1.函數名指向的是函數的記憶體地址。

​ 函數名 + ()就可以執行次函數。

def func():
    print(666)

func()
# 1. 函數名指向的是函數的記憶體地址。
# 函數名 + ()就可以執行次函數。
print(func,type(func))

666
<function func at 0x0000022029A21E18> <class 'function'>

Process finished with exit code 0

2, 函數名就是變數。

def func():
    print(666)

f=func
f1=f
f2=f1
f2()

666

Process finished with exit code 0
  • 圖形解釋

def func():
    print('in func')


def func1():
    print('in fun1')

func1=func
func1()
#列印那個結果?
#分析:類比如下
a=1
b=2
a=b
print(a)

in func
2

Process finished with exit code 0

3.函數名可以作為容器類數據類型的元素

# 3. 函數名可以作為容器類數據類型的元素

# a = 1
# b = 2
# c = 3
# l1 = [a,b,c]
# print(l1)

def func1():
    print('in fun1')

def func2():
    print('in fun2')

def func3():
    print('in fun3')

a1=[func1,func2,func3]
for i in a1:
    i()
    
in fun1
in fun2
in fun3

Process finished with exit code 0

4.函數名可以作為函數的參數

def func():
    print('in func')

def fun1(x):
    x()  #相當於func()
    print('in func1')

fun1(func)

in func
in func1

Process finished with exit code 0

5.函數名可以作為函數的返回值

# 5. 函數名可以作為函數的返回值
def func():
    print('in func')

def func1(x):
    print('in func1')
    return x

ret=func1(func)
ret()

in func1
in func

Process finished with exit code 0

4、新特性:格式化輸出

1.舊的格式化方式

%s   和   format
#舊個格式化輸出,太麻煩了
name = '太白'
age = 18
msg = '我叫%s,今年%s' %(name,age)
print(msg)
msg1 = '我叫{},今年{}'.format(name,age)
print(msg)

我叫太白,今年18
我叫太白,今年18

Process finished with exit code 0

2.新特性:格式化輸出

# 新特性:格式化輸出
name = '太白'
age = 18
msg = f'我叫{name},今年{age}'
print(msg)

我叫太白,今年18

Process finished with exit code 0

2.1 可以加表達式

# 可以加表達式
dic = {'name':'alex','age': 73}
msg = f'我叫{dic["name"]},今年{dic["age"]}'
print(msg)

count = 7
print(f'最終結果:{count**2}')
name = 'barry'
msg = f'我的名字是{name.upper()}'
print(msg)

最終結果:49
我的名字是BARRY

Process finished with exit code 0

2.2 結合函數寫

# 結合函數寫:
def _sum(a,b):
    return a + b

msg = f'最終的結果是:{_sum(10,20)}'
print(msg)
# ! , : { } ;這些標點不能出現在{} 這裡面。

最終的結果是:30

Process finished with exit code 0

優點:

  1. 結構更加簡化。

  2. 可以結合表達式,函數進行使用。

  3. 效率提升很多。

5、迭代器:(重點)

1.可迭代對象

  • 字面意思:對象?python中一切皆對象。一個實實在在存在的值,對象。

    可迭代?:更新迭代。重覆的,迴圈的一個過程,更新迭代每次都有新的內容,

    可以進行迴圈更新的一個實實在在值。

    專業角度:可迭代對象? 內部含有'__iter__'方法的對象,可迭代對象。

    目前學過的可迭代對象?str list tuple dict set range 文件句柄

  • 獲取對象的所有方法並且以字元串的形式放在列表裡

語法:dir()
  • 判斷一個對象是否是可迭代對象
    • 用dir()獲取所有方法,如果內部含有'__iter__'方法,就是可迭代對象
#獲取對象的所有方法

# s1 = 'fjdskl'
# print(dir(s1))

l1=[1,2,3]
print(dir(l1))
print('__iter__' in dir(l1))    #列印結果True  就表示可迭代對象

['__add__', '__class__', '__contains__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__rmul__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'append', 'clear', 'copy', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort']

True

Process finished with exit code 0
  • 小結

    • 字面意思:可以進行迴圈更新的一個實實在在值。

    • 專業角度: 內部含有'__iter__'方法的對象,可迭代對象。

    • 判斷一個對象是不是可迭代對象: '__iter__' in dir(對象)

    • str list tuple dict set range

    • 優點:

      1. 存儲的數據直接能顯示,比較直觀。
      2. 擁有的方法比較多,操作方便。
    • 缺點:

      1. 占用記憶體。

      2. 不能直接通過for迴圈,不能直接取值(索引,key)。

2.迭代器的定義

  • 字面意思:更新迭代,器:工具:可更新迭代的工具。
  • 專業角度:內部含有'__iter__'方法並且含有'__next__'方法的對象就是迭代器。
  • 可以判斷是否是迭代器:'__iter__' and '__next__' 在不在dir(對象)

3.判斷一個對象是否是迭代器

#判斷文件是否是迭代器
with open('文件1',encoding='utf-8',mode='w') as f1:
    print(('__iter__' in dir(f1)) and ('__next__' in dir(f1)))

s1 = 'fjdag'
print('__next__' in dir(s1))

True
False

Process finished with exit code 0

4.可迭代對象如何轉化成迭代器

`iter([1,2,3])`
#或者
s1.__iter__()
s1 = 'fjdag'
obj=iter(s1)
#s1.__iter__()
print(obj)
print('__next__' in dir(obj))

<str_iterator object at 0x0000029B94D1A278>
True

Process finished with exit code 0

5.迭代器的取值

  • 一個next 取一個值

  • 多一個next 就會報錯

s1 = 'python'
obj=iter(s1)

print(next(obj))
print(next(obj))
print(next(obj))
print(next(obj))
print(next(obj))
print(next(obj))
p
y
t
h
o
n

Process finished with exit code 0
  • 報錯結果
Traceback (most recent call last):
  File "D:/PycharmProjects/zixue_python/day_01/12_迭代器.py", line 25, in <module>
    print(next(obj))
StopIteration

6.要迭代器有什麼用?

  • 當數據很大的時候,我要考慮把這些大批量數據存儲起來,需要節省記憶體,所以要考慮迭代器
  1. 節省記憶體。

    • 自己理解如何節省記憶體:比如定義一個列表,那根據記憶體機制,內置中會保存這個列表中所有數據,然後分配記憶體地址(即id)

      如果把這個列表轉換為迭代器,這時候next一次,取一個值,然後存儲這個值

  2. 惰性機制,next一次,取一個值。

7.while迴圈模擬for迴圈機制

l1 = [11,22,33,44,55,66,77,88,99,1111,1133,15652]
# 將可迭代對象轉化成迭代器。
obj = iter(l1)
while 1:
    try:
        print(next(obj))
    except StopIteration:
        break

8.可迭代對象與迭代器的對比

  • 可迭代對象是一個操作方法比較多,比較直觀,存儲數據相對少(幾百萬個對象,8G記憶體是可以承受的)的一個數據集。
  • 當你側重於對於數據可以靈活處理,並且記憶體空間足夠,將數據集設置為可迭代對象是明確的選擇。
  • 是一個非常節省記憶體,可以記錄取值位置,可以直接通過迴圈+next方法取值,但是不直觀,操作方法比較單一的數據集。
  • 當你的數據量過大,大到足以撐爆你的記憶體或者你以節省記憶體為首選因素時,將數據集設置為迭代器是一個不錯的選擇。

day12:

1、生成器(重點)

  • 生成器:python社區,生成器與迭代器看成是一種。生成器的本質就是迭代器。唯一的區別:生成器是我們自己用python代碼構建的數據結構。迭代器都是提供的,或者轉化得來的

1.獲取生成器的三種方式:

  • 生成器函數。
  • 生成器表達式。
  • python內置函數或者模塊提供。
    • (其實1,3兩種本質上差不多,都是通過函數的形式生成,只不過1是自己寫的生成器函數,3是python提供的生成器函數而已)

2.獲取迭代器的方式:

  • python提供的,比如文件句柄
  • 通過 iter 轉化的

3.生成器函數 獲得生成器:

  • 將函數中的return換成yield,這樣func就不是函數了,而是一個生成器函數
  • 讀取生成器的內容是,一個next 對應一個yield
#函數
# def func():
#     print(111)
#     print(222)
#     return 3
#
# ret=func()
# print(ret)

#生成器
def func():
    print(111)
    print(222)
    yield 3
    a=1
    b=2
    c=a+b
    print(c)
    yield 4

ret=func()
print(ret)
print(next(ret))
print(next(ret))

#一個next 對應一個yield

<generator object func at 0x000002974F4E60A0>
111
222
3
3
4
Process finished with exit code 0

面試題:return 和 yield 的區別?

  • return:函數中只存在一個return結束函數,並且給函數的執行者返回值。
  • yield:只要函數中有yield那麼它就是生成器函數而不是函數了。生成器函數中可以存在多個yield,yield不會結束生成器函數,一個yield對應一個next。

練習題:吃包子

  • 需求:老男孩向樓下賣包子的老闆訂購了5000個包子.包子鋪老闆非常實在,一下就全部都做出來了 
def func():
    l1=[]
    for i in range(1,5001):
        l1.append(f'{i}號包子')
    return l1

ret=func()
print(ret)
  • 這樣做沒有問題,但是我們由於學生沒有那麼多,只吃了2000個左右,剩下的8000個,就只能占著一定的空間,放在一邊了。如果包子鋪老闆效率夠高,我吃一個包子,你做一個包子,那麼這就不會占用太多空間存儲了,完美。(用生成器做)
def eat_func():
    for i in range(1,5001):
        yield f'{i}號包子'

ret=eat_func()
for i in range(200):
    print(next(ret))

思考:這兩者的區別:

第一種是直接把包子全部做出來,占用記憶體。

第二種是吃一個生產一個,非常的節省記憶體,而且還可以保留上次的位置。

4.yield from

  • 比如我yield 一個列表,我不想返回一個列表,想返回列表裡的內容,就用yield from,將這個列表變成了迭代器返回
#yield from
def func():
    l1=[1,2,3,4,5]
    yield l1

ret=func()
print(next(ret))
#得到的是一個列表
#但是我不希望是一個列表  ----(1)yield from   (2)用*解包
#(1)yield from
def func():
    l1=[1,2,3,4,5]
    yield from l1
    #將l1這個列表變成了迭代器返回
ret=func()
print(next(ret))

# (2)用*解包
def func():
    l1=[1,2,3,4,5]
    return l1

ret=func()
print(*ret)

[1, 2, 3, 4, 5]
1
1 2 3 4 5

Process finished with exit code 0

2.列表推導式,生成器表達式

  • 列表推導式:用一行代碼構建一個比較複雜有規律的列表

  • 列表推導式:

    • 迴圈模式:[變數(加工後的變數) for 變數 in iterable]
    • 篩選模式:[變數(加工後的變數) for 變數 in iterable if 條件]

1,列表推導式練習題:

#需求:創建一個有規律的列表,如1-10

#(1).常規方法:
l1=[]
for i in range(1,11):
    l1.append(i)
print(l1)

#(2).用列表推導式:
l2=[i for i in range(1,11)]
print(l2)

#用列表推導式(兩種模式)
#1.迴圈模式
#需求1:將10以內所有整數的平方寫入列表
l3=[i**2 for i in range(1,11)]
print(l3)

#需求2:將100以內所有偶數寫入列表
l4=[i for i in range(2,101,2) ]
print(l4)

#需求3:從python1期到python100期寫入列表
l5=[f'python{i}期' for i in range(1,101)]
print(l5)

#2.篩選模式  (在迴圈模式後面加上  if條件)

#需求1:將100以內所有偶數寫入列表
l6=[i for i in range(1,101) if i%2 == 0]
print(l6)

#需求2:將30以內能被3整取的數寫入列表
l7=[i for i in range(1,31) if i%3 ==0]
print(l7)

#需求3:過濾掉長度小於3的字元串列表,並將剩下的轉換成大寫字母
l8 = ['wusir', 'laonanhai', 'aa', 'b', 'taibai']
print([i.upper() for i in l8 if len(i)>3 ])

#需求4:找到嵌套列表中名字含有兩個‘e’的所有名字(有難度)
names = [['Tom', 'Billy', 'Jefferson', 'Andrew', 'Wesley', 'Steven', 'Joe'],
         ['Alice', 'Jill', 'Ana', 'Wendy', 'Jennifer', 'Sherry', 'Eva']]

#傳統方法:
l9=[]
for i in names:
    for name in i:
        if name.count('e')==2:
            l9.append(name)
print(l9)

#列表推導式:篩選模式
l10=[name for i in names for name in i if name.count('e')==2]
print(l10)

2,生成器表達式

  • 生成器表達式和列表推導式的語法上一模一樣,只是把[]換成()就行了。

    比如將十以內所有數的平方放到一個生成器表達式中

l11=(i for i in range(1,11))
print(l11)
print(next(l11))
print(next(l11))

<generator object <genexpr> at 0x00000264FE056048>
1
2

Process finished with exit code 0
  • 生成器表達式也可以進行篩選
# 獲取1-100內能被3整除的數
gen = (i for i in range(1,100) if i % 3 == 0)
for num in gen:
    print(num)

3.生成器表達式和列表推導式區別:

  1. 列表推導式比較耗記憶體,所有數據一次性載入到記憶體。而.生成器表達式遵循迭代器協議,逐個產生元素。
  2. 得到的值不一樣,列表推導式得到的是一個列表.生成器表達式獲取的是一個生成器
  3. 列表推導式一目瞭然,生成器表達式只是一個記憶體地址

3,內置函數

  • 函數就是以功能為導向,一個函數封裝一個功能,那麼Python將一些常用的功能(比如len)給我們封裝成了一個一個的函數,供我們使用
  • python 提供了68個內置函數
一帶而過:
all()  any()  bytes() callable() chr() complex() divmod() eval() exec() format() frozenset() globals() hash() help() id() input() int()  iter() locals() next()  oct()  ord()  pow()    repr()  round()

重點講解:
abs() enumerate() filter()  map() max()  min() open()  range() print()  len()  list()  dict() str()  float() reversed()  set()  sorted()  sum()    tuple()  type()  zip()  dir() 

未來會講:
classmethod()  delattr() getattr() hasattr()  issubclass()  isinstance()  object() property()  setattr()  staticmethod()  super()

eval() ---掌握

  • 執行字元串類型的代碼(外衣),並返回最終結果
#-----如將字元串類型轉化為數字類型
s1='1+3'
print(s1)
print(type(s1))

print(eval(s1))
print(type(eval(s1)))

1+3
<class 'str'>
4
<class 'int'>

Process finished with exit code 0
#-----如將字元串類型轉化為字典類型
s2='{"name":"mike"}'
print(s2,type(s2))
print(eval(s2),type(eval(s2)))

{"name":"mike"} <class 'str'>
{'name': 'mike'} <class 'dict'>

Process finished with exit code 0

exec()

  • 與eval幾乎一樣 ,執行字元串類型的代碼
msg="""
list=[]
for i in range(10):
    list.append(i)
print(list)
"""

print(msg)
exec(msg)

#**************************************************************************
list=[]
for i in range(10):
    list.append(i)
print(list)

[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

Process finished with exit code 0

hash()

  • hash:獲取一個對象(可哈希對象:int,str,Bool,tuple)的哈希值。
  • 加密演算法需要哈希值
print(hash('123456'))
4362549678203584146

Process finished with exit code 0
#使用python 進行 MD5加密介面測試:
import hashlib


password='123456'
obj=hashlib.md5(password.encode())
passwordmd5=obj.hexdigest()
print(passwordmd5)

e10adc3949ba59abbe56e057f20f883e

Process finished with exit code 0

help() ---掌握

  • help:函數用於查看函數或模塊用途的詳細說明。
print(help(str.split))

Help on method_descriptor:

split(...)
    S.split(sep=None, maxsplit=-1) -> list of strings
    
    Return a list of the words in S, using sep as the
    delimiter string.  If maxsplit is given, at most maxsplit
    splits are done. If sep is not specified or is None, any
    whitespace string is a separator and empty strings are
    removed from the result.

None

Process finished with exit code 0

callable() ---重點

  • callable:函數用於檢查一個對象是否是可調用的。如果返回True,object仍然可能調用失敗;但如果返回False,調用對象ojbect絕對不會成功。
s1='12332312'
def func():
    pass

print(callable(s1))
print(callable(func))

False
True

Process finished with exit code 0

bytes() ---重點

  • 把字元串轉換成bytes(位元組)類型

    #bytes
    s1='太白'
    b=s1.encode('utf-8')
    print(b)
    
    #或者用bytes將字元串轉換為位元組
    b=bytes(s1,encoding='utf-8')
    print(b)
    
    # 將位元組轉換成字元串
    c= str(b,encoding='utf-8')
    print(c)
    太白
    
    b'\xe5\xa4\xaa\xe7\x99\xbd'
    b'\xe5\xa4\xaa\xe7\x99\xbd'
    
    Process finished with exit code 0
    

print() 屏幕輸出

int():pass

str():pass

bool():pass

set(): pass

**list() **

  • 將一個可迭代對象轉換成列表

    l1='qweqeqweqweqweaaddff'
    l2=list(l1)
    print(l2)
    
    ['q', 'w', 'e', 'q', 'e', 'q', 'w', 'e', 'q', 'w', 'e', 'q', 'w', 'e', 'a', 'a', 'd', 'd', 'f', 'f']
    
    Process finished with exit code 0
    

**tuple() **

將一個可迭代對象轉換成元組

tu1 = tuple('abcd')
print(tu1)  
# ('a', 'b', 'c', 'd')

**dict() **

  • 通過相應的方式創建字典

    #dict
    #創建字典的方式
    #1.直接創建
    dic=dict([(1,'one'),(2,'two')])
    print(dic)
    
    #2.元組的解構
    dic=dict(one=1,twe=2)
    print(dic)
    
    #3.fromkeys
    #4.update
    #5.字典的推導式
    
    {1: 'one', 2: 'two'}
    {'one': 1, 'twe': 2}
    
    Process finished with exit code 0
    

**abs() **

  • 返回絕對值
i = -5
print(abs(i))  # 5

reversed()與reverse() ---重點

  • **reversed() ** 將一個序列翻轉, 返回翻轉序列的迭代器 ---

  • reverse() 對原列表進行倒序。翻轉

    l1=[i for i in range(10)]
    print(l1)
    l1.reverse()  #  對原列表進行倒序。翻轉
    print(l1)
    
    l2=[i for i in range(10)]
    print(l2)
    obj=reversed(l2)
    print(obj)
    print(next(obj))
    print(list(obj))
    
    [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
    [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
    
    [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
    <list_reverseiterator object at 0x000002409D57A1D0>
    9
    [8, 7, 6, 5, 4, 3, 2, 1, 0]
    
    Process finished with exit code 0
    

zip() 拉鏈方法 ---重點

  • 函數用於將可迭代的對象作為參數,將對象中對應的元素打包成一個個元組,然後返回由這些元祖組成的內容,如果各個迭代器的元素個數不一致,則按照長度最短的返回

    lst1 = [1,2,3]
    lst2 = ['a','b','c','d']
    lst3 = (11,12,13,14,15)
    
    obj=zip(lst1,lst2,lst3)
    print(obj)
    
    for i in zip(lst1,lst2,lst3):
        print(i)
        
    <zip object at 0x00000244D015A648>  
    (1, 'a', 11)
    (2, 'b', 12)
    (3, 'c', 13)
    
    Process finished with exit code 0
    

以下方法最最要:

min( ),max()

l1=[33,2,1,54,7,-1,-9]
print(min(l1))

#以絕對值的方式取最小值
#(1)第一種方法
l2=[]
def func(a):
    return abs(a)

for i in l1:
    l2.append(func(i))
print(l2)
print(min(l2))
#(2)第二種方法

l1=[33,2,1,54,7,-1,-9]
#print(min(l1,key=abs))

def abss(a):
    return abs(a)
'''
        第一次:a = 33  以絕對值取最小值  33
        第二次:a = 2  以絕對值取最小值  2
        第三次:a = 3  以絕對值取最小值  2
        ......
        第六次:a = -1   以絕對值取最小值  1
'''
print(min(l1,key=abss))
#凡是可以加key的:它會自動的將可迭代對象中的每個元素按照順序傳入key對應的函數中

1

Process finished with exit code 0

練習題:

dic={'a':3,
     'b':2,
     'c':1}
#求出值最小的鍵值
print(min(dic))    #a  min預設會按照字典的鍵去比較大小。
#
# def func(i):
#     return dic[i]
# print(min(dic,key=func))

#優化下函數--使用匿名函數
print(min(dic,key=lambda i:dic[i]))

a
c

Process finished with exit code 0

sorted() 排序函數

  • [ˈsɔːtɪd] sou 太d

  • 排序函數, 不是對原列表進行排序,返回的是一個新列表,預設從低到高

    l1=[22,33,1,2,8,7,6,5]
    l2=sorted(l1)
    print(l1)
    print(l2)
    
    [22, 33, 1, 2, 8, 7, 6, 5]
    [1, 2, 5, 6, 7, 8, 22, 33]
    
    Process finished with exit code 0
    
    l2=[('太白',18),('alex',73),('wu',35),('口天吳',41)]
    print(sorted(l2))
    
    #想按照成績去排序
    print(sorted(l2,key=lambda x:x[1]))  #返回的是一個列表 ,預設從低到高
    print(sorted(l2,key=lambda x:x[1],reverse=True))    #設置從高到底
    
    [('alex', 73), ('wu', 35), ('口天吳', 41), ('太白', 18)]
    [('太白', 18), ('wu', 35), ('口天吳', 41), ('alex', 73)]
    [('alex', 73), ('口天吳', 41), ('wu', 35), ('太白', 18)]
    
    Process finished with exit code 0
    

filter()

  • 篩選過濾(類似於列表推導式的帥選模式) 返回的是迭代器

  • 語法: filter(function,iterable)
    
    function: 用來篩選的函數,在filter中會自動的把iterable中的元素傳遞給function,然後根據function返回的True或者False來判斷是否保留此項數據
    
    iterable:可迭代對象
    
    # filter 篩選過濾
    l1=[2,3,4,1,6,7,8]
    #把小於3的元素剔除
    #(1)列表推導式篩選
    print([i for i in l1 if i>3])   #返回的是列表
    
    #(2)使用 filter
    ret=filter(lambda x:x>3,l1)    #返回的是迭代器
    print(ret)
    print(list(ret))
    
    [4, 6, 7, 8]
    <filter object at 0x00000181997B9828>
    [4, 6, 7, 8]
    
    Process finished with exit code 0
    

map()

  • 會根據提供的函數對指定的序列做映射。(相當於列表推導式的迴圈模式)
  • 通俗地講就是以參數序列中的每個元素分別調用參數中的函數(func()),把每次調用後返回的結果保存到返回值中
映射函數

語法: map(function,iterable) 可以對可迭代對象中的每一個元素進映射,分別取執行function
>>> def square(x):
>>>     return x ** 2
>>> map(square,[1,2,3,4,5])
<map at 0xbd26f28>
>>> list(map(square,[1,2,3,4,5]))
[1, 4, 9, 16, 25]
#map()
#計算列表中每個元素的平方,返回新列表
lst = [1,2,3,4,5]

#(1)列表推導式的迴圈模式
print([i**2 for i in lst])

#(2)map()
ret=map(lambda x:x**2,lst)
print(ret)
print(list(ret))

[1, 4, 9, 16, 25]
<map object at 0x000001DF48669080>
[1, 4, 9, 16, 25]

Process finished with exit code 0

4、匿名函數

語法:

函數名 = lambda 參數:返回值
fun1=lambda a,b:a+b
  1. 此函數不是沒有名字,他是有名字的,他的名字就是你給其設置的變數,比如func.
  2. lambda 是定義匿名函數的關鍵字,相當於函數的def.
  3. lambda 後面直接加形參,形參加多少都可以,只要用逗號隔開就行。
  4. 返回值在冒號之後設置,返回值和正常的函數一樣,可以是任意數據類型。
  5. 匿名函數不管多複雜.只能寫一行.且邏輯結束後直接返回數據
def  func(a,b):
    return a+b

#構建匿名函數
fun1=lambda a,b:a+b
print(fun1(1,2))

接下來做幾個匿名函數的小題:

寫匿名函數:接收一個可切片的數據,返回索引為0與2的對應的元素(元組形式)。

fun2=lambda a:(a[0],a[2])
print(fun2([22,33,44,55]))
(22, 44)

Process finished with exit code 0

5、閉包

5.1閉包的定義:

  1. 閉包是嵌套在函數中的函數。

  2. 閉包必須是內層函數對外層函數的變數(非全局變數)的引用。

閉包的現象:

  • 被引用的非全局變數也稱作自由變數,這個自由變數會與內層函數產生一個綁定關係。

  • 自由變數不會再記憶體中消失。

  1. '''
    由於閉包這個概念比較難以理解,尤其是初學者來說,相對難以掌握,所以我們通過示例去理解學習閉包。
    給大家提個需求,然後用函數去實現:完成一個計算不斷增加的系列值的平均值的需求。
    
    例如:整個歷史中的某個商品的平均收盤價。什麼叫平局收盤價呢?就是從這個商品一齣現開始,每天記錄當天價格,然後計算他的平均值:平均值要考慮直至目前為止所有的價格。
    比如大眾推出了一款新車:小白轎車。
    第一天價格為:100000元,平均收盤價:100000元
    第二天價格為:110000元,平均收盤價:(100000 + 110000)/2 元
    第三天價格為:120000元,平均收盤價:(100000 + 110000 + 120000)/3 元
    ........
    '''
    # 封閉的東西: 保證數據的安全。
    
    # 方案一:
    # l1=[]  #全局變數
    # def make_averager(new_value):
    #     l1.append(new_value)
    #     total=sum(l1)
    #     average=total/len(l1)
    #     return average
    #
    # print(make_averager(100000))
    # print(make_averager(110000))
    # print(make_averager(120000))
    
    # 方案二: 數據安全,l1不能是全局變數。
    # def make_averager(new_value):
    #     l1 = []
    #     l1.append(new_value)
    #     total = sum(l1)
    #     averager = total/len(l1)
    #     return averager
    
    # print(make_averager(100000))
    # print(make_averager(110000))
    # 此方案不行 因為每次執行的時候,l1列表都會重新賦值成空列表[]
    
    # 方案三: 閉包
    
    def make_averager():
        l1 = []
        def averager(new_value):
            l1.append(new_value)
            total = sum(l1)
            averager1 = total/len(l1)
            return averager1
        return averager
    
    avg=make_averager()      #avg這個變數得到是 averager這個函數名
    print(avg(100000))              #相當於執行---averager(100000)
    print(avg(110000))
    
    #思考:avg=make_averager() 這句代碼執行完,理論上l1這個列表消失了,可是為什麼沒有消失?
    #因為產生了閉包
    
    100000.0
    105000.0
    
    Process finished with exit code 0
    

5.2閉包的作用:

  • 保存局部信息不被銷毀,保證數據的安全性

5.3如何判斷一個嵌套函數是不是閉包

  • 1,閉包只能存在嵌套函數中
  • 2, 內層函數對外層函數非全局變數的引用(使用),就會形成閉包。
# 例一:
def wrapper():
    a = 1
    def inner():
        print(a)
    return inner
ret = wrapper()
#例一是閉包

# 例二:
a = 2
def wrapper():
    def inner():
        print(a)
    return inner
ret = wrapper()
#例二不是閉包
# 例三:

def wrapper(a,b):
    def inner():
        print(a)
        print(b)
    return inner
a = 2
b = 3
ret = wrapper(a,b)
#也是閉包

5.4如何用代碼判斷閉包?

  • 查看這個函數有沒有自由變數就行了
# 函數名.__code__.co_freevars 查看函數的自由變數
print(avg.__code__.co_freevars)  # ('series',)

# 函數名.__code__.co_varnames 查看函數的局部變數
print(avg.__code__.co_varnames)  # ('new_value', 'total')
# 函數名.__closure__ 獲取具體的自由變數對象,也就是cell對象。
# (<cell at 0x0000020070CB7618: int object at 0x000000005CA08090>,)
# cell_contents 自由變數具體的值
print(avg.__closure__[0].cell_contents)  # []

上面例三最難判斷是不是閉包,為了進一步確認,用代碼判斷閉包

# 例三:

def wrapper(a,b):
    def inner():
        print(a)
        print(b)
    return inner
a = 2
b = 3
ret = wrapper(a,b)

#如何用代碼判斷閉包?
print(ret.__code__.co_freevars)

('a', 'b')

Process finished with exit code 0

5.5閉包的應用

  1. 可以保存一些非全局變數但是不易被銷毀、改變的數據。
  2. 裝飾器。

5.6閉包面試題怎麼問?

  • 什麼是閉包? 閉包有什麼作用。

day14:

1、開放封閉原則:

裝飾器:裝飾,裝修,房子就可以住,如果裝修,不影響你住,而且體驗更加,讓你生活中增加了很多功能:洗澡,看電視,沙發。
器:工具。
開放封閉原則:
開放:對代碼的拓展開放的, 更新地圖,加新槍,等等。
封閉:對源碼的修改是封閉的。閃躲用q。就是一個功能,一個函數。 別人赤手空拳打你,用機槍掃你,扔雷.....這個功能不會改變。

裝飾器:完全遵循開放封閉原則。
所以裝飾器最終最完美的定義就是: 在不改變原函數的代碼以及調用方式的前提下,為其增加新的功能。
裝飾器就是一個函數。(裝飾器就是一個閉包)

2、裝飾器的初識:

  • 版本一: Mike 寫一些代碼,測試一下index()函數的執行效率。

    #
    import time
    #print(time.time())  # 格林威治時間。
    
    def index():
        '''有很多代碼.....'''
        time.sleep(2)  # 模擬的網路延遲或者代碼效率
        print('歡迎登錄博客園首頁')
    
    def dariy():
        '''有很多代碼.....'''
        time.sleep(3) # 模擬的網路延遲或者代碼效率
        print('歡迎登錄日記頁面')
    
    start_time=time.time()
    index()
    end_time=time.time()
    print(end_time-start_time)
    
    start_time=time.time()
    dariy()
    end_time=time.time()
    print(end_time-start_time)
    
    歡迎登錄博客園首頁
    2.0008764266967773
    歡迎登錄日記頁面
    3.0002834796905518
    
    Process finished with exit code 0
    

版本一的問題:如果測試別人的代碼,必須重新複製粘貼。

  • 版本二:利用函數,解決代碼重覆使用的問題(解決版本一問題)

    #版本二:利用函數,解決代碼重覆使用的問題(解決版本一問題)
    
    import time
    def index():
        '''有很多代碼.....'''
        time.sleep(2) # 模擬的網路延遲或者代碼效率
        print('歡迎登錄博客園首頁')
    
    def dariy():
        '''有很多代碼.....'''
        time.sleep(3) # 模擬的網路延遲或者代碼效率
        print('歡迎登錄日記頁面')
    
    def timmer(f):
        start_time = time.time()
        f()
        end_time = time.time()
        print(f'測試本函數的執行效率:{end_time-start_time}')
    
    timmer(index)
    
    #版本二還是有問題: 原來index函數源碼沒有變化,給原函數添加了一個新的功能測試原函數的執行效率的功能。
    #滿足開放封閉原則麽?原函數的調用方式改變了。
    
    歡迎登錄博客園首頁
    測試本函數的執行效率:2.0008556842803955
    
    Process finished with exit code 0
    
  • 版本三:不能改變原函數的調用方式。

    #版本三:不能改變原函數的調用方式。
    import time
    def index():
        '''有很多代碼.....'''
        time.sleep(2) # 模擬的網路延遲或者代碼效率
        print('歡迎登錄博客園首頁')
    
    
    def timmer(f):   # f = index  (funciton index123)
        def inner():   # inner :(funciton inner123)
            start_time = time.time()
            f()   #index() (funciton index123)
            end_time = time.time()
            print(f'測試本函數的執行效率:{end_time-start_time}')
        return  inner    # (funciton inner123)
    
    # ret=timmer(index)
    # ret()   #相當於inner()
    index=timmer(index)   # inner (funciton inner123)
    index()  # inner()
    
  • 版本四:具體研究(裝飾器的本質就是閉包)

    import time
    def index():
        '''有很多代碼.....'''
        time.sleep(2) # 模擬的網路延遲或者代碼效率
        print('歡迎登錄博客園首頁')
    
    def timmer(f):
        f = index
        # f = <function index at 0x0000023BA3E8A268>
        def inner():
            start_time = time.time()
            f()
            end_time = time.time()
            print(f'測試本函數的執行效率{end_time-start_time}')
        return inner
    
    index = timmer(index)
    index()
    
  • 版本五:python做了一個優化;提出了一個語法糖的概念。 標準版的裝飾器

    (@timmer #等於index = timmer(index))

    #版本五:python做了一個優化;提出了一個語法糖的概念。 標準版的裝飾器
    import time
    
    #timmer裝飾器
    def timmer(f):
    
        def inner():
            start_time = time.time()
            f()
            end_time = time.time()
            print(f'測試本函數的執行效率:{end_time-start_time}')
        return inner
    
    
    @timmer    #等於index = timmer(index)
    def index():
        '''有很多代碼.....'''
        time.sleep(2) # 模擬的網路延遲或者代碼效率
        print('歡迎登錄博客園首頁')
    
    @timmer
    def dariy():
        '''有很多代碼.....'''
        time.sleep(3) # 模擬的網路延遲或者代碼效率
        print('歡迎登錄日記頁面')
    
    
    # index = timmer(index)
    # index()
    #
    # dariy = timmer(dariy)
    # dariy()
    
    index()
    dariy()
    
  • 版本六:被裝飾函數帶返回值

    #版本六:被裝飾函數帶返回值
    import time
    
    #timmer裝飾器
    def timmer(f):
    
        def inner():
            start_time = time.time()
            r=f()
            #print(f'這個是f():{f()}!!!')
            end_time = time.time()
            print(f'測試本函數的執行效率:{end_time-start_time}')
            return r
        return inner
    
    @timmer    #等於index = timmer(index)
    def index():
        '''有很多代碼.....'''
        time.sleep(0.6) # 模擬的網路延遲或者代碼效率
        print('歡迎登錄博客園首頁')
        return 666
    
    # 加上裝飾器不應該改變原函數的返回值,所以666 應該返回給我下麵的ret,
    # 但是下麵的這個ret實際接收的是inner函數的返回值,而666返回給的是裝飾器裡面的
    # f() 也就是 r,我們現在要解決的問題就是將r給inner的返回值。
    ret=index()
    print(ret)
    
    歡迎登錄博客園首頁
    測試本函數的執行效率:0.6006889343261719
    666
    
    Process finished with exit code 0
    
  • 版本七:被裝飾函數帶參數

    #版本七:被裝飾函數帶參數
    import time
    
    #timmer裝飾器
    def timmer(f):
    
        def inner(*args,**kwargs):
            #  函數的定義:* 聚合  args = ('李舒淇',18)
            start_time = time.time()
            r=f(*args,**kwargs)
            # 函數的執行:* 打散:f(*args) --> f(*('李舒淇
    
您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 一、繼承的基本概念 ​ 繼承:子類繼承父類的屬性和行為 ​ 作用:代碼復用 繼承分類: 1. 按訪問屬性分為public、private、protected三類 1)public: 父類屬性無更改,pubic, private, protected 仍是自己本身(子類成員函數可以訪問父類的publi ...
  • 引言 使用Spring Innitializer創建SpringBoot項目 解決方案 引言 筆者使用的IDEA版本為2020.2.4, 在使用Spring Innitializer創建SpringBoot項目後, SpringBoot項目無法被識別IDEA正常識別, 本文將來解決這個問題. 使用S ...
  • 1、錯誤提示信息如下: com.alibaba.fastjson.JSONException: exepct '[', but string, pos 4, json : "[{"attrId":33,"attrName":"粗跟"},{"attrId":44,"attrName":"厚底"}]" ...
  • Java流程式控制制:用戶交互Scanner、選擇結構 用戶交互Scanner Scanner類用於獲取用戶的輸入 基本語法: Scanner s = new Scanner(System.in);s.close(); package com.qiu.first.scanner;​import java ...
  • 1 func absInt(x int) int { 2 if x < 0 { 3 return -x 4 } 5 return x 6 } 下麵會用到此方法, int類型值取絕對值 1 type sp_item struct { 2 x int 3 y int 4 g int 5 h int 6 ...
  • (Java刷題常用的數據結構總結) 1. 基礎運算 //int型相關操作 Integer.INT_MAX;//int型最大值 Integer.INT_MIN;//int型最小值 long name;//註意:沒有c語言裡面的long long (int)n1%(int)n2;//取餘運算,針對int ...
  • 歡迎關註個人公眾號:愛喝可可牛奶 LeetCode 39. 組合總和 40.組合總和II 131.分割迴文串 LeetCode 39. 組合總和 分析 回溯可看成對二叉樹節點進行組合枚舉,分為橫向和縱向 每次往sum添加新元素時,必須明確從can哪個位置開始,定義變數pos 返回條件 sum == ...
  • 一、m_sequencer 1、什麼是m_sequencer m_sequencer是定義在uvm_sequencer_item中的,uvm_sequencer_base類型的句柄,也就是說 m_sequencer是uvm_sequencer_item的成員變數 m_sequencer是指向uvm_ ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...