教程地址:python進階 - 慕課網
python函數式編程
變量可以指向函數:
f = abs
print(f) #<built-in function abs>
print(f(-20)) #20
函數名其實就是指向函數的變量
def add(x,y,f):
? ? return f(x)+f(y);
num = add(-5,9,abs)
print(num) ?#14
map函數
map()是Python 內置的高階函數蚪战,它接收一個函數f和一個list,并通過把函數f 依次作用在 list 的每個元素上尊勿,得到一個新的 list 并返回。
L = [-1,2,3]
rs = map(abs,L)
print(rs) ?#<map object at 0x005FF1B0>
print(list(rs)) ?#[1, 2, 3]
list() 方法用于將集合轉換為列表缴允。tuple() 方法用于將集合轉換為元組破停。
Map函數除了支持列表list之外也是支持tuple,set等集合的骇扇。
reduce函數
reduce()函數接收的參數和 map()類似,一個函數f间坐,一個list灾挨,但行為和map()不同,reduce()傳入的函數 f 必須接收兩個參數竹宋,reduce()對list的每個元素反復調用函數f劳澄,并返回最終結果值。
在python3中蜈七,reduce函數無法直接使用了养距。
被放在了functools模塊中,要使用需要先引入from functools import reduce
from functools import reduce
def f(x, y):
? ? return x + y
print(reduce(f,[1,3,5,7,9])) ? #25
f(...f(f(1,3),5)..)類似這種機制顽馋,一一調用,且前一個的結果和后一個元素作為下一次的參數带斑。
reduce()還可以接收第3個可選參數,作為計算的初始值配深。
例:求積,并指定初始值
def prod(x, y):
? ? return x*y
print(reduce(prod, [2, 5, 12],100)) ? #12000
print(reduce(prod, {2, 5, 12},100)) ? #12000
同樣的reduce()函數還支持tuple,set這些集合。
filter()函數
filter()函數是Python?內置的另一個有用的高階函數妹懒,filter()函數接收一個函數f和一個集合,這個函數f?的作用是對每個元素進行判斷双吆,返回 True或 False眨唬,filter()根據判斷結果自動過濾掉不符合條件的元素,返回由符合條件元素組成的filter對象伊诵。
def is_odd(x):
? ? return x % 2 == 1
rs = filter(is_odd, [1, 4, 6, 7, 9, 12, 17])
print(rs) ? #<filter object at 0x01DCFCF0>
print(list(rs)) ? #[1, 7, 9, 17]
利用filter()单绑,可以完成很多有用的功能,例如曹宴,刪除 None 或者空字符串搂橙。用來批量過濾參數最合適不過了。
sorted()函數
Python內置的sorted()函數可對list進行排序:
print(sorted([36, 5, 12, 9, 21])) ? #[5, 9, 12, 21, 36]
Python3中sorted()函數笛坦,還可以接收其他參數:
sorted(iterable区转,key=None,reverse=False)
Key接受一個函數,這個函數只有一個參數版扩,默認為None:這個函數用戶從每個元素中提取一個用于比較的關鍵字废离。
reverse是一個布爾值,如果設置為True礁芦,則列表元素將被倒序排列蜻韭,默認False
def sort_age(x):
? ? return x[2]
rs = sorted([('john', 'A', 15), ('jane', 'B', 12), ('dave','B', 10)],key=sort_age,reverse=True)
print(rs) ? ?#[('john', 'A', 15), ('jane', 'B', 12), ('dave', 'B', 10)]
python中函數返回函數
Python的函數不但可以返回int、str柿扣、list肖方、dict等數據類型,還可以返回函數未状!
def f():
? ? print ('call f()...')
? ? def g():
? ? ? ? print('call g()...')
? ? return g
x = f() ? ?#call f()...
x() ? ?#call g()...
還是那么理解俯画,返回函數其實就是返回一個變量,只不過這個變量不是指向字符串什么的司草,而是指向一個函數艰垂。
python中的閉包
閉包在很多語言都存在的,包括php,js埋虹。只是寫法和作用可能各不相同
常見的python閉包寫法:
def calc_sum(lst):
? ? def lazy_sum():
? ? ? ? return sum(lst)
return lazy_sum
在calc_sum的內部定義一個新的函數猜憎,lazy_sum,并返回這個函數搔课。且這個函數使用的其外部函數calc_sum的參數拉宗,也就是lst變量。
注意:發(fā)現沒法把lazy_sum移到calc_sum的外部,因為它引用了calc_sum的參數lst旦事。
像這種內層函數引用了外層函數的變量(參數也算變量)魁巩,然后返回內層函數的情況,稱為閉包(Closure)姐浮。
閉包的特點是返回的函數還引用了外層函數的局部變量谷遂,所以,要正確使用閉包卖鲤,就要確保引用的局部變量在函數返回后不能變肾扰。
python中的匿名函數
高階函數可以接收函數做參數,有些時候蛋逾,我們不需要顯式地定義函數集晚,直接傳入匿名函數更方便。
在Python中区匣,對匿名函數提供了有限支持
例偷拔,計算f(x)=x2時,除了定義一個f(x)的函數外亏钩,還可以直接傳入匿名函數:
rs = map(lambda x: x * x, [1, 2, 3, 4, 5, 6, 7, 8, 9])
print(list(rs)) ? #[1, 4, 9, 16, 25, 36, 49, 64, 81]
通過對比可以看出莲绰,匿名函數lambda x: x * x實際上就是:
def f(x):
? ? return x * x
關鍵字lambda表示匿名函數,冒號前面的x 表示函數參數姑丑。
匿名函數有個限制蛤签,就是只能有一個表達式,不寫return栅哀,返回值就是該表達式的結果震肮。
返回函數的時候,也可以返回匿名函數留拾。
Python中的decorator裝飾器
例子:
def new_fn(f):
? ? def fn(x):
? ? ? ? print('call:'+f.__name__+'()')
? ? ? ? return f(x)
? ? return fn
@new_fn
def f1(x):
? ? return x*2
print(f1(2))#4
@new_fn
def f2(x):
? ? return x*3
print(f2(2))#6
python中編寫無參數的decorator
Python的decorator本質上就是一個高階函數戳晌,它接收一個函數作為參數,然后间驮,返回一個新函數。
使用decorator 用Python提供的@語法马昨,這樣可以避免手動編寫f = decorate(f)這樣的代碼竞帽。
看上面的new_fn高階函數,他的參數寫死了鸿捧,接受一個參數屹篓,當使用new_fn裝飾的函數參數數量不是1個時候,就會報錯匙奴。
比如:
@new_fn
def f1(x,y):
? ? return x*y
print(f1(2,4))
#TypeError: fn() takes 1 positional argument but 2 were given報錯
要讓@log自適應任何參數定義的函數堆巧,可以利用Python的*args和**kw,保證任意個數的參數總是能正常調用:
#裝飾器
def new_fn(f):
? ? def fn(*args, **kw):
? ? ? ? print('call:'+f.__name__+'()')
? ? ? ? return f(*args, **kw)
? ? return fn
@new_fn
def f1(x,y):
? ? return x*y
print(f1(2,4)) #8
python中編寫帶參數的decorator
使用裝飾器對函數進行擴展,在上面的new_fn函數中谍肤,發(fā)現對于被裝飾的函數啦租,log打印的語句是不能變的(除了函數名)。
如果有的函數非常重要荒揣,希望打印出'[INFO] call xxx()...'篷角,有的函數不太重要,希望打印出'[DEBUG] call xxx()...'系任,這時恳蹲,log函數本身就需要傳入'INFO'或'DEBUG'這樣的參數,類似這樣:
可以使用閉包俩滥,對裝飾器函數再進行擴展:
@log('DEBUG')
def log(prefix):
? ? def log_decorator(f):
? ? ? ? def wrapper(*args, **kw):
? ? ? ? ? ? print('[%s] %s()...' % (prefix, f.__name__))
? ? ? ? ? ? return f(*args, **kw)
? ? ? ? return wrapper
? ? return log_decorator
@log('DEBUG')
def test(x):
? ? print(x)
test('測試') ? #[DEBUG] test()...測試
python完善的裝飾器decorator
@decorator可以動態(tài)實現函數功能的增加嘉蕾,但是,經過@decorator“改造”后的函數霜旧,和原函數相比错忱,除了功能多一點外,函數的屬性上是存在不同的。
在沒有decorator的情況下颁糟,打印函數名:
def f1(x):
? ? return
print(f1.__name__) ? ?#f1
在有decorator的情況下航背,打印函數名:
@new_fn
def f2(x):
? ? return
print(f2.__name__) ? #fn ?fn為裝飾器new_fn內部定義的閉包函數。
可見棱貌,由于decorator返回的新函數函數名已經不是'f2'玖媚,而是@new_fn內部定義的'fn'。這對于那些依賴函數名的代碼就會失效婚脱。decorator還改變了函數的__doc__等其它屬性今魔。如果要讓調用者看不出一個函數經過了@decorator的“改造”,就需要把原函數的一些屬性復制到新函數中:
def new_fn(f):
? ? def fn(*args, **kw):
? ? ? ? print('call:'+f.__name__+'()')
? ? ? ? return f(*args, **kw)
? ? fn.__name__ = f.__name__
? ? fn.__doc__ = f.__doc__
? ? return fn
這樣寫decorator很不方便障贸,因為我們也很難把原函數的所有必要屬性都一個一個復制到新函數上错森,所以Python內置的functools可以用來自動化完成這個“復制”的任務:
import functools
#裝飾器
def new_fn(f):
? ? @functools.wraps(f)
? ? def fn(*args, **kw):
? ? ? ? print('call:'+f.__name__+'()')
? ? ? ? return f(*args, **kw)
? ? return fn
使用functools.wraps方法,復制函數屬性篮洁。涩维。
模塊和包
Python導入模塊
#整個包導入
import math ? #導入math模塊
math.pow(函數)瓦阐、math.pi(變量)...訪問math模塊中所定義的所有公開的函數,變量和類
#只導入部分函數
from math import pow, sin, log
這樣篷牌,可以直接引用pow, sin, log這3個函數睡蟋,且都不需要加前綴(math.),但math的其他函數沒有導入進來枷颊。
#如果遇到名字沖突
比如math模塊有一個log函數戳杀,logging模塊也有一個log函數该面,如果同時使用,如何解決名字沖突信卡?
import math, logging
math.log(10) ? ?# 調用的是math的log函數
logging.log(10, 'something') ? # 調用的是logging的log函數
如果使用from...import導入log函數隔缀,勢必引起沖突。這時坐求,可以給函數起個“別名”來避免沖突:
from math import log
from logging import log as logger ? ? ?# logging的log現在變成了logger
log(10) ? ?# 調用的是math的log
logger(10, 'import from logging') ? ? # 調用的是logging的log
Python的模塊是可以在代碼運行時動態(tài)引入的蚕泽,比如:
try:
? ? from cStringIO import StringIO
except ImportError:
? ? from StringIO import StringIO
當cStringIO引入失敗時,就會去引入StringIO
try的作用是捕獲錯誤桥嗤,并在捕獲到指定錯誤時執(zhí)行except語句须妻。
Python在引入模塊時使用__future__
Python的新版本會引入新的功能,但是泛领,實際上這些功能在上一個老版本中就已經存在了荒吏。要“試用”某一新的特性,就可以通過導入__future__模塊的某些功能來實現渊鞋。
from __future__ import division ? # 在Python 2.7中引入3.x的除法規(guī)則(整數相除也保留小數)绰更,導入__future__的division
Python安裝第三方模塊
使用pip來管理python的模塊。
pip install redis.py ? #安裝模塊redis (安裝完成應該下下載到/python3.6/lib/site-packages下面)
pip list #列出已安裝的包
更多看pip --help
python面向對象編程
對面對象的三大特征是锡宋,封裝儡湾、繼承执俩、多態(tài)。
定義類class類名(繼承類):
class Person(object):
? ? pass
按照Python的編程習慣,類名以大寫字母開頭衡奥,緊接著是(object),表示該類是從哪個類繼承下來的失息。這里表示從object類繼承档址。
創(chuàng)建類的實例類名()的形式創(chuàng)建:
xiaoming = Person()
xiaohong = Person()
Python中創(chuàng)建實例屬性
xiaoming.name = 'Xiao Ming' ?#就是直接通過 實例.屬性名=屬性值 直接賦值即可
也可以是函數名,或者定義好的表達式:
xiaoming.name1 = abs
def abc(x):
? ? return x+1;
xiaoming.name2 = abc
print(xiaoming.name1(-1)) ? #1
print(xiaoming.name2(-1)) ? #0
反正就是很靈活辰晕,慢慢試吧蛤迎。(靈活和蛇很貼切叭肥)
Python中初始化實例屬性
在python中通過給類名定義__init__()方法來進行初始化含友,__init__方法會在實例化類的時候被執(zhí)行,且會帶入實例化時傳入的參數(第一個參數已經確定了,是當前實例窘问,且習慣用法是使用self,傳入的參數跟在后面)辆童。
class Person(object):
? ? def __init__(self,name):
? ? ? ? self.name = name
xiaoming = Person('小明')
print(xiaoming.name) ?#小明
xiaohong = Person('小紅')
print(xiaohong.name) ?#小紅
Python類中的訪問限制
Python對屬性權限的控制是通過屬性名來實現的,如果一個屬性由雙下劃線開頭(__)惠赫,且不以雙下劃線(__)為結尾把鉴,該屬性就無法被外部訪問。
首尾都是雙下劃線的屬性在python類中被稱為特殊屬性儿咱,自定義的屬性習慣上不用這種形式定義庭砍。
我現在__init__方法中指定了__b屬性,self.__b = 2混埠,再調用:
print(xiaoming.__b) ?#'Person' object has no attribute '__b'
Python類中創(chuàng)建類屬性
類是模板怠缸,而實例則是根據類創(chuàng)建的對象。
綁定在一個實例上的屬性不會影響其他實例钳宪,但是揭北,類本身也是一個對象,如果在類上綁定一個屬性吏颖,則所有實例都可以訪問類的屬性搔体,并且半醉,所有實例訪問的類屬性都是同一個奉呛!也就是說登馒,實例屬性每個實例各自擁有陈轿,互相獨立,而類屬性有且只有一份潜秋。
定義類屬性可以直接寫在類中的:
class Person(object):
? ? country = 'China'
? ? def __init__(self,name):
? ? ? ? self.name = name
? ? ? ? self._a = 1
? ? ? ? self.__b = 2
因為類屬性是直接綁定在類上的罗售,所以寨躁,訪問類屬性不需要創(chuàng)建實例,就可以直接訪問:
print(Person.country) ???#China
該類的所有實例也能訪問:
xiaoming = Person('小明')
print(xiaoming.country) #China
類屬性也可以動態(tài)添加和修改:
Person.address = '福州'
print(xiaoming.address) ?#福州
因為類屬性只有一份放钦,所以最筒,當Person類的address改變時,所有實例訪問到的類屬性都改變了邢锯。
當實例屬性和類屬性重名時丹擎,實例屬性優(yōu)先級高,它將屏蔽掉對類屬性的訪問护戳。
Python中定義實例方法
一個實例的私有屬性就是以__開頭的屬性(除了特殊屬性),無法被外部訪問
雖然私有屬性無法從外部訪問钳枕,但是鱼炒,從類的內部是可以訪問的俐巴。
實例的方法就是在類中定義的函數,它的第一個參數永遠是self(習慣定義)缀磕,指向調用該方法的實例本身,其他參數和一個普通函數是完全一樣的:
class Person(object):
? ? country = 'China'
? ? def __init__(self,name):
? ? ? ? self.name = name
? ? ? ? self.__name = '人'
? ? def getCountry(self):
? ? ? ? return self.country
實例方法必須在實例上調用:
xiaoming = Person('小明')
print(xiaoming.getCountry()) #China
Python類中的方法也是屬性
在類中定義的實例方法其實也是屬性牲剃,它實際上是一個函數對象。
Python中定義類方法
和屬性類似聪舒,方法也分實例方法和類方法箱残。
在class中定義的全部是實例方法,實例方法第一個參數self是實例本身盼理。
class Person(object):
? ? count = 0
? ? @classmethod
? ? def how_many(cls):
? ? ? ? return cls.count
? ? def __init__(self, name):
? ? ? ? self.name = name
? ? ? ? Person.count = Person.count + 1
通過標記一個@classmethod榜揖,該方法將綁定到Person類上,而非類的實例妨猩。類方法的第一個參數將傳入類本身壶硅,通常將參數名命名為cls椒舵,上面的cls.count實際上相當于Person.count笔宿。
因為是在類上調用,而非實例上調用炬灭,因此類方法無法獲得任何實例變量重归,只能獲得類的引用。
Python中類的繼承
如果已經定義了Person類笨腥,需要定義新的Student和Teacher類時士鸥,可以直接從Person類繼承:
class Person(object):
? ? def __init__(self, name, gender):
? ? ? ? self.name = name
? ? ? ? self.gender = gender
定義Student類時烤礁,只需要把額外的屬性加上,例如score:
class Student(Person):
? ? def __init__(self, name, gender, score):
? ? ? ? super(Student, self).__init__(name, gender)
? ? ? ? self.score = score
一定要用super(Student, self).__init__(name, gender)去初始化父類鲤脏,否則窥突,繼承自Person的Student將沒有name和gender阻问。
函數super(Student, self)將返回當前類繼承的父類,即Person,然后調用__init__()方法彻桃,注意self參數已在super()中傳入邻眷,在__init__()中將隱式傳遞,不需要寫出(也不能寫)驯镊。
Python中繼承一個類完整例子:
class Person(object):
? ? def __init__(self, name, score):
? ? ? ? self.name = name
? ? ? ? self.score = score
class Student(Person):
? ? def __init__(self, name, score):
? ? ? ? super(Student, self).__init__(name, score)
ming = Student('小明',90);
print(ming.score) ? #90
Python中判斷類型
函數isinstance()可以判斷一個變量的類型,既可以用在Python內置的數據類型如str冯乘、list裆馒、dict,也可以用在我們自定義的類绒窑,它們本質上都是數據類型些膨。
比如判斷上面例子中肢预,實例ming的父類是不是Person:
print(isinstance(ming,Person)) #True
print(isinstance(ming,object)) #True
類的多態(tài)
動態(tài)語言調用實例方法烫映,不檢查類型,只要方法存在族淮,參數正確祝辣,就可以調用。
Python中類的繼承孕荠,可以在子類中重寫父類的屬性方法等岛琼。
Python提供了open()函數來打開一個磁盤文件,并返回File對象困檩。File對象有一個read()方法可以讀取文件內容:
例如悼沿,從文件讀取內容并解析為JSON結果:
import json
f = open('/path/to/file.json', 'r')
print( json.load(f) )
由于Python的動態(tài)特性,json.load()并不一定要從一個File對象讀取內容义郑。任何對象交汤,只要有read()方法芙扎,就稱為File-like Object,都可以傳給json.load()圈浇。
請嘗試編寫一個File-like Object汉额,把一個字符串r'["Tim", "Bob", "Alice"]'包裝成File-like Object并由json.load()解析。
例子:py3.6
import json
class Students(object):
? ? def read(self):
? ? ? ? return r'["Tim", "Bob", "Alice"]'
s = Students()
print(json.load(s)) ?#['Tim', 'Bob', 'Alice']
面向對象的三個基本特征:封裝收壕,繼承,多態(tài)
多重繼承
多重繼承的目的是從兩種繼承樹中分別選擇并繼承出子類圃验,以便組合功能使用。
舉個例子摊聋,Python的網絡服務器有TCPServer麻裁、UDPServer色迂、UnixStreamServer、UnixDatagramServer馏慨,而服務器運行模式有多進程ForkingMixin和多線程ThreadingMixin兩種写隶。
要創(chuàng)建多進程模式的TCPServer:
class MyTCPServer(TCPServer, ForkingMixin)
? ? pass
要創(chuàng)建多線程模式的UDPServer:
class MyUDPServer(UDPServer, ThreadingMixin):
? ? pass
如果沒有多重繼承,要實現上述所有可能的組合需要4x2=8 個子類冕房。
Python中獲取對象信息
isinstance()判斷它是否是某種類型的實例
type()函數獲取變量的類型耙册,它返回一個Type對象
print(type(123)) # <class 'int'>? 整形
print(type(ming)) # <class '__main__.Student'>Student類的實例
print(type(Student)) # <class 'type'>類
可以用dir()函數獲取變量的所有屬性:dir(ming),dir(123)這種用法
獲取所有屬性,包括特殊屬性饶辙,其中def的方法也是屬性弃揽,只不過這個屬性變量是指向一個函數就是了矿微。
過濾特殊屬性,可以使用filter函數
def normalAttr(x):
? ? h = x[0:2]
? ? if h == '__':
? ? ? ? return False
? ? return True
rs = filter(normalAttr, dir(ming))
print(list(rs)) ? #['lak', 'name', 'score']
dir()返回的屬性是字符串列表蒿辙,如果已知一個屬性名稱思灌,要獲取或者設置對象的屬性熄守,就需要用getattr()和setattr( )函數了:
>>> getattr(s, 'name') # 獲取name屬性
'Bob'
>>> setattr(s, 'name', 'Adam')# 設置新的name屬性
>>> s.name
'Adam'
>>> getattr(s, 'age')# 獲取age屬性裕照,但是屬性不存在,報錯
Traceback (most recent call last):
File "", line 1, in
AttributeError: 'Student' object has no attribute 'age'
>>> getattr(s, 'age', 20)# 獲取age屬性羔砾,如果屬性不存在政溃,就返回默認值20:
20
#傳入**kw 即可傳入任意數量的參數,并通過 setattr() 綁定屬性屿聋。
class Person(object):
? ? def __init__(self, name, gender, **kw):
? ? ? ? self.name = name
? ? ? ? self.gender = gender
? ? ? ? for k in kw:
? ? ? ? ? ? setattr(self, k, kw[k])
p = Person('Bob', 'Male', age=18, course='Python')
print(p.age) #18
print(p.course) #Python
Python的特殊方法(魔術方法)
Python中的__str__
如果要把一個類的實例變成str楚殿,就需要實現特殊方法__str__():
class Test(object):
? ? def __init__(self,name,say):
? ? ? ? self.name = name
? ? ? ? self.say = say
? ? def __str__(self):
? ? ? ? return '(Person: %s)' % [self.name,self.say]
t = Test('測試','__str__魔術方法')
print(t) #(Person: ['測試', '__str__魔術方法'])
Python中的__call__
在Python中,函數其實是一個對象:
f = abs
print(f.__name__) ?#abs
print(f(-123)) ?#123
由于?f?可以被調用变隔,所以匣缘,f?被稱為可調用對象。
所有的函數都是可調用對象培慌。
一個類實例也可以變成一個可調用對象吵护,只需要實現一個特殊方法__call__()。
我們把Person類變成一個可調用對象:
class Person(object):
? ? def __init__(self, name, gender):
? ? ? ? self.name = name
? ? ? ? self.gender = gender
? ? def __call__(self, friend):
? ? ? ? print('My name is %s...' % self.name)
? ? ? ? print('My friend is %s...' % friend)
p = Person('Bob', 'male')
p('Tim')
# My name is Bob...
# My friend is Tim...