0 global和nonlocal關鍵字

注意:global是把該變數宣告為全域變數,而nonlocal是把該變數宣告為上一級外部函式的區域變數
x = -1
def outer():
x = 0
def inner():
nonlocal x
x = 1
print('inner:', x)
inner()
print('outer:', x)
outer()
print('global:', x)!

x = -1
def outer():
x = 0
def inner():
global x
x = 1
print('inner:', x)
inner()
print('outer:', x)
outer()
print('global:', x)

1 可迭代協議和迭代器協議
可迭代協議:凡是含有__iter__方法的都是可迭代的,(在python中,可迭代的資料型別有字串,串列,元組,字典,集合,列舉,range等,int和布爾型別不包含__iter__方法)
迭代器協議:凡是含有__iter__和__next__方法的都是迭代器
For回圈的實質就是在呼叫__next__方法
l = [1, 2, ‘8hjg’, 0]
for i in l:
pass
‘’’
for回圈的實質為:
- iterator = l.iter()#可迭代的資料型別使用__iter__方法后,就回傳了一個迭代器
- iterator.next()#對于迭代器使用__next__方法后,相當于從迭代器中一個一個取值
- 當出現false后,執行結束
‘’’
迭代器的優勢:
a)迭代器中的值會一個一個的被取出
b)節省記憶體空間,回圈一次產生一個變數,而不是一次性全部產生
尤其是對于檔案句柄和range()而言,特別節省記憶體空間
2生成器
生成器的本質還是迭代器,凡是含有yield關鍵字的函式都是生成器函式
0)yield不能與return混用,且必須寫在函式內部
1)生成器函式在被執行后會回傳一個生成器作為回傳值 ,而函式內部的陳述句并不會被執行
2)一個簡單的生成器函式的例子


列印結果如下圖:

簡單介紹一下return和yield的區別吧:
當一個程式執行到return時,程式結束,return之后的陳述句不會再執行,換句話說,return就是程式結束的標志,而yield不會引起程式結束,當程式運行到yield時,相當于暫停了,如果下一次又呼叫函式,需要執行yield時,接著上次暫停的地方繼續執行(不知道這么理解對不對)
我個人喜歡用一些生活中的例子來進行類比:
執行程式相當于在跑馬拉松,當出現return時,相當于選手退出比賽,當出現yield時,相當于選手暫停休息了一會,當生成器函式被再次呼叫時,選手又接著上次暫停的地方繼續開始跑,直到出現下一個yield
說到跑步的例子,我就不得不再說一下break和continue的區別了:
這也不能說是我思維混亂,只能說思維比較跳躍吧
由于break和continue一般是在回圈中使用的,所以類比成在操場跑圈吧
break相當于選手直接退賽,不再參與跑圈這一運動,而continue相當于選手放棄了這圈中沒有跑完的這一部分,直接從下一圈的起點開始跑
3 生成器進階知識
0)一個實體:
def genreatoion():
print(123)
count = yield 1
print('=====', count)
yield 2
g = genreatoion()
ret = g.__next__()
print(ret)
ret = g.send('hello')
print(ret)

我們注意到,在上面這段代碼中,我們使用了send方法,而且不同于之前的yield形式,我們對yield進行了一個賦值,
注意:# send和__next__功能相似,send在獲取下一個值的時候,將上一個yield位置賦值給一個變數
使用send的兩個注意點:
第一次使用生成器函式時,必須先使用__next__函式,因為send要獲取上一個yield位置,經過驗證,可以用send(None)來代替 # 最后一個yield不接受外部的值
1)一個移動平均值的例子:
def average():
ave = 0
sum = 0
count = 0
while True:
num = yield ave
sum += num
count += 1
ave = sum/count
g = average()
g.__next__()
ave1 = g.send(10)
ave1 = g.send(20)
print(ave1)
這個代碼雖然很簡單,但是在while中,num = yield ave這條陳述句的位置很重要,如果這條陳述句放在回圈的最后一句,執行就會出現錯誤,因為num沒有被定義
2)我們也可以把g.__next__這條陳述句放在裝飾器中
def init(func):
def inner(*args, **kwargs):
ret1 = func(*args, **kwargs)
ret1.__next__()
return ret1
return inner
@init#init = init(average)
def average():
ave = 0
sum = 0
count = 0
while True:
num = yield ave
sum += num
count += 1
ave = sum/count
g = average()
ave1 = g.send(10)
ave1 = g.send(20)
print(ave1)
這段代碼相當于裝飾器和生成器進階的融合,一定要好好理解
4yield from 陳述句
0)在python2中,如果要逐個獲取字串中每一個變數,只能使用如下方式
def geration():
a = '67890'
b = '12345'
for i in a:
yield I
for i in b:
yield i
g = geration()
for i in g:
print(i)
1) 在python3中,更新了yield from陳述句,它的使用如下;
def geration():
a = '67890'
b = '12345'
yield from a
yield from b
g = geration()
for i in g:
print(i)
兩種方法的結果都是一樣的,所以在今后的學習中,遇到了yield from陳述句,一定不要驚訝

