python学习笔记_序
說明
這里的python筆記也是之前留下的,看了下時間,大概是今年寒假期間學的,主要是跟著廖大的教程來。也是半途而廢的計劃啊(干了多少半途而廢的事情)。
教程地址:Python教程--廖雪峰
數(shù)據(jù)類型
還沒學之前就有所耳聞,Python是動態(tài)語言,不需要定義類型名的,語法上已經(jīng)將這一塊封裝好了。
除了整形、浮點型和字符串外,Python也有布爾值(有特殊的運算符and\or),“常量”(Python的常量其實是一個變量)。但最大的不同點是,Python有獨特的“列表”和“字典”類型。
列表:分為list(列表)和tuple(元組),list是可變的,而tuple是不可變的。list有點像一個數(shù)組但是內(nèi)部保存的元素可以是多種且不同的數(shù)據(jù)類型。tuple也很類似,但是它內(nèi)部的元素必須確定,即不可改變。
有一個比較容易搞混的例子:t = ('a', 'b', ['A', 'B']),它是正確的。在tuple的指向里,三個元素的位置是確定不變的;在list中,它儲存元素的空間是可以更換不同的元素的。
dict:當看到dict的特性時,我立馬想到數(shù)據(jù)結(jié)構(gòu)里的哈希表,查了一下確實是如此。那就很好理解了,key值是索引哈希值所在位置的鑰匙。另一個方面,dict的特性就像查字典那樣,時間永遠是常量級的,典型的空間換時間思想。一個很重要的地方時,key必須是一個不可變的數(shù)據(jù)類型,也就是說list是不行的,某種情況下的tuple也是不行的(上面的例子)。
set:從詞義上理解就很清晰了,就是一個集合。事實上它所儲存的元素是不可重復(fù)的。當然,dict的特性也保證了key所對應(yīng)的value也是不可重復(fù)的,要不然查找效率就不是常量級了。
函數(shù)
函數(shù)定義
函數(shù)沒有太大的變化,記錄一下之前沒見過的。
isinstance():檢查參數(shù)類型是否合格。
返回多個值:C/C++是不允許返回多個值的,如果實在需要,只能用數(shù)組指針。Python之所以可以返回多個值,其實是tuple的作用。
import XX:類似于頭文件
函數(shù)參數(shù)
Python的參數(shù)有點復(fù)雜,它不需要定義參數(shù)是哪種數(shù)據(jù)類型(一開始學真的很不習慣,被C虐慣了),但可以根據(jù)參數(shù)的作用來區(qū)分參數(shù)類型,一共有5類:必選、默認、可變、關(guān)鍵詞、命名關(guān)鍵詞。并且參數(shù)的先后順序要嚴格按照這個來,否則會產(chǎn)生歧義。如調(diào)用fun(a, b = 1, c)傳入(1, 2)時,我們不知道應(yīng)該是報錯還是按照調(diào)用(1, 1, 2)來。
必選:也叫位置參數(shù),調(diào)用時必須有一個值占位
默認:提前設(shè)置好參數(shù)的值,如果調(diào)用時沒有值占位,那么就使用默認值
可變:傳入的參數(shù)個數(shù)是可變的,在參數(shù)前加一個*,看起來有點像指針,函數(shù)內(nèi)部接收到的其實是一個tuple,因此代碼不會變化
關(guān)鍵字:**kw,允許傳入任意個含參數(shù)名的參數(shù),這些關(guān)鍵字參數(shù)在函數(shù)內(nèi)部自動組裝成一個dict。
命名關(guān)鍵字:限制關(guān)鍵字參數(shù)名字,需要用到特殊分隔符 * , * 后面的參數(shù)被視為命名關(guān)鍵字;如果函數(shù)中有可變參數(shù),就不需要再使用分隔符了
對于任意函數(shù),都可以通過類似func(*args, **kw)的形式調(diào)用它,無論它的參數(shù)是如何定義的。
大神的小結(jié):
Python的函數(shù)具有非常靈活的參數(shù)形態(tài),既可以實現(xiàn)簡單的調(diào)用,又可以傳入非常復(fù)雜的參數(shù)。
默認參數(shù)一定要用不可變對象,如果是可變對象,程序運行時會有邏輯錯誤!
要注意定義可變參數(shù)和關(guān)鍵字參數(shù)的語法:
args是可變參數(shù),args接收的是一個tuple;
kw是關(guān)鍵字參數(shù),kw接收的是一個dict。
以及調(diào)用函數(shù)時如何傳入可變參數(shù)和關(guān)鍵字參數(shù)的語法:
可變參數(shù)既可以直接傳入:func(1, 2, 3),又可以先組裝list或tuple,再通過args傳入:func((1, 2, 3));
關(guān)鍵字參數(shù)既可以直接傳入:func(a=1, b=2),又可以先組裝dict,再通過 * * kw傳入:func(* * {'a': 1, 'b': 2})。
使用* args和kw是Python的習慣寫法,當然也可以用其他參數(shù)名,但最好使用習慣用法。
命名的關(guān)鍵字參數(shù)是為了限制調(diào)用者可以傳入的參數(shù)名,同時可以提供默認值。
定義命名的關(guān)鍵字參數(shù)在沒有可變參數(shù)的情況下不要忘了寫分隔符,否則定義的將是位置參數(shù)。
函數(shù)遞歸
講到一個尾遞歸的東西,可總結(jié)為:返回值只能是函數(shù)自身而不能是一個表達式
尾遞歸很容易改成循環(huán)的形式,但是很多編輯器沒有做到。印象中C好像是做到了?
高級特性
在Python中,代碼不是越多越好,而是越少越好。代碼不是越復(fù)雜越好,而是越簡單越好。
切片
用法是[x:x],提取list或tuple里這一部分的數(shù)據(jù),十分方便
迭代
語法是 for ... in ...,不僅可以迭代list或tuple,還可以操作dict
dict內(nèi)部元素儲存是無序的,所以迭代的結(jié)果可能會和儲存的順序不一樣。另外,dict迭代默認情況下是key,可用for value in d.value() 迭代value,還可以for k, v in d.item() 來同時迭代key和value。
字符串迭代,要實現(xiàn)同時索引對和元素對的迭代,可以使用內(nèi)置的enumerate()函數(shù)。
判斷是否為可迭代對象,通過collections的Iterable類型來判斷。
列表生成式
格式:[元素或表達式 , 循環(huán)式]
循環(huán)式還可以用兩層循環(huán)。
運用列表生成式,可以寫出非常簡潔的代碼。例如,列出當前目錄下的所有文件和目錄名,可以通過一行代碼實現(xiàn):
確實不愧為“人生苦短,我用python”,腦海中想著如果要用C的話,先不論要設(shè)多少個邊界條件,關(guān)鍵是也不知道怎么寫啊……
生成器generator
生成器可以看成是一個集成了算法的列表。
要比較列表生成式和生成器的差別,可以看一段代碼:
因而生成器是要通過一邊循環(huán)一遍計算得到所有的結(jié)果的。
另外,如果在一個函數(shù)里添加了yield X,函數(shù)就變成了生成器。而yield的作用是輸出當前生成器計算所得的X的結(jié)果。
可迭代對象和迭代器
可迭代對象 Iterable:可以直接作用于for循環(huán)的對象,包括集合數(shù)據(jù)類型和generator
迭代器 Iterator:可以被next()函數(shù)調(diào)用并不斷下一個值的對象。
可以isinstance()函數(shù)來區(qū)分可迭代對象和迭代器。
函數(shù)式編程
說實話沒看明白,不過上網(wǎng)搜索了一下,函數(shù)式和命令式同屬于面向過程的思維。函數(shù)式編程式接近數(shù)學計算。只能明白這么多了。
高階函數(shù) Higher-order function
總結(jié)起來就是大神說的那么幾句話:變量可以指向函數(shù)、函數(shù)名也是變量、函數(shù)可以作為變量傳參。
雖然很多人都說學新語言就要放下固有語言的成見,但是我還是不由自主地對比起C/C++與python在這一點的不同。變量指向函數(shù)可以通過函數(shù)指針解決,但應(yīng)該不是一個層次的東西。python的核心是為了實現(xiàn)函數(shù)作為參數(shù)傳遞的機制,這是高階函數(shù)的特征。而C/C++并無意做到這一點,它們無法忍受未知的事物作為參數(shù),而一個函數(shù)在未被調(diào)用之前,很明顯會被看作未知事物。
幾個高階函數(shù)
map: 接受兩個參數(shù),第一個函數(shù)對象, 一個Interable。map將的函數(shù)作用到序列的每個元素,并把結(jié)果作為新的Interable。
reduce: 把一個函數(shù)作用在一個序列上,這個函數(shù)必須接受兩個參數(shù),reduce把結(jié)果繼續(xù)和序列的下一個元素累積計算
filter: 用于過濾序列。返回值決定保留還是丟棄該元素
sorted: 排序函數(shù),可以接受一個key函數(shù)來實現(xiàn)自定義排序
返回函數(shù)
即將函數(shù)作為結(jié)果值返回。這一部分著實有點難以理解,換個名字我就嚇了一跳,它就是赫赫有名的“閉包”。
舉教程的例子:
def lazy_sum(*args):def sum():ax = 0for n in args:ax = ax + nreturn axreturn sumf = lazy_sum(1, 3, 5, 7, 9)當我們調(diào)用lazy_sum() 時,返回的是求和函數(shù)。只有調(diào)用函數(shù)f 時,才能計算真正求和的結(jié)果。
需要注意的兩個地方是:
匿名函數(shù)
關(guān)鍵詞lambda,就是一個表達式,表達式的值就是返回值。可以把匿名函數(shù)賦給一個變量,也可以把它作為返回值返回
裝飾器 decorator
假如要增強某一個函數(shù)的功能,比如說在函數(shù)調(diào)用前后打印日志,但又不希望改變原函數(shù)的定義,可以使用裝飾器在代碼運行階段動態(tài)增強功能。
本質(zhì)上裝飾器就是一個返回函數(shù)的高階函數(shù),如:
def log(func):def wrapper(*args, **kw):print('call %s():' % func.__name__)return func(*args, **kw)return wrapper@log def now():print('2015-3-25')輸出結(jié)果: >>> now() call now(): 2015-3-25實質(zhì)上是實現(xiàn)了now = log(now) 的功能。
偏函數(shù)
利用functools.partial 幫助創(chuàng)建一個偏函數(shù),這個可以固定某些參數(shù),返回一個新函數(shù),調(diào)用這個新函數(shù)會更簡單。
創(chuàng)建偏函數(shù)時,實際上可以接受函數(shù)對象、* args、**kw這三個參數(shù)的傳入。
模塊
在python文件中,一個py文件就是一個模塊。使用模塊可以提高代碼的可維護性,其次方便其它地方的引用。python有內(nèi)置的模塊和來自第三方的模塊。使用模塊還可以避免函數(shù)名和變量名沖突。相同名字的函數(shù)和變量完全可以分別存在不同的模塊中。但是我們自己命名的函數(shù)盡量不要與內(nèi)置函數(shù)名沖突。
為了避免同樣的模塊名沖突,python引進了按目錄來組織模塊的方法,稱為包。引入了包以后,只要頂層的包名不與別人的沖突,那所有模塊都不會與別人沖突。每一個包目錄下面都會有一個_init_.py的文件,否則python會把這個目錄當成普通目錄。
使用模塊
python的函數(shù)和變量可以通過前綴_來改變作用域。正常的變量和函數(shù)是公開的,即public,可以直接訪問,;而類似_XXX_的變量是特殊變量也可以直接引用,但是有特殊用途,比如_name_;類似_XXX和__XXX這樣的函數(shù)就是非公開的,即private。
雖然如此,但是在python中其實并沒有一種方法可以完全限制住private變量或函數(shù)的訪問,只是從編程習慣上我們不應(yīng)該引用它。
類和實例
和C++很像,定義類也是通過class關(guān)鍵字。class后緊跟著類名,緊接著是(object),表示這個類是從哪個類繼承下來的。
創(chuàng)建實例是用類+()實現(xiàn)的。可以自由地給一個實例變量綁定一些屬性。
由于類可以起到模板的作用,因此可以通過定義一個特殊的__init__方法,在創(chuàng)建實例的時候,把我們認為必須強制綁定的屬性綁上去。__init__方法的第一個參數(shù)是self,表示創(chuàng)建的實例本身。
可以將函數(shù)定義在類里,實現(xiàn)數(shù)據(jù)封裝。
和靜態(tài)語言不同(如C++),Python允許對實例變量綁定任何數(shù)據(jù),也就是說,對于兩個實例變量,雖然它們都是同一個類的不同實例,但擁有的變量名稱都可能不同。
訪問限制
如果要讓內(nèi)部屬性不被外部訪問,可以把屬性的名稱前面加上兩個下劃線__。在python中,實例的變量名如果加上了兩個下劃線,就變成了一個私有變量。
其實python的私有變量是沒有得到像C++那樣的機制保護的。事實上,之所以不能直接訪問私有變量是因為解釋器對外會把__XXX改成_Object__XXX,所以仍然可以通過后者來訪問這個私有變量。
繼承和多態(tài)
這一小節(jié)對面對對象編程來說是重點。剛學C++時,繼承還好理解,多態(tài)就有些費解了。正好現(xiàn)在再看看python的繼承和多態(tài),兩相對比來理解。
理解繼承的時候,一定要想象一棵樹(最好是數(shù)據(jù)結(jié)構(gòu)的那種樹),根結(jié)點是所有子孫結(jié)點的父輩結(jié)點。同樣的,一棵繼承樹的根類是下面所有結(jié)點的父類,它們共同繼承了這個父類的全部功能。
理解多態(tài)時,我是對比重載和多態(tài)的相同和不同之處。和C++不同的是,python是不支持函數(shù)名重載的,內(nèi)部對比無從說起。但是在類的繼承中,相同的函數(shù)名子類可以覆蓋父類。在引用子類的實例時,實例的函數(shù)是子類所覆蓋的那個函數(shù)。
看一個例子來理解會好一點:
因為繼承,所以所有的子類都可以作為animal參數(shù);又因為多態(tài),所以在引用參數(shù)的內(nèi)部函數(shù)時其實是引用了覆蓋了的函數(shù)。
廖老師的總結(jié):
這就是多態(tài)真正的威力:調(diào)用方只管調(diào)用,不管細節(jié),而當我們新增一種Animal的子類時,只要確保run()方法編寫正確,不用管原來的代碼是如何調(diào)用的。這就是著名的“開閉”原則:
1.對擴展開放:允許新增Animal子類;
2.對修改封閉:不需要修改依賴Animal類型的run_twice()等函數(shù)。
最后是靜態(tài)語言和動態(tài)語言在繼承上的不同。靜態(tài)語言,以C++為例,像上面那個例子,傳入的參數(shù)必須只能是animal或它的子類;而動態(tài)語言,如python,只要那個實例有run()方法,那就可以傳入,亦被稱為“鴨子類型”。
獲取對象信息
當我們拿到一個對象的參數(shù)時,可以使用type() isinstance() dir() 來知道這個對象是什么類型,有哪些方法。
type()
基本類型、指向函數(shù)或類的變量都可以用type()判斷,返回的是對應(yīng)的Class()類型。
如果要判斷一個對象是否為函數(shù),可以使用types模塊中定義的常量。
isinstance()
可用于判斷class的類型,告訴我們一個對象是否是某一個類型。
能用type()判斷的都可以用isinstance()判斷,而后者還可以判斷一個變量是否是某些類型中的一種。如:
dir()
用于獲得一個對象的所有屬性和方法,返回值是一個包含字符串的list。
類似__xxx__的屬性和方法在Python中都是有特殊用途的,比如__len__方法返回長度。在Python中,如果你調(diào)用len()函數(shù)試圖獲取一個對象的長度,實際上,在len()函數(shù)內(nèi)部,它自動去調(diào)用該對象的__len__()方法,所以,下面的代碼是等價的:
我們自己寫的類,如果也想用len(myObj)的話,就自己寫一個__len__()方法:
>>> class MyDog(object): ... def __len__(self): ... return 100 ... >>> dog = MyDog() >>> len(dog) 100僅僅把屬性和方法列出來是不夠的,配合getattr() setattr() hasattr,我們可以直接操作一個對象的狀態(tài)。
用這些函數(shù),可以測試對象的屬性或方法。如果試圖獲取不存在的屬性,會拋出AttributeError的錯誤;可以傳入一個默認參數(shù),如果屬性不存在,就返回默認值。
實例屬性和類屬性
看下面這段測試程序:
>>> class Student(object): ... name = 'Student' ... >>> s = Student() # 創(chuàng)建實例s >>> print(s.name) # 打印name屬性,因為實例并沒有name屬性,所以會繼續(xù)查找class的name屬性 Student >>> print(Student.name) # 打印類的name屬性 Student >>> s.name = 'Michael' # 給實例綁定name屬性 >>> print(s.name) # 由于實例屬性優(yōu)先級比類屬性高,因此,它會屏蔽掉類的name屬性 Michael >>> print(Student.name) # 但是類屬性并未消失,用Student.name仍然可以訪問 Student >>> del s.name # 如果刪除實例的name屬性 >>> print(s.name) # 再次調(diào)用s.name,由于實例的name屬性沒有找到,類的name屬性就顯示出來了 Student類屬性可以用作一個類所有對象的全局變量,在統(tǒng)計一些數(shù)據(jù)的時候特別有用。
使用__slots__
python中,動態(tài)綁定允許我們在程序運行的過程中動態(tài)給實例或class加上屬性和方法。但是,如果我們想要限制實例的屬性,可以定義一個特殊的變量__slots__,來限制該class實例能添加的屬性。
需要注意的是,__slots__定義的屬性只能對當前類起作用,對繼承的子類是不起作用的。但是,如果子類也定義了__slots__,子類允許定義的屬性就是自身的加上父類的。
使用@propert
在綁定屬性時,為了避免參數(shù)值出現(xiàn)不符合常理的情況,我們需要對它進行限制。一種方法是將類的變量類型設(shè)置為私有,利用類的方法來訪問;但是對于一些情況來說這種方式顯得有些麻煩。
要想達到既能檢查參數(shù),又可以用類似屬性這樣簡單的方式來訪問類的變量的目的,可以使用python內(nèi)置的@property裝飾器,將一個方法編程屬性來調(diào)用。如:
把一個getter方法變成屬性,只需要加上@property就可以了;此外,@property又創(chuàng)建了另一個裝飾器@score.setter,負責把一個setter方法變成屬性賦值。于是我們就有了一個可控的屬性操作。
>>> s = Student() >>> s.score = 60 # OK,實際轉(zhuǎn)化為s.set_score(60) >>> s.score # OK,實際轉(zhuǎn)化為s.get_score() 60 >>> s.score = 9999 Traceback (most recent call last):... ValueError: score must between 0 ~ 100!通過這個裝飾器,我們還可以只定義getter方法,實現(xiàn)只讀屬性。
多重繼承
應(yīng)用到多重繼承的設(shè)計通常被稱為MixIn。MixIn的目的是給一個類增加多個功能,這樣在設(shè)計類的時候,我們優(yōu)先考慮通過多重繼承來組合多個MixIn的功能。這樣一來,我們不需要復(fù)雜而龐大的繼承鏈,只要選擇組合不同的類的功能,就可以快速構(gòu)造出所需的子類。
需要注意的是,只允許單一繼承的語言不能使用MixIn設(shè)計。
定制類
python的class還有許多有特殊作用的函數(shù),可以幫助我們定制類。
__ str __
打印一個類的實例時,若是想要按照我們的想法打印,只需要定義__str__方法。如:
>>> class Student(object): ... def __init__(self, name): ... self.name = name ... def __str__(self): ... return 'Student object (name: %s)' % self.name ... >>> print(Student('Michael')) Student object (name: Michael)但是若想直接敲變量不用print,這時候直接顯示變量調(diào)用的不是__str__(),而是__repr__()。兩者的區(qū)別是前者返回用戶看到的字符串,而后者返回程序開發(fā)者看到的字符串,即后者是為調(diào)試服務(wù)的。
解決的方法就是再定義一個__repr__(),可以直接用賦值語句將__str__()賦給它。
__ iter __
如果一個類想被用于for···in···循環(huán),就必須實現(xiàn)__iter__()方法,該方法返回一個迭代對象,然后python的for循環(huán)就會不斷調(diào)用該迭代對象的__next__()方法拿到下一個循環(huán)值,知道遇到StopInteration錯誤退出循環(huán)。如:
class Fib(object):def __init__(self):self.a, self.b = 0, 1 # 初始化兩個計數(shù)器a, bdef __iter__(self):return self # 實例本身就是迭代對象,故返回自己def __next__(self):self.a, self.b = self.b, self.a + self.b # 計算下一個值if self.a > 100000: # 退出循環(huán)的條件return StopInteration()return self.a # 返回下一個值若將fib實例用于for循環(huán):
>>> for n in Fib(): ··· print(n) ··· 1 1 2 3 5 ··· 46368 75025__ getitem __
Fib實例雖然能作用于for循環(huán),看起來和list有點像,但是把它當作list來使用還是不行的。要表現(xiàn)得像list那樣按照下標取出元素,需實現(xiàn)__getitem__()方法。 ``` class Fib(object): def __getitem__(self, n): a, b = 1, 1 for x in range(n): a, b = b, a + b return a ``` 如果要實現(xiàn)list的切片方法,需要作判斷傳入的參數(shù)是一個int還是一個slice對象。如果把對象看成dict,getitem的參數(shù)也可能是一個可以作key的object,例如str。 與之對應(yīng)的是setitem()方法,把對象視作list或dict來對集合賦值。最后還有一個delitem()`方法,用于刪除某個元素。
總之,通過上面的方法,我們自己定義的類表現(xiàn)得和python自帶的list、tuple和dict沒什么區(qū)別,這完全歸功于動態(tài)語言的“鴨子類型”,不需要強制繼承某個接口。
__ getattar __
正常情況下,當我們調(diào)用類的方法或?qū)傩詴r,如果不存在就會報錯。寫一個__getattar__()方法可以動態(tài)返回一個屬性。
只有在沒有找到屬性的情況下,才調(diào)用__getattar__。此外如果調(diào)用如s.abc都會返回None,這是因為我們定義的__getattar__默認返回就是None。要讓class只響應(yīng)特定的幾個屬性,我們就要按照約定,拋出AttributeError的錯誤。
這實際上可以把一個類的所有屬性和方法全部動態(tài)化處理,不需要任何特殊手段。這種完全動態(tài)調(diào)用的特性可以針對完全動態(tài)的情況作調(diào)用。
現(xiàn)在很多網(wǎng)站都搞REST API,比如新浪微博、豆瓣啥的,調(diào)用API的URL類似:
http://api.server/user/friends
http://api.server/user/timeline/list
如果要寫SDK,給每個URL對應(yīng)的API都寫一個方法,那得累死,而且,API一旦改動,SDK也要改。
利用完全動態(tài)的__getattar__,可以寫一個鏈式調(diào)用。
還有些REST API會把參數(shù)放到URL中,比如GitHub的API:
GET /users/:user/repos調(diào)用時,需要把:user替換為實際用戶名:
Chain().users('michael').repos__ call __
一個對象實例可以有自己的屬性和方法,當我們調(diào)用實例方法時,我們用instance.method()來調(diào)用。如果要想在實例本身上調(diào)用,只需要定義一個__call__方法。
class Student(object):def __init__(self, name):self.name = namedef __call__(self):print('My name is %s.' % self.name)調(diào)用方法如下:
>>> s = Student('Michael') >>> s() # self參數(shù)不要傳入 My name is Michael.__call__()還可以定義參數(shù)。對實例進行直接調(diào)用就好比對一個函數(shù)進行調(diào)用一樣,所以你完全可以把對象看成一個函數(shù),把函數(shù)看成對象。
但是如何判斷一個變量時對象還是函數(shù)呢?其實,更多的時候我們需要判斷的是一個對象是否能被調(diào)用,能被調(diào)用的對象就是一個Callable對象。通過callable()函數(shù),我們就可以判斷一個對象是否是“可調(diào)用”對象。
使用枚舉類
當我們需要定義大量常量時,可以為這樣的枚舉類型定義一個class類型,然后,每個常量都是class的一個唯一實例。
from enum import EnumMonth = Enum('Month', ('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'))這樣我們就獲得了Month類型的枚舉類,可以直接使用Month.Jan來引用一個常量,或者枚舉它的所有成員。
for name, member in Month.__members__.items():print(name, '=>', member, ',', member.value)value屬性是自動賦給成員的int常量,默認從1開始計數(shù)。
如果需要更精確地控制枚舉類型,可以從Enum派生出自定義類:
from enum import Enum, unique@unique class Weekday(Enum):Sun = 0 # Sun的value被設(shè)定為0Mon = 1Tue = 2Wed = 3Thu = 4Fri = 5Sat = 6@unique裝飾器可以幫助我們檢查保證沒有重復(fù)。訪問這些枚舉類型可以有多種方式,既可以用成員名稱引用枚舉變量,又可以直接根據(jù)value的值獲得枚舉類型。
總結(jié):Enum可以把一組相關(guān)常量定義在一個class中,且class不可變,而且成員可以直接比較。
使用元類
type()
動態(tài)語言和靜態(tài)語言最大的不同,就是函數(shù)和類的定義,不是編譯時定義的,而是運行時動態(tài)創(chuàng)建的。
比方說我們要定義一個Hello的class,就寫一個hello.py的模塊。
class Hello(object):def hello(self, name = 'world'):print('Hello, %s.', % name)當Python解釋器載入hello模塊時,就會依次執(zhí)行該模塊的所有語句,執(zhí)行結(jié)果就是動態(tài)創(chuàng)建除一個Hello的class對象,測試如下:
>>> from hello import Hello >>> h = Hello() >>> h.hello() Hello, world. >>> print(type(Hello)) <class 'type'> >>> print(type(h)) <class 'hello.Hello'>type()函數(shù)可以查看一個類型或變量的類型,Hello是一個class,它的類型就是type,而h是一個實例,它的類型就是class Hello。
我們說class的定義是運行時創(chuàng)建的,而創(chuàng)建class的方法就是使用type()函數(shù)。
type()函數(shù)既可以返回一個對象的類型,又可以創(chuàng)建新的類型,比如,我們可以通過type()函數(shù)創(chuàng)建出Hello類,而無需通過class Hello(Object)...的定義:
>>> def fn(self, name = 'world'): #先定義函數(shù) ... print('Hello, %s' % name) ... >>> Hello = type('Hello', (Object), dict(hello=fn)) #創(chuàng)建Hello class >>> h = Hello() >>> h.Hello() Hello, world >>> print(type(Hello)) <class 'type'> >>> print(type(h)) <class '_main_.Hello'>要創(chuàng)建一個class對象,type()函數(shù)依次傳入3個參數(shù):
通過type()函數(shù)創(chuàng)建的類和直接寫class是完全一樣的,因為Python解釋器遇到class定義時,僅僅是掃描一下class定義的語法,然后調(diào)用type()函數(shù)創(chuàng)建出class。
正常情況下,我們都用class Xxx...來定義類,但是,type()函數(shù)也允許我們動態(tài)創(chuàng)建出類來,也就是說,動態(tài)語言本身支持運行期動態(tài)創(chuàng)建類,這和靜態(tài)語言有非常大的不同,要在靜態(tài)語言運行期創(chuàng)建類,必須構(gòu)造源代碼字符串再調(diào)用編譯器,或者借助一些工具生成字節(jié)碼實現(xiàn),本質(zhì)上都是動態(tài)編譯,會非常復(fù)雜。
metaclass 元類
除了使用type()動態(tài)創(chuàng)建類外,要控制類的創(chuàng)建行為,還可以使用metaclass。
當我們定義了類以后,就可以根據(jù)這個類創(chuàng)建出實例,所以:先定義類,然后創(chuàng)建實例。
但是如果我們想創(chuàng)建出類呢?那就必須根據(jù)metaclass創(chuàng)建出類,所以:先定義metaclass,然后創(chuàng)建類。
連接起來就是:先定義metaclass,就可以創(chuàng)建類,最后創(chuàng)建實例。
所以metaclass允許你創(chuàng)建類或修改類。
后面的……沒看懂,先不記了。
轉(zhuǎn)載于:https://www.cnblogs.com/ChanWunsam/p/10018246.html
總結(jié)
以上是生活随笔為你收集整理的python学习笔记_序的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。