摘要:本文是通過廖雪峰的網站學習而整理的真的是很好的教程,省得我花錢買書了,然后我沒有去再整理總結語法,而是直接通過寫出代碼段來體現自己的學習,也方便以后的快速復習回顧。
不想再像以前那樣,什么都從頭開始學習語法、總結語法,這樣反而會過分糾結于語法,耽誤了開發,畢竟語言的主要屬性是工具,次要的屬性是語言本身。
所以還是先熟練使用語言去進行開發,等足夠熟悉了,再去研究語言本身(編譯原理……)。
另外對于算法、設計模式、數據結構、網絡知識、操作系統…… 的學習可以專門針對性的學習,當然更好的方法是以使用語言為主線,通過具體的應用和實踐來推動這些技術知識的學習。
本文是通過廖雪峰的網站學習而整理的(真的是很好的教程,省得我花錢買書了!),然后我沒有去再整理總結語法,而是直接通過寫出代碼段來體現自己的學習,也方便以后的快速復習、回顧。畢竟學習一門語言不是一天可以完成的,所以本文也不是一蹴而就的,而是會一直更新。
也沒有必要再對代碼做過多的文字解釋,一切都能通過代碼本身體現。
交互式命令行在Python的交互式命令行寫程序,比如>>>print("hello world"),好處是一下就能得到結果,壞處是沒法保存,下次還想運行的時候,還要再敲一遍。
所以實際的開發中,我們使用一個文本編輯器來寫代碼,然后保存為一個文件,這樣程序就可以反復運行了。比如將print("heool world")寫在文檔里注意print前面不能有任何空格,并保存為 hello.py,然后使用命令行進入該文件所在的目錄,執行python hello.py就可以解析執行這個源文件了。
絕對不要使用windows自帶的記事本:記事本會自作聰明地在文件開始的地方加上幾個特殊字符(UTF-8 BOM),結果會導致程序運行出現莫名其妙的錯誤。
直接運行py文件能不能像.exe文件那樣直接運行.py文件呢?在Windows上是不行的,但是,在Mac和Linux上是可以的,方法是在.py文件(比如是hello.py)的第一行加上一個特殊的注釋:
#!/usr/bin/env python3 print("hello, world")
接著,通過命令行給hello.py以執行權限:chmod a+x hello.py,然后就可以在文件所在目錄下直接輸入./hello.py運行。
這個和Shell有些像!
輸入和輸出 輸出>>>print("測試一個運算式", "1+2=", 1+2) 測試一個運算式 1+2= 3
注意遇到print里面將參數分開的逗號會輸出空格。
輸入>>>name = input() xumenger
當你輸入完name = input()并按下回車后,Python交互式命令行就等待你的輸入,以這個例子,輸入xumenger,然后回車完成輸入,這時候輸入的字符串就存入到name變量里了。
綜合輸出輸入的例子編寫一個test.py文件
name = input("請你輸入你的姓名: ") print("hello", name)
input里面的字符串參數作為輸出提示用,然后等待輸入,輸入的字符串(比如xumenger)存入name,然后再輸出 hello xumenger。
input()和print()是在命令行下面最基本的輸入和輸出,但是,用戶也可以通過其他更高級的圖形界面完成輸入和輸出,比如,在網頁上的一個文本框輸入自己的名字,點擊“確定”后在網頁上看到輸出信息。
2015.09.06 23:40,明天開始學習Python基礎,先去睡覺!
Python基礎Python語法簡單,采用縮進來控制邏輯。沒有規定是幾個空格還是Tab,但是按照約定俗成的管理,應該始終堅持使用4個空格的縮進。在文本編輯器中,需要設置把Tab自動轉換為4個空格,確保不混用Tab和空格。
# print absolute value of an integer: a = 100 if a >= 0: print(a) else: print(-a)
以#開頭的語句是注釋,當語句以:結尾時,縮進的語句視為代碼塊。
數據類型整數、浮點數
整數和浮點數在計算機內部存儲的方式是不同的,整數運算永遠是精確的(除法難道也是精確的?是的!),而浮點數運算則可能會有四舍五入的誤差。
字符串(使用單引號或者雙引號引起來)
如果字符串內部包含"又包含"怎么辦,需要用來轉義
print("I"m "OK"!")
表示:I"m "OK"!
判斷
if age==18 print("值為18") else print("值不是18")
布爾值(True、False,用and、or和not運算)
空值
用 None表示,None不能理解為0,因為0是有意義的,而None是一個特殊的空值。
變量
=是賦值號,另外Python是動態語言,變量本身類型不固定。與之對應的是靜態語言,靜態語言在定義變量的時候必須指定變量的類型,如果賦值的時候類型不匹配,就會報錯,像C、C++、Java。
a="ABC"
Python解釋器解釋它時,,干了兩件事
在內存中創建一個"ABC"的字符串
在內存中創建了一個名為a的變量,并將它指向"ABC"
也可以把一個變量a賦值給另一個變量b,這個操作實際上是把變量b指向變量a所指向的數據,例如下面的代碼:
a = "ABC" b = a a = "XYZ" print(b)
最后一行打印出變量b的內容到底是"ABC"呢還是"XYZ"?如果從數學意義上理解,就會錯誤地得出b和a相同,也應該是"XYZ",但實際上b的值是"ABC"。一步一步理解代碼
在內存中創建 "ABC" 字符串
在內存中創建 a 變量,并將 a 指向 "ABC"
在內存中創建 b 變量,因為將 a 值賦給 b,所以這是b也指向 "ABC"
然后又在內存中創建了 "XYZ" 字符串,并將 a 指向 "XYZ",但是此時b 還是指向 "ABC"
除法10/3 得到的結果是 3.3333333333333
9/3 得到的結果是 3.0
10//3 得到的結果是 3
10%3 得到的結果是 1
這個知識點以前我一直存在疑惑,廖雪峰的教程里面講得還是很好的,點擊這里認真看。重點是字符編碼的原理、現在計算機系統通用的字符編碼工作方式、Python中的編碼方式、亂碼的解釋。
在最新的Python 3版本中,字符串是以 Unicode編碼的,也就是說Python的字符串支持多種語言。
關于Python的字符串類型和bytes類型、在網絡傳輸或者保存到磁盤時候字符串類型與bytes類型的轉換,參考對應的廖雪峰的 字符串和編碼 教程,有詳細的講解。
1個中文字符經過UTF-8編碼后通常會占用3個字節,而1個英文字符只占用1個字節。
格式化print("Hi, %s, you have $%d." % ("loser", 10))
輸出是:Hi, loser, you have $10
print("Age: %s, Gender: %s" % (25, True))
輸出是:Age: 25, Gender: True
列表:listlist是一種有序的集合用[],可以隨時添加和刪除其中的元素
mates = ["Machael", "Bob", "Tracy"] print("%s" % (len(mates)) print(mates[0]) #第一個元素 print(mates[3]) #越界報錯 print(mates[-1]) #倒數第一個元素 print(mates[-2]) #倒數第二個元素 print(mates[-4]) #越界報錯 mates.append("xumenger") #list是可變列表,現在是在list末尾追加元素 mates.insert(1,"Joker") #插入一個元素在第二個位置 mates.pop() #刪除末尾的元素 mates.pop(1) #刪除第二個元素 mates[1] = "change" #替換第2個元素 L= ["test", 123, True] #list里面可以是不同類型的元素 s= ["test", 1, ["asp", 2], True] #list的元素可以是另一個list list1 = ["test", True] list2 = [2, False, list1] print(list2[2][0]) #可以看成是一個二維數組,類似的還有三維、四維……數組,不過很少用到。元組:tuple
tuple和list很相似,但是tuple是一旦初始化就不能再修改的,用()
mates= ("xumeng", "joker", "test")
現在,mates這個tuple不能變了,它也沒有append(),insert()這樣的方法。其他獲取元素的方法和list是一樣的,你可以正常地使用mates[0],mates[-1],但不能賦值成另外的元素。
不可變的tuple有什么意義?因為tuple不可變,所以代碼更安全。如果可能,能用tuple代替list就盡量用tuple。
t=(1)定義的不是tuple,是1這個數!這是因為括號()既可以表示tuple,又可以表示數學公式中的小括號,這就產生了歧義,因此,Python規定,這種情況下,按小括號進行計算,計算結果自然是1。
要想定義只有一個元素的tuple,應該這樣t=(1,)
“可變的tuple”,可以是tuple有一個list元素,然后里面的list可變,可以看教程中 對應部分 通過展示內存中的存儲原理來說明原因:
t = ("a", "b", ["A", "B"]) t[2][0] = "X"條件判斷
a = input("輸入你的年齡: ") age = int(a) #因為input輸入的是字符串,所以要轉換成整型 if age >= 18: #注意冒號 : print("adult, your age is", age) elif age >= 6: #注意冒號 : print("teenager, your age is", age) else: #注意冒號 : print("kid")循環
names = ["nn", "aa", "mm"]
for name in names: #注意冒號 :
print(name)
sum = 0
for x in[1, 2, 3, 4, 5]
sum = sum +x
print(sum)
sum = 0
for x in range(101): #range(101)就可以生成0-100的整數序列
sum = sum + x
print(sum)
sum = 0
n = 99
while n>0: #注意冒號:
sum = sum +n n= n-2
print(sum)
字典:dictPython內置了字典:dict的支持,全稱為dictionary,在其他語言中也成為map,使用鍵-值(key-value)存儲,具有幾塊的查找速度,注意使用{}。
d = {"key1": 95, "key2": 54, "key3": 100} print(d["key1"]) #把數據放入dict的方法,除了初始化時指定外,還可以通過key放入 d["key4"] = 123 #由于一個key只能對應一個value,所以,多次對一個key放入value,后面的值會把前面的值沖掉 d["key1"] = 111 if "key2" in d: #判斷某個鍵是不是在dict中 print("在") if get("key2", -1) == -1: #如果有"key2"則獲取對應的value,否則就返回-1 print("不在") d.pop("key3") #使用pop刪除對應的鍵和值
為什么dict查找速度這么快?因為dict的實現原理和查字典是一樣的。假設字典包含了1萬個漢字,我們要查某一個字,一個辦法是把字典從第一頁往后翻,直到找到我們想要的字為止,這種方法就是在list中查找元素的方法,list越大,查找越慢。
第二種方法是先在字典的索引表里(比如部首表)查這個字對應的頁碼,然后直接翻到該頁,找到這個字。無論找哪個字,這種查找速度都非常快,不會隨著字典大小的增加而變慢。
你可以猜到,這種key-value存儲方式,在放進去的時候,必須根據key算出value的存放位置,這樣,取的時候才能根據key直接拿到value。
請務必注意,dict內部存放的順序和key放入的順序是沒有關系的。
dict可以用在需要高速查找的很多地方,在Python代碼中幾乎無處不在,正確使用dict非常重要,需要牢記的第一條就是dict的key必須是不可變對象。
這是因為dict根據key來計算value的存儲位置,如果每次計算相同的key得出的結果不同,那dict內部就完全混亂了。這個通過key計算位置的算法稱為哈希算法(Hash)。
要保證hash的正確性,作為key的對象就不能變。在Python中,字符串、整數等都是不可變的,因此,可以放心地作為key。而list是可變的,就不能作為key。
setset和dict類似,也是一組key的集合,但是不存儲value,由于key不能重復,所以在set中,沒有重復的key。
#要創建一個set,需要提供一個list作為輸入結合 s1 = set([1, 2, 3]) #重復元素在set中被過濾,比如下面的語句,其實只要1,2,3 s= set([1, 1, 2, 2, 2, 3, 3]) #通過add(key)添加元素,重復添加的元素只會保留一個 s.add(4) #remove(key) 刪除元素 s.remove(1,2,3) #set可以看成數學意義上的無序和無重復元素的集合,因此,兩個set可以做數學意義上的交集、并集等操作 s2 = set([1,2,3]) s3 = set([2,3,4]) s4 = s2 & s3 #s4是s2和s3的交集 s5 = s2 | s3 #s4是s2和s3的并集
set和dict的唯一區別僅在于沒有存儲對應的value,但是,set的原理和dict一樣,所以,同樣不可以放入可變對象,因為無法判斷兩個可變對象是否相等,也就無法保證set內部“不會有重復元素”。試試把list放入set,看看是否會報錯。
廖雪峰的 講解dict和set的文章 的最后通過說明內存里面的原理講解了可變對象與不可變對象!很好的理解Python和內存機制的一個知識點!
tuple雖然是不變對象,但試試把(1, 2, 3)和(1, [2, 3])放入dict或set中,并解釋結果。
s1 = set([(1,2,3), (2,3,4)]) #這樣的tuple可以放進set s2 = set([(1,2, [1,2]), (2,3, [6,8])]) #這樣的tuple不能放進set,這是“可變的tuple”
2015.09.07 23:45, 明天開始學習 Python的函數 ,現在趕緊睡覺,身體最重要!
函數 調用函數要調用一個函數,需要知道函數的名稱和參數,比如求絕對值的函數abs,只有一個參數。可以直接從Python的官方網站查看文檔:http://docs.python.org/3/library/functions.html#abs
也可以在交互式命令行通過help(abs)查看abs函數的幫助信息。
print(abs(-1)) print(max(1,2,3,4)) print(max(1,2,3)) print(int("123")) #強制類型轉換 print(float("12.34)) print(str(100)) print(bool(1)) #輸出True print(bool("")) #輸出False
函數名其實是一個指向函數對象的引用,完全可以把一個函數名賦值給一個變量,相當于給這個函數起了一個別名:
a=abs print(a(-1)) m=max print(max(1,2,3))定義函數
在Python中,定義一個函數要使用def語句,依次寫出函數名、括號、括號里的參數和冒號: ,然后,在縮進塊中編寫函數體,函數的返回值用return語句返回。
def my_abs(x): if not isinstance(x, (int, float)): raise TypeError("bad operand type") if x>=0: return x else: return -x
上面的函數中,對參數類型做檢查,只允許整數和浮點數類型的參數,否則就raise一個異常。如果有必要,可以先對參數的數據類型做檢查,就像這個函數定義,就可以保證只處理int和float,而假如傳入的是str就會拋出異常。
請注意,函數體內部的語句在執行時,一旦執行到return時,函數就執行完畢,并將結果返回。因此,函數內部通過條件判斷和循環可以實現非常復雜的邏輯。
如果沒有return語句,函數執行完畢后也會返回結果,只是結果為None。return None可以簡寫為return。
import math def move(x, y, step, angle=0): nx = x + step * math.cos(angle) ny = y - step * math.sin(angle) return nx, ny
import math語句表示導入math包,并允許后續代碼引用math包里的sin、cos等函數。上面的這個函數有兩個返回值,我們可以這樣調用
x, y = move(100, 100, 60, math, pi/6) print(x, y)
但是,其實這個只是假象,Python函數返回的仍然是單一值:
r = move(100, 100, 60, math.pi / 6) print(r) #得到和上面一樣的結果
原來返回值是一個tuple!但是,在語法上,返回一個tuple可以省略括號,而多個變量可以同時接收一個tuple,按位置賦給對應的值,所以,Python的函數返回多值其實就是返回一個tuple,但寫起來更方便。
Python強大的函數參數廖雪峰的函數的參數 這一章講解了位置參數、默認參數、因為參數類型不是不變對象導致使用默認參數出現的"意外"、list和tuple與可變參數、dict與關鍵字參數、命名關鍵參數。
在Python中定義函數,可以用必選參數、默認參數、可變參數、關鍵字參數和命名關鍵字參數,這5種參數都可以組合使用,除了可變參數無法和命名關鍵字參數混合。但是請注意,參數定義的順序必須是:必選參數、默認參數、可變參數/命名關鍵字參數和關鍵字參數。
這個太他媽強大了!!但是有點沒看懂,確實純粹的死記硬背還是不太有效,可以等到具體項目應用的時候在參考這篇教程!結合具體的應用再來深入的理解,絕對事半功倍,現在就需要知道Python中有很強大的函數參數的語法,等到具體用的時候知道到哪里去找相關的資料就行了!
遞歸函數def fact(n) if n==1: return 1 return n* fact(n-1)
使用遞歸函數需要注意防止棧溢出。在計算機中,函數調用是通過棧(stack)這種數據結構實現的,每當進入一個函數調用,棧就會加一層棧幀,每當函數返回,棧就會減一層棧幀。由于棧的大小不是無限的,所以,遞歸調用的次數過多,會導致棧溢出。可以試試fact(1000)。
解決遞歸調用棧溢出的方法是通過尾遞歸優化,具體應用見廖雪峰的遞歸函數 的教程。
尾遞歸調用時,如果做了優化,棧不會增長,因此,無論多少次調用也不會導致棧溢出。遺憾的是,大多數編程語言沒有針對尾遞歸做優化,Python解釋器也沒有做優化,所以,即使把上面的fact(n)函數改成尾遞歸方式,也會導致棧溢出。
高級特性掌握了Python的數據類型、語句和函數,基本上就可以編寫出很多有用的程序了。
比如構造一個1, 3, 5, 7, ..., 99的列表,可以通過循環實現:
L = [] n = 1 while n <= 99: L.append(n) n = n + 2
取list的前一半的元素,也可以通過循環實現。
但是在Python中,代碼不是越多越好,而是越少越好。代碼不是越復雜越好,而是越簡單越好。用任何的語言編程都應該是這樣。
基于這一思想,我們來介紹Python中非常有用的高級特性,1行代碼能實現的功能,決不寫5行代碼。請始終牢記,代碼越少,開發效率越高。
切片L = ["Michael", "Sarah", "Tracy", "Bob", "Jack"] L1 = L[:3] #["Michael", "Sarah", "Tracy"] L2 = L[1:3] #["Sarah", "Tracy"] L3 = L[-2:] #["Bob", "Jack"] L4 = L[-2:-1] #["Bob"] #list的第一個元素的索引是0,倒數第一個元素的索引是-1 LL=list(range(100)) #[1,2,3,...,99] LL1=L[-10:] #[90,91,...,99] 后10個數 LL2=L[10:20] #[10,11,12,...,19] 前11-20個數 LL3=L[:10:2] #[0,2,4,6,8] 前10個數,每兩個取一個 LL4=L[::5] #[0,5,10,...,90,95] 所有數,每5個取一個 LL5=L[:] #甚至什么都不寫,只寫[:]就可以原樣復制一個list
tuple也是一種list,唯一區別是tuple不可變。因此,tuple也可以用切片操作,只是操作的結果仍是tuple。
T=(0,1,2,3,4,5) T1=T[:3]
字符串"xxx"也可以看成是一種list,每個元素就是一個字符。因此,字符串也可以用切片操作,只是操作結果仍是字符串:
T="ABCDEFG" T1=T[:3] #"ABC" T2=T[::2] #"ACEG"
在很多編程語言中,針對字符串提供了很多各種截取函數(例如,substring),其實目的就是對字符串切片。Python沒有針對字符串的截取函數,只需要切片一個操作就可以完成,非常簡單。
有了切片操作,很多地方循環就不再需要了。Python的切片非常靈活,一行代碼就可以實現很多行循環才能完成的操作。
迭代Python的for循環抽象程度要高于Java的for循環,因為Python的for循環不僅可以用在list或tuple上,還可以作用在其他可迭代對象上。
list這種數據類型雖然有下標,但很多其他數據類型是沒有下標的,但是,只要是可迭代對象,無論有無下標,都可以迭代,比如dict就可以迭代。
d= {"a":1, "b":2, "c":3} for key in d: print(key) #輸出a c b
為什么輸出的結果是a c b,不是a b c,因為dict的存儲不是按照list的方式順序排列,所以,迭代出的結果順序很可能不一樣。關于dict的存儲的知識,請參見對應的dict教程。
默認情況下,dict迭代的是key。如果要迭代value,可以用for value in d.values(),如果要同時迭代key和value,可以用for k, v in d.items()。
由于字符串也是可迭代對象,所以可以用于for循環。
for ch in "ABCD": print ch
所以,當我們使用for循環時,只要作用于一個可迭代對象,for循環就可以正常運行,而我們不太關心該對象究竟是list還是其他數據類型。那么,如何判斷一個對象是可迭代對象呢?方法是通過collections模塊的Iterable類型判斷:
from collections import Iterable isinstance("abc", Iterable) # str是否可迭代 True isinstance([1,2,3], Iterable) # list是否可迭代 True isinstance(123, Iterable) # 整數是否可迭代 False
最后一個小問題,如果要對list實現類似Java那樣的下標循環怎么辦?Python內置的enumerate函數可以把一個list變成索引-元素對,這樣就可以在for循環中同時迭代索引和元素本身:
for i, value in enumerate(["A", "B", "C"]): print(i, value)
上面的for循環里,同時引用了兩個變量,在Python里是很常見的,比如下面的代碼:
for x, y in [(1, 1), (2, 4), (3, 9)]: print(x, y)列表生成式
L=list(range(1,11)) #生成[1,2,3,4,5,6,7,8,9,10] L1=[x*x for x in range(1,11)] #生成[1*1,2*2,...,10*10]
寫列表生成式時,把要生成的元素比如x * x放到前面,后面跟for循環,就可以把list創建出來,十分有用,多寫幾次,很快就可以熟悉這種語法。
for循環后面還可以加上if判斷,這樣我們就可以篩選出僅偶數的平方:
L= [x * x for x in range(1, 11) if x % 2 == 0] #[4, 16, 36, 64, 100]
還可以使用兩層循環,可以生成全排列:
L= [m + n for m in "ABC" for n in "XYZ"] #["AX", "AY", "AZ", "BX", "BY", "BZ", "CX", "CY", "CZ"]
三層和三層以上的循環就很少用到了。
例程,列出當前目錄下的所有文件和目錄名
import os # 導入os模塊,模塊的概念后面講到 L=[d for d in os.listdir(".")] # os.listdir可以列出文件和目錄 print(L)
for循環其實可以同時使用兩個甚至多個變量,比如dict的items()可以同時迭代key和value:
d = {"x": "A", "y": "B", "z": "C" } for k, v in d.items(): print(k, "=", v)
因此,列表生成式也可以使用兩個變量來生成list:
d = {"x": "A", "y": "B", "z": "C" } L= [k + "=" + v for k, v in d.items()]
把一個list中所有的字符串變成小寫:
L = ["Hello", "World", "IBM", "Apple"] L1= [s.lower() for s in L]生成器
通過列表生成式,我們可以直接創建一個列表。但是,受到內存限制,列表容量肯定是有限的。而且,創建一個包含100萬個元素的列表,不僅占用很大的存儲空間,如果我們僅僅需要訪問前面幾個元素,那后面絕大多數元素占用的空間都白白浪費了。
所以,如果列表元素可以按照某種算法推算出來,那我們是否可以在循環的過程中不斷推算出后續的元素呢?這樣就不必創建完整的list,從而節省大量的空間。在Python中,這種一邊循環一邊計算的機制,稱為生成器:generator。
要創建一個generator,有很多種方法。第一種方法很簡單,只要把一個列表生成式的[]改成(),就創建了一個generator:
L = [x * x for x in range(10)] print(L) #[0, 1, 4, 9, 16, 25, 36, 49, 64, 81] g = (x * x for x in range(10)) #()是一個generator print(g) #at 0x1022ef630>
我們可以直接打印出list的每一個元素,但我們怎么打印出generator的每一個元素呢?如果要一個一個打印出來,可以通過next()函數獲得generator的下一個返回值。我們講過,generator保存的是算法,每次調用next(g),就計算出g的下一個元素的值,直到計算到最后一個元素,沒有更多的元素時,拋出StopIteration的錯誤。
但是,如果每次輸出都調用next(g)實在是太{{BANNED}}了,正確的方法是使用for循環,因為generator也是可迭代對象:
g = (x*x for x in range(10)) for n in g print(n)
所以,我們創建了一個generator后,基本上永遠不會調用next(),而是通過for循環來迭代它,并且不需要關心StopIteration的錯誤。
generator非常強大。如果推算的算法比較復雜,用類似列表生成式的for循環無法實現的時候,還可以用函數來實現。這篇對應的教程中還講了更為牛逼的generator的使用方法!
迭代器我們已經知道,可以直接作用于for循環的數據類型有以下幾種:
一類是集合數據類型,如list、tuple、dict、set、str等;
一類是generator,包括生成器和帶yield的generator function。
這些可以直接作用于for循環的對象統稱為可迭代對象:Iterable。
可以使用isinstance()判斷一個對象是否是Iterable對象:
from collections import Iterable isinstance([], Iterable) #True isinstance({}, Iterable) #True isinstance("abc", Iterable) #True isinstance((x for x in range(10)), Iterable) #True isinstance(100, Iterable) #False
而生成器不但可以作用于for循環,還可以被next()函數不斷調用并返回下一個值,直到最后拋出StopIteration錯誤表示無法繼續返回下一個值了。
可以被next()函數調用并不斷返回下一個值的對象稱為迭代器:Iterator。
可以使用isinstance()判斷一個對象是否是Iterator對象:
from collections import Iterator isinstance((x for x in range(10)), Iterator) #True isinstance([], Iterator) #False isinstance({}, Iterator) #False isinstance("abc", Iterator) #False
生成器都是Iterator對象,但list、dict、str雖然是Iterable,卻不是Iterator。
把list、dict、str等Iterable變成Iterator可以使用iter()函數:
isinstance(iter([]), Iterator) #True isinstance(iter("abc"), Iterator) #True
你可能會問,為什么list、dict、str等數據類型不是Iterator?
這是因為Python的Iterator對象表示的是一個數據流,Iterator對象可以被next()函數調用并不斷返回下一個數據,直到沒有數據時拋出StopIteration錯誤。可以把這個數據流看做是一個有序序列,但我們卻不能提前知道序列的長度,只能不斷通過next()函數實現按需計算下一個數據,所以Iterator的計算是惰性的,只有在需要返回下一個數據時它才會計算。
Iterator甚至可以表示一個無限大的數據流,例如全體自然數。而使用list是永遠不可能存儲全體自然數的。
凡是可作用于for循環的對象都是Iterable類型。
凡是可作用于next()函數的對象都是Iterator類型,它們表示一個惰性計算的序列。
Python的for循環本質上就是通過不斷調用next()函數實現的,例如:
for x in [1, 2, 3, 4, 5]: pass
實際上完全等價于:
# 首先獲得Iterator對象: it = iter([1, 2, 3, 4, 5]) # 循環: while True: try: # 獲得下一個值: x = next(it) except StopIteration: # 遇到StopIteration就退出循環 break函數式編程
函數式編程的一個特點就是,允許把函數本身作為參數傳入另一個函數,還允許返回一個函數!
Python對函數式編程提供部分支持。由于Python允許使用變量,因此,Python不是純函數式編程語言。
高階函數那么函數名是什么呢?函數名其實就是指向函數的變量!對于abs()這個函數,完全可以把函數名abs看成變量,它指向一個可以計算絕對值的函數!
如果把abs指向其他對象,會有什么情況發生?
abs = 10 abs(-10)
報錯:
Traceback (most recent call last):
File "
TypeError: "int" object is not callable
把abs指向10后,就無法通過abs(-10)調用該函數了!因為abs這個變量已經不指向求絕對值函數而是指向一個整數10!
當然實際代碼絕對不能這么寫,這里是為了說明函數名也是變量。要恢復abs函數,請重啟Python交互環境。
由于abs函數實際上是定義在__builtin__模塊中的,所以要讓修改abs變量的指向在其它模塊也生效,要用__builtin__.abs = 10。
既然變量可以指向函數,函數的參數能接收變量,那么一個函數就可以接收另一個函數作為參數,這種函數就稱之為高階函數。例子
def add(x, y, f): return f(x) + f(y)
可以這樣調用:
add(-5, 6, abs)
編寫高階函數,就是讓函數的參數能夠接收別的函數。函數式編程就是指這種高度抽象的編程范式。
map/reduce我們先看map。map()函數接收兩個參數,一個是函數,一個是Iterable,map將傳入的函數依次作用到序列的每個元素,并把結果作為新的Iterator返回。
def f(x): return x*x r = map(f, [1,2,3,4,5,6,7,8,9]) print(list(r)) #[1,4,16,25,36,49,64,81]
再看reduce的用法。reduce把一個函數作用在一個序列[x1, x2, x3, ...]上,這個函數必須接收兩個參數,reduce把結果繼續和序列的下一個元素做累積計算,其效果就是:
reduce(f, [x1, x2, x3, x4]) = f(f(f(x1, x2), x3), x4)
例子,比方說對一個序列求和,就可以用reduce實現:
from functools import reduce
def add(x, y):
return x + y
print(reduce(add, [1, 3, 5, 7, 9])) #輸出25
這個例子本身沒多大用處,但是,如果考慮到字符串str也是一個序列,對上面的例子稍加改動,配合map(),我們就可以寫出把str轉換為int的函數:
from functools import reduce def fn(x, y): return x * 10 + y def char2num(s): return {"0": 0, "1": 1, "2": 2, "3": 3, "4": 4, "5": 5, "6": 6, "7": 7, "8": 8, "9": 9}[s] print(reduce(fn, map(char2num, "13579"))) #輸出13579
整理成一個str2int的函數就是:
from functools import reduce def str2int(s): def fn(x, y): return x * 10 + y def char2num(s): return {"0": 0, "1": 1, "2": 2, "3": 3, "4": 4, "5": 5, "6": 6, "7": 7, "8": 8, "9": 9}[s] return reduce(fn, map(char2num, s))
還可以用lambda函數進一步簡化成:
from functools import reduce def char2num(s): return {"0": 0, "1": 1, "2": 2, "3": 3, "4": 4, "5": 5, "6": 6, "7": 7, "8": 8, "9": 9}[s] def str2int(s): return reduce(lambda x, y: x * 10 + y, map(char2num, s))
也就是說,假設Python沒有提供int()函數,你完全可以自己寫一個把字符串轉化為整數的函數,而且只需要幾行代碼!
filterPython內建的filter()函數用于過濾序列。
和map()類似,filter()也接收一個函數和一個序列。和map()不同的時,filter()把傳入的函數依次作用于每個元素,然后根據返回值是True還是False決定保留還是丟棄該元素。
例如,在一個list中,刪掉偶數,只保留奇數,可以這么寫:
def is_odd(n): return n % 2 == 1 list(filter(is_odd, [1, 2, 4, 5, 6, 9, 10, 15])) # 結果: [1, 5, 9, 15]
可見用filter()這個高階函數,關鍵在于正確實現一個“篩選”函數。
注意到filter()函數返回的是一個Iterator,也就是一個惰性序列,所以要強迫filter()完成計算結果,需要用list()函數獲得所有結果并返回list。
sorted排序也是在程序中經常用到的算法。無論使用冒泡排序還是快速排序,排序的核心是比較兩個元素的大小。如果是數字,我們可以直接比較,但如果是字符串或者兩個dict呢?直接比較數學上的大小是沒有意義的,因此,比較的過程必須通過函數抽象出來。通常規定,對于兩個元素x和y,如果認為x < y,則返回-1,如果認為x == y,則返回0,如果認為x > y,則返回1,這樣,排序算法就不用關心具體的比較過程,而是根據比較結果直接排序。
Python內置的sorted()函數就可以對list進行排序:
sorted([36, 5, -12, 9, -21]) #[-21, -12, 5, 9, 36]
此外,sorted()函數也是一個高階函數,它還可以接收一個key函數來實現自定義的排序,例如按絕對值大小排序:
sorted([36, 5, -12, 9, -21], key=abs) #[5, 9, -12, -21, 36] #key指定的函數將作用于list的每一個元素上,并根據key函數返回的結果進行排序。
我們再看一個字符串排序的例子:
sorted(["bob", "about", "Zoo", "Credit"]) #["Credit", "Zoo", "about", "bob"]
默認情況下,對字符串排序,是按照ASCII的大小比較的,由于"Z" < "a",結果,大寫字母Z會排在小寫字母a的前面。
現在,我們提出排序應該忽略大小寫,按照字母序排序。要實現這個算法,不必對現有代碼大加改動,只要我們能用一個key函數把字符串映射為忽略大小寫排序即可。忽略大小寫來比較兩個字符串,實際上就是先把字符串都變成大寫(或者都變成小寫),再比較。
這樣,我們給sorted傳入key函數,即可實現忽略大小寫的排序:
sorted(["bob", "about", "Zoo", "Credit"], key=str.lower) #["about", "bob", "Credit", "Zoo"]
要進行反向排序,不必改動key函數,可以傳入第三個參數reverse=True:
sorted(["bob", "about", "Zoo", "Credit"], key=str.lower, reverse=True) #["Zoo", "Credit", "bob", "about"]
從上述例子可以看出,高階函數的抽象能力是非常強大的,而且,核心代碼可以保持得非常簡潔。
返回函數高階函數除了可以接受函數作為參數外,還可以把函數作為結果值返回。
我們來實現一個可變參數的求和。通常情況下,求和的函數是這樣定義的:
def calc_sum(*args): ax = 0 for n in args: ax = ax + n return ax
但是,如果不需要立刻求和,而是在后面的代碼中,根據需要再計算怎么辦?可以不返回求和的結果,而是返回求和的函數:
def lazy_sum(*args): def sum(): ax = 0 for n in args: ax = ax + n return ax return sum
當我們調用lazy_sum()時,返回的并不是求和結果,而是求和函數:
f = lazy_sum(1, 3, 5, 7, 9) f
調用函數f時,才真正計算求和的結果:
f() #25閉包
注意到返回的函數在其定義內部引用了局部變量args,所以,當一個函數返回了一個函數后,其內部的局部變量還被新函數引用,所以,閉包用起來簡單,實現起來可不容易。
另一個需要注意的問題是,返回的函數并沒有立刻執行,而是直到調用了f()才執行。我們來看一個例子:
def count(): fs = [] for i in range(1, 4): def f(): return i*i fs.append(f) return fs f1, f2, f3 = count()
在上面的例子中,每次循環,都創建了一個新的函數,然后,把創建的3個函數都返回了。
你可能認為調用f1(),f2()和f3()結果應該是1,4,9,但實際結果是:
f1() #9 f2() #9 f3() #9
全部都是9!原因就在于返回的函數引用了變量i,但它并非立刻執行。等到3個函數都返回時,它們所引用的變量i已經變成了3,因此最終結果為9。
返回閉包時牢記的一點就是:返回函數不要引用任何循環變量,或者后續會發生變化的變量。
如果一定要引用循環變量怎么辦?方法是再創建一個函數,用該函數的參數綁定循環變量當前的值,無論該循環變量后續如何更改,已綁定到函數參數的值不變:
def count(): def f(j): def g(): return j*j return g fs = [] for i in range(1, 4): fs.append(f(i)) # f(i)立刻被執行,因此i的當前值被傳入f() return fs
再看看結果:
f1, f2, f3 = count() f1() #1 f2() #4 f3() #9
缺點是代碼較長,可利用lambda函數縮短代碼。
匿名函數當我們在傳入函數時,有些時候,不需要顯式地定義函數,直接傳入匿名函數更方便。
在Python中,對匿名函數提供了有限支持。還是以map()函數為例,計算f(x)=x2時,除了定義一個f(x)的函數外,還可以直接傳入匿名函數:
list(map(lambda x: x * x, [1, 2, 3, 4, 5, 6, 7, 8, 9])) #[1, 4, 9, 16, 25, 36, 49, 64, 81]
通過對比可以看出,匿名函數lambda x: x * x實際上就是:
def f(x): return x * x
關鍵字lambda表示匿名函數,冒號前面的x表示函數參數。
匿名函數有個限制,就是只能有一個表達式,不用寫return,返回值就是該表達式的結果。
用匿名函數有個好處,因為函數沒有名字,不必擔心函數名沖突。此外,匿名函數也是一個函數對象,也可以把匿名函數賦值給一個變量,再利用變量來調用該函數:
f = lambda x: x * x f #at 0x101c6ef28> f(5) #25
同樣,也可以把匿名函數作為返回值返回,比如:
def build(x, y): return lambda: x * x + y * y裝飾器
def log(func): def wrapper(*args, **kw): print("call %s():" % func.__name__) return func(*argc, **kw) return wrapper
觀察上面的log,因為它是一個decorator,所以接受一個函數作為參數,并返回一個函數。我們要借助Python的@語法,把decorator置于函數的定義處:
@log def now(): print("2015-3-25")
調用now()函數,不僅會運行now()函數本身,還會在運行now()函數前打印一行日志:
now() #call now(): #2015-3-25
把@log放到now()函數的定義處,相當于執行了語句:
now = log(now)
由于log()是一個decorator,返回一個函數,所以,原來的now()函數仍然存在,只是現在同名的now變量指向了新的函數,于是調用now()將執行新函數,即在log()函數中返回的wrapper()函數。
wrapper()函數的參數定義是(args, *kw),因此,wrapper()函數可以接受任意參數的調用。在wrapper()函數內,首先打印日志,再緊接著調用原始函數。
在面向對象(OOP)的設計模式中,decorator被稱為裝飾模式。OOP的裝飾模式需要通過繼承和組合來實現,而Python除了能支持OOP的decorator外,直接從語法層次支持decorator。Python的decorator可以用函數實現,也可以用類實現。
decorator可以增強函數的功能,定義起來雖然有點復雜,但使用起來非常靈活和方便。
偏函數functools.partial就是幫助我們創建一個偏函數的,不需要我們自己定義int2(),可以直接使用下面的代碼創建一個新的函數int2:
import functools int2 = functools.partial(int, base=2) print(int2("1000000")) #64 print(int2("1010101")) #85
Python的functools模塊提供了很多有用的功能,其中一個就是偏函數(Partial function)。要注意,這里的偏函數和數學意義上的偏函數不一樣。
沒太看懂,還是等到具體研究一個項目源碼,以及自己做開發的時候再去結合實踐深入理解吧!
2015.09.08 23:59:00 明天繼續看 模塊 的教程,今天對很多知識點并沒有真正理解,都是有一些印象,所以必須等到自己研究源碼、自己開發的時候,結合運行的效果和理論知識去達到真正的深入的理解。現在趕緊睡覺!
模塊在計算機程序的開發過程中,隨著程序代碼越寫越多,在一個文件里代碼就會越來越長,越來越不容易維護。
為了編寫可維護的代碼,我們把很多函數分組,分別放到不同的文件里,這樣,每個文件包含的代碼就相對較少,很多編程語言都采用這種組織代碼的方式。在Python中,一個.py文件就稱之為一個模塊(Module)。
引入了包以后,只要頂層的包名不與別人沖突,那所有模塊都不會與別人沖突。現在,abc.py模塊的名字就變成了mycompany.abc,類似的,xyz.py的模塊名變成了mycompany.xyz。
請注意,每一個包目錄下面都會有一個__init__.py的文件,這個文件是必須存在的,否則,Python就把這個目錄當成普通目錄,而不是一個包。__init__.py可以是空文件,也可以有Python代碼,因為__init__.py本身就是一個模塊,而它的模塊名就是mycompany。
類似的,可以有多級目錄,組成多級層次的包結構。
使用模塊 安裝第三方模塊很多強大的第三方庫,要能夠充分利用好它們為我服務!!!
面向對象編程面向對象編程——Object Oriented Programming,簡稱OOP,是一種程序設計思想。OOP把對象作為程序的基本單元,一個對象包含了數據和操作數據的函數。
面向過程的程序設計把計算機程序視為一系列的命令集合,即一組函數的順序執行。為了簡化程序設計,面向過程把函數繼續切分為子函數,即把大塊函數通過切割成小塊函數來降低系統的復雜度。
而面向對象的程序設計把計算機程序視為一組對象的集合,而每個對象都可以接收其他對象發過來的消息,并處理這些消息,計算機程序的執行就是一系列消息在各個對象之間傳遞。
2015.09.22,明天繼續學習《面向對象編程》
比如一個類的代碼如下
class Student(object): def __init__(self, name, score): self.name = name self.score = score def print_score(self): print("%s: %s" % (self.name, self.score))
可以這樣使用這個類創建對象
bart = Student("Bart Simpson", 59) lisa = Student("Lisa Simpson", 87) bart.print_score() lisa.print_score()
面向對象的設計思想是從自然界中來的,因為在自然界中,類(Class)和實例(Instance)的概念是很自然的。Class是一種抽象概念,比如我們定義的Class——Student,是指學生這個概念,而實例(Instance)則是一個個具體的Student,比如,Bart Simpson和Lisa Simpson是兩個具體的Student。
所以,面向對象的設計思想是抽象出Class,根據Class創建Instance。
面向對象的抽象程度又比函數要高,因為一個Class既包含數據,又包含操作數據的方法。
數據封裝、繼承和多態是面向對象的三大特點,我們后面會詳細講解。
類和實例關于面向對象設計其實和C++、Delphi……都很像,但是具體的語法可能不同,不過這都是一些表面化的東西。具體去參考Python的編程規范、語法就好了。
這篇教程里面有關于類、實例、實例的內存地址……的講解,所以要好好看看!
__init__方法是類的構造方法,self這個特殊變量的理解。
和普通的函數相比,在類中定義的函數只有一點不同,就是第一個參數永遠是實例變量self,并且,調用時,不用傳遞該參數。除此之外,類的方法和普通函數沒有什么區別,所以,你仍然可以用默認參數、可變參數、關鍵字參數和命名關鍵字參數。
要定義一個方法,除了第一個參數是self外,其他和普通函數一樣。要調用一個方法,只需要在實例變量上直接調用,除了self不用傳遞,其他參數正常傳入。
因為Python是靜態語言,所以語法上還會有其他更多的區別,所以一定要和其他的之前我了解的語言在語法方面區分開
訪問限制一些關于變量的權限、訪問限制、命名規范的說明。總的來說就是,Python本身沒有任何機制阻止你干壞事,一切全靠自覺。
繼承和多態在繼承關系中,如果一個實例的數據類型是某個子類,那它的數據類型也可以被看做是父類。但是,反過來就不行。可以使用isistance()函數來進行判斷。
這篇教程很好的講解了多態的表現形式!!具體的編程語法、代碼實現的細節,認真參考這篇教程!!
獲取對象信息type()
isinstance()
dir():如果要獲得一個對象的所有屬性和方法,可以使用dir()函數,它返回一個包含字符串的list,比如,獲得一個str對象的所有屬性和方法。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/37600.html
摘要:月份發布了第版,收到不少網友的良好建議,所以又抽空進行了完善,當然也拖了不少時間。本書主要介紹的基本使用,這也是我一開始在學習過程中經常用到的。第章實戰,介紹了如何開發一個簡單的應用。聲明本書由編寫,采用協議發布。 showImg(https://segmentfault.com/img/remote/1460000007484050?w=200&h=152); 書籍地址 head-f...
摘要:此文是記錄我在學習的過程,主要目的是怕學了后面的了前面的,方便復習。不得不承認,老外的課程確實比國內的課程更有趣。所以提升性能的個關鍵降低電子需要行徑的距離提高每秒發射電子的次數。 此文是記錄我在udacity.com學習python的過程,主要目的是怕學了后面的wan了前面的,方便復習。也希望能幫到和我一樣的初學者,這個課程的是Udacity的《計算機基礎導論》,https://cl...
摘要:降采樣的目的是為了綜合所有不同清晰度的圖像進行關鍵點提取,這種關鍵點攜帶了不同清晰度的信息,對縮放具有不變性。是對的一種改進,主要特點是快速。的達到維,導致的比較耗時,使用哈爾小波轉換得到的方向,讓的降到維,減少了一半,提高了匹配速度。 尺度不變特征變換(Scale-invariant feature transform, 簡稱SIFT)是圖像局部特征提取的現代方法——基于區域/圖像塊...
摘要:寫在前面金三銀四又到了一年一度的跳槽季相信大家都在準備自己面試筆記我也針對自己工作中所掌握或了解的一些東西做了一個目錄總結方便自己復習詳細內容會在之后一一對應地補充上去有些在我的個人主頁筆記中也有相關記錄這里暫且放一個我的面試知識點目錄大家 寫在前面: 金三銀四, 又到了一年一度的跳槽季, 相信大家都在準備自己面試筆記, 我也針對自己工作中所掌握或了解的一些東西做了一個目錄總結,方便自...
閱讀 3110·2021-11-10 11:36
閱讀 3312·2021-10-13 09:40
閱讀 6051·2021-09-26 09:46
閱讀 662·2019-08-30 15:55
閱讀 1409·2019-08-30 15:53
閱讀 1579·2019-08-29 13:55
閱讀 2997·2019-08-29 12:46
閱讀 3204·2019-08-29 12:34