免费视频淫片aa毛片_日韩高清在线亚洲专区vr_日韩大片免费观看视频播放_亚洲欧美国产精品完整版

打開APP
userphoto
未登錄

開通VIP,暢享免費電子書等14項超值服

開通VIP
python核心知識點1
1.列表解析式和字典解析式
列表解析式語法 [expr for value in collection if condition]
生成式語法 (expr for value in collection if condition) 生成器
簡單用法
In [1]: [i*1 for i in range(5)]Out[2]: [0, 1, 2, 3, 4]In [29]: {x:random() for x in 'abc'}Out[29]: {'a': 2, 'b': 2, 'c': 2}復(fù)雜用法
>>> [(x,y) for x in range(5) if x%2==0 for y in range(5) if y %2==1] [(0, 1), (0, 3), (2, 1), (2, 3), (4, 1), (4, 3)]2.三元運算
# 普通條件語句if 1 == 1: name = 'wupeiqi'else: name = 'alex' # 三元運算name = 'wupeiqi' if 1 == 1 else 'alex'3.map,reduce,filter,sorted函數(shù)的用法
map()函數(shù)接收兩個參數(shù),一個是函數(shù)名,一個是序列,map將傳入的函數(shù)依次作用到序列的每個元素,并把結(jié)果作為新的list返回。
>>> def f(x): return x * x >>> map(f, [1, 2, 3, 4, 5, 6, 7, 8, 9])[1, 4, 9, 16, 25, 36, 49, 64, 81] >>> map(str, [1, 2, 3, 4, 5, 6, 7, 8, 9])['1', '2', '3', '4', '5', '6', '7', '8', '9']
map通常與lambda結(jié)合使用,寫出精致而高效的語句
reduce()把一個函數(shù)作用在一個序列[x1, x2, x3...]上,這個函數(shù)必須接收兩個參數(shù),reduce把結(jié)果繼續(xù)和序列的下一個元素做累積計算,結(jié)果返回的是一個值。其效果就是:
reduce(f, [x1, x2, x3, x4]) = f(f(f(x1, x2), x3), x4)>>> def fn(x, y): ... return x * 10 + y...>>> reduce(fn, [1, 3, 5, 7, 9])13579filter()和map()也接收一個函數(shù)名和一個序列。和map()不同的是,filter()把傳入的函數(shù)依次作用于每個元素,然后根據(jù)返回值是True還是False決定保留還是丟棄該元素。
例如,在一個list中,刪掉偶數(shù),只保留奇數(shù),可以這么寫:
def is_odd(n): return n % 2 == 1filter(is_odd, [1, 2, 4, 5, 6, 9, 10, 15])# 結(jié)果: [1, 5, 9, 15]sorted(iterable[,cmp,[,key[,reverse=True]]])
iterable包括字符串,序列,字典等可迭代對象,默認情況下reverse=True(升序),reverse=False為降序
sorted與sort的區(qū)別???
用list.sort()方法來排序,此時list本身將被修改
In [1]: a=[3,5,9,5,2]In [2]: b=sorted(a)In [3]: bOut[4]: [2, 3, 5, 5, 9]In [5]: aOut[6]: [3, 5, 9, 5, 2]In [7]: a.sort()In [8]: aOut[9]: [2, 3, 5, 5, 9]
綜合練習
num = input('輸入一個整數(shù):')#將整數(shù)轉(zhuǎn)換成字符串s = str(num)#定義map參數(shù)函數(shù)def f(s): #字符與數(shù)字字典 dic = {'1':1,'2':2,'3':3,'4':4,'5':5,"6":6,'7':7,'8':8,'9':9,'0':0} return dic[s]#定義reduce參數(shù)函數(shù)def add(x,y): return x + y#調(diào)用map()函數(shù),將字符串轉(zhuǎn)換成對應(yīng)數(shù)字序列,并打印s = map(f,s)print "輸入整數(shù)%d的組成數(shù)字為%s"%(num,s),#調(diào)用reduce函數(shù),對數(shù)字序列求和,并打印Sum = reduce(add,s)print "其和為:%d"%Sum4.lambda匿名函數(shù)
lambda arg:expression# 定義函數(shù)(普通方式)def func(arg): return arg + 1# 執(zhí)行函數(shù)result = func(123)# ###################### lambda ####################### 定義函數(shù)(lambda表達式)my_lambda = lambda arg : arg + 1# 執(zhí)行函數(shù)result = my_lambda(123)5.dict按值搜索最大值或最小值
In [1]: d = {'a':7,'b':8,'c':5}In [2]: min(d.items(),key=lambda x:x[1])Out[3]:('c', 5)In [4]: min(d.items(),key=lambda x:x[1])[1]Out[5]:5In [6]: d.items()#返回元素為元祖的序列Out[7]: dict_items([('b', 8), ('a', 7), ('c', 5)]) Out[8]: help(min)min(iterable, *[, default=obj, key=func]) -> valuemin(arg1, arg2, *args, *[, key=func]) -> value6.for循環(huán)中變量i的作用域
在for 循環(huán)里,變量一直存在在上下文中。就算是在循環(huán)外面,變量引用仍然有效。這里有個問題容易被忽略,如果在循環(huán)之前已經(jīng)有一個同名對象存在,這個對象是被覆蓋的。
x = 5for x in range(10): passprint(x)#9----------------------------------for i in range(4): d = i*2 s = "string" + str(i)print(d)#6print(s)#srting37.值引用和地址引用
python中變量名里存儲的是對象的地址(引用),這區(qū)別于c語言
a=10時的內(nèi)存示意圖
image
In [1]: m=10000In [2]: id(m)Out[2]: 4365939312In [3]: n=m #地址賦值In [4]: id(n)Out[4]: 4365939312了解python里的賦值的含義???
def f(m): | def的時候就已經(jīng)在棧中存放了 m=[2,3] #賦值相當于重新綁定對象 | 變量m,當把a傳給m時,m引用了a | 的內(nèi)容,但是對m賦值的時候,ma = [4,5] | 指向了新值,不影響a對應(yīng)的值。f(a) |print(a) #[4,5] |def f(m): m.append(4)a = [4,5]f(a)print(a) #[4,5,4]
python函數(shù)參數(shù)傳遞是傳對象或者說是傳對象的引用。函數(shù)參數(shù)在傳遞的過程中將整個對象傳入,對可變對象的修改在函數(shù)外部以及內(nèi)部都可見,調(diào)用者和被調(diào)用者之間共享這個對象,而對于不可變對象,由于并不能真正被修改,因此,修改往往是通過生成一個新對象然后賦值實現(xiàn)的?!?1條建議之31》
當一個引用傳遞給函數(shù)的時候,函數(shù)自動復(fù)制一份引用,這個函數(shù)里的引用和外邊的引用沒有半毛關(guān)系了.所以第一個例子里函數(shù)把引用指向了一個不可變對象,當函數(shù)返回的時候,外面的引用沒半毛感覺.而第二個例子就不一樣了,函數(shù)內(nèi)的引用指向的是可變對象,對它的操作就和定位了指針地址一樣,在內(nèi)存里進行修改.
8.append的陷阱
import randomlis = [1,2,3,4]l = []for i in range(5): random.shuffle(lis) print(lis, hex(id(lis))) l.append(lis) #append()傳入list的引用print(l)([1, 3, 4, 2], '0x107227950')([3, 4, 1, 2], '0x107227950')([3, 2, 4, 1], '0x107227950')([2, 3, 4, 1], '0x107227950')([4, 3, 1, 2], '0x107227950')[[4, 3, 1, 2], [4, 3, 1, 2], [4, 3, 1, 2], [4, 3, 1, 2], [4, 3, 1, 2]]9.__new__和__init__的區(qū)別
按字面意思理解就可以。 __new__ 用于創(chuàng)建對象,而 __init__ 是在創(chuàng)建完成之后初始化對象狀態(tài)。Python 中只不過是把創(chuàng)建對象和初始化這兩個概念分開了,而其他語言中直接用一個構(gòu)造函數(shù)解決。
class A(object): def __new__(cls, *args, **kwargs): print cls print hex(id(cls)) #cls就是類對象A # print args # print kwargs print "===========================================" instance = object.__new__(cls, *args, **kwargs) #instance就是實例對象a1 print hex(id(instance)) return instance #若沒有return語句,則無法創(chuàng)建實例對象a1 # def __init__(self, a, b): # print "init gets called" # print "self is", self # self.a, self.b = a, ba1=A()print "==========================================="print Aprint hex(id(A))print "==========================================="print a1print hex(id(a1))Build Result:<class '__main__.A'>0x22163e0===========================================0x21d6a10===========================================<class '__main__.A'>0x22163e0===========================================<__main__.A object at 0x021D6A10>0x21d6a10
從上例看出,對象實例化的內(nèi)部過程:
當執(zhí)行a1=A(),從上例看出,對象實例化的內(nèi)部過程:當執(zhí)行a1=A(),首先調(diào)用__new__方法,由于__new__方法返回的是一個實例對象(a1),故相當于創(chuàng)建了一個實例對象,再次調(diào)用__init__方法,來對變量初始化,再次調(diào)用__init__方法,來對變量初始化。
__new__方法默認返回實例對象供__init__方法、實例方法使用。
__init__ 方法為初始化方法,為類的實例提供一些屬性或完成一些動作。
__new__方法創(chuàng)建實例對象供__init__方法使用,__init__方法定制實例對象。
__new__ 方法必須返回值,__init__方法不需要返回值。(如果返回非None值就報錯)
一般用不上__new__方法
10.__repr__定制類的用法
通常情況下
In [1]: class A(object): ...: pass ...:In [2]: a=A()In [3]: print a<__main__.A object at 0x03CB0FD0>
我們可以修改輸出a的表達式,例如:
In [4]: from PIL import Image ...: ...: a=Image.Image() ...:In [5]: print a<PIL.Image.Image image mode= size=0x0 at 0x3CB2190>
查看Image類,發(fā)現(xiàn)__repre__方法被重寫了
def __repr__(self): return "<%s.%s image mode=%s size=%dx%d at 0x%X>" % ( self.__class__.__module__, self.__class__.__name__, self.mode, self.size[0], self.size[1], id(self))11.類屬性和實例屬性
一.類屬性和實例屬性
當有類屬性時,再定義實例屬性是因為實例屬性的變量名指向了新的值,類屬性并沒有被改變。說到底就是類對象和實例對象一般不共有同一屬性和方法。
class Person(object): """定義一個具有類屬性的類""" _die = True def __init__(self, age): self.age = age #age不是類屬性pa = Person(20)print(Person._die, id(Person._die))#輸出值:True 1531278160print(pa._die, id(pa._die))#輸出值:True 1531278160pa._die = 1 print(pa._die, id(pa._die))#輸出值:1 1531459344del pa._dieprint(pa._die, id(pa._die))#輸出值:True 1531278160class Test(object): name = 'scolia'a = Test()a.name = 'scolia good' # 通過實例進行修改,此時實例的name屬性是重新創(chuàng)建的,與類的name屬性不是同一個print(Test.name) #scoliaprint(a.name) #scolia good
本質(zhì):當函數(shù)內(nèi)訪問變量時,會先在函數(shù)內(nèi)部查詢有沒有這個變量,如果沒有,就到外層中找。這里的情況是我在實例中訪問一個屬性,但是我實例中沒有,我就試圖去創(chuàng)建我的類中尋找有沒有這個屬性。找到了,就有,沒找到,就拋出異常。當我去賦值實例對象中沒有的變量時,其實就是對該對象創(chuàng)建一個變量,這是由于python是動態(tài)語言決定的。而當我試圖用實例去修改一個在類中不可變的屬性的時候,我實際上并沒有修改,而是在我的實例中創(chuàng)建了這個屬性。而當我再次訪問這個屬性的時候,我實例中有,就不用去類中尋找了。
如果用一張圖來表示的話:
image
關(guān)于類與實例對象的深處理解:
創(chuàng)建的實例對象不包含任何的屬性(構(gòu)造函數(shù)除外),但是跟成員方法綁定。
訪問的時候是訪問類對象中的屬性
賦值的時候,實例對象重新創(chuàng)建變量名
類訪問成員方法時,必須帶上綁定的實例對象,即<類.方法(實例對象)>
類方法(@classmethod)和成員方法的區(qū)別??? 類.方法()直接訪問
In [25]: class A(object): ...: m=1 ...: n=2 ...:In [26]: a=A()In [27]: A.__dict__Out[27]:dict_proxy({'__dict__': <attribute '__dict__' of '__doc__': None, '__module__': '__main__', '__weakref__': <attribute '__weakref_ 'm': 1, 'n': 2})In [28]: a.__dict__Out[28]: {}In [29]: A.mOut[29]: 1In [30]: a.mOut[30]: 1In [31]: hex(id(A.m))Out[31]: '0x1e0a5d8'In [32]: hex(id(a.m))Out[32]: '0x1e0a5d8'In [33]: a.m=12 #賦值之后不在是同一個mIn [35]: hex(id(a.m))Out[35]: '0x1e0a554'In [36]: hex(id(A.m))Out[36]: '0x1e0a5d8'
兩種寫法的區(qū)別:
In [53]: class Kls(object): ...: no_inst = 0 ...: def __init__(self): ...: Kls.no_inst = Kls.no_inst + 1 ...:In [54]: k1=Kls()In [55]: k1.__dict__Out[55]: {}In [56]: class Kls(object): ...: no_inst = 0 ...: def __init__(self): ...: self._inst = self.no_inst + 1 ...:In [57]: k1=Kls()In [58]: k1.__dict__Out[58]: {'_inst': 1}12.@classmethod和@staticmethod
一般來說,要使用某個類的方法,需要先實例化一個對象再調(diào)用方法。而使用@staticmethod或@classmethod,就可以不需要實例化,直接類名.方法名()來調(diào)用。這有利于組織代碼,把某些應(yīng)該屬于某個類的函數(shù)給放到那個類里去,同時有利于命名空間的整潔。
既然@staticmethod和@classmethod都可以直接類名.方法名()來調(diào)用,那他們有什么區(qū)別呢?從它們的使用上來看
@staticmethod不需要表示自身對象的self和自身類的cls參數(shù),就跟使用函數(shù)一樣。類似于全局函數(shù)。
@classmethod也不需要self參數(shù),但第一個參數(shù)需要是表示自身類的cls參數(shù)。
@classmethod 是一個函數(shù)修飾符,它表示接下來的是一個類方法,而對于平常我們見到的則叫做實例方法。 類方法的第一個參數(shù)cls,而實例方法的第一個參數(shù)是self,表示該類的一個實例。普通對象方法至少需要一個self參數(shù),代表類對象實例。
類方法有類變量cls傳入,從而可以用cls做一些相關(guān)的處理。并且有子類繼承時,調(diào)用該類方法時,傳入的類變量cls是子類,而非父類。 對于類方法,可以通過類來調(diào)用,就像Test.foo()
如果要調(diào)用@staticmethod方法,通過類對象或?qū)ο髮嵗{(diào)用;而@classmethod因為持有cls參數(shù),可以來調(diào)用類的屬性,類的方法,實例化對象等,避免硬編碼。==@classmethod最常見的用途是定義備選構(gòu)造方法==
下面上代碼。
class A(object): bar = 1 #這是類的屬性,而非實例的屬性 def foo(self): #這是實例的方法,注意只要帶有self就是實例的方法或者屬性 print 'foo' @staticmethod def static_foo(): print 'static_foo' print A.bar @classmethod def class_foo(cls): print 'class_foo' print cls.bar print cls().foo()A.static_foo()A.class_foo()============================================out:static_foo1class_foo1foo
@classmethod means: when this method is called, we pass the class as the first argument instead of the instance of that class (as we normally do with methods). This means you can use the class and its properties inside that method rather than a particular instance.
@staticmethod means: when this method is called, we don't pass an instance of the class to it (as we normally do with methods). This means you can put a function inside a class but you can't access the instance of that class (this is useful when your method does not use the instance).
13.assert的用法
在開發(fā)一個程序時候,與其讓它運行時崩潰,不如在它出現(xiàn)錯誤條件時就崩潰(返回錯誤)。這時候斷言assert 就顯得非常有用。
assert的語法:
assert expression1 [“,” expression2]
等價語句:
if not expression1: raise Exception(expression2)
example:
In [6]: x = 1In [7]: y = 2In [8]: assert x == y,'not equal’. #若x == y,則會繼續(xù)執(zhí)行下去----------------------------------------------------------AssertionError Traceback (most recent call last)<ipython-input-8-30354b60974a> in <module>()----> 1 assert x == y,'not equal'AssertionError: not equal14.format的用法
它通過{}和:來代替%。
用法是:字符串.format(args)
1.通過位置
In [1]: '{0},{1}'.format('kzc',18) Out[1]: 'kzc,18'In [2]: '{},{}'.format('kzc',18) Out[2]: 'kzc,18'In [3]: '{1},{0},{1}'.format('kzc',18) Out[3]: '18,kzc,18'
字符串的format函數(shù)可以接受不限個參數(shù),位置可以不按順序,可以不用或者用多次,不過2.6不能為空{(diào)},2.7才可以。
2.通過關(guān)鍵字參數(shù)
In [5]: '{name},{age}'.format(age=18,name='kzc') Out[5]: 'kzc,18'
3.通過下標
In [7]: p=['kzc',18]In [8]: '{0[0]},{0[1]}'.format(p)Out[8]: 'kzc,18'
4.格式限定符
它有著豐富的的“格式限定符”(語法是{}中帶:號),比如:
填充與對齊填充常跟對齊一起使用
^、<、>分別是居中、左對齊、右對齊,后面帶寬度
:號后面帶填充的字符,只能是一個字符,不指定的話默認是用空格填充
比如
In [15]: '{:>8}'.format('189')Out[15]: ' 189'In [16]: '{:0>8}'.format('189')Out[16]: '00000189'In [17]: '{:a>8}'.format('189')Out[17]: 'aaaaa189'
精度與類型f精度常跟類型f一起使用
In [44]: '{:.2f}'.format(321.33345)Out[44]: '321.33'
其中.2表示長度為2的精度,f表示float類型。
其他類型主要就是進制了,b、d、o、x分別是二進制、十進制、八進制、十六進制。
In [54]: '{:b}'.format(17)Out[54]: '10001'In [55]: '{:d}'.format(17)Out[55]: '17'In [56]: '{:o}'.format(17)Out[56]: '21'In [57]: '{:x}'.format(17)Out[57]: '11'
用,號還能用來做金額的千位分隔符。
In [47]: '{:,}'.format(1234567890)Out[47]: '1,234,567,890'15.*和**收集參數(shù)以及解包的用法
在def函數(shù)時,參數(shù)前的*和**:*收集其余的位置參數(shù),并返回一個元祖;**收集關(guān)鍵字參數(shù),并返回一個字典。關(guān)鍵字參數(shù)就是參數(shù)列表中有專門定義“參數(shù)=值”
例:
def print_params_2(title,*params1,**params2): print title print params1 print params2 print_params_2("params:",1,2,3,x=5,y=6)params:(1,2,3){‘x’:5,’y’:6}
解包:當調(diào)用函數(shù)時,將*args或**kwargs作為參數(shù)傳入
def foo(*args,**kwargs): print(args) print(kwargs) print(*args) print(*kwargs)foo(1,2,3,a=4,b=5)########################(1, 2, 3){'a': 4, 'b': 5}1 2 3a b
當定義函數(shù)時,*和**是用來收集參數(shù)
當調(diào)用函數(shù)時,*是用來解包參數(shù)
16.zip的用法
zip(list1,list2)返回一個list,元素是list1和list2組成的元組
>>> zip([1,2,3],[6,7,8])[(1, 6), (2, 7), (3, 8)]>>> zip((1,2,3),(6,7,8))[(1, 6), (2, 7), (3, 8)]17.__closure__之閉包
def outlayer(): a = 30 b = 15 c = 20 print(hex(id(b))) print(hex(id(c))) def inlayer(x): return 2*x+b+c #引用外部變量b和c,b和c有時又叫做環(huán)境變量 #只可以訪問環(huán)境變量,若要修改,需要加nonlocal return inlayer # return a function objecttemp = outlayer()print('='*10)print(outlayer.__closure__) #outlayer函數(shù)的__closure__為空print('='*10)print(temp.__closure__) #只包含b和c環(huán)境變量,a對于inlayer不是,因為沒有調(diào)用0x100275d000x100275da0==========None==========0x100275d000x100275da0(<cell at 0x1006c8ca8: int object at 0x100275d00>,<cell at 0x1006c8ee8: int object at 0x100275da0>)
一個函數(shù)inlayer和它的環(huán)境變量b,c合在一起,就構(gòu)成了一個閉包(closure)。在Python中,所謂的閉包是一個包含有環(huán)境變量取值的函數(shù)對象。環(huán)境變量取值被保存在函數(shù)對象的__closure__屬性中。
18.nonlocal的用法
nonlocal關(guān)鍵字用來在函數(shù)或其他作用域中使用(修改)外層(非全局)變量,一般出現(xiàn)在閉包或者裝飾器中。
def outlayer(): c = 20 def inlayer(x): nonlocal c #聲明使用上層函數(shù)中的c,不可以使用global c = c+1 print('c is', c) return 2*x+c return inlayer temp = outlayer()print(temp(5))c is 213119.Counter()簡單統(tǒng)計個數(shù)
Counter是collections模塊中的一個類,可以用來簡單統(tǒng)計容器中數(shù)據(jù)的個數(shù)
In [1]: from collections import CounterIn [2]: b=[1,2,3,4,5,0,6,3,4,6,0]In [3]: c = Counter(b)In [4]: cOut[5]: Counter({0: 2, 1: 1, 2: 1, 3: 2, 4: 2, 5: 1, 6: 2})20.mro搜索順序
MRO(Method Resolution Order)
class Root: pass class A(Root): pass class B(Root): pass class C(A, B): pass print(C.mro) # 輸出結(jié)果為: # (<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class '__main__.Root'>, <class 'object'>)
MRO實際上是對繼承樹做層序遍歷的結(jié)果,把一棵帶有結(jié)構(gòu)的樹變成了一個線性的表,所以沿著這個列表一直往上, 就可以無重復(fù)的遍歷完整棵樹, 也就解決了多繼承中的Diamond問題。
20.super的用法
super是一個類。調(diào)用super()這個方法時,只是返回一個super對象,并不做其他的操作。然后對這個super對象進行方法調(diào)用時,發(fā)生的事情如下:
找到第一個參數(shù)的__mro__列表中的下一個直接定義了該方法的類
該(父)類調(diào)用方法,參數(shù)綁定的對象為子類對象
class Root: def __init__(self): print('Root')class A(Root): def __init__(self): print('A1') super().__init__() # 等同于super(A, self).__init__(self) print('A2')a = A()A1RootA2
在A的構(gòu)造方法中,先調(diào)用super()得到一個super對象,然后向這個對象調(diào)用__init__方法,這時super對象會搜索A的__mro__列表,找到第一個定義了__init__方法的類, 于是就找到了Root, 然后調(diào)用Root.__init__(self),這里的self是super()的第二個參數(shù),是編譯器自動填充的,也就是A的__init__的第一個參數(shù),這樣就完成__init__方法調(diào)用的分配。
注意: 在許多語言的繼承中,==子類必須調(diào)用父類的構(gòu)造方法,就是為了保證子類的對象能夠填充上父類的屬性!==而不是初始化一個父類對象…。Python中就好多了,所謂的調(diào)用父類構(gòu)造方法, 就是明明白白地把self傳給父類的構(gòu)造方法,
如果沒有多繼承, super其實和通過父類來調(diào)用方法差不多. 但, super還有個好處: 當B繼承自A, 寫成了A.__init__, 如果根據(jù)需要進行重構(gòu)全部要改成繼承自 E,那么全部都得改一次! 這樣很麻煩而且容易出錯! 而使用super()就不用一個一個改了(只需類定義中改一改就好了)
補充:對面向?qū)ο蟮睦斫?div style="height:15px;">
其實我覺得Python里面這樣的語法更容易理解面向?qū)ο蟮谋举|(zhì), 比Java中隱式地傳this更容易理解。所謂函數(shù),就是一段代碼,接受輸入,返回輸出。所謂方法,就是一個函數(shù)有了一個隱式傳遞的參數(shù)。所以方法就是一段代碼,是類的所有實例共享, 唯一不同的是各個實例調(diào)用的時候傳給方法的this 或者self不一樣而已。
構(gòu)造方法是什么呢?其實也是一個實例方法啊,它只有在對象生成了之后才能調(diào)用,所以Python中__init__方法的參數(shù)是self啊。調(diào)用構(gòu)造方法時其實已經(jīng)為對象分配了內(nèi)存, 構(gòu)造方法只是起到初始化的作用,也就是為這段內(nèi)存里面賦點初值而已。
Java中所謂的靜態(tài)變量其實也就是類的變量, 其實也就是為類也分配了內(nèi)存,里面存了這些變量,所以Python中的類對象我覺得是很合理的,也比Java要直觀。至于靜態(tài)方法,那就與對象一點關(guān)系都沒有了,本質(zhì)就是個獨立的函數(shù),只不過寫在了類里面而已。而Python中的classmethod其實也是一種靜態(tài)方法,不過它會依賴于cls對象,這個cls就是類對象,但是只要想用這個方法,類對象必然是存在的,不像實例對象一樣需要手動的實例化,所以classmethod也可以看做是一種靜態(tài)變量。而staticmethod就是真正的靜態(tài)方法了,是獨立的函數(shù),不依賴任何對象。
Java中的實例方法是必須依賴于對象存在的, 因為要隱式的傳輸this,如果對象不存在這個this也沒法隱式了。所以在靜態(tài)方法中是沒有this指針的,也就沒法調(diào)用實例方法。而Python中的實例方法是可以通過類名來調(diào)用的。只不過因為這時候self沒辦法隱式傳遞,所以必須得顯式地傳遞。
22.__call__回調(diào)函數(shù)
一般來說,魔法方法都是隱式調(diào)用的
__call__()放在類中定義。
python中,函數(shù)是一個對象
>>> f = abs #絕對值函數(shù)>>> f.__name__'abs'>>> f(-10)10
由于 f 可以被調(diào)用,所以,f 被稱為可調(diào)用對象。所有的函數(shù)都是可調(diào)用對象。
一個類實例也可以變成一個可調(diào)用對象,只需要實現(xiàn)一個特殊方法__call__()。
我們把 Person 類變成一個可調(diào)用對象:
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
現(xiàn)在可以對 Person 實例直接調(diào)用:
>>> person = Person('Bob', 'male')>>> person('Tim')My name is Bob...My friend is Tim...
單看 person('Tim') 你無法確定 p 是一個函數(shù)還是一個類實例,所以,在Python中,函數(shù)也是對象,對象和函數(shù)的區(qū)別并不顯著。
23.dict的get函數(shù)
get函數(shù)相比較于通過下標的方式獲取值得好處就是,即使字典中沒有想要的key,也會根據(jù)get后面的參數(shù)輸出結(jié)果,對用戶友好;而下標方式直接報錯
>>> a = {'a':1,'b':2}>>> a.get('a')1>>> a.get('c',"None")'None'24.hasattr() getattr() setattr() 函數(shù)使用方法詳解
hasattr(object, name)
判斷一個對象里面是否有name屬性或者name方法,返回BOOL值,有name特性返回True, 否則返回False。
需要注意的是name要用括號括起來
>>> class test():... name="xiaohua"... def run(self):... return "HelloWord"...>>> t=test()>>> hasattr(t, "name") #判斷對象有name屬性True>>> hasattr(t, "run") #判斷對象有run方法True
getattr(object, name[,default])
獲取對象object的屬性或者方法,如果存在打印出來,如果不存在,打印出默認值,默認值可選。
需要注意的是,如果是返回的對象的方法,返回的是方法的內(nèi)存地址,如果需要運行這個方法,
可以在后面添加一對括號。
>>> class test():... name="xiaohua"... def run(self):... return "HelloWord"...>>> t=test()>>> getattr(t, "name") #獲取name屬性,存在就打印出來。'xiaohua'>>> getattr(t, "run") #獲取run方法,存在就打印出方法的內(nèi)存地址。<bound method test.run of <__main__.test instance at 0x0269C878>>>>> getattr(t, "run"()) #獲取run方法,后面加括號可以將這個方法運行。'HelloWord'>>> getattr(t, "age") #獲取一個不存在的屬性。Traceback (most recent call last): File "<stdin>", line 1, in <module>AttributeError: test instance has no attribute 'age'>>> getattr(t, "age","18") #若屬性不存在,返回一個默認值。'18'
setattr(object, name, values)
給對象的屬性賦值,若屬性不存在,先創(chuàng)建再賦值。
>>> class test():... name="xiaohua"... def run(self):... return "HelloWord"...>>> t=test()>>> hasattr(t, "age") #判斷屬性是否存在False>>> setattr(t, "age", "18") #為屬相賦值,并沒有返回值>>> hasattr(t, "age") #屬性存在了True
一種綜合的用法是:判斷一個對象的屬性是否存在,若不存在就添加該屬性。
>>> class test():... name="xiaohua"... def run(self):... return "HelloWord"...>>> t=test()>>> getattr(t, "age") #age屬性不存在Traceback (most recent call last): File "<stdin>", line 1, in <module>AttributeError: test instance has no attribute 'age'>>> getattr(t, "age", setattr(t, "age", "18")) #age屬性不存在時,設(shè)置該屬性'18'>>> getattr(t, "age") #可檢測設(shè)置成功'18'25.object,type和metaclass的關(guān)系
type類是元類,用來創(chuàng)建類(對象)的,事實是調(diào)用了type的__new__創(chuàng)建的類(對象)
__new__是用來創(chuàng)建實例對象的,如果沒有復(fù)寫__new__方法,事實上是調(diào)用了object的__new__
metaclass關(guān)鍵字是用來定制元類的,這樣子后續(xù)我們定義的類就可以用定制的元類來創(chuàng)建。經(jīng)典的運用就是ORM
type也是繼承了object,并對object的__new__進行了復(fù)寫
父類(對象)先創(chuàng)建,之后才是子類(對象)
26.淺拷貝和深拷貝
普通的賦值在python中是淺拷貝,及傳遞的是對象的引用;若要深拷貝,必須導(dǎo)入copy模塊
copy.copy 淺拷貝 只拷貝父對象,不會拷貝對象的內(nèi)部的子對象。
copy.deepcopy 深拷貝 拷貝對象及其子對象
import copya = [1, 2, 3, 4, ['a', 'b']] #原始對象 b = a #賦值,傳對象的引用c = copy.copy(a) #對象拷貝,淺拷貝d = copy.deepcopy(a) #對象拷貝,深拷貝 a.append(5) #修改對象aa[4].append('c') #修改對象a中的['a', 'b']數(shù)組對象 print 'a = ', aprint 'b = ', bprint 'c = ', cprint 'd = ', d輸出結(jié)果:a = [1, 2, 3, 4, ['a', 'b', 'c'], 5]b = [1, 2, 3, 4, ['a', 'b', 'c'], 5]c = [1, 2, 3, 4, ['a', 'b', 'c']]d = [1, 2, 3, 4, ['a', 'b']]
深拷貝在拷貝的時候,若拷貝對象里包含引用,不僅拷貝引用,而且把引用的內(nèi)容也再復(fù)制一份。理解深的含義。
27.私有屬性是可以訪問的
python中定義私有屬性的方法是在變量名前加'_'或者'__'
_foo:一種約定,用來指定變量私有。程序員用來指定私有變量的一種方式。但是外部仍可直接訪問
__foo:這個有真正的意義:解析器用_classname__foo來代替這個名字,以區(qū)別和其他類相同的命名。
但是我們可以訪問私有屬性,因為python中私有屬性是通過名字重整的機制實現(xiàn)的,改變了私有屬性名,從而報出name defined異常。
In [19]: class A(): ...: def __init__(self): ...: self.__num=100 ...: In [20]: a=A()In [21]: a.__num---------------------------------------------------------------AttributeError: 'A' object has no attribute '__num'In [22]: dir(a)Out[22]: ['_A__num', '__class__',... '__weakref__']In [23]: a._A__numOut[23]: 10028.屬性攔截器__getattribute__
當訪問對象的屬性時,其實先調(diào)用對象中的魔法方法__getattribute__
class Itcast(object): def __init__(self,subject1): self.subject1 = subject1 self.subject2 = 'cpp' #屬性訪問時攔截器,打log def __getattribute__(self,obj): if obj == 'subject1': print('log subject1') return 'redirect python' else: #測試時注釋掉這2行,將找不到subject2 return object.__getattribute__(self,obj) def show(self): print('this is Itcast')s = Itcast("python")print(s.subject1)print(s.subject2)log subject1redirect pythoncpp29.對象中都是屬性,方法只不過是屬性的引用
In [24]: class A(): ...: pass ...: In [25]: a=A()In [26]: a.f---------------------------------------------------------------AttributeError: 'A' object has no attribute 'f'In [27]: a.f()---------------------------------------------------------------AttributeError: 'A' object has no attribute 'f'
所以當調(diào)用a.f()的時候也會默認執(zhí)行類中__getattribute__方法
30.GIL全局解釋器鎖
從名字上看能告訴我們很多東西,很顯然,這是一個加在解釋器上的全局(從解釋器的角度看)鎖(從互斥或者類似角度看)。GIL使得同一時刻只有一個線程在一個CPU上執(zhí)行字節(jié)碼,無法保證多個線程映射到多個CPU上執(zhí)行。對于任何Python程序,不管有多少的處理器,任何時候都總是只有一個線程在執(zhí)行。事實上,線程并不是完全運行完成后釋放GIL,所以線程安全也是相對的
在Python多線程下,每個線程的執(zhí)行方式:
1.獲取GIL
2.執(zhí)行代碼直到sleep或者是python虛擬機將其掛起(虛擬機會根據(jù)執(zhí)行的字節(jié)碼行數(shù)以及時間片釋放GIL)或者遇到IO操作
3.釋放GIL
可見,某個線程想要執(zhí)行,必須先拿到GIL,我們可以把GIL看作是“通行證”,并且在一個python進程中,GIL只有一個。拿不到通行證的線程,就不允許進入CPU執(zhí)行
解決辦法就是多進程和協(xié)程(協(xié)程也只是單CPU,單線程但是能減小切換代價提升性能)。
31.當函數(shù)的參數(shù)是list引發(fā)的問題
==比較的是值的大??;is是比較地址???
32.上下文管理 __enter__,__exit__
應(yīng)用場景:
文件的讀寫
數(shù)據(jù)庫的讀寫操作
Flask的上下文管理
上下文管理協(xié)議:當使用with語句時,解釋器會自動調(diào)用 __enter__,__exit__
class Sample: def __enter__(self): print('enter') #進入資源 return self def __exit__(self, exc_type, exc_val, exc_tb): print('exit') #釋放資源 def do_something(self): print('do something')with Sample() as sample: sample.do_something()
輸出
enterdo somethingexit
進入with語句,調(diào)用__enter__;退出with語句,調(diào)用__exit__
事實上sample并不是sample=Sample(),而是__enter__返回的對象,即如果__enter__沒有return,sample將為None。
exc_type:異常類型;exc_val:異常值;exc_tb:traceback。如果with語句中有異常發(fā)生,__exit__中會收集這些異常信息。
33.__slots__
Python是一門動態(tài)語言,可以在運行過程中,修改實例的屬性和增刪方法。一般,任何類的實例包含一個字典dict,Python通過這個字典可以將任意屬性綁定到實例上。有時候我們只想使用固定的屬性,而不想任意綁定屬性,這時候我們可以定義一個屬性名稱集合,只有在這個集合里的名稱才可以綁定。slots就是完成這個功能的。
class test_slots(object): __slots__='x','y' def printHello(self): print 'hello!' class test(object): def printHello(self): print 'hello' print dir(test_slots) #可以看到test_slots類結(jié)構(gòu)里面包含__slots__,x,y print dir(test)#test類結(jié)構(gòu)里包含__dict__ print '**************************************' ts=test_slots() t=test() print dir(ts) #可以看到ts實例結(jié)構(gòu)里面包含__slots__,x,y,不能任意綁定屬性 print dir(t) #t實例結(jié)構(gòu)里包含__dict__,可以任意綁定屬性 print '***************************************' ts.x=11 #只能綁定__slots__名稱集合里的屬性 t.x=12 #可以任意綁定屬性 print ts.x,t.x ts.y=22 #只能綁定__slots__名稱集合里的屬性 t.y=23 #可以任意綁定屬性 print ts.y,t.y #ts.z=33 #無法綁定__slots__集合之外的屬性(AttributeError: 'test_slots' object has no attribute 'z') t.z=34 #可以任意綁定屬性 print t.z
正如上面所說的,默認情況下,Python的新式類和經(jīng)典類的實例都有一個dict來存儲實例的屬性。這在一般情況下還不錯,而且非常靈活,乃至在程序中可以隨意設(shè)置新的屬性。但是,對一些在”編譯”前就知道有幾個固定屬性的小class來說,這個dict就有點浪費內(nèi)存了。當需要創(chuàng)建大量實例的時候,這個問題變得尤為突出。一種解決方法是在新式類中定義一個slots屬性。slots聲明中包含若干實例變量,并為每個實例預(yù)留恰好足夠的空間來保存每個變量;這樣Python就不會再使用dict,從而節(jié)省空間。
34.__unicode__,__str__
__unicode__,__str__類似于java的toString方法。在java中,當打印對象的時候,其實是調(diào)用了對象的toString方法。同樣,我們可以定制對象打印輸出的格式
unicode是用在python2里,而str是用在python3里
#不復(fù)寫__str__方法,調(diào)用的是object的方法class A: def __init__(self,name,age): self.name=name self.age=agea=A('lu',25)print(a) #<__main__.A object at 0x101978cf8>#復(fù)寫__str__方法,調(diào)用的是object的方法class A: def __init__(self,name,age): self.name=name self.age=age def __str__(self): return self.name + ' is ' + str(self.age) + ' years old'a=A('lu',25)print(a) #lu is 25 years old35.文件緩沖
36.for不可對可迭代對象進行修改
在使用python的可迭代對象時有一條定律,那就是永遠都不要對迭代對象進行數(shù)據(jù)的修改。 我們可以用copy(),生成一個副本,對這個副本進行遍歷。
In [10]: l = ['a','b','c','d']In [11]: for index,key in enumerate(l): ...: if key == 'c': ...: l.pop(index)In [12]: l['a', 'b', 'c'] ########推薦 for index,key in enumerate(l.copy()): ...: if key == 'c': ...: l.pop(index)37.__getitem__和 __setitem__方法來實現(xiàn)“[ ]”符號的使用
class Map: def __init__(self): self.Q_key = [] self.Q_value = [] def put(self, key, value): self.Q_key.append(key) self.Q_value.append(value) def get(self, key, default=None): for index,k in enumerate(self.Q_key): if k == key: return self.Q_value[index] return default def __getitem__(self, key): return self.get(key) def __setitem__(self, key, value): self.put(key, value)m = Map()m['a']=1m['b']=2print(m['b'])38.list的倒序切片
In [1]: l=[1,2,3,4,5,6]In [7]: l[-1:1:-1]Out[7]: [6, 5, 4, 3]In [8]: l[-1:0:-1]Out[8]: [6, 5, 4, 3, 2]In [9]: l[-1::-1]Out[9]: [6, 5, 4, 3, 2, 1]39.字節(jié)流與字符流
40.__name__的含義
#a.pyprint(__name__)
運行a.py,執(zhí)行結(jié)果為__main__。
但如果對于一個b.py作為模塊在a中使用,b的__name__將變成對應(yīng)b的模塊名
#b.pyprint(__name__)#a.pyimport b
運行a.py(如果運行b.py,結(jié)果扔是main),執(zhí)行結(jié)果為b
所以為了測試模塊的相應(yīng)功能,而避免在主程序中運行,通常我們需要在執(zhí)行的代碼中這樣寫
if __name__ == '__main__': do something
這樣子就能很好的控制模塊的有效輸出
41.為什么不能用可變對象作為函數(shù)的默認參數(shù)值
先來看一道題目:
>>> def func(numbers=[], num=1):... numbers.append(num)... return numbers>>> func()[1]>>> func()[1, 1]>>> func()[1, 1, 1]
我們似乎發(fā)現(xiàn)了一個Bug,每次用相同的方式調(diào)用函數(shù) func() 時,返回結(jié)果竟然不一樣,而且每次返回的列表在不斷地變長。
>>> id(func())4330472840>>> id(func())4330472840
從上面可以看出,函數(shù)的返回值其實是同一個列表對象,因為他們的id值是一樣的,只不過是列表中的元素在變化。為什么會這樣呢?
這要從函數(shù)的特性說起,在 Python 中,函數(shù)是第一類對象(function is the first class object),換而言之,函數(shù)也是對象,跟整數(shù)、字符串一樣可以賦值給變量、當做參數(shù)傳遞、還可以作為返回值。函數(shù)也有自己的屬性,比如函數(shù)的名字、函數(shù)的默認參數(shù)列表。
# 函數(shù)的名字>>> func.__name__ 'func'# 函數(shù)的默認參數(shù)列表>>> func.__defaults__ ([1, 1, 1, 1, 1], 1)
def是一條可執(zhí)行語句,Python 解釋器執(zhí)行 def 語句時,就會在內(nèi)存中就創(chuàng)建了一個函數(shù)對象(此時,函數(shù)里面的代碼邏輯并不會執(zhí)行,因為還沒調(diào)用嘛),在全局命名空間,有一個函數(shù)名(變量叫 func)會指向該函數(shù)對象,記住,至始至終,不管該函數(shù)調(diào)用多少次,函數(shù)對象只有一個,就是function object,不會因為調(diào)用多次而出現(xiàn)多個函數(shù)對象。
image
函數(shù)對象生成之后,它的屬性:名字和默認參數(shù)列表都將初始化完成。
image
初始化完成時,屬性 __default__ 中的第一個默認參數(shù) numbers 指向一個空列表。
當函數(shù)第一次被調(diào)用時,就是第一次執(zhí)行 func()時,開始執(zhí)行函數(shù)里面的邏輯代碼(此時函數(shù)不再需要初始化了),代碼邏輯就是往numbers中添加一個值為1的元素
image
第二次調(diào)用 func(),繼續(xù)往numbers中添加一個元素
image
第三次、四次依此類推。
所以現(xiàn)在你應(yīng)該明白為什么調(diào)用同一個函數(shù),返回值確每次都不一樣了吧。因為他們共享的是同一個列表(numbers)對象,只是每調(diào)用一次就往該列表中增加了一個元素
如果我們顯示地指定 numbers 參數(shù),結(jié)果截然不同。
>>> func(numbers=[10, 11])[10, 11, 1]
image
因為numbers被重新賦值了,它不再指向原來初始化時的那個列表了,而是指向了我們傳遞過去的那個新列表對象,因此返回值變成了 [10, 11, 1]
那么我們應(yīng)該如何避免前面那種情況發(fā)生呢?就是不要用可變對象作為參數(shù)的默認值。
正確方式:
>>> def func(numbers=None, num=1):... if numbers is None:... numbers = [num]... else:... numbers.append(num)... return numbers...>>> func()[1]>>> func()[1]>>> func()[1]
如果調(diào)用時沒有指定參數(shù),那么調(diào)用方法時,默認參數(shù) numbers 每次都被重新賦值了,所以,每次調(diào)用的時候numbers都將指向一個新的對象。這就是與前者的區(qū)別所在。
那么,是不是說我們永遠都不應(yīng)該用可變對象來作為參數(shù)的默認值了嗎?并不是,既然Python有這樣的語法,就一定有他的應(yīng)用場景,就像 for … else 語法一樣。我們可以用可變對象來做緩存功能
例如:計算一個數(shù)的階乘時可以用一個可變對象的字典當作緩存值來實現(xiàn)緩存,緩存中保存計算好的值,第二次調(diào)用的時候就無需重復(fù)計算,直接從緩存中拿。
def factorial(num, cache={}): if num == 0: return 1 if num not in cache: print('xxx') cache[num] = factorial(num - 1) * num return cache[num]print(factorial(4))print("-------")print(factorial(4))
輸出:
---第一次調(diào)用---xxxxxxxxxxxx24---第二次調(diào)用---24
第二次調(diào)用的時候,直接從 cache 中拿了值,所以,你說用可變對象作為默認值是 Python 的缺陷嗎?也并不是,對吧!你還是當作一種特性來使用。
函數(shù)的基本注意點
1.函數(shù)基礎(chǔ)之globals()和locals()
函數(shù)名.__doc__,返回函數(shù)的解釋,對于函數(shù)定義時,可以將解釋通過字符串寫到執(zhí)行語句中。
def abs(a=3) “this is new function“>>>abs.__doc__“this is new function“函數(shù)體執(zhí)行到return語句就結(jié)束,不管后面是否還有語句。
當def一個函數(shù)時,其實就是創(chuàng)建了一個函數(shù)對象,并將函數(shù)對象返回給函數(shù)名。
函數(shù)定義時的參數(shù)(a=2)可以通過函數(shù)對象的__defaults__查看
每次對函數(shù)的調(diào)用都創(chuàng)建了一個新的本地作用域。
作用域(命名空間),一個隱藏的字典(鍵為函數(shù)名,值為函數(shù)對象的地址)
globals()函數(shù)返回全局變量的字典;locals()函數(shù)返回局部變量的字典(根據(jù)當前上下文來判斷,若處在全局的位置,等同于globals(),見下例)。locals()函數(shù)只有在函數(shù)執(zhí)行過程在才用意義,因為函數(shù)調(diào)用完成后,局部變量會從棧中刪除,所以再用locals(),沒有局部變量的字典
>>> def test(arg): z = 1 print locals()>>> test(4){'z': 1, 'arg': 4}>>> test('doulaixuexi'){'z': 1, 'arg': 'doulaixuexi'}>>> print(locals())# 輸出與globals()一樣的結(jié)果>>> print(globals())在函數(shù)的定義中可以訪問全局變量(依據(jù)LEGB原則搜索變量),但不可以修改全局變量,除非使用global聲明變量
a = 3def test(arg): z = 1 global a a = 100test(4)print(a)#100vars()函數(shù)返回在當前一個作用域(命名空間)的字典
補充:
Python使用叫做名字空間的東西來記錄變量的軌跡。名字空間只是一個 字典,它的鍵字就是變量名,字典的值就是那些變量的值。實際上,名字空間可以象Python的字典一樣進行訪問,一會我們就會看到。
在一個Python程序中的任何一個地方,都存在幾個可用的名字空間。每個函數(shù)都有著自已的名字空間,叫做局部名字空間,它記錄了函數(shù)的變量,包括 函數(shù)的參數(shù)和局部定義的變量。每個模塊擁有它自已的名字空間,叫做全局名字空間,它記錄了模塊的變量,包括函數(shù)、類、其它導(dǎo)入的模塊、模塊級的變量和常 量。還有就是內(nèi)置名字空間,任何模塊均可訪問它,它存放著內(nèi)置的函數(shù)和異常。
2.LEGB命名空間
當一行代碼要使用變量x的值時,Python會到所有可用的名字空間去查找變量,按照如下順序:
1. Local:局部名字空間特指當前函數(shù)或類的方法。如果函數(shù)定義了一個局部變量 x ,Python將使用這個變量,然后停止搜索。2. Enclosing:外部嵌套函數(shù)的命名空間(見閉包)3. Global:全局名字空間特指當前的模塊。如果模塊定義了一個名為 x 的變量,函數(shù)或類,Python將使用這個變量然后停止搜索。4. Builtin:內(nèi)置名字空間對每個模塊都是全局的。作為最后的嘗試,Python將假設(shè) x 是內(nèi)置函數(shù)或變量。
如果Python在這些名字空間找不到x,它將放棄查找并引發(fā)一個 NameError 的異常,同時傳遞There is no variable named 'x' 這樣一條信息。
像Python中的許多事情一樣,名字空間在運行時直接可以訪問。特別地,局部名字空間可以通過內(nèi)置的 locals 函數(shù)來訪問。全局(模塊級別)名字空間可以通過 globals 函數(shù)來訪問。
變量名解析:LEGB
Python中一切都是對象。每個對象都有一個名字,名字位于名字空間中,用名字變量引用對象。對象存在于內(nèi)存中一段空間。每個對象在內(nèi)存中都有地址。
在Python里類型本身是對象,和實例對象一樣儲存在堆中,對于解釋器來說類對象和實例對象沒有根本上的別。
所謂“定義一個函數(shù)”,實際上也就是生成一個函數(shù)對象。而“定義一個方法”就是生成一個函數(shù)對象,并把這個對象放在一個類的__dict__中。
函數(shù)對象結(jié)構(gòu)定義:
typedef struct { PyObject_HEAD PyObject *func_code; // PyCodeObject PyObject *func_globals; // 所在模塊的全局名字空間 PyObject *func_defaults; // 參數(shù)默認值列表 PyObject *func_closure; // 閉包列表 PyObject *func_doc; // __doc__ PyObject *func_name; // __name__ PyObject *func_dict; // __dict__ PyObject *func_weakreflist; // 弱引用鏈表 PyObject *func_module; // 所在 Module} PyFunctionObject;對象基本上可以看做數(shù)據(jù)(特性)以及由一系列可以存取,操作這些數(shù)據(jù)的方法所組成的集合
所有的對象都屬于某一個類,稱為類的實例
self指的是實例對象本身
查看a是否是b的子類,可以使用issubclass(子類,超類);查看a的基類a.__bases__
匿名函數(shù)lambda 用法lambda argument1,argument2:expression using argument.功能類似于def,只是不需要再去定義一個函數(shù)名。結(jié)果返回的是一個函數(shù)對象。
def add1(a,b): return a+badd1(2,3)=> 5g = lambda a,b:a+bg(2,3)=>53.閉包
內(nèi)層函數(shù)引用了外層函數(shù)的變量(包括它的參數(shù)),然后返回內(nèi)層函數(shù)的情況,這就是閉包。形式上,在函數(shù)的內(nèi)部定義函數(shù),并引用外部變量。
函數(shù)對象是使用def語句定義的,函數(shù)對象的作用域與def所在的層級相同。比如下面代碼,我們在line_conf函數(shù)的隸屬范圍內(nèi)定義的函數(shù)line,就只能在line_conf的隸屬范圍內(nèi)調(diào)用。
def line_conf(): def line(x): return 2*x+1 print(line(5)) # within the scopeline_conf() #結(jié)果11print(line(5)) # 錯誤,不能再外部調(diào)用內(nèi)部函數(shù)line
可以在內(nèi)部函數(shù)line()的定義中引用(非修改)外部的變量
def line_conf(): b = 15 def line(x): return 2*x+b #引用外部變量b,b有時又叫做環(huán)境變量 return line # return a function objectmy_line = line_conf()print(my_line(5)) #結(jié)果為25print(line_conf()(5)) #另一種引用方式,結(jié)果為25
一個函數(shù)line和它的環(huán)境變量b合在一起,就構(gòu)成了一個閉包(closure)。在Python中,所謂的閉包是一個包含有環(huán)境變量取值的函數(shù)對象。環(huán)境變量取值被保存在函數(shù)對象的__closure__屬性中
在python2.X中不能對外部變量賦值,而在python3中增加了nonlocal關(guān)鍵字
def Fun1(): x=5 def Fun2(): x=x*x return x return Fun2()Fun1() #錯誤
為什么需要閉包?
def line_conf(a, b): def line(x): return ax + b return lineline1 = line_conf(1, 1)line2 = line_conf(4, 5)print(line1(5), line2(5))
這個例子中,函數(shù)line與環(huán)境變量a,b構(gòu)成閉包。在創(chuàng)建閉包的時候,我們通過line_conf的參數(shù)a,b說明了這兩個環(huán)境變量的取值,這樣,我們就確定了函數(shù)的最終形式(y = x + 1和y = 4x + 5)。我們只需要變換參數(shù)a,b,就可以獲得不同的直線表達函數(shù)。由此,我們可以看到,閉包也具有提高代碼可復(fù)用性的作用。
如果沒有閉包,我們需要每次創(chuàng)建直線函數(shù)的時候同時說明a,b,x。這樣,我們就需要更多的參數(shù)傳遞,也減少了代碼的可移植性。利用閉包,我們實際上創(chuàng)建了泛函。line函數(shù)定義一種廣泛意義的函數(shù)。這個函數(shù)的一些方面已經(jīng)確定(必須是直線),但另一些方面(比如a和b參數(shù)待定)。隨后,我們根據(jù)line_conf傳遞來的參數(shù),通過閉包的形式,將最終函數(shù)確定下來。
本站僅提供存儲服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊舉報
打開APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
分享 收藏 導(dǎo)長圖 關(guān)注 下載文章
綁定賬號成功
后續(xù)可登錄賬號暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點擊這里聯(lián)系客服!

聯(lián)系客服