從函數(shù)到高級(jí)魔法方法
Python 是一種通用編程語(yǔ)言,其在科學(xué)計(jì)算和機(jī)器學(xué)習(xí)領(lǐng)域具有廣泛的應(yīng)用季惯。如果我們打算利用 Python 來(lái)執(zhí)行機(jī)器學(xué)習(xí)霞丧,那么對(duì) Python 有一些基本的了解就是至關(guān)重要的目锭。本 Python 入門(mén)系列體驗(yàn)就是為這樣的初學(xué)者精心準(zhǔn)備的厌秒。
函數(shù)
函數(shù)的定義
還記得 Python 里面“萬(wàn)物皆對(duì)象”么?Python 把函數(shù)也當(dāng)成對(duì)象汉形,可以從另一個(gè)函數(shù)中返回出來(lái)而去構(gòu)建高階函數(shù)纸镊,比如: 參數(shù)是函數(shù)、返回值是函數(shù)概疆。
- 函數(shù)以def關(guān)鍵字開(kāi)頭逗威,后接函數(shù)名和圓括號(hào)()。
- 函數(shù)執(zhí)行的代碼以冒號(hào)起始届案,并且縮進(jìn)庵楷。
- runturn[表達(dá)式]結(jié)束函數(shù),選擇性地返回一個(gè)值給調(diào)用方楣颠。不帶表達(dá)式的return相當(dāng)于返回None尽纽。
def functionname (parameters):
“函數(shù)文檔字符串”
functionsuite
return [expression]
函數(shù)的調(diào)用
def printme(str):
print(str)
printme("我要調(diào)用用戶自定義函數(shù)!") # 我要調(diào)用用戶自定義函數(shù)!
printme("再次調(diào)用同一函數(shù)") # 再次調(diào)用同一函數(shù)
temp = printme('hello') # hello
print(temp) # None
函數(shù)文檔
def MyFirstFunction(name):
"函數(shù)定義過(guò)程中name是形參"
# 因?yàn)門(mén)a只是一個(gè)形式,表示占據(jù)一個(gè)參數(shù)位置
print('傳遞進(jìn)來(lái)的{0}叫做實(shí)參童漩,因?yàn)門(mén)a是具體的參數(shù)值弄贿!'.format(name))
MyFirstFunction('老馬的程序人生')
# 傳遞進(jìn)來(lái)的老馬的程序人生叫做實(shí)參,因?yàn)門(mén)a是具體的參數(shù)值矫膨!
print(MyFirstFunction.__doc__)
# 函數(shù)定義過(guò)程中name是形參
help(MyFirstFunction)
# Help on function MyFirstFunction in module __main__:
# MyFirstFunction(name)
# 函數(shù)定義過(guò)程中name是形參
函數(shù)參數(shù)
Python 的函數(shù)具有非常靈活多樣的參數(shù)形態(tài)差凹,既可以實(shí)現(xiàn)簡(jiǎn)單的調(diào)用,又可以傳入非常復(fù)雜的參數(shù)侧馅。從簡(jiǎn)到繁的參數(shù)形態(tài)如下:
- 位置參數(shù) (positional argument)
- 默認(rèn)參數(shù) (default argument)
- 可變參數(shù) (variable argument): *args - 可變參數(shù)危尿,可以是從零個(gè)到任意個(gè),自動(dòng)組裝成元組馁痴。
加了星號(hào)(*)的變量名會(huì)存放所有未命名的變量參數(shù) - 關(guān)鍵字參數(shù) (keyword argument): **kw - 關(guān)鍵字參數(shù)谊娇,可以是從零個(gè)到任意個(gè),自動(dòng)組裝成字典罗晕。
「可變參數(shù)」和「關(guān)鍵字參數(shù)」的同異總結(jié)如下:
- 可變參數(shù)允許傳入零個(gè)到任意個(gè)參數(shù)济欢,它們?cè)诤瘮?shù)調(diào)用時(shí)自動(dòng)組裝為一個(gè)元組 (tuple)赠堵。
- 關(guān)鍵字參數(shù)允許傳入零個(gè)到任意個(gè)參數(shù),它們?cè)诤瘮?shù)內(nèi)部自動(dòng)組裝為一個(gè)字典 (dict)法褥。
- 命名關(guān)鍵字參數(shù) (name keyword argument)
- *, nkw - 命名關(guān)鍵字參數(shù)茫叭,用戶想要輸入的關(guān)鍵字參數(shù),定義方式是在nkw 前面加個(gè)分隔符 *半等。
- 如果要限制關(guān)鍵字參數(shù)的名字揍愁,就可以用「命名關(guān)鍵字參數(shù)」
- 使用命名關(guān)鍵字參數(shù)時(shí),要特別注意不能缺少參數(shù)名酱鸭。
def printinfo(arg1, *, nkw, **kwargs):
print(arg1)
print(nkw)
print(kwargs)
printinfo(70, nkw=10, a=1, b=2)
# 70
# 10
# {'a': 1, 'b': 2}
printinfo(70, 10, a=1, b=2)
# TypeError: printinfo() takes 1 positional argument but 2 were given
沒(méi)有寫(xiě)參數(shù)名nwk吗垮,因此 10 被當(dāng)成「位置參數(shù)」垛吗,而原函數(shù)只有 1 個(gè)位置函數(shù)凹髓,現(xiàn)在調(diào)用了 2 個(gè),因此程序會(huì)報(bào)錯(cuò)怯屉。
- 參數(shù)組合
在 Python 中定義函數(shù)蔚舀,可以用位置參數(shù)、默認(rèn)參數(shù)锨络、可變參數(shù)赌躺、命名關(guān)鍵字參數(shù)和關(guān)鍵字參數(shù),這 5 種參數(shù)中的 4 個(gè)都可以一起使用羡儿,但是注意礼患,參數(shù)定義的順序必須是:
- 位置參數(shù)、默認(rèn)參數(shù)掠归、可變參數(shù)和關(guān)鍵字參數(shù)缅叠。
- 位置參數(shù)、默認(rèn)參數(shù)虏冻、命名關(guān)鍵字參數(shù)和關(guān)鍵字參數(shù)肤粱。
要注意定義可變參數(shù)和關(guān)鍵字參數(shù)的語(yǔ)法:
- *args 是可變參數(shù),args 接收的是一個(gè) tuple
- **kw 是關(guān)鍵字參數(shù)厨相,kw 接收的是一個(gè) dict
命名關(guān)鍵字參數(shù)是為了限制調(diào)用者可以傳入的參數(shù)名领曼,同時(shí)可以提供默認(rèn)值。定義命名關(guān)鍵字參數(shù)不要忘了寫(xiě)分隔符 *蛮穿,否則定義的是位置參數(shù)庶骄。
警告:雖然可以組合多達(dá) 5 種參數(shù),但不要同時(shí)使用太多的組合践磅,否則函數(shù)很難懂单刁。
函數(shù)的返回值
def add(a, b):
return a + b
print(add(1, 2)) # 3
print(add([1, 2, 3], [4, 5, 6])) # [1, 2, 3, 4, 5, 6]
def back():
return [1, '小馬的程序人生', 3.14]
print(back()) # [1, '小馬的程序人生', 3.14]
def back():
return 1, '小馬的程序人生', 3.14
print(back()) # (1, '小馬的程序人生', 3.14)
def printme(str):
print(str)
temp = printme('hello') # hello
print(temp) # None
print(type(temp)) # <class 'NoneType'>
變量作用域
- Python 中,程序的變量并不是在哪個(gè)位置都可以訪問(wèn)的音诈,訪問(wèn)權(quán)限決定于這個(gè)變量是在哪里賦值的幻碱。
- 定義在函數(shù)內(nèi)部的變量擁有局部作用域绎狭,該變量稱(chēng)為局部變量。
- 定義在函數(shù)外部的變量擁有全局作用域褥傍,該變量稱(chēng)為全局變量儡嘶。
- 局部變量只能在其被聲明的函數(shù)內(nèi)部訪問(wèn),而全局變量可以在整個(gè)程序范圍內(nèi)訪問(wèn)恍风。
def discounts(price, rate):
final_price = price * rate
return final_price
old_price = float(input('請(qǐng)輸入原價(jià):')) # 98
rate = float(input('請(qǐng)輸入折扣率:')) # 0.9
new_price = discounts(old_price, rate)
print('打折后價(jià)格是:%.2f' % new_price) # 88.20
- 當(dāng)內(nèi)部作用域想修改外部作用域的變量時(shí)蹦狂,就要用到global和nonlocal關(guān)鍵字了。
num = 1
def fun1():
global num # 需要使用 global 關(guān)鍵字聲明
print(num) # 1
num = 123
print(num) # 123
fun1()
print(num) # 123
內(nèi)嵌函數(shù)
def outer():
print('outer函數(shù)在這被調(diào)用')
def inner():
print('inner函數(shù)在這被調(diào)用')
inner() # 該函數(shù)只能在outer函數(shù)內(nèi)部被調(diào)用
outer()
# outer函數(shù)在這被調(diào)用
# inner函數(shù)在這被調(diào)用
閉包
- 是函數(shù)式編程的一個(gè)重要的語(yǔ)法結(jié)構(gòu)朋贬,是一種特殊的內(nèi)嵌函數(shù)凯楔。
- 如果在一個(gè)內(nèi)部函數(shù)里對(duì)外層非全局作用域的變量進(jìn)行引用,那么內(nèi)部函數(shù)就被認(rèn)為是閉包锦募。
- 通過(guò)閉包可以訪問(wèn)外層非全局作用域的變量摆屯,這個(gè)作用域稱(chēng)為 閉包作用域。
def make_counter(init):
counter = [init]
def inc(): counter[0] += 1
def dec(): counter[0] -= 1
def get(): return counter[0]
def reset(): counter[0] = init
return inc, dec, get, reset
inc, dec, get, reset = make_counter(0)
inc()
inc()
inc()
print(get()) # 3
dec()
print(get()) # 2
reset()
print(get()) # 0
如果要修改閉包作用域中的變量則需要 nonlocal 關(guān)鍵字
def outer():
num = 10
def inner():
nonlocal num # nonlocal關(guān)鍵字聲明
num = 100
print(num)
inner()
print(num)
outer()
# 100
# 100
遞歸
- 如果一個(gè)函數(shù)在內(nèi)部調(diào)用自身本身糠亩,這個(gè)函數(shù)就是遞歸函數(shù)虐骑。
# 利用循環(huán)
i = 0
j = 1
lst = list([i, j])
for k in range(2, 11):
k = i + j
lst.append(k)
i = j
j = k
print(lst)
# [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
# 利用遞歸
def recur_fibo(n):
if n <= 1:
return n
return recur_fibo(n - 1) + recur_fibo(n - 2)
lst = list()
for k in range(11):
lst.append(recur_fibo(k))
print(lst)
# [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
Lambda表達(dá)式
匿名函數(shù)的定義
在 Python 里有兩類(lèi)函數(shù):
- 第一類(lèi):用 def 關(guān)鍵詞定義的正規(guī)函數(shù)
- 第二類(lèi):用 lambda 關(guān)鍵詞定義的匿名函數(shù)
Python 使用 lambda 關(guān)鍵詞來(lái)創(chuàng)建匿名函數(shù),而非def關(guān)鍵詞赎线,它沒(méi)有函數(shù)名廷没,其語(yǔ)法結(jié)構(gòu)如下:
lambda argument_list: expression
- lambda - 定義匿名函數(shù)的關(guān)鍵詞。
- argument_list - 函數(shù)參數(shù)垂寥,它們可以是位置參數(shù)颠黎、默認(rèn)參數(shù)、關(guān)鍵字參數(shù)滞项,和正規(guī)函數(shù)里的參數(shù)類(lèi)型一樣狭归。
- :- 冒號(hào),在函數(shù)參數(shù)和表達(dá)式中間要加個(gè)冒號(hào)蓖扑。
- expression - 只是一個(gè)表達(dá)式唉铜,輸入函數(shù)參數(shù),輸出一些值律杠。
注意:
- expression 中沒(méi)有 return 語(yǔ)句潭流,因?yàn)?lambda 不需要它來(lái)返回,表達(dá)式本身結(jié)果就是返回值柜去。
- 匿名函數(shù)擁有自己的命名空間灰嫉,且不能訪問(wèn)自己參數(shù)列表之外或全局命名空間里的參數(shù)。
def sqr(x):
return x ** 2
print(sqr)
# <function sqr at 0x000000BABD3A4400>
y = [sqr(x) for x in range(10)]
print(y)
# [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
lbd_sqr = lambda x: x ** 2
print(lbd_sqr)
# <function <lambda> at 0x000000BABB6AC1E0>
y = [lbd_sqr(x) for x in range(10)]
print(y)
# [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
sumary = lambda arg1, arg2: arg1 + arg2
print(sumary(10, 20)) # 30
func = lambda *args: sum(args)
print(func(1, 2, 3, 4, 5)) # 15
匿名函數(shù)的應(yīng)用
函數(shù)式編程 是指代碼中每一塊都是不可變的嗓奢,都由純函數(shù)的形式組成讼撒。這里的純函數(shù),是指函數(shù)本身相互獨(dú)立、互不影響根盒,對(duì)于相同的輸入钳幅,總會(huì)有相同的輸出,沒(méi)有任何副作用炎滞。
非函數(shù)式編程
def f(x):
for i in range(0, len(x)):
x[i] += 10
return x
x = [1, 2, 3]
f(x)
print(x)
# [11, 12, 13]
函數(shù)式編程
def f(x):
y = []
for item in x:
y.append(item + 10)
return y
x = [1, 2, 3]
f(x)
print(x)
# [1, 2, 3]
匿名函數(shù) 常常應(yīng)用于函數(shù)式編程的高階函數(shù) (high-order function)中敢艰,主要有兩種形式:
- 參數(shù)是函數(shù)(filter, map)
- 返回值是函數(shù)(closure)
如,在 filter和map函數(shù)中的應(yīng)用:
- filter(function, iterable) 過(guò)濾序列册赛,過(guò)濾掉不符合條件的元素钠导,返回一個(gè)迭代器對(duì)象,如果要轉(zhuǎn)換為列表森瘪,可以使用 list() 來(lái)轉(zhuǎn)換牡属。
odd = lambda x: x % 2 == 1
templist = filter(odd, [1, 2, 3, 4, 5, 6, 7, 8, 9])
print(list(templist)) # [1, 3, 5, 7, 9]
- map(function, *iterables) 根據(jù)提供的函數(shù)對(duì)指定序列做映射。
m1 = map(lambda x: x ** 2, [1, 2, 3, 4, 5])
print(list(m1))
# [1, 4, 9, 16, 25]
m2 = map(lambda x, y: x + y, [1, 3, 5, 7, 9], [2, 4, 6, 8, 10])
print(list(m2))
# [3, 7, 11, 15, 19]
除了 Python 這些內(nèi)置函數(shù)扼睬,我們也可以自己定義高階函數(shù)逮栅。
def apply_to_list(fun, some_list):
return fun(some_list)
lst = [1, 2, 3, 4, 5]
print(apply_to_list(sum, lst))
# 15
print(apply_to_list(len, lst))
# 5
print(apply_to_list(lambda x: sum(x) / len(x), lst))
# 3.0
類(lèi)與對(duì)象
對(duì)象=屬性+方法
對(duì)象是類(lèi)的實(shí)例。換句話說(shuō)痰驱,類(lèi)主要定義對(duì)象的結(jié)構(gòu)证芭,然后我們以類(lèi)為模板創(chuàng)建對(duì)象。類(lèi)不但包含方法定義担映,而且還包含所有實(shí)例共享的數(shù)據(jù)。
- 封裝:信息封閉技術(shù)
class Turtle: # Python中的類(lèi)名約定以大寫(xiě)字母開(kāi)頭
"""關(guān)于類(lèi)的一個(gè)簡(jiǎn)單例子"""
# 屬性
color = 'green'
weight = 10
legs = 4
shell = True
mouth = '大嘴'
# 方法
def climb(self):
print('我正在很努力的向前爬...')
def run(self):
print('我正在飛快的向前跑...')
def bite(self):
print('咬死你咬死你!!')
def eat(self):
print('有得吃叫潦,真滿足...')
def sleep(self):
print('困了蝇完,睡了,晚安矗蕊,zzz')
tt = Turtle()
print(tt)
# <__main__.Turtle object at 0x0000007C32D67F98>
print(type(tt))
# <class '__main__.Turtle'>
print(tt.__class__)
# <class '__main__.Turtle'>
print(tt.__class__.__name__)
# Turtle
tt.climb()
# 我正在很努力的向前爬...
tt.run()
# 我正在飛快的向前跑...
tt.bite()
# 咬死你咬死你!!
# Python類(lèi)也是對(duì)象短蜕。它們是type的實(shí)例
print(type(Turtle))
# <class 'type'>
- 繼承:子類(lèi)自動(dòng)共享父類(lèi)之間數(shù)據(jù)和方法的機(jī)制
class MyList(list):
pass
lst = MyList([1, 5, 2, 7, 8])
lst.append(9)
lst.sort()
print(lst)
# [1, 2, 5, 7, 8, 9]
- 多態(tài):不同對(duì)象對(duì)同一方法響應(yīng)不同的行動(dòng)
class Animal:
def run(self):
raise AttributeError('子類(lèi)必須實(shí)現(xiàn)這個(gè)方法')
class People(Animal):
def run(self):
print('人正在走')
class Pig(Animal):
def run(self):
print('pig is walking')
class Dog(Animal):
def run(self):
print('dog is running')
def func(animal):
animal.run()
func(Pig())
# pig is walking
self是什么
Python 的 self 相當(dāng)于 C++ 的 this 指針。
class Test:
def prt(self):
print(self)
print(self.__class__)
t = Test()
t.prt()
# <__main__.Test object at 0x000000BC5A351208>
# <class '__main__.Test'>
類(lèi)的方法與普通的函數(shù)只有一個(gè)特別的區(qū)別 —— 它們必須有一個(gè)額外的第一個(gè)參數(shù)名稱(chēng)(對(duì)應(yīng)于該實(shí)例傻咖,即該對(duì)象本身)朋魔,按照慣例它的名稱(chēng)是 self。在調(diào)用方法時(shí)卿操,我們無(wú)需明確提供與參數(shù) self 相對(duì)應(yīng)的參數(shù)警检。
class Ball:
def setName(self, name):
self.name = name
def kick(self):
print("我叫%s,該死的,誰(shuí)踢我..." % self.name)
a = Ball()
a.setName("球A")
b = Ball()
b.setName("球B")
c = Ball()
c.setName("球C")
a.kick()
# 我叫球A,該死的害淤,誰(shuí)踢我...
b.kick()
# 我叫球B,該死的扇雕,誰(shuí)踢我...
Python的魔法方法
據(jù)說(shuō),Python 的對(duì)象天生擁有一些神奇的方法窥摄,它們是面向?qū)ο蟮?Python 的一切...
它們是可以給你的類(lèi)增加魔力的特殊方法...
如果你的對(duì)象實(shí)現(xiàn)了這些方法中的某一個(gè)镶奉,那么這個(gè)方法就會(huì)在特殊的情況下被 Python 所調(diào)用,而這一切都是自動(dòng)發(fā)生的...
類(lèi)有一個(gè)名為init(self[, param1, param2...])的魔法方法,該方法在類(lèi)實(shí)例化時(shí)會(huì)自動(dòng)調(diào)用哨苛。
class Ball:
def __init__(self, name):
self.name = name
def kick(self):
print("我叫%s,該死的鸽凶,誰(shuí)踢我..." % self.name)
a = Ball("球A")
b = Ball("球B")
c = Ball("球C")
a.kick()
# 我叫球A,該死的,誰(shuí)踢我...
b.kick()
# 我叫球B,該死的建峭,誰(shuí)踢我...
公有和私有
在 Python 中定義私有變量只需要在變量名或函數(shù)名前加上“__”兩個(gè)下劃線吱瘩,那么這個(gè)函數(shù)或變量就會(huì)為私有的了。
類(lèi)的私有屬性實(shí)例
class JustCounter:
__secretCount = 0 # 私有變量
publicCount = 0 # 公開(kāi)變量
def count(self):
self.__secretCount += 1
self.publicCount += 1
print(self.__secretCount)
counter = JustCounter()
counter.count() # 1
counter.count() # 2
print(counter.publicCount) # 2
# Python的私有為偽私有
print(counter._JustCounter__secretCount) # 2
print(counter.__secretCount)
# AttributeError: 'JustCounter' object has no attribute '__secretCount'
類(lèi)的私有方法實(shí)例
class Site:
def __init__(self, name, url):
self.name = name # public
self.__url = url # private
def who(self):
print('name : ', self.name)
print('url : ', self.__url)
def __foo(self): # 私有方法
print('這是私有方法')
def foo(self): # 公共方法
print('這是公共方法')
self.__foo()
x = Site('老馬的程序人生', 'https://blog.csdn.net/LSGO_MYP')
x.who()
# name : 老馬的程序人生
# url : https://blog.csdn.net/LSGO_MYP
x.foo()
# 這是公共方法
# 這是私有方法
x.__foo()
# AttributeError: 'Site' object has no attribute '__foo'
繼承
Python 同樣支持類(lèi)的繼承迹缀,派生類(lèi)的定義如下所示:
class DerivedClassName(BaseClassName):
statement-1 ... statement-N
BaseClassName(基類(lèi)名)必須與派生類(lèi)定義在一個(gè)作用域內(nèi)使碾。除了類(lèi),還可以用表達(dá)式祝懂,基類(lèi)定義在另一個(gè)模塊中時(shí)這一點(diǎn)非常有用:
class DerivedClassName(modname.BaseClassName):
statement-1 ... statement-N
Python 雖然支持多繼承的形式票摇,但我們一般不使用多繼承,因?yàn)槿菀滓鸹靵y砚蓬。
# 類(lèi)定義
class People:
# 定義基本屬性
name = ''
age = 0
# 定義私有屬性,私有屬性在類(lèi)外部無(wú)法直接進(jìn)行訪問(wèn)
__weight = 0
# 定義構(gòu)造方法
def __init__(self, n, a, w):
self.name = n
self.age = a
self.__weight = w
def speak(self):
print("%s 說(shuō): 我 %d 歲矢门。" % (self.name, self.age))
# 單繼承示例
class Student(People):
grade = ''
def __init__(self, n, a, w, g):
# 調(diào)用父類(lèi)的構(gòu)函
People.__init__(self, n, a, w)
self.grade = g
# 覆寫(xiě)父類(lèi)的方法
def speak(self):
print("%s 說(shuō): 我 %d 歲了,我在讀 %d 年級(jí)" % (self.name, self.age, self.grade))
# 另一個(gè)類(lèi)灰蛙,多重繼承之前的準(zhǔn)備
class Speaker:
topic = ''
name = ''
def __init__(self, n, t):
self.name = n
self.topic = t
def speak(self):
print("我叫 %s祟剔,我是一個(gè)演說(shuō)家,我演講的主題是 %s" % (self.name, self.topic))
# 多重繼承
class Sample01(Speaker, Student):
a = ''
def __init__(self, n, a, w, g, t):
Student.__init__(self, n, a, w, g)
Speaker.__init__(self, n, t)
# 方法名同摩梧,默認(rèn)調(diào)用的是在括號(hào)中排前地父類(lèi)的方法
test = Sample01("Tim", 25, 80, 4, "Python")
test.speak()
# 我叫 Tim物延,我是一個(gè)演說(shuō)家,我演講的主題是 Python
class Sample02(Student, Speaker):
a = ''
def __init__(self, n, a, w, g, t):
Student.__init__(self, n, a, w, g)
Speaker.__init__(self, n, t)
# 方法名同仅父,默認(rèn)調(diào)用的是在括號(hào)中排前地父類(lèi)的方法
test = Sample02("Tim", 25, 80, 4, "Python")
test.speak()
# Tim 說(shuō): 我 25 歲了叛薯,我在讀 4 年級(jí)
組合
class Turtle:
def __init__(self, x):
self.num = x
class Fish:
def __init__(self, x):
self.num = x
class Pool:
def __init__(self, x, y):
self.turtle = Turtle(x)
self.fish = Fish(y)
def print_num(self):
print("水池里面有烏龜%s只,小魚(yú)%s條" % (self.turtle.num, self.fish.num))
p = Pool(2, 3)
p.print_num()
# 水池里面有烏龜2只笙纤,小魚(yú)3條
類(lèi)耗溜、類(lèi)對(duì)象和實(shí)例對(duì)象
類(lèi)對(duì)象:創(chuàng)建一個(gè)類(lèi),其實(shí)也是一個(gè)對(duì)象也在內(nèi)存開(kāi)辟了一塊空間省容,稱(chēng)為類(lèi)對(duì)象抖拴,類(lèi)對(duì)象只有一個(gè)。
實(shí)例對(duì)象:就是通過(guò)實(shí)例化類(lèi)創(chuàng)建的對(duì)象腥椒,稱(chēng)為實(shí)例對(duì)象阿宅,實(shí)例對(duì)象可以有多個(gè)
類(lèi)屬性:類(lèi)里面方法外面定義的變量稱(chēng)為類(lèi)屬性。類(lèi)屬性所屬于類(lèi)對(duì)象并且多個(gè)實(shí)例對(duì)象之間共享同一個(gè)類(lèi)屬性寞酿,說(shuō)白了就是類(lèi)屬性所有的通過(guò)該類(lèi)實(shí)例化的對(duì)象都能共享家夺。
實(shí)例屬性:實(shí)例屬性和具體的某個(gè)實(shí)例對(duì)象有關(guān)系,并且一個(gè)實(shí)例對(duì)象和另外一個(gè)實(shí)例對(duì)象是不共享屬性的伐弹,說(shuō)白了實(shí)例屬性只能在自己的對(duì)象里面使用拉馋,其他的對(duì)象不能直接使用榨为,因?yàn)閟elf是誰(shuí)調(diào)用,它的值就屬于該對(duì)象煌茴。
注意:屬性與方法名相同随闺,屬性會(huì)覆蓋方法。
什么是綁定蔓腐?
Python 嚴(yán)格要求方法需要有實(shí)例才能被調(diào)用矩乐,這種限制其實(shí)就是 Python 所謂的綁定概念。
Python 對(duì)象的數(shù)據(jù)屬性通常存儲(chǔ)在名為.__ dict__的字典中回论,我們可以直接訪問(wèn)dict散罕,或利用 Python 的內(nèi)置函數(shù)vars()獲取.__ dict__。
class CC:
def setXY(self, x, y):
self.x = x
self.y = y
def printXY(self):
print(self.x, self.y)
dd = CC()
print(dd.__dict__)
# {}
print(vars(dd))
# {}
print(CC.__dict__)
# {'__module__': '__main__', 'setXY': <function CC.setXY at 0x000000C3473DA048>, 'printXY': <function CC.printXY at 0x000000C3473C4F28>, '__dict__': <attribute '__dict__' of 'CC' objects>, '__weakref__': <attribute '__weakref__' of 'CC' objects>, '__doc__': None}
dd.setXY(4, 5)
print(dd.__dict__)
# {'x': 4, 'y': 5}
print(vars(CC))
# {'__module__': '__main__', 'setXY': <function CC.setXY at 0x000000632CA9B048>, 'printXY': <function CC.printXY at 0x000000632CA83048>, '__dict__': <attribute '__dict__' of 'CC' objects>, '__weakref__': <attribute '__weakref__' of 'CC' objects>, '__doc__': None}
print(CC.__dict__)
# {'__module__': '__main__', 'setXY': <function CC.setXY at 0x000000632CA9B048>, 'printXY': <function CC.printXY at 0x000000632CA83048>, '__dict__': <attribute '__dict__' of 'CC' objects>, '__weakref__': <attribute '__weakref__' of 'CC' objects>, '__doc__': None}
一些相關(guān)的內(nèi)置函數(shù)(BIF)
- issubclass(class, classinfo) 方法用于判斷參數(shù) class 是否是類(lèi)型參數(shù) classinfo 的子類(lèi)傀蓉。
- 一個(gè)類(lèi)被認(rèn)為是其自身的子類(lèi)欧漱。
- classinfo可以是類(lèi)對(duì)象的元組,只要class是其中任何一個(gè)候選類(lèi)的子類(lèi)葬燎,則返回True误甚。
- isinstance(object, classinfo) 方法用于判斷一個(gè)對(duì)象是否是一個(gè)已知的類(lèi)型,類(lèi)似type()谱净。
- type()不會(huì)認(rèn)為子類(lèi)是一種父類(lèi)類(lèi)型窑邦,不考慮繼承關(guān)系。
- isinstance()會(huì)認(rèn)為子類(lèi)是一種父類(lèi)類(lèi)型壕探,考慮繼承關(guān)系冈钦。
- 如果第一個(gè)參數(shù)不是對(duì)象,則永遠(yuǎn)返回False浩蓉。
- 如果第二個(gè)參數(shù)不是類(lèi)或者由類(lèi)對(duì)象組成的元組派继,會(huì)拋出一個(gè)TypeError異常。
- hasattr(object, name)用于判斷對(duì)象是否包含對(duì)應(yīng)的屬性捻艳。
- getattr(object, name[, default])用于返回一個(gè)對(duì)象屬性值。
- setattr(object, name, value)對(duì)應(yīng)函數(shù) getattr()庆猫,用于設(shè)置屬性值认轨,該屬性不一定是存在的。
- delattr(object, name)用于刪除屬性月培。
- class property([fget[, fset[, fdel[, doc]]]])用于在新式類(lèi)中返回屬性值嘁字。
fget -- 獲取屬性值的函數(shù)
fset -- 設(shè)置屬性值的函數(shù)
fdel -- 刪除屬性值函數(shù)
doc -- 屬性描述信息
魔法方法
魔法方法總是被雙下劃線包圍,例如init杉畜。
魔法方法是面向?qū)ο蟮?Python 的一切纪蜒,如果你不知道魔法方法,說(shuō)明你還沒(méi)能意識(shí)到面向?qū)ο蟮?Python 的強(qiáng)大此叠。
魔法方法的“魔力”體現(xiàn)在它們總能夠在適當(dāng)?shù)臅r(shí)候被自動(dòng)調(diào)用纯续。
魔法方法的第一個(gè)參數(shù)應(yīng)為cls(類(lèi)方法) 或者self(實(shí)例方法)。
- clc:代表一個(gè)類(lèi)的名稱(chēng)
- self: 代表一個(gè)實(shí)例對(duì)象的名稱(chēng)
基本的魔法方法
- init(self[, ...]) 構(gòu)造器,當(dāng)一個(gè)實(shí)例被創(chuàng)建的時(shí)候調(diào)用的初始化方法猬错。
- new(cls[, ...]) 在一個(gè)對(duì)象實(shí)例化的時(shí)候所調(diào)用的第一個(gè)方法窗看,在調(diào)用init初始化前,先調(diào)用new倦炒。
new至少要有一個(gè)參數(shù)cls显沈,代表要實(shí)例化的類(lèi),此參數(shù)在實(shí)例化時(shí)由 Python 解釋器自動(dòng)提供逢唤,后面的參數(shù)直接傳遞給init拉讯。
new對(duì)當(dāng)前類(lèi)進(jìn)行了實(shí)例化,并將實(shí)例返回鳖藕,傳給init的self魔慷。但是,執(zhí)行了new吊奢,并不一定會(huì)進(jìn)入init盖彭,只有new返回了,當(dāng)前類(lèi)cls的實(shí)例页滚,當(dāng)前類(lèi)的init才會(huì)進(jìn)入召边。
class A(object):
def __init__(self, value):
print("into A __init__")
self.value = value
def __new__(cls, *args, **kwargs):
print("into A __new__")
print(cls)
return object.__new__(cls)
class B(A):
def __init__(self, value):
print("into B __init__")
self.value = value
def __new__(cls, *args, **kwargs):
print("into B __new__")
print(cls)
return super().__new__(cls, *args, **kwargs)
b = B(10)
# 結(jié)果:
# into B __new__
# <class '__main__.B'>
# into A __new__
# <class '__main__.B'>
# into B __init__
class A(object):
def __init__(self, value):
print("into A __init__")
self.value = value
def __new__(cls, *args, **kwargs):
print("into A __new__")
print(cls)
return object.__new__(cls)
class B(A):
def __init__(self, value):
print("into B __init__")
self.value = value
def __new__(cls, *args, **kwargs):
print("into B __new__")
print(cls)
return super().__new__(A, *args, **kwargs) # 改動(dòng)了cls變?yōu)锳
b = B(10)
# 結(jié)果:
# into B __new__
# <class '__main__.B'>
# into A __new__
# <class '__main__.A'>
- 若new沒(méi)有正確返回當(dāng)前類(lèi)cls的實(shí)例,那init是不會(huì)被調(diào)用的裹驰,即使是父類(lèi)的實(shí)例也不行隧熙,將沒(méi)有init被調(diào)用。
- 利用new實(shí)現(xiàn)單例模式幻林。
class Earth:
pass
a = Earth()
print(id(a)) # 260728291456
b = Earth()
print(id(b)) # 260728291624
class Earth:
__instance = None # 定義一個(gè)類(lèi)屬性做判斷
def __new__(cls):
if cls.__instance is None:
cls.__instance = object.__new__(cls)
return cls.__instance
else:
return cls.__instance
a = Earth()
print(id(a)) # 512320401648
b = Earth()
print(id(b)) # 512320401648
- del(self) 析構(gòu)器贞盯,當(dāng)一個(gè)對(duì)象將要被系統(tǒng)回收之時(shí)調(diào)用的方法。
Python 采用自動(dòng)引用計(jì)數(shù)(ARC)方式來(lái)回收對(duì)象所占用的空間沪饺,當(dāng)程序中有一個(gè)變量引用該 Python 對(duì)象時(shí)躏敢,Python 會(huì)自動(dòng)保證該對(duì)象引用計(jì)數(shù)為 1;當(dāng)程序中有兩個(gè)變量引用該 Python 對(duì)象時(shí)整葡,Python 會(huì)自動(dòng)保證該對(duì)象引用計(jì)數(shù)為 2件余,依此類(lèi)推,如果一個(gè)對(duì)象的引用計(jì)數(shù)變成了 0遭居,則說(shuō)明程序中不再有變量引用該對(duì)象啼器,表明程序不再需要該對(duì)象,因此 Python 就會(huì)回收該對(duì)象俱萍。
大部分時(shí)候端壳,Python 的 ARC 都能準(zhǔn)確、高效地回收系統(tǒng)中的每個(gè)對(duì)象枪蘑。但如果系統(tǒng)中出現(xiàn)循環(huán)引用的情況损谦,比如對(duì)象 a 持有一個(gè)實(shí)例變量引用對(duì)象 b岖免,而對(duì)象 b 又持有一個(gè)實(shí)例變量引用對(duì)象 a,此時(shí)兩個(gè)對(duì)象的引用計(jì)數(shù)都是 1成翩,而實(shí)際上程序已經(jīng)不再有變量引用它們觅捆,系統(tǒng)應(yīng)該回收它們,此時(shí) Python 的垃圾回收器就可能沒(méi)那么快麻敌,要等專(zhuān)門(mén)的循環(huán)垃圾回收器(Cyclic Garbage Collector)來(lái)檢測(cè)并回收這種引用循環(huán)栅炒。
算術(shù)運(yùn)算符
- add(self, other)定義加法的行為:+
- sub(self, other)定義減法的行為:-
- mul(self, other)定義乘法的行為:*
- truediv(self, other)定義真除法的行為:/
- floordiv(self, other)定義整數(shù)除法的行為://
- mod(self, other) 定義取模算法的行為:%
- divmod(self, other)定義當(dāng)被 divmod() 調(diào)用時(shí)的行為
- divmod(a, b)把除數(shù)和余數(shù)運(yùn)算結(jié)果結(jié)合起來(lái),返回一個(gè)包含商和余數(shù)的元組(a // b, a % b)术羔。
- pow(self, other[, module])定義當(dāng)被 power() 調(diào)用或 ** 運(yùn)算時(shí)的行為
- lshift(self, other)定義按位左移位的行為:<<
- rshift(self, other)定義按位右移位的行為:>>
- and(self, other)定義按位與操作的行為:&
- xor(self, other)定義按位異或操作的行為:^
- or(self, other)定義按位或操作的行為:|
反算術(shù)運(yùn)算符
- radd(self, other)定義加法的行為:+
- rsub(self, other)定義減法的行為:-
- rmul(self, other)定義乘法的行為:*
- rtruediv(self, other)定義真除法的行為:/
- rfloordiv(self, other)定義整數(shù)除法的行為://
- rmod(self, other) 定義取模算法的行為:%
- rdivmod(self, other)定義當(dāng)被 divmod() 調(diào)用時(shí)的行為
- rpow(self, other[, module])定義當(dāng)被 power() 調(diào)用或 ** 運(yùn)算時(shí)的行為
- rlshift(self, other)定義按位左移位的行為:<<
- rrshift(self, other)定義按位右移位的行為:>>
- rand(self, other)定義按位與操作的行為:&
- rxor(self, other)定義按位異或操作的行為:^
- ror(self, other)定義按位或操作的行為:|
增量賦值運(yùn)算符
- iadd(self, other)定義賦值加法的行為:+=
- isub(self, other)定義賦值減法的行為:-=
- imul(self, other)定義賦值乘法的行為:*=
- itruediv(self, other)定義賦值真除法的行為:/=
- ifloordiv(self, other)定義賦值整數(shù)除法的行為://=
- imod(self, other)定義賦值取模算法的行為:%=
- ipow(self, other[, modulo])定義賦值冪運(yùn)算的行為:**=
- ilshift(self, other)定義賦值按位左移位的行為:<<=
- irshift(self, other)定義賦值按位右移位的行為:>>=
- iand(self, other)定義賦值按位與操作的行為:&=
- ixor(self, other)定義賦值按位異或操作的行為:^=
- ior(self, other)定義賦值按位或操作的行為:|=
一元運(yùn)算符
- neg(self)定義正號(hào)的行為:+x
- pos(self)定義負(fù)號(hào)的行為:-x
- abs(self)定義當(dāng)被abs()調(diào)用時(shí)的行為
- invert(self)定義按位求反的行為:~x
屬性訪問(wèn)
- getattr(self, name): 定義當(dāng)用戶試圖獲取一個(gè)不存在的屬性時(shí)的行為赢赊。
- getattribute(self, name):定義當(dāng)該類(lèi)的屬性被訪問(wèn)時(shí)的行為(先調(diào)用該方法,查看是否存在該屬性级历,若不存在释移,接著去調(diào)用getattr)。
- setattr(self, name, value):定義當(dāng)一個(gè)屬性被設(shè)置時(shí)的行為寥殖。
- delattr(self, name):定義當(dāng)一個(gè)屬性被刪除時(shí)的行為玩讳。
class C:
def __getattribute__(self, item):
print('__getattribute__')
return super().__getattribute__(item)
def __getattr__(self, item):
print('__getattr__')
def __setattr__(self, key, value):
print('__setattr__')
super().__setattr__(key, value)
def __delattr__(self, item):
print('__delattr__')
super().__delattr__(item)
c = C()
c.x
# __getattribute__
# __getattr__
c.x = 1
# __setattr__
del c.x
# __delattr__
描述符
描述符就是將某種特殊類(lèi)型的類(lèi)的實(shí)例指派給另一個(gè)類(lèi)的屬性。
- get(self, instance, owner)用于訪問(wèn)屬性嚼贡,它返回屬性的值熏纯。
- set(self, instance, value)將在屬性分配操作中調(diào)用,不返回任何內(nèi)容粤策。
- del(self, instance)控制刪除操作樟澜,不返回任何內(nèi)容。
class MyDecriptor:
def __get__(self, instance, owner):
print('__get__', self, instance, owner)
def __set__(self, instance, value):
print('__set__', self, instance, value)
def __delete__(self, instance):
print('__delete__', self, instance)
class Test:
x = MyDecriptor()
t = Test()
t.x
# __get__ <__main__.MyDecriptor object at 0x000000CEAAEB6B00> <__main__.Test object at 0x000000CEABDC0898> <class '__main__.Test'>
t.x = 'x-man'
# __set__ <__main__.MyDecriptor object at 0x00000023687C6B00> <__main__.Test object at 0x00000023696B0940> x-man
del t.x
# __delete__ <__main__.MyDecriptor object at 0x000000EC9B160A90> <__main__.Test object at 0x000000EC9B160B38>
定制序列
協(xié)議(Protocols)與其它編程語(yǔ)言中的接口很相似叮盘,它規(guī)定你哪些方法必須要定義秩贰。然而,在 Python 中的協(xié)議就顯得不那么正式柔吼。事實(shí)上毒费,在 Python 中,協(xié)議更像是一種指南愈魏。
迭代器
- 迭代是 Python 最強(qiáng)大的功能之一蝗罗,是訪問(wèn)集合元素的一種方式。
- 迭代器是一個(gè)可以記住遍歷的位置的對(duì)象蝌戒。
- 迭代器對(duì)象從集合的第一個(gè)元素開(kāi)始訪問(wèn),直到所有的元素被訪問(wèn)完結(jié)束沼琉。
- 迭代器只能往前不會(huì)后退北苟。
- 字符串,列表或元組對(duì)象都可用于創(chuàng)建迭代器
- 迭代器有兩個(gè)基本的方法:iter() 和 next()打瘪。
- iter(object) 函數(shù)用來(lái)生成迭代器友鼻。
- next(iterator[, default]) 返回迭代器的下一個(gè)項(xiàng)目傻昙。
- iterator -- 可迭代對(duì)象
- default -- 可選,用于設(shè)置在沒(méi)有下一個(gè)元素時(shí)返回該默認(rèn)值彩扔,如果不設(shè)置妆档,又沒(méi)有下一個(gè)元素則會(huì)觸發(fā) StopIteration 異常。
links = {'B': '百度', 'A': '阿里', 'T': '騰訊'}
it = iter(links)
while True:
try:
each = next(it)
except StopIteration:
break
print(each)
# B
# A
# T
it = iter(links)
print(next(it)) # B
print(next(it)) # A
print(next(it)) # T
print(next(it)) # StopIteration
把一個(gè)類(lèi)作為一個(gè)迭代器使用需要在類(lèi)中實(shí)現(xiàn)兩個(gè)魔法方法 iter() 與 next() 虫碉。
- iter(self)定義當(dāng)?shù)萜髦械脑氐男袨榧值耄祷匾粋€(gè)特殊的迭代器對(duì)象, 這個(gè)迭代器對(duì)象實(shí)現(xiàn)了
- next() 方法并通過(guò) StopIteration 異常標(biāo)識(shí)迭代的完成敦捧。
- next() 返回下一個(gè)迭代器對(duì)象须板。
- StopIteration 異常用于標(biāo)識(shí)迭代的完成,防止出現(xiàn)無(wú)限循環(huán)的情況兢卵,在 next() 方法中我們可以設(shè)置在完成指定循環(huán)次數(shù)后觸發(fā) StopIteration 異常來(lái)結(jié)束迭代习瑰。
class Fibs:
def __init__(self, n=10):
self.a = 0
self.b = 1
self.n = n
def __iter__(self):
return self
def __next__(self):
self.a, self.b = self.b, self.a + self.b
if self.a > self.n:
raise StopIteration
return self.a
fibs = Fibs(100)
for each in fibs:
print(each, end=' ')
# 1 1 2 3 5 8 13 21 34 55 89
生成器
- 在 Python 中,使用了 yield 的函數(shù)被稱(chēng)為生成器(generator)秽荤。
- 跟普通函數(shù)不同的是甜奄,生成器是一個(gè)返回迭代器的函數(shù),只能用于迭代操作窃款,更簡(jiǎn)單點(diǎn)理解生成器就是一個(gè)迭代器课兄。
- 在調(diào)用生成器運(yùn)行的過(guò)程中,每次遇到 yield 時(shí)函數(shù)會(huì)暫停并保存當(dāng)前所有的運(yùn)行信息雁乡,返回 yield 的值, 并在下一次執(zhí)行 next() 方法時(shí)從當(dāng)前位置繼續(xù)運(yùn)行第喳。
- 調(diào)用一個(gè)生成器函數(shù),返回的是一個(gè)迭代器對(duì)象踱稍。
def myGen():
print('生成器執(zhí)行曲饱!')
yield 1
yield 2
myG = myGen()
for each in myG:
print(each)
'''
生成器執(zhí)行信轿!
1
2
'''
myG = myGen()
print(next(myG))
# 生成器執(zhí)行房匆!
# 1
print(next(myG)) # 2
print(next(myG)) # StopIteration
用生成器實(shí)現(xiàn)斐波那契數(shù)列蹋嵌。
def libs(n):
a = 0
b = 1
while True:
a, b = b, a + b
if a > n:
return
yield a
for each in libs(100):
print(each, end=' ')
# 1 1 2 3 5 8 13 21 34 55 89