摘要:進階細節根據慕課網七月老師視頻整理一切皆對象對與來說,一切皆對象,包括函數。閉包的意義在于返回了一個現場,如果沒有閉包,很容易被外部的變量所影響。重復定義可見此時返回了不再是閉包了。注意該方法的返回值只能是布爾類型,即或。
Python進階細節
根據慕課網七月老師視頻整理
一切皆對象對與Python來說,一切皆對象,包括函數。在其他語言比如c++中,函數只是一段可執行的代碼,只要你獲得入口地址就可以調用這段代碼。但是Python中不一樣,Python中一切皆對象。Python中的函數,可以作為另一個函數的參數傳入到另外一個函數里,也可以當作另外一個函數的返回值,甚至可以賦值給一個變量。
def a(): pass print(type(a))閉包# 可見是一個class,是一個類。
閉包指的是:函數+環境變量(環境變量不能是全局變量)。
python在函數內部還可以定義函數,但該函數作用域只在外部函數內部有效,除非作為外部函數的返回值被返回,在外部函數的外部用一個變量接收后就可以調用它了。
def curve_pre(): a = 25 # 這里定義了環境變量 def curve(x): return a*x*x return curve a = 10 # 在外部定義了a為10 f = curve_pre() print(f(2)) print(f.__closure__) # 可以通過這個內置變量來查看閉包里的內容 print(f.__closure__[0].cell_contents) 100 (,) 25 |
可見返回了一個對象。你再在外面定義了變量,也不會改變閉包內的環境變量。
閉包的意義在于返回了一個現場,如果沒有閉包,很容易被外部的變量所影響。
閉包返回現場的環境變量,不能在閉包里定義的函數里面再被定義了,而且函數里必須要有調用環境變量的地方,否則就不叫做閉包了。
def f1(): a = 20 def f2(): a = 10 # 重復定義 return a return f2 f = f1() print(f.__closure__) None # 可見此時返回了None,不再是閉包了。本質上是認為此時a被認為是一個局部變量,不再是環境變量了! -------------------------------------------------------------------------- # 如果想要環境變量在函數里被改變,可以這樣: def f1(): a = 25 def f2(): nonlocal a # nonlocal關鍵字強制讓a不再為局部變量,跳到上一級作為了環境變量。 a = a + 10 return a return f2 f = f1() print(f.__closure__) print(f()) print(f()) print(f()) (2. 閉包的優點,) 35 45 55 # 可以看到a的值是可以保存的,這是因為閉包的環境變量具有保存現場的功能,記憶住上次調用的狀態,所以可以這樣做。 --------------------------------------------------------------------------- def f1(): a = 20 def f2(): return 2 # 里面不再調用a了 return f2 f = f1() print(f.__closure__) None # 可見此時仍然不是閉包 --------------------------------------------------------------------------- def f1(): a = 20 def f2(): s = a+20 return 2 return f2 f = f1() print(f.__closure__) ( | ,) # 可見就算返回值里不包括a,但是只要調用了,就可以是一個閉包。 |
從閉包可以看出函數式編程的優點。如果出現需要保存值進行迭代的情況,就不得不定義一個全局變量來保存上一次的值。但是在閉包里不需要使用到全局變量,只需要閉包里定義的環境變量即可記憶上一次的狀態,這樣就具有了封閉性,否則過多的使用全局變量會使代碼變得混亂。這里再注意一個問題:
a = 10 def f1(x): a_new = a + x a = a_new print(f1(5)) Traceback (most recent call last): File "c4.py", line 7, in3. 閉包的一個經典例子print(f1(5)) File "c4.py", line 4, in f1 a_new = a + x UnboundLocalError: local variable "a" referenced before assignment # 看起來美滋滋其實報錯了。再Python里,如果再定義了a的話,無論是在哪里,在定義的時候系統會默認a是局部變量不再是全局變量了。所以在執行代碼的時候,就會出現了找不到局部變量a的情況,因為f1中第一段代碼中用到了局部變量a,但是此時還沒有定義啊。 ---------------------------------------------------------------------- # 可以這么解決: a = 10 def f1(x): global a # 定義global關鍵字強制認為是全局變量 a_new = a + x a = a_new return a print(f1(5)) print(f1(10)) 15 25 # 可見這時候全局變量起到了保存的功能,但相對閉包,就顯得很Low了。
def testFun(): temp = [lambda x : i*x for i in range(4)] return temp for everyLambda in testFun(): print (everyLambda(2)) # 運行后結果竟然是: 6 6 6 6
這里testfun()返回一個temp,temp是一個列表,everyLambda每次返回的都是temp里列表的值,參數2賦給x。
三元表達式python中的三元表達式和其他語言中的不太一樣。
條件為真時返回的結果 if 判斷條件 else 條件為假時返回的結果
x if x > y else y
其他很多語言中是這么定義的:
x > y ? x:y
map不是一個函數而是一個類。map和lambda表示結合起來一起用會非常好。
map(func, *iterables) --> map object
iterables是可迭代的類型,序列和元組都可以。*號表示是可變參數,可以傳入多個不同值的取值。
a = [1,2,3,4,5] def square(x): return x*x r = map(square, a) print(r) print(list(r))匿名函數(lambda表達式)
lambda表達式也叫做匿名函數。
lambda的定義:lambda parameter_list: expression
expression意思是表達式,所以后面只能是表達式,不能是語句,比如賦值語句等是不可以的。
lambda表達式最后返回的是表達式計算出的值,和return后是一樣的。
def add(x, y): return x+y lambda x, y : x + y
lambda表達式一般和三元表達式和map連接在一起用會更加整潔。
x = 1,2,3,4,5,6 y = 1,2,3,4,5,6 r = map(lambda x,y:x+y, x,y) print(tuple(r)) (2, 4, 6, 8, 10, 12)reduce函數
reduce 是一個函數。
def reduce(function, sequence, initial=None)
function : 這里需要注意函數必須且只能有兩個參數
sequence: 序列
initial: 初始值
reduce的含義是連續調用函數進行連續計算
from functools import reduce # 需要引入這個函數才可以使用 list_x = ["1","2","3"] r = reduce(lambda x,y:x+y, list_x, "a") print(r) a123 # 執行情況 :(("a"+"1")+"2")+"3" # 每次執行完一次函數的結果在下一次調用函數的時候會傳入到函數的參數中進行計算。初始值是給出的開始的參數之一。filter類
class filter(function or None, iterable)
表示過濾不符合條件的值。當函數返回為True時保留,False時剔除。當函數為None時,剔除調iterable中本來就為False的值
# ord()返回ascII碼值 list_x = ["a", "B", "c", "D"] r = filter(lambda x: False if ord(x)>64 and ord(x)<91 else True, list_x) print(list(r)) ["a", "c"]裝飾器
編寫代碼一個原則是:對修改是封閉的,對拓展是開放的。
如果想在很多個函數里,每個函數都實現相同的功能,用裝飾器是最方便的,不用在每個函數里重復定義,而且調用起來很方便,和“裝飾”的意思很像。
import time def decorator(func): def wrapper(*args, **kw): # *args是可變參數,**kw是可變關鍵字參數,這樣可以接受除了有默認參數類型以外所有類型的函數參數 print(time.time()) func(*args, **kw) return wrapper # 就是一個閉包,傳入的函數func是環境變量 @decorator # @ 是一個語法糖 def f1(func_name): print("this is f1" + func_name) @decorator def f2(func_name1, func_name2): print("this is f2" + func_name1 + func_name2) @decorator def f3(func_name1, func_name2="f3",*args, **kw): print("this is f3"+func_name1+func_name2) print(kw) f1("f1") # 可見雖然在定義的時候麻煩了一些,但是調用的時候很方便。 f2("f2","f2") f3("f3","f3",a="1",b="2",c="3") # 可變關鍵字參數 1519276076.973657 #時間戳 this is f1f1 1519276076.9746575 this is f2f2f2 1519276076.9746575 this is f3f3f3 {"a": "1", "b": "2", "c": "3"}生成器
通過列表生成式可以直接創建一個列表,但是我們如果只想訪問前面幾個元素,不想利用后面的元素,我們可以定義一個生成器(generator),一邊循環一邊計算,有一種方法很簡單,只要把列表生成式的[]改為()就可以創建一個generator.
L = [x * x for x in range(10)] L [0, 1, 4, 9, 16, 25, 36, 49, 64, 81] g = (x * x for x in range(10)) gat 0x104feab40>
如果需要打印出來元素,可以通過生成器的next()方法。
g.next() 0 g.next() 1 g.next() 4
generator是可以迭代的:
g = (x * x for x in range(10)) for n in g: ... print n ... 0 1 4 9
可以把一個函數寫成生成器,把return改為yield:
def fib(max): n, a, b = 0, 0, 1 while n < max: yield b a, b = b, a + b n = n + 1 fib(6)for n in fib(6): ... print n ... 1 1 2 3 5 8
定義成生成器后,generator的執行流程和函數并不一樣,函數是順序執行,遇到return語句或者最后一行函數語句就返回。而變成generator的函數,在每次調用next()的時候執行,遇到yield語句返回,再次執行時從上次返回的yield語句處繼續執行。
另外一個例子:
def odd(): ... print "step 1" ... yield 1 ... print "step 2" ... yield 3 ... print "step 3" ... yield 5 ... o = odd() o.next() step 1 1 o.next() step 2 3 o.next() step 3 5 o.next() Traceback (most recent call last): File "字典來代替swich", line 1, in StopIteration
Python中沒有swich這種結構,可以用字典來實現。
day = 0 def get_monday(): return "Monday" def get_Tuseday(): return "Tuseday" def get_Wednesday(): return "Wednesday" def default(): return "Unknow" #返回函數可以定義跟多操作,也可以直接返回值 swicher = { 0: get_monday, 1: get_Tuseday, 2: get_Wednesday } # 字典的get方法可以獲取鍵對應的值,如果鍵不存在則返回指定的默認值 day_time = swicher.get(day, default())() # 后面再加一個括號來調用函數 print(day_time) monday列表推導式
對于列表推導式,其實不只是列表,也可以是元組,集合,字典進行推倒
a = [1,2,3,4,5] b = [i**2 for i in a if i>=3] print(b) [9,16,25] --------------------------------------------------- # 對于字典可以這樣: sdict = { "q":"烈焰沖擊", "w":"天女散花", "e":"致命一擊" } dic = {value:key for key,value in sdict.items()} # 最外面加花括號就是字典或者集合 dic1 = [key for key,value in sdict.items()] dic2 = (key for key,value in sdict.items()) print(dic) print(dic1) print(dic2) # 如果是元組,元組是不可遍歷的對象,所以需要下面這樣取出對象 for i in dic2: print(i,end=" ") {"烈焰沖擊": "q", "天女散花": "w", "致命一擊": "e"} ["q", "w", "e"]Noneat 0x0000021A3430E150> q w e
None代表空,并不是False,也不是[],""
print(type(None)) print(type(False)) print(type([])) print(type(""))
我們可以看見,這些在本質上都是不一樣的,類型都不一樣,只不過我們在進行邏輯判斷的時候,有時會像下面這樣做:
a = None/false/[]/"" if not a: ..... # 判斷的時候會這樣做
不建議使用if a is None:這種語句,我們可以看到類型是不一樣的,有時會出錯。
對象存在不一定是True在上面對None的分析中,在邏輯判斷的時候之所以可以判斷None為False,是因為每個對象和bool類型之間都是有聯系的,所以可以進行邏輯判斷。但是我們自定義的對象卻不一定了,返回True和False因不同的對象而不同。我們自定義的對象,如類,和我們的內置方法有關系。
類中有兩個內置方法會影響對類的布爾類型的取值:
__len__:這個內置方法返回的是類的長度,外部調用len()時會返回該方法的返回值,返回值只有布爾類型或者int類型。
__bool__:這個內置方法返回的類的bool類型的取值,當這個方法在類里面定義以后,返回值只看其返回值,__len__的返回值不再起作用。注意該方法的返回值只能是布爾類型,即True或False。
class Rest(): def __len__(self): return 5 def __bool__(self): return False print(len(Rest())) print(bool(Rest())) 5 False裝飾器的副作用
加上裝飾器后會改變函數的名字。
def decorator(func): def wrapper(): print("this is decorator") func() return wrapper @decorator def f1(): print(f1.__name__) # 打印函數名字,不加裝飾器是f1 f1() this is decorator wrapper
可見會出錯,加上裝飾器后,如果不想改變名字,可以這樣做:
from functools import wraps def decorator(func): @wraps(func) def wrapper(): print("this is decorator") func() return wrapper @decorator def f1(): print(f1.__name__) # 打印函數名字,不加裝飾器是f1 f1() this is decorator f1可哈希(hashable)對象和不可變性(immutable)
可哈希(hashable)和不可改變性(immutable)
如果一個對象在自己的生命周期中有一哈希值(hash value)是不可改變的,那么它就是可哈希的(hashable)的,因為這些數據結構內置了哈希值,每個可哈希的對象都內置了__hash__方法,所以可哈希的對象可以通過哈希值進行對比,也可以作為字典的鍵值和作為set函數的參數,但是list是unhashable,所以不可以作為字典的鍵值。所有python中所有不可改變的的對象(imutable objects)都是可哈希的,比如字符串,元組,也就是說可改變的容器如字典,列表不可哈希(unhashable)。我們用戶所定義的類的實例對象默認是可哈希的(hashable),它們都是唯一的,而hash值也就是它們的id()。
哈希
它是一個將大體量數據轉化為很小數據的過程,甚至可以僅僅是一個數字,以便我們可以用在固定的時間復雜度下查詢它,所以,哈希對高效的算法和數據結構很重要。
不可改變性
它指一些對象在被創建之后不會因為某些方式改變,特別是針對任何可以改變哈希對象的哈希值的方式
聯系
因為哈希鍵一定是不可改變的,所以它們對應的哈希值也不改變。如果允許它們改變,,那么它們在數據結構如哈希表中的存儲位置也會改變,因此會與哈希的概念違背,效率會大打折扣
具體的我們可以參考官方文檔和以下博客:
關于python內置__eq__函數的探索
關于可哈希對象的理解
1.賦值方法:
list1 = [1,2,3] list2 = list1 print(id(list1),id(list2)) 2577180416904 2577180416904
可見這和在c語言中不一樣,二者的id是一樣的,換句話說,你改變 list2,同時也會改變 list1
2.淺拷貝:
import copy list1 = [1,2,3] list2 = copy.copy(list1) print(id(list1),id(list2)) list2.append(4) print(list1,list2) 2522465131400 2522465130824 [1, 2, 3] [1, 2, 3, 4]
但是淺拷貝,對于里面的元素,如果是不可變類型,會直接拷貝,但是對于可變類型只是指向它而已,例如看下面的代碼:
import copy list1 = [1,2,[1,2,3]] list2 = copy.copy(list1) print(id(list1),id(list2)) list2[2].append(4) print(list1,list2) 2710366738760 2710368236680 [1, 2, [1, 2, 3, 4]] [1, 2, [1, 2, 3, 4]] # 都變化了
3.深拷貝
import copy list1 = [1,2,[1,2,3]] list2 = copy.deepcopy(list1) print(id(list1),id(list2)) list2[2].append(4) print(list1,list2) 1660389185864 1660390683784 [1, 2, [1, 2, 3]] [1, 2, [1, 2, 3, 4]]
可見深拷貝才是真正的拷貝。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/44702.html
摘要:很簡單,這個模塊實現了開辟一塊共享內存空間,就好比中的方法一樣,有興趣的同學可以去查閱。查了下資料,返回的對象控制了一個進程,可用于多進程之間的安全通信,其支持的類型有和等。 有關于 multiprocessing 中共享變量的問題 現在的cpu都很強大,比方我用的至強2620有24核可以同時工作,并行執行進程程序。這在計算密集型的程序是很需要的,如沙漠中的綠洲,令人重獲新生。那么,問...
摘要:楚江數據是專業的互聯網數據技術服務,現整理出零基礎如何學爬蟲技術以供學習,。本文來源知乎作者路人甲鏈接楚江數據提供網站數據采集和爬蟲軟件定制開發服務,服務范圍涵蓋社交網絡電子商務分類信息學術研究等。 楚江數據是專業的互聯網數據技術服務,現整理出零基礎如何學爬蟲技術以供學習,http://www.chujiangdata.com。 第一:Python爬蟲學習系列教程(來源于某博主:htt...
摘要:學單片機多去官網上查資料,下載手冊,像我入門的單片機經常去官網,還有學的系列板子,公司的官網的官方例程給的很詳細,在英文視角閱讀對你大有益處。 目錄 1.C語言經典 2.單片機系列 3.Python方面 4.嵌入式LWip協議 5.Android 6.C++經典書籍 7.Linux開發 ...
摘要:可選控制所返回的函數的屬性。模式被用到,因為定義函數需要用多個語句。聚合全部內容,并將動態創建的函數指定給一個變量。寫完之后,我偶然發現,在自己列的計劃轉載清單中,有這一篇相關的文章,它介紹了動態定義函數的方法。 showImg(https://segmentfault.com/img/bVbp3eZ?w=4272&h=2828);標題:Python Tips: Dynamic fun...
閱讀 1706·2021-11-12 10:36
閱讀 1621·2021-11-12 10:36
閱讀 3446·2021-11-02 14:46
閱讀 3809·2019-08-30 15:56
閱讀 3557·2019-08-30 15:55
閱讀 1465·2019-08-30 15:44
閱讀 1048·2019-08-30 14:00
閱讀 2739·2019-08-29 18:41