1.1==,is的使用
·is是比較兩個(gè)引用是否指向了同一個(gè)對(duì)象(引用比較)。
·==是比較兩個(gè)對(duì)象是否相等。
1.2深拷貝、淺拷貝
1.2.1淺拷貝
淺拷貝是對(duì)于一個(gè)對(duì)象的頂層拷貝
通俗的理解是:拷貝了引用,并沒有拷貝內(nèi)容
1.2.2深拷貝
copy.deepcopy
1.2.3拷貝的其他方式
淺拷貝對(duì)不可變類型和可變類型的copy不同
In [88]: a = [11,22,33]
In [89]: b = copy.copy(a)
In [90]: id(a)
Out[90]:59275144
In [91]: id(b)
Out[91]:59525600
In [92]: a.append(44)
In [93]: a
Out[93]: [11,22,33,44]
In [94]: b
Out[94]: [11,22,33]
In [95]:
In [95]:
In [95]: a = (11,22,33)
In [96]: b = copy.copy(a)
In [97]: id(a)
Out[97]:58890680
In [98]: id(b)
Out[98]:58890680
·分片表達(dá)式可以賦值一個(gè)序列
a ="abc"
b = a[:]
·字典的copy方法可以拷貝一個(gè)字典
d = dict(name="zhangsan", age=27)
co = d.copy()
·有些內(nèi)置函數(shù)可以生成拷貝(list)
a = list(range(10))
b = list(a)
·copy模塊中的copy函數(shù)
importcopy
a = (1,2,3)
b = copy.copy(a)
1.3屬性property
面試題:
1、你對(duì)面向?qū)ο蟮睦斫?/p>
2、面向?qū)ο蟮奶卣魇鞘裁?/p>
3、對(duì)封裝的理解?
封裝,類本身就是一個(gè)封裝,封裝了屬性和方法。方法也是封裝,對(duì)一些業(yè)務(wù)邏輯的封裝。私有也是封裝,將一些方法和屬性私有化,對(duì)外提供可訪問的接口。
4、對(duì)繼承的理解
將共性的內(nèi)容放在父類中,子類只需要關(guān)注自己特有的內(nèi)容,共性的繼承過來就行了。
這樣簡(jiǎn)化開發(fā),符合邏輯習(xí)慣,利于擴(kuò)展。
5、多態(tài)的理解
多態(tài),一個(gè)對(duì)象在不同的情況下顯示不同的形態(tài)。在python中因?yàn)槭侨躅愋驼Z(yǔ)言,對(duì)類型沒有限定,所有python中不完全支持多態(tài),但是多態(tài)的思想呢,python也是能體現(xiàn)的。
1.3.1私有屬性添加getter和setter方法
classMoney(object):
def__init__(self):
self.__money =0
defgetMoney(self):
returnself.__money
defsetMoney(self, value):
ifisinstance(value, int):
self.__money = value
else:
print("error:不是整型數(shù)字")
1.3.2使用property升級(jí)getter和setter方法
classMoney(object):
def__init__(self):
self.__money =0
defgetMoney(self):
returnself.__money
defsetMoney(self, value):
ifisinstance(value, int):
self.__money = value
else:
print("error:不是整型數(shù)字")
money = property(getMoney, setMoney)
運(yùn)行結(jié)果:
In [1]:fromget_setimportMoney
In [2]:
In [2]: a = Money()
In [3]:
In [3]: a.money
Out[3]:0
In [4]: a.money =100
In [5]: a.money
Out[5]:100
In [6]: a.getMoney()
Out[6]:100
1.3.3使用property取代getter和setter方法
@property成為屬性函數(shù),可以對(duì)屬性賦值時(shí)做必要的檢查,并保證代碼的清晰短小,主要有2個(gè)作用
·將方法轉(zhuǎn)換為只讀
·重新實(shí)現(xiàn)一個(gè)屬性的設(shè)置和讀取方法,可做邊界判定
classMoney(object):
def__init__(self):
self.__money =0
@property
defmoney(self):
returnself.__money
@money.setter
defmoney(self, value):
ifisinstance(value, int):
self.__money = value
else:
print("error:不是整型數(shù)字")
運(yùn)行結(jié)果
In [3]: a = Money()
In [4]:
In [4]:
In [4]: a.money
Out[4]:0
In [5]: a.money =100
In [6]: a.money
Out[6]:100
1.4生成器
1.4.1什么是生成器
通過列表生成式,我們可以直接創(chuàng)建一個(gè)列表。但是,受到內(nèi)存限制,列表容量肯定是有限的。而且,創(chuàng)建一個(gè)包含100萬(wàn)個(gè)元素的列表,不僅占用很大的存儲(chǔ)空間,如果我們僅僅需要訪問前面幾個(gè)元素,那后面絕大多數(shù)元素占用的空間都白白浪費(fèi)了。所以,如果列表元素可以按照某種算法推算出來,那我們是否可以在循環(huán)的過程中不斷推算出后續(xù)的元素呢?這樣就不必創(chuàng)建完整的list,從而節(jié)省大量的空間。在Python中,這種一邊循環(huán)一邊計(jì)算的機(jī)制,稱為生成器:generator。
1.4.2創(chuàng)建生成器方法1
要?jiǎng)?chuàng)建一個(gè)生成器,有很多種方法。第一種方法很簡(jiǎn)單,只要把一個(gè)列表生成式的[ ]改成( )
In [15]: L = [ x*2forxinrange(5)]
In [16]: L
Out[16]: [0,2,4,6,8]
In [17]: G = ( x*2forxinrange(5))
In [18]: G
Out[18]: at0x7f626c132db0>
In [19]:
創(chuàng)建L和G的區(qū)別僅在于最外層的[ ]和( ),L是一個(gè)列表,而G是一個(gè)生成器。我們可以直接打印出L的每一個(gè)元素,但我們?cè)趺创蛴〕鯣的每一個(gè)元素呢?如果要一個(gè)一個(gè)打印出來,可以通過next()函數(shù)獲得生成器的下一個(gè)返回值:
In [19]: next(G)
Out[19]: 0
In [20]: next(G)
Out[20]: 2
In [21]: next(G)
Out[21]: 4
In [22]: next(G)
Out[22]: 6
In [23]: next(G)
Out[23]: 8
In [24]: next(G)
---------------------------------------------------------------------------
StopIteration ????????????????????????????Traceback (most recent call last)
in ()
----> 1 next(G)
StopIteration:
In [25]:
In [26]: G = ( x*2forxinrange(5))
In [27]:forxinG:
....: ????print(x)
....:
0
2
4
6
8
In [28]:
生成器保存的是算法,每次調(diào)用next(G),就計(jì)算出G的下一個(gè)元素的值,直到計(jì)算到最后一個(gè)元素,沒有更多的元素時(shí),拋出StopIteration的異常。當(dāng)然,這種不斷調(diào)用next()實(shí)在是太變態(tài)了,正確的方法是使用for循環(huán),因?yàn)樯善饕彩强傻鷮?duì)象。所以,我們創(chuàng)建了一個(gè)生成器后,基本上永遠(yuǎn)不會(huì)調(diào)用next(),而是通過for循環(huán)來迭代它,并且不需要關(guān)心StopIteration異常。
1.4.3創(chuàng)建生成器方法2
generator非常強(qiáng)大。如果推算的算法比較復(fù)雜,用類似列表生成式的for循環(huán)無(wú)法實(shí)現(xiàn)的時(shí)候,還可以用函數(shù)來實(shí)現(xiàn)。
比如,著名的斐波拉契數(shù)列(Fibonacci),除第一個(gè)和第二個(gè)數(shù)外,任意一個(gè)數(shù)都可由前兩個(gè)數(shù)相加得到:
1, 1, 2, 3, 5, 8, 13, 21, 34, ...
斐波拉契數(shù)列用列表生成式寫不出來,但是,用函數(shù)把它打印出來卻很容易:
In [28]:deffib(times):
....: ????n =0
....: ????a,b =0,1
....:whilen
....: ????????print(b)
....: ????????a,b = b,a+b
....: ????????n+=1
....:return'done'
....:
In [29]: fib(5)
1
1
2
3
5
Out[29]:'done'
仔細(xì)觀察,可以看出,fib函數(shù)實(shí)際上是定義了斐波拉契數(shù)列的推算規(guī)則,可以從第一個(gè)元素開始,推算出后續(xù)任意的元素,這種邏輯其實(shí)非常類似generator。
也就是說,上面的函數(shù)和generator僅一步之遙。要把fib函數(shù)變成generator,只需要把print(b)改為yield b就可以了:
In [30]: def fib(times):
....: ????n = 0
....: ????a,b = 0,1
....: ????while n
....: ????????yield b
....: ????????a,b = b,a+b
....: ????????n+=1
....: ????return 'done'
....:
In [31]: F = fib(5)
In [32]: next(F)
Out[32]: 1
In [33]: next(F)
Out[33]: 1
In [34]: next(F)
Out[34]: 2
In [35]: next(F)
Out[35]: 3
In [36]: next(F)
Out[36]: 5
In [37]: next(F)
---------------------------------------------------------------------------
StopIteration ????????????????????????????Traceback (most recent call last)
in ()
----> 1 next(F)
StopIteration: done
在上面fib的例子,我們?cè)谘h(huán)過程中不斷調(diào)用yield,就會(huì)不斷中斷。當(dāng)然要給循環(huán)設(shè)置一個(gè)條件來退出循環(huán),不然就會(huì)產(chǎn)生一個(gè)無(wú)限數(shù)列出來。同樣的,把函數(shù)改成generator后,我們基本上從來不會(huì)用next()來獲取下一個(gè)返回值,而是直接使用for循環(huán)來迭代:
In [38]:forninfib(5):
....: ????print(n)
....:
1
1
2
3
5
In [39]:
但是用for循環(huán)調(diào)用generator時(shí),發(fā)現(xiàn)拿不到generator的return語(yǔ)句的返回值。如果想要拿到返回值,必須捕獲StopIteration錯(cuò)誤,返回值包含在StopIteration的value中:
In [39]: g = fib(5)
In [40]:whileTrue:
....:try:
....: ????????x = next(g)
....: ????????print("value:%d"%x)
....:exceptStopIterationase:
....: ????????print("生成器返回值:%s"%e.value)
....:break
....:
value:1
value:1
value:2
value:3
value:5
生成器返回值:done
In [41]:
1.4.4send
例子:執(zhí)行到y(tǒng)ield時(shí),gen函數(shù)作用暫時(shí)保存,返回i的值;temp接收下次c.send("python"),send發(fā)送過來的值,c.next()等價(jià)c.send(None)
In [10]:defgen():
....: ????i =0
....:whilei<5:
....: ????????temp =yieldi
....: ????????print(temp)
....: ????????i+=1
....:
使用next函數(shù)
In [11]: f = gen()
In [12]: next(f)
Out[12]: 0
In [13]: next(f)
None
Out[13]: 1
In [14]: next(f)
None
Out[14]: 2
In [15]: next(f)
None
Out[15]: 3
In [16]: next(f)
None
Out[16]: 4
In [17]: next(f)
None
---------------------------------------------------------------------------
StopIteration ????????????????????????????Traceback (most recent call last)
in ()
----> 1 next(f)
StopIteration:
使用__next__()方法
In [18]: f = gen()
In [19]: f.__next__()
Out[19]: 0
In [20]: f.__next__()
None
Out[20]: 1
In [21]: f.__next__()
None
Out[21]: 2
In [22]: f.__next__()
None
Out[22]: 3
In [23]: f.__next__()
None
Out[23]: 4
In [24]: f.__next__()
None
---------------------------------------------------------------------------
StopIteration ????????????????????????????Traceback (most recent call last)
in ()
----> 1 f.__next__()
StopIteration:
使用send
In [43]: f = gen()
In [44]: f.__next__()
Out[44]:0
In [45]: f.send('haha')
haha
Out[45]:1
In [46]: f.__next__()
None
Out[46]:2
In [47]: f.send('haha')
haha
Out[47]:3
In [48]:
1.4.5實(shí)現(xiàn)多任務(wù)
模擬多任務(wù)實(shí)現(xiàn)方式之一:協(xié)程
def test1():
while True:
print("--1--")
yield None
def test2():
while True:
print("--2--")
yield None
t1 = test1()
t2 = test2()
while True:
t1.__next__()
t2.__next__()
總結(jié)
生成器是這樣一個(gè)函數(shù),它記住上一次返回時(shí)在函數(shù)體中的位置。對(duì)生成器函數(shù)的第二次(或第n次)調(diào)用跳轉(zhuǎn)至該函數(shù)中間,而上次調(diào)用的所有局部變量都保持不變。
生成器不僅“記住”了它數(shù)據(jù)狀態(tài);生成器還“記住”了它在流控制構(gòu)造(在命令式編程中,這種構(gòu)造不只是數(shù)據(jù)值)中的位置。
生成器的特點(diǎn):
1.節(jié)約內(nèi)存
2.迭代到下一次的調(diào)用時(shí),所使用的參數(shù)都是第一次所保留下的,即是說,在整個(gè)所有函數(shù)調(diào)用的參數(shù)都是第一次所調(diào)用時(shí)保留的,而不是新創(chuàng)建的
1.5迭代器
迭代是訪問集合元素的一種方式。迭代器是一個(gè)可以記住遍歷的位置的對(duì)象。迭代器對(duì)象從集合的第一個(gè)元素開始訪問,直到所有的元素被訪問完結(jié)束。迭代器只能往前不會(huì)后退。
1.5.1可迭代對(duì)象
以直接作用于for循環(huán)的數(shù)據(jù)類型有以下幾種:
一類是集合數(shù)據(jù)類型,如list、tuple、dict、set、str等;
一類是generator,包括生成器和帶yield的generator function。
這些可以直接作用于for循環(huán)的對(duì)象統(tǒng)稱為可迭代對(duì)象:Iterable。
1.5.2判斷是否可以迭代
可以使用isinstance()判斷一個(gè)對(duì)象是否是Iterable對(duì)象:
In [50]:fromcollectionsimportIterable
In [51]: isinstance([], Iterable)
Out[51]:True
In [52]: isinstance({}, Iterable)
Out[52]:True
In [53]: isinstance('abc', Iterable)
Out[53]:True
In [54]: isinstance((xforxinrange(10)), Iterable)
Out[54]:True
In [55]: isinstance(100, Iterable)
Out[55]:False
而生成器不但可以作用于for循環(huán),還可以被next()函數(shù)不斷調(diào)用并返回下一個(gè)值,直到最后拋出StopIteration錯(cuò)誤表示無(wú)法繼續(xù)返回下一個(gè)值了。
1.5.3迭代器
可以被next()函數(shù)調(diào)用并不斷返回下一個(gè)值的對(duì)象稱為迭代器:Iterator。
可以使用isinstance()判斷一個(gè)對(duì)象是否是Iterator對(duì)象:
In [56]:fromcollectionsimportIterator
In [57]: isinstance((xforxinrange(10)), Iterator)
Out[57]:True
In [58]: isinstance([], Iterator)
Out[58]:False
In [59]: isinstance({}, Iterator)
Out[59]:False
In [60]: isinstance('abc', Iterator)
Out[60]:False
In [61]: isinstance(100, Iterator)
Out[61]:False
1.5.4iter()函數(shù)
生成器都是Iterator對(duì)象,但list、dict、str雖然是Iterable,卻不是Iterator。
把list、dict、str等Iterable變成Iterator可以使用iter()函數(shù):
In [62]: isinstance(iter([]), Iterator)
Out[62]:True
In [63]: isinstance(iter('abc'), Iterator)
Out[63]:True
總結(jié)
·凡是可作用于for循環(huán)的對(duì)象都是Iterable類型;
·凡是可作用于next()函數(shù)的對(duì)象都是Iterator類型
·集合數(shù)據(jù)類型如list、dict、str等是Iterable但不是Iterator,不過可以通過iter()函數(shù)獲得一個(gè)Iterator對(duì)象。
·目的是在使用集合的時(shí)候,減少占用的內(nèi)存。
1.6閉包
1.61.1.1什么是閉包.1函數(shù)引用
deftest1():
print("--- in test1 func----")
#調(diào)用函數(shù)
test1()
#引用函數(shù)
ret = test1
print(id(ret))
print(id(test1))
#通過引用調(diào)用函數(shù)
ret()
運(yùn)行結(jié)果:
---intest1 func----
140212571149040
140212571149040
---intest1 func----
1.6.2 什么是閉包
定義一個(gè)函數(shù)
deftest(number):
#在函數(shù)內(nèi)部再定義一個(gè)函數(shù),并且這個(gè)函數(shù)用到了外邊函數(shù)的變量,那么將這個(gè)函數(shù)以及用到的一些變量稱之為閉包
deftest_in(number_in):
print("in test_in函數(shù), number_in is %d"%number_in)
returnnumber+number_in
#其實(shí)這里返回的就是閉包的結(jié)果
returntest_in
#給test函數(shù)賦值,這個(gè)20就是給參數(shù)number
ret = test(20)
#注意這里的100其實(shí)給參數(shù)number_in
print(ret(100))
#注意這里的200其實(shí)給參數(shù)number_in
print(ret(200))
運(yùn)行結(jié)果:
intest_in函數(shù), number_inis100
120
intest_in函數(shù), number_inis200
220
1.6.3看一個(gè)閉包的實(shí)際例子:
defline_conf(a, b):
defline(x):
returna*x + b
returnline
line1 = line_conf(1,1)
line2 = line_conf(4,5)
print(line1(5))
print(line2(5))
這個(gè)例子中,函數(shù)line與變量a,b構(gòu)成閉包。在創(chuàng)建閉包的時(shí)候,我們通過line_conf的參數(shù)a,b說明了這兩個(gè)變量的取值,這樣,我們就確定了函數(shù)的最終形式(y = x + 1和y = 4x + 5)。我們只需要變換參數(shù)a,b,就可以獲得不同的直線表達(dá)函數(shù)。由此,我們可以看到,閉包也具有提高代碼可復(fù)用性的作用。
如果沒有閉包,我們需要每次創(chuàng)建直線函數(shù)的時(shí)候同時(shí)說明a,b,x。這樣,我們就需要更多的參數(shù)傳遞,也減少了代碼的可移植性。
閉包思考:
1.閉包似優(yōu)化了變量,原來需要類對(duì)象完成的工作,閉包也可以完成
2.由于閉包引用了外部函數(shù)的局部變量,則外部函數(shù)的局部變量沒有及時(shí)釋放,消耗內(nèi)存
1.7.裝飾器
裝飾器是程序開發(fā)中經(jīng)常會(huì)用到的一個(gè)功能,用好了裝飾器,開發(fā)效率如虎添翼,所以這也是Python面試中必問的問題,但對(duì)于好多初次接觸這個(gè)知識(shí)的人來講,這個(gè)功能有點(diǎn)繞,自學(xué)時(shí)直接繞過去了,然后面試問到了就掛了,因?yàn)檠b飾器是程序開發(fā)的基礎(chǔ)知識(shí),這個(gè)都不會(huì),別跟人家說你會(huì)Python,看了下面的文章,保證你學(xué)會(huì)裝飾器。
1.7.1裝飾器的理解
1、先明白這段代碼
####第一波####
deffoo():
print('foo')
foo#表示是函數(shù)
foo()#表示執(zhí)行foo函數(shù)
####第二波####
deffoo():
print('foo')
foo =lambdax: x +1
foo()#執(zhí)行下面的lambda表達(dá)式,而不再是原來的foo函數(shù),因?yàn)閒oo這個(gè)名字被重新指向了另外一個(gè)匿名函數(shù)
2、需求來了
初創(chuàng)公司有N個(gè)業(yè)務(wù)部門,1個(gè)基礎(chǔ)平臺(tái)部門,基礎(chǔ)平臺(tái)負(fù)責(zé)提供底層的功能,如:數(shù)據(jù)庫(kù)操作、redis調(diào)用、監(jiān)控API等功能。業(yè)務(wù)部門使用基礎(chǔ)功能時(shí),只需調(diào)用基礎(chǔ)平臺(tái)提供的功能即可。如下:
###############基礎(chǔ)平臺(tái)提供的功能如下###############
deff1():
print('f1')
deff2():
print('f2')
deff3():
print('f3')
deff4():
print('f4')
###############業(yè)務(wù)部門A調(diào)用基礎(chǔ)平臺(tái)提供的功能###############
f1()
f2()
f3()
f4()
###############業(yè)務(wù)部門B調(diào)用基礎(chǔ)平臺(tái)提供的功能###############
f1()
f2()
f3()
f4()
目前公司有條不紊的進(jìn)行著,但是,以前基礎(chǔ)平臺(tái)的開發(fā)人員在寫代碼時(shí)候沒有關(guān)注驗(yàn)證相關(guān)的問題,即:基礎(chǔ)平臺(tái)的提供的功能可以被任何人使用?,F(xiàn)在需要對(duì)基礎(chǔ)平臺(tái)的所有功能進(jìn)行重構(gòu),為平臺(tái)提供的所有功能添加驗(yàn)證機(jī)制,即:執(zhí)行功能前,先進(jìn)行驗(yàn)證。
老大把工作交給Low B,他是這么做的:
跟每個(gè)業(yè)務(wù)部門交涉,每個(gè)業(yè)務(wù)部門自己寫代碼,調(diào)用基礎(chǔ)平臺(tái)的功能之前先驗(yàn)證。誒,這樣一來基礎(chǔ)平臺(tái)就不需要做任何修改了。太棒了,有充足的時(shí)間泡妹子...
當(dāng)天Low B被開除了…
老大把工作交給Low BB,他是這么做的:
###############基礎(chǔ)平臺(tái)提供的功能如下###############
deff1():
#驗(yàn)證1
#驗(yàn)證2
#驗(yàn)證3
print('f1')
deff2():
#驗(yàn)證1
#驗(yàn)證2
#驗(yàn)證3
print('f2')
deff3():
#驗(yàn)證1
#驗(yàn)證2
#驗(yàn)證3
print('f3')
deff4():
#驗(yàn)證1
#驗(yàn)證2
#驗(yàn)證3
print('f4')
###############業(yè)務(wù)部門不變###############
###業(yè)務(wù)部門A調(diào)用基礎(chǔ)平臺(tái)提供的功能###
f1()
f2()
f3()
f4()
###業(yè)務(wù)部門B調(diào)用基礎(chǔ)平臺(tái)提供的功能###
f1()
f2()
f3()
f4()
過了一周Low BB被開除了…
老大把工作交給Low BBB,他是這么做的:
只對(duì)基礎(chǔ)平臺(tái)的代碼進(jìn)行重構(gòu),其他業(yè)務(wù)部門無(wú)需做任何修改
###############基礎(chǔ)平臺(tái)提供的功能如下###############
defcheck_login():
#驗(yàn)證1
#驗(yàn)證2
#驗(yàn)證3
pass
deff1():
check_login()
print('f1')
deff2():
check_login()
print('f2')
deff3():
check_login()
print('f3')
deff4():
check_login()
print('f4')
老大看了下Low BBB的實(shí)現(xiàn),嘴角漏出了一絲的欣慰的笑,語(yǔ)重心長(zhǎng)的跟Low BBB聊了個(gè)天:
老大說:
寫代碼要遵循開放封閉原則,雖然在這個(gè)原則是用的面向?qū)ο箝_發(fā),但是也適用于函數(shù)式編程,簡(jiǎn)單來說,它規(guī)定已經(jīng)實(shí)現(xiàn)的功能代碼不允許被修改,但可以被擴(kuò)展,即:
·封閉:已實(shí)現(xiàn)的功能代碼塊
·開放:對(duì)擴(kuò)展開發(fā)
如果將開放封閉原則應(yīng)用在上述需求中,那么就不允許在函數(shù)f1、f2、f3、f4的內(nèi)部進(jìn)行修改代碼,老板就給了Low BBB一個(gè)實(shí)現(xiàn)方案:
defw1(func):
definner():
#驗(yàn)證1
#驗(yàn)證2
#驗(yàn)證3
func()
returninner
@w1
deff1():
print('f1')
@w1
deff2():
print('f2')
@w1
deff3():
print('f3')
@w1
deff4():
print('f4')
對(duì)于上述代碼,也是僅僅對(duì)基礎(chǔ)平臺(tái)的代碼進(jìn)行修改,就可以實(shí)現(xiàn)在其他人調(diào)用函數(shù)f1 f2 f3 f4之前都進(jìn)行【驗(yàn)證】操作,并且其他業(yè)務(wù)部門無(wú)需做任何操作。
Low BBB心驚膽戰(zhàn)的問了下,這段代碼的內(nèi)部執(zhí)行原理是什么呢?
老大正要生氣,突然Low BBB的手機(jī)掉到地上,恰巧屏保就是Low BBB的女友照片,老大一看一緊一抖,喜笑顏開,決定和Low BBB交個(gè)好朋友。
詳細(xì)的開始講解了:
單獨(dú)以f1為例:
defw1(func):
definner():
#驗(yàn)證1
#驗(yàn)證2
#驗(yàn)證3
func()
returninner
@w1
deff1():
print('f1')
python解釋器就會(huì)從上到下解釋代碼,步驟如下:
1.def w1(func): ==>將w1函數(shù)加載到內(nèi)存
2.@w1
沒錯(cuò),從表面上看解釋器僅僅會(huì)解釋這兩句代碼,因?yàn)楹瘮?shù)在沒有被調(diào)用之前其內(nèi)部代碼不會(huì)被執(zhí)行。
從表面上看解釋器著實(shí)會(huì)執(zhí)行這兩句,但是@w1這一句代碼里卻有大文章,@函數(shù)名 是python的一種語(yǔ)法糖。
上例@w1內(nèi)部會(huì)執(zhí)行一下操作:
執(zhí)行w1函數(shù)
執(zhí)行w1函數(shù) ,并將@w1下面的函數(shù)作為w1函數(shù)的參數(shù),即:@w1等價(jià)于w1(f1)所以,內(nèi)部就會(huì)去執(zhí)行:
definner():
#驗(yàn)證1
#驗(yàn)證2
#驗(yàn)證3
f1()# func是參數(shù),此時(shí)func等于f1
returninner#返回的inner,inner代表的是函數(shù),非執(zhí)行函數(shù),其實(shí)就是將原來的f1函數(shù)塞進(jìn)另外一個(gè)函數(shù)中
w1的返回值
將執(zhí)行完的w1函數(shù)返回值 賦值 給@w1下面的函數(shù)的函數(shù)名f1即將w1的返回值再重新賦值給f1,即:
新f1 =definner():
#驗(yàn)證1
#驗(yàn)證2
#驗(yàn)證3
原來f1()
returninner
所以,以后業(yè)務(wù)部門想要執(zhí)行f1函數(shù)時(shí),就會(huì)執(zhí)行 新f1函數(shù),在新f1函數(shù)內(nèi)部先執(zhí)行驗(yàn)證,再執(zhí)行原來的f1函數(shù),然后將原來f1函數(shù)的返回值返回給了業(yè)務(wù)調(diào)用者。
如此一來,即執(zhí)行了驗(yàn)證的功能,又執(zhí)行了原來f1函數(shù)的內(nèi)容,并將原f1函數(shù)返回值 返回給業(yè)務(wù)調(diào)用著
Low BBB你明白了嗎?要是沒明白的話,我晚上去你家?guī)湍憬鉀Q吧?。。?/p>
1.1.2多個(gè)裝飾器
#定義函數(shù):完成包裹數(shù)據(jù)
defmakeBold(fn):
defwrapped():
return""+ fn() +""
returnwrapped
#定義函數(shù):完成包裹數(shù)據(jù)
defmakeItalic(fn):
defwrapped():
return""+ fn() +""
returnwrapped
@makeBold
deftest1():
return"hello world-1"
@makeItalic
deftest2():
return"hello world-2"
@makeBold
@makeItalic
deftest3():
return"hello world-3"
print(test1()))
print(test2()))
print(test3()))
運(yùn)行結(jié)果:
hello world-1
hello world-2
hello world-3
1.7.3裝飾器(decorator)功能
1.引入日志
2.函數(shù)執(zhí)行時(shí)間統(tǒng)計(jì)
3.執(zhí)行函數(shù)前預(yù)備處理
4.執(zhí)行函數(shù)后清理功能
5.權(quán)限校驗(yàn)等場(chǎng)景
6.緩存
1.7.4裝飾器示例
1.7.4.1例1:無(wú)參數(shù)的函數(shù)
fromtimeimportctime, sleep
deftimefun(func):
defwrappedfunc():
print("%s called at %s"%(func.__name__, ctime()))
func()
returnwrappedfunc
@timefun
deffoo():
print("I am foo")
foo()
sleep(2)
foo()
上面代碼理解裝飾器執(zhí)行行為可理解成
foo = timefun(foo)
#foo先作為參數(shù)賦值給func后,foo接收指向timefun返回的wrappedfunc
foo()
#調(diào)用foo(),即等價(jià)調(diào)用wrappedfunc()
#內(nèi)部函數(shù)wrappedfunc被引用,所以外部函數(shù)的func變量(自由變量)并沒有釋放
#func里保存的是原foo函數(shù)對(duì)象
1.7.2例2:被裝飾的函數(shù)有參數(shù)
fromtimeimportctime, sleep
deftimefun(func):
defwrappedfunc(a, b):
print("%s called at %s"%(func.__name__, ctime()))
print(a, b)
func(a, b)
returnwrappedfunc
@timefun
deffoo(a, b):
print(a+b)
foo(3,5)
sleep(2)
foo(2,4)
1.7.3例3:被裝飾的函數(shù)有不定長(zhǎng)參數(shù)
fromtimeimportctime, sleep
deftimefun(func):
defwrappedfunc(*args, **kwargs):
print("%s called at %s"%(func.__name__, ctime()))
func(*args, **kwargs)
returnwrappedfunc
@timefun
deffoo(a, b, c):
print(a+b+c)
foo(3,5,7)
sleep(2)
foo(2,4,9)
1.7.4例4:裝飾器中的return
fromtimeimportctime, sleep
deftimefun(func):
defwrappedfunc():
print("%s called at %s"%(func.__name__, ctime()))
func()
returnwrappedfunc
@timefun
deffoo():
print("I am foo")
@timefun
defgetInfo():
return'----hahah---'
foo()
sleep(2)
foo()
print(getInfo())
執(zhí)行結(jié)果:
foo called at Fri Nov421:55:352016
I am foo
foo called at Fri Nov421:55:372016
I am foo
getInfo called at Fri Nov421:55:372016
None
如果修改裝飾器為return func(),則運(yùn)行結(jié)果:
foo called at Fri Nov421:55:572016
I am foo
foo called at Fri Nov421:55:592016
I am foo
getInfo called at Fri Nov421:55:592016
----hahah---
總結(jié):
·一般情況下為了讓裝飾器更通用,可以有return
1.7.5例5:裝飾器帶參數(shù),在原有裝飾器的基礎(chǔ)上,設(shè)置外部變量
#decorator2.py
fromtimeimportctime, sleep
deftimefun_arg(pre="hello"):
deftimefun(func):
defwrappedfunc():
print("%s called at %s %s"%(func.__name__, ctime(), pre))
returnfunc()
returnwrappedfunc
returntimefun
@timefun_arg("wangcai")
deffoo():
print("I am foo")
@timefun_arg("python")
deftoo():
print("I am too")
foo()
sleep(2)
foo()
too()
sleep(2)
too()
可以理解為
foo()==timefun_arg("wangcai")(foo)()
1.2python是動(dòng)態(tài)語(yǔ)言
1.2.1動(dòng)態(tài)語(yǔ)言的定義
動(dòng)態(tài)編程語(yǔ)言是高級(jí)程序設(shè)計(jì)語(yǔ)言的一個(gè)類別,在計(jì)算機(jī)科學(xué)領(lǐng)域已被廣泛應(yīng)用。它是一類在運(yùn)行時(shí)可以改變其結(jié)構(gòu)的語(yǔ)言:例如新的函數(shù)、對(duì)象、甚至代碼可以被引進(jìn),已有的函數(shù)可以被刪除或是其他結(jié)構(gòu)上的變化。動(dòng)態(tài)語(yǔ)言目前非常具有活力。例如JavaScript便是一個(gè)動(dòng)態(tài)語(yǔ)言,除此之外如PHP、Ruby、Python等也都屬于動(dòng)態(tài)語(yǔ)言,而C、C++等語(yǔ)言則不屬于動(dòng)態(tài)語(yǔ)言。----來自 維基百科
1.2.2運(yùn)行的過程中給對(duì)象綁定(添加)屬性
>>>classPerson(object):
def__init__(self, name = None, age = None):
self.name = name
self.age = age
>>>P = Person("小明","24")
>>>
在這里,我們定義了1個(gè)類Person,在這個(gè)類里,定義了兩個(gè)初始屬性name和age,但是人還有性別??!如果這個(gè)類不是你寫的是不是你會(huì)嘗試訪問性別這個(gè)屬性呢?
>>>P.sex ="male"
>>>P.sex
'male'
>>>
這時(shí)候就發(fā)現(xiàn)問題了,我們定義的類里面沒有sex這個(gè)屬性啊!怎么回事呢? 這就是動(dòng)態(tài)語(yǔ)言的魅力和坑! 這里 實(shí)際上就是 動(dòng)態(tài)給實(shí)例綁定屬性!
1.2.3運(yùn)行的過程中給類綁定(添加)屬性
>>>P1 = Person("小麗","25")
>>>P1.sex
Traceback (most recent call last):
File"", line1,in
P1.sex
AttributeError: Person instance has no attribute'sex'
>>>
我們嘗試打印P1.sex,發(fā)現(xiàn)報(bào)錯(cuò),P1沒有sex這個(gè)屬性!----給P這個(gè)實(shí)例綁定屬性對(duì)P1這個(gè)實(shí)例不起作用! 那我們要給所有的Person的實(shí)例加上sex屬性怎么辦呢? 答案就是直接給Person綁定屬性!
>>>> Person.sex =None#給類Person添加一個(gè)屬性
>>>P1 = Person("小麗","25")
>>>print(P1.sex)#如果P1這個(gè)實(shí)例對(duì)象中沒有sex屬性的話,那么就會(huì)訪問它的類屬性
None#可以看到?jīng)]有出現(xiàn)異常
>>>
1.2.4運(yùn)行的過程中給類綁定(添加)方法
我們直接給Person綁定sex這個(gè)屬性,重新實(shí)例化P1后,P1就有sex這個(gè)屬性了! 那么function呢?怎么綁定?
>>>classPerson(object):
def__init__(self, name = None, age = None):
self.name = name
self.age = age
defeat(self):
print("eat food")
>>>defrun(self, speed):
print("%s在移動(dòng),速度是%d km/h"%(self.name, speed))
>>>P = Person("老王",24)
>>>P.eat()
eat food
>>>
>>>P.run()
Traceback (most recent call last):
File"", line1,in
P.run()
AttributeError: Person instance has no attribute'run'
>>>
>>>
>>>importtypes
>>>P.run = types.MethodType(run, P)
>>>P.run(180)
老王在移動(dòng),速度是180km/h
既然給類添加方法,是使用類名.方法名= xxxx,那么給對(duì)象添加一個(gè)方法也是類似的對(duì)象.方法名= xxxx
完整的代碼如下:
importtypes
#定義了一個(gè)類
classPerson(object):
num =0
def__init__(self, name = None, age = None):
self.name = name
self.age = age
defeat(self):
print("eat food")
#定義一個(gè)實(shí)例方法
defrun(self, speed):
print("%s在移動(dòng),速度是%d km/h"%(self.name, speed))
#定義一個(gè)類方法
@classmethod
deftestClass(cls):
cls.num =100
#定義一個(gè)靜態(tài)方法
@staticmethod
deftestStatic():
print("---static method----")
#創(chuàng)建一個(gè)實(shí)例對(duì)象
P = Person("老王",24)
#調(diào)用在class中的方法
P.eat()
#給這個(gè)對(duì)象添加實(shí)例方法
P.run = types.MethodType(run, P)
#調(diào)用實(shí)例方法
P.run(180)
#給Person類綁定類方法
Person.testClass = testClass
#調(diào)用類方法
print(Person.num)
Person.testClass()
print(Person.num)
#給Person類綁定靜態(tài)方法
Person.testStatic = testStatic
#調(diào)用靜態(tài)方法
Person.testStatic()
1.2.5運(yùn)行的過程中刪除屬性、方法
刪除的方法:
1.del對(duì)象.屬性名
2.delattr(對(duì)象, "屬性名")
通過以上例子可以得出一個(gè)結(jié)論:相對(duì)于動(dòng)態(tài)語(yǔ)言,靜態(tài)語(yǔ)言具有嚴(yán)謹(jǐn)性!所以,玩動(dòng)態(tài)語(yǔ)言的時(shí)候,小心動(dòng)態(tài)的坑!
那么怎么避免這種情況呢?請(qǐng)使用__slots__,
1.2.6__slots__
現(xiàn)在我們終于明白了,動(dòng)態(tài)語(yǔ)言與靜態(tài)語(yǔ)言的不同
動(dòng)態(tài)語(yǔ)言:可以在運(yùn)行的過程中,修改代碼
靜態(tài)語(yǔ)言:編譯時(shí)已經(jīng)確定好代碼,運(yùn)行過程中不能修改
如果我們想要限制實(shí)例的屬性怎么辦?比如,只允許對(duì)Person實(shí)例添加name和age屬性。
為了達(dá)到限制的目的,Python允許在定義class的時(shí)候,定義一個(gè)特殊的__slots__變量,來限制該class實(shí)例能添加的屬性:
>>>classPerson(object):
__slots__ = ("name","age")
>>>P = Person()
>>>P.name ="老王"
>>>P.age =20
>>>P.score =100
Traceback (most recent call last):
File"", line1,in
AttributeError: Person instance has no attribute'score'
>>>
1.2.6.1注意:
·使用__slots__要注意,__slots__定義的屬性僅對(duì)當(dāng)前類實(shí)例起作用,對(duì)繼承的子類是不起作用的
In [67]:classTest(Person):
...:pass
...:
In [68]: t = Test()
In [69]: t.score =100
?