四倡缠、函數(shù)式編程:
函數(shù)式編程,有利于代碼的管理迫皱,后期的代碼的迭代和修改矫钓。函數(shù)的封裝參數(shù)的傳遞,返回值的返回舍杜。
高階函數(shù):
變量可以指向函數(shù)新娜、函數(shù)名也可能是變量(abs取絕對(duì)值)
def add(x, y, f):
? ? return f(x) + f(y)
把函數(shù)作為參數(shù)傳入,這樣的函數(shù)稱為高階函數(shù)既绩,函數(shù)式編程就是指這種高度抽象的編程范式概龄。
map/reduce:
Python內(nèi)建了map()和reduce()函數(shù)。
map()函數(shù)接收兩個(gè)參數(shù)饲握,一個(gè)是函數(shù)私杜,一個(gè)是Iterable蚕键,map將傳入的函數(shù)依次作用到序列的每個(gè)元素,并把結(jié)果作為新的Iterator返回衰粹。
map()傳入的第一個(gè)參數(shù)是mix锣光,即函數(shù)對(duì)象本身。由于結(jié)果r是一個(gè)Iterator铝耻,Iterator是惰性序列誊爹,因此通過(guò)list()函數(shù)讓它把整個(gè)序列都計(jì)算出來(lái)并返回一個(gè)list。
一個(gè)序列求和瓢捉,就可以用reduce實(shí)現(xiàn):
運(yùn)算可以直接用Python內(nèi)建函數(shù)sum()频丘,沒(méi)必要?jiǎng)佑胷educe。
filter:
Python內(nèi)建的filter()函數(shù)用于過(guò)濾序列泡态。
通過(guò)filter去除某些不需要的值
只保留偶數(shù)
filter()的作用是從一個(gè)序列中篩出符合條件的元素搂漠。由于filter()使用了惰性計(jì)算,所以只有在取filter()結(jié)果的時(shí)候某弦,才會(huì)真正篩選并每次返回下一個(gè)篩出的元素桐汤。
sorted:
主要是排序:
sorted()也是一個(gè)高階函數(shù)。用sorted()排序的關(guān)鍵在于實(shí)現(xiàn)一個(gè)映射函數(shù)靶壮。
要進(jìn)行反向排序怔毛,不必改動(dòng)key函數(shù),可以傳入第三個(gè)參數(shù)reverse=Trues
sorted傳入key函數(shù)亮钦,即可實(shí)現(xiàn)忽略大小寫(xiě)的排序:
返回函數(shù):
高階函數(shù)除了可以接受函數(shù)作為參數(shù)外,還可以把函數(shù)作為結(jié)果值返回充活。
def lazy_sum(*args):? ? def sum():? ? ? ? ax = 0? ? ? ? for n in args:? ? ? ? ? ? ax = ax + n? ? ? ? return ax? ? return sum
匿名函數(shù):
在傳入函數(shù)時(shí)蜂莉,有些時(shí)候,不需要顯式地定義函數(shù)混卵,直接傳入匿名函數(shù)更方便映穗。
list(map(lambda x: x * x, [1, 2, 3, 4, 5, 6, 7, 8, 9]))
相當(dāng)于下面的代碼:
轉(zhuǎn)換代碼:
def is_odd(n):? ? return n % 2 == 1? L = list(filter(is_odd, range(1, 20)))? x = list(filter(lambda x: x % 2 == 1, range(1,20))) print(x)
裝飾器:
由于函數(shù)也是一個(gè)對(duì)象,而且函數(shù)對(duì)象可以被賦值給變量幕随,所以蚁滋,通過(guò)變量也能調(diào)用該函數(shù)。
#!/usr/bin/env python3 # -*- coding: utf-8 -*-? import time, functools? def metric(fn):? ? print('%s executed in %s ms' % (fn.__name__, 10.24))? ? return fn? @metric def fast(x, y):? ? time.sleep(0.0012)? ? return x + y;? @metric def slow(x, y, z):? ? time.sleep(0.1234)? ? return x * y * z;? if __name__ == '__main__':? f = fast(11, 22)? s = slow(11, 22, 33)
偏函數(shù):
Python的functools模塊提供了很多有用的功能赘淮,其中一個(gè)就是偏函數(shù)(Partial function)辕录。
int()函數(shù)還提供額外的base參數(shù),默認(rèn)值為10梢卸。如果傳入base參數(shù)走诞,就可以做N進(jìn)制的轉(zhuǎn)換:
五、模塊:
注意:自己創(chuàng)建模塊時(shí)要注意命名蛤高,不能和Python自帶的模塊名稱沖突蚣旱。例如碑幅,系統(tǒng)自帶了sys模塊,自己的模塊就不可命名為sys.py塞绿,否則將無(wú)法導(dǎo)入系統(tǒng)自帶的sys模塊沟涨。
使用模塊:
Python本身就內(nèi)置了很多非常有用的模塊,只要安裝完畢异吻,這些模塊就可以立刻使用裹赴。
#!/usr/bin/env python3 # -*- coding: utf-8 -*-? ' a test module '? __author__ = 'Michael Liao' __age__ = '23'? import sys? def test():? ? args = sys.argv? ? if len(args)==1:? ? ? ? print('Hello, %s!' % args[0])? ? elif len(args)==2:? ? ? ? print('Hello, %s!' % args[1])? ? else:? ? ? ? print('Too many arguments!')? if __name__=='__main__':? ? test()
作用域:
在一個(gè)模塊中,我們可能會(huì)定義很多函數(shù)和變量涧黄,但有的函數(shù)和變量我們希望給別人使用篮昧,有的函數(shù)和變量我們希望僅僅在模塊內(nèi)部使用。在Python中笋妥,是通過(guò)_前綴來(lái)實(shí)現(xiàn)的懊昨。
正常的函數(shù)和變量名是公開(kāi)的(public),可以被直接引用春宣,比如:abc酵颁,x123,PI等月帝;
類似_xxx和__xxx這樣的函數(shù)或變量就是非公開(kāi)的(private)躏惋,不應(yīng)該被直接引用,比如_abc嚷辅,__abc等簿姨;
安裝第三方模塊:
在Python中,安裝第三方模塊簸搞,是通過(guò)包管理工具pip完成的扁位。
六、面向?qū)ο缶幊?/p>
主要是抽象對(duì)象的一些特性趁俊,實(shí)例化具體的一個(gè)里面的一個(gè)處理域仇。
在Python中,所有數(shù)據(jù)類型都可以視為對(duì)象寺擂,當(dāng)然也可以自定義對(duì)象暇务。自定義的對(duì)象數(shù)據(jù)類型就是面向?qū)ο笾械念悾–lass)的概念。
可以類比java的編程
類和實(shí)例:
面向?qū)ο笞钪匾母拍罹褪穷悾–lass)和實(shí)例(Instance)怔软,必須牢記類是抽象的模板垦细,比如Student類,而實(shí)例是根據(jù)類創(chuàng)建出來(lái)的一個(gè)個(gè)具體的“對(duì)象”挡逼,每個(gè)對(duì)象都擁有相同的方法蝠检,但各自的數(shù)據(jù)可能不同。
class Student(object):? ? pass
class后面緊接著是類名挚瘟,即Student叹谁,類名通常是大寫(xiě)開(kāi)頭的單詞饲梭,緊接著是(object),表示該類是從哪個(gè)類繼承下來(lái)的焰檩,繼承的概念后面再講憔涉,通常,如果沒(méi)有合適的繼承類析苫,就使用object類兜叨,這是所有類最終都會(huì)繼承的類。
數(shù)據(jù)封裝:
面向?qū)ο缶幊痰囊粋€(gè)重要特點(diǎn)就是數(shù)據(jù)封裝衩侥。在上面的Student類中国旷,每個(gè)實(shí)例就擁有各自的name和score這些數(shù)據(jù).
>>> class stu(object):
...? ? def __init__(self,name,age):
...? ? ? ? ? ? self.name = name
...? ? ? ? ? ? self.age = age
...
>>> lisa = stu('lisa',12)
>>> lisa.name
'lisa'
訪問(wèn)限制:
在Class內(nèi)部,可以有屬性和方法茫死,而外部代碼可以通過(guò)直接調(diào)用實(shí)例變量的方法來(lái)操作數(shù)據(jù)跪但,這樣,就隱藏了內(nèi)部的復(fù)雜邏輯峦萎。
如果要讓內(nèi)部屬性不被外部訪問(wèn)屡久,可以把屬性的名稱前加上兩個(gè)下劃線__,在Python中爱榔,實(shí)例的變量名如果以__開(kāi)頭被环,就變成了一個(gè)私有變量(private),只有內(nèi)部可以訪問(wèn)详幽,外部不能訪問(wèn)筛欢,所以,我們把Student類改一改:
>>> class stu(object):
...? ? def __init__(self,name,age):
...? ? ? ? ? ? self.__name = name
...? ? ? ? ? ? self.__age = age
...
前面添加兩個(gè)__這個(gè)就不能外部修改內(nèi)容:
改完后唇聘,對(duì)于外部代碼來(lái)說(shuō)版姑,沒(méi)什么變動(dòng),但是已經(jīng)無(wú)法從外部訪問(wèn)實(shí)例變量.__name和實(shí)例變量.__age了:
>>> bart = Student('Bart Simpson', 59)
>>> bart.__name
Traceback (most recent call last):
? File "<stdin>", line 1, in <module>
AttributeError: 'Student' object has no attribute '__name'
雙下劃線開(kāi)頭的實(shí)例變量是不是一定不能從外部訪問(wèn)呢雳灾?其實(shí)也不是漠酿。不能直接訪問(wèn)__name是因?yàn)镻ython解釋器對(duì)外把__name變量改成了_Student__name冯凹,所以谎亩,仍然可以通過(guò)_Student__name來(lái)訪問(wèn)__name變量:
繼承和多態(tài):
在OOP程序設(shè)計(jì)中,當(dāng)我們定義一個(gè)class的時(shí)候宇姚,可以從某個(gè)現(xiàn)有的class繼承匈庭,新的class稱為子類(Subclass),而被繼承的class稱為基類浑劳、父類或超類(Base class阱持、Super class)。
#!/usr/bin/env python3 # -*- coding: utf-8 -*-? class Animal(object):? ? def run(self):? ? ? ? print('Animal is running...')? class dog(Animal):? pass? if __name__=='__main__':? ? dog().run()
dog 繼承Animal的屬性魔熏,還有類是一個(gè)object衷咽,需要調(diào)run方法鸽扁。
#!/usr/bin/env python3 # -*- coding: utf-8 -*-? class Animal(object):? ? def run(self):? ? ? ? print('Animal is running...')? class dog(Animal):? def dog1(self):? print('dog is ...')? if __name__=='__main__':? ? dog().dog1()
繼承還可以一級(jí)一級(jí)地繼承下來(lái),就好比從爺爺?shù)桨职窒馄⒃俚絻鹤舆@樣的關(guān)系桶现。而任何類,最終都可以追溯到根類object鼎姊,這些繼承關(guān)系看上去就像一顆倒著的樹(shù)骡和。比如如下的繼承樹(shù):
靜態(tài)語(yǔ)言 vs 動(dòng)態(tài)語(yǔ)言:
對(duì)于靜態(tài)語(yǔ)言(例如Java)來(lái)說(shuō),如果需要傳入Animal類型相寇,則傳入的對(duì)象必須是Animal類型或者它的子類慰于,否則,將無(wú)法調(diào)用run()方法唤衫。
對(duì)于Python這樣的動(dòng)態(tài)語(yǔ)言來(lái)說(shuō)婆赠,則不一定需要傳入Animal類型。我們只需要保證傳入的對(duì)象有一個(gè)run()方法就可以了.
小結(jié)
繼承可以把父類的所有功能都直接拿過(guò)來(lái)战授,這樣就不必重零做起页藻,子類只需要新增自己特有的方法,也可以把父類不適合的方法覆蓋重寫(xiě)植兰。
動(dòng)態(tài)語(yǔ)言的鴨子類型特點(diǎn)決定了繼承不像靜態(tài)語(yǔ)言那樣是必須的份帐。
獲取對(duì)象信息:
使用type():
使用isinstance():
使用dir():
對(duì)于class的繼承關(guān)系來(lái)說(shuō),使用type()就很不方便楣导。我們要判斷class的類型废境,可以使用isinstance()函數(shù)。
如果要獲得一個(gè)對(duì)象的所有屬性和方法筒繁,可以使用dir()函數(shù)噩凹,它返回一個(gè)包含字符串的list,比如毡咏,獲得一個(gè)str對(duì)象的所有屬性和方法.
實(shí)例屬性和類屬性:
由于Python是動(dòng)態(tài)語(yǔ)言驮宴,根據(jù)類創(chuàng)建的實(shí)例可以任意綁定屬性。
給實(shí)例綁定屬性的方法是通過(guò)實(shí)例變量呕缭,或者通過(guò)self變量:
#!/usr/bin/env python3 # -*- coding: utf-8 -*-? class Student(object):? ? def __init__(self, name):? ? ? ? self.name = name? s = Student('Bob') s.score = 90? if __name__=='__main__':? s = Student('Bob')? s.score = 90? print(s.score)
小結(jié)
實(shí)例屬性屬于各個(gè)實(shí)例所有堵泽,互不干擾;
類屬性屬于類所有恢总,所有實(shí)例共享一個(gè)屬性迎罗;
不要對(duì)實(shí)例屬性和類屬性使用相同的名字,否則將產(chǎn)生難以發(fā)現(xiàn)的錯(cuò)誤片仿。
七纹安、面向?qū)ο蟾呒?jí)編程
使用__slots__
正常情況下,當(dāng)我們定義了一個(gè)class,創(chuàng)建了一個(gè)class的實(shí)例后厢岂,我們可以給該實(shí)例綁定任何屬性和方法光督,這就是動(dòng)態(tài)語(yǔ)言的靈活性。
正常的情況下可以隨意的添加
為了達(dá)到限制的目的塔粒,Python允許在定義class的時(shí)候可帽,定義一個(gè)特殊的__slots__變量,來(lái)限制該class實(shí)例能添加的屬性:
class Student(object):? ? __slots__ = ('name', 'age') # 用tuple定義允許綁定的屬性名稱
>>> class student(object): ...? ? __slots__ = ('name','age') ... >>> stu = student() >>> stu.name = 'bob' >>> stu.age = 12 >>> stu.score = 12 Traceback (most recent call last):? File "<stdin>", line 1, in <module> AttributeError: 'student' object has no attribute 'score' >>>
由于'score'沒(méi)有被放到__slots__中窗怒,所以不能綁定score屬性映跟,試圖綁定score將得到AttributeError的錯(cuò)誤。
使用__slots__要注意扬虚,__slots__定義的屬性僅對(duì)當(dāng)前類實(shí)例起作用努隙,對(duì)繼承的子類是不起作用的:
class stu2(student): passs s2 = stu2() s2.score = 9999
除非在子類中也定義__slots__,這樣辜昵,子類實(shí)例允許定義的屬性就是自身的__slots__加上父類的__slots__————
新的繼承的不要添加__slots__屬性
使用@property
可以通過(guò)一個(gè)set_score()方法來(lái)設(shè)置成績(jī)荸镊,再通過(guò)一個(gè)get_score()來(lái)獲取成績(jī),這樣堪置,在set_score()方法里躬存,就可以檢查參數(shù):
注意到這個(gè)神奇的@property,我們?cè)趯?duì)實(shí)例屬性操作的時(shí)候舀锨,就知道該屬性很可能不是直接暴露的岭洲,而是通過(guò)getter和setter方法來(lái)實(shí)現(xiàn)的。
@property廣泛應(yīng)用在類的定義中坎匿,可以讓調(diào)用者寫(xiě)出簡(jiǎn)短的代碼盾剩,同時(shí)保證對(duì)參數(shù)進(jìn)行必要的檢查,這樣替蔬,程序運(yùn)行時(shí)就減少了出錯(cuò)的可能性告私。