5 串列推導式和生成器運算式
0)串列推導式
egg_list = ['雞蛋%d' % i for i in range(10)]
print(egg_list)

1)生成器運算式
egg_list1 = ('雞蛋%d' % i for i in range(10))
print(egg_list1)
for i in egg_list1:
print(i)

生成器運算式回傳的永遠是一個生成器,只有對生成器中的每一個元素進行輸出,才能達到我們想要的效果
兩者的區別:
0)串列推導式使用的是[] 而生成器運算式使用的是()
1) 與串列推導式相比較,生成器運算式幾乎不占用記憶體
因為它一次取一個,而串列推導式一次全部都取出來了
2)生成器運算式回傳的是一個生成器,這也是最關鍵的一點
6 各種串列推導式
0)30以內能被3整除的數的平方
egg_list = [i*i for i in range(30) if i % 3 == 0]
print(egg_list)
注意:一個完整的串列推導式要有if,if的作用相當去篩選,沒有if相當于遍歷

1)嵌套串列中,e出現2次的名字
先for大串列再for小串列
7 字典推導式
0)將字典的key和value對調
mask = {'a': 10, 'b': 20}
antimask = {mask[k]: k for k in mask}
print(antimask)

這里簡單介紹一下什么是可哈希的,什么是不可哈希的?
不嚴謹但易懂的解釋:
一個物件在其生命周期內,如果保持不變,就是hashable(可哈希的),
hashable ≈ imutable 可哈希 ≈ 不可變
在Python中:
list、set和dictionary 都是可改變的,比如可以通過list.append(),set.remove(),dict[‘key’] = value對其進行修改,所以它們都是不可哈希的;
而tuple和string是不可變的,只可以做復制或者切片等操作,所以它們就是可哈希的,
總結一下,可哈希的資料型別有元組,字串,以及字典的鍵
8 集合推導式
0)尋找集合中每一個元素的平方
suqrat = {i*i for i in [1, -1, 2]}
print(suqrat)

注意:集合本身具有去重功能
為什么沒有元組推導式呢?其實這是一個很有趣的問題!!!
我在最后會公布答案
9 直接賦值,淺拷貝和深拷貝
直接賦值:相當于給原物件新貼了一個標簽
a = 1
b = a
print('a:', id(a))
print('b:', id(b))


淺拷貝:a 和 b 是獨立的物件,但他們的子物件還是指向統一物件(是參考),
使用淺拷貝和深拷貝時,需要引入copy模塊,即要在開始前加上import copy
import copy
a = [1, {1, 2, 3}]
b = copy.copy(a)
a.append(2)
print(a, b)
a[1].add(4)
print(a, b)


深拷貝:a和b是獨立的物件,他們的子物件也是獨立的物件
import copy
a = [1, {1, 2, 3}]
b = copy.deepcopy(a)
a.append(2)
print(a, b)
a[1].add(4)
print(a, b)


有人把直接賦值比喻成:舊瓶裝舊酒;淺拷貝比喻成:新瓶裝舊酒;深拷貝比喻成:新瓶裝新酒,我覺得很有意思
知道為什么沒有元組推導式嗎?
因為
因為元組的符號是(),這個符號已經被生成器運算式用了,哈哈哈!
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/163559.html
標籤:其他
