Python面向对象、魔法方法
文章目錄
- 寫在篇前
- 封裝
- 繼承
- 單繼承
- Mixin
- 抽象
- 多態(tài)
- 特殊方法&屬性
- 特殊屬性
- 魔法方法
- 輔助知識
- OOP實用函數(shù)
- 迭代器生成器
寫在篇前
??OOP(Object Oriented Programming),即面向?qū)ο蟮某绦蛟O(shè)計,不同于傳統(tǒng)的面向過程的程序設(shè)計,它大大地降低了軟件開發(fā)的難度,使編程就像搭積木一樣簡單,是當(dāng)今編程以及模式設(shè)計一股勢不可擋的潮流。OOP達(dá)到了軟件工程的三個主要目標(biāo):重用性、靈活性和擴(kuò)展性。面向?qū)ο缶幊痰幕A(chǔ)就是類,所謂“類生一,一生二,二生三,三生萬物”,類是對現(xiàn)實世界事物的抽象,一個類包括了現(xiàn)實世界中一組對象的方法和屬性。
封裝
??封裝就是把數(shù)據(jù)和操作數(shù)據(jù)的方法綁定起來,通過已定義的接口實現(xiàn)對數(shù)據(jù)的訪問以及修改,屏蔽繁雜的技術(shù)細(xì)節(jié)。編寫一個類就是對數(shù)據(jù)和數(shù)據(jù)操作的封裝,可以說,封裝就是隱藏一切可隱藏的東西,只向外界提供最簡單的編程接口。如下示例代碼,即是封裝了一個Student類,包含類屬性Student_number,對象屬性__id,name,score,以及一個打印學(xué)生成績的方法print_info(),這樣實例化對象之后沒就可以直接調(diào)用print_info()方法獲取學(xué)生信息,而無需關(guān)注其實現(xiàn)細(xì)節(jié)。
#! /usr/bin/python # _*_ coding: utf-8 _*_ __author__ = 'Jeffery'class Student(object):"""Student class"""# student_number是一個類屬性, 可通過類訪問,也可通過對象訪問,所有對象共享該表量student_number = 0# 方法可有默認(rèn)參數(shù)、可變參數(shù)、關(guān)鍵字參數(shù)和命名關(guān)鍵字參數(shù)def __init__(self, sid, name, score):"""Student class init method"""Student.student_number += 1self.__id = sidself.name = nameself.score = scoredef print_info(self):"""get Student info"""print('%s: %s' % (self.name, self.score), end=' ')bart = Student('001', 'Bart Simpson', 59) bart.print_score() print(bart.student_number) # 1 print(Student.student_number) # 1lisa = Student('002', 'Lisa Simpson', 87) lisa.print_score() print(bart.student_number) # 2 print(Student.student_number) # 2繼承
??繼承是從已有類得到繼承信息創(chuàng)建新類的過程,一方面可以繼承父類所有的屬性和方法,另一方面可以增加或則重寫父類方法,以適應(yīng)具體業(yè)務(wù)流程。
單繼承
?這里緊接上面的例子,創(chuàng)建一個MasterStudent類,使其繼承Student類,這樣子類并擁有了父類的所有方法和屬性(實際上父類__init__()方法不會被繼承,這一點放到后面再討論);另外再增加新的屬性major和新的方法change_major()。
class MasterStudent(Student):"""MasterStudent class"""def __init__(self, sid, name, score, major):"""Student class init method"""super().__init__(sid, name, score)self.major = majordef print_info(self):"""get Student score"""super().print_info() # 調(diào)用父類方法print('and majoring in %s' % self.major)def change_major(self, major):"""change major"""self.major = majorif __name__ == '__main__':ms = MasterStudent('001', 'jeffery', '90', 'bio')ms.print_info()??關(guān)于繼承,需要注意的一個問題是構(gòu)造方法(__new__)和初始化方法(__init__)的執(zhí)行順序:
class A(object):def __new__(cls, *args, **kwargs):print('A __new__')return super().__new__(cls, *args, **kwargs)def __init__(self):print('A __init__')class B(A):def __new__(cls, *args, **kwargs):print('B __new__')return super().__new__(cls, *args, **kwargs)def __init__(self):print('B __init__')super().__init__() # 輸出結(jié)果 B __new__ A __new__ B __init__ A __init__Mixin
?Mixin是python中多繼承的一種方案,但是事實上它和一般意義上的多繼承(其他編程語言如C++)是有區(qū)別的,它更像是Java中的接口,但是是提供默認(rèn)實現(xiàn)的接口(Java接口默認(rèn)不提供實現(xiàn))。舉個例子,民航飛機(jī)是一種交通工具,對于土豪們來說直升機(jī)也是一種交通工具。對于這兩種交通工具,它們都有一個功能是飛行,但是轎車沒有。所以,我們不可能將飛行功能寫在交通工具這個父類中。但是如果民航飛機(jī)和直升機(jī)都各自寫自己的飛行方法,又違背了代碼盡可能重用的原則(如果以后飛行工具越來越多,那會出現(xiàn)許多重復(fù)代碼)。怎么辦,那就只好讓這兩種飛機(jī)同時繼承交通工具以及飛行器兩個父類,這樣就出現(xiàn)了多重繼承。這時又違背了繼承必須是”is-a”關(guān)系。這個難題該怎么破?
?下面,我們用python實現(xiàn)一下:
class Transportation(object):"""交通工具的基類"""passclass FlyMixin(object):"""Mixin 類,告訴其他人,這是一個"接口",能夠擴(kuò)充類的某一個功能"""def fly_able(self):passclass Airliner(Transportation, FlyMixin):passclass Helicopter(Transportation, FlyMixin):pass?Mixin其實應(yīng)用非常廣泛,在django中,如某一個頁面需要登陸才允許訪問,則可以讓相應(yīng)View繼承LoginRequiredMixin
class LogoutView(LoginRequiredMixin, View):"""比如退出登陸,當(dāng)然禁止沒有登陸的用戶執(zhí)行該操作"""def get(self, request):logout(request)return HttpResponseRedirect(reverse("index"))?看完代碼,你會很明顯的發(fā)現(xiàn)FlyMixin不就是一個普通類嗎?所謂Mixin不就是多繼承嗎?對,但是我們一般約定以Mixin結(jié)尾的類作為接口類,用來靈活擴(kuò)充類的功能。
抽象
?抽象類是一個特殊的類,它從一堆類中抽取相同的內(nèi)容,組建成一個新的類。它的特殊之處在于只能被繼承,不能被實例化。比如三角形類、正方形類、菱形類都是屬于圖形類,都應(yīng)該有邊長屬性,求面積、求周長的方法。下面舉一個實例,說明python中抽象類的使用。
class A(object):"""抽象類不能被實例化"""__metaclass__ = abc.ABCMeta@abc.abstractmethoddef load(self, _input):pass@abc.abstractmethoddef save(self, output, data):passclass B(A):def load(self, _input):return _input.read()def save(self, output, data):return output.write(data)if __name__ == '__main__':print(issubclass(B, A))print(isinstance(B(), A))print(A.__subclasses__())?還有一種通過__subclasshook__魔法方法的用法我在這也提一下:
class C(object, metaclass=abc.ABCMeta):@abc.abstractmethoddef say(self):pass@classmethoddef __subclasshook__(cls, _cls):"""該方法意味著只要一個類實現(xiàn)了他的抽象方法,就會被認(rèn)為是該類的子類"""print('class invoke ********')if cls is C:if any("say" in B.__dict__ for B in _cls.__mro__):return Truereturn NotImplementedclass D(object):def say(self):print('function say implemented in subclass')print(issubclass(D, C)) print(isinstance(D(), C)) print(D.__dict__) print(C.__subclasshook__(D))多態(tài)
?多態(tài)按字面的意思就是多種形態(tài)。當(dāng)類之間存在層次結(jié)構(gòu),并且類之間是通過繼承關(guān)聯(lián)時,就會用到多態(tài),多態(tài)意味著調(diào)用成員函數(shù)時,會根據(jù)調(diào)用函數(shù)的對象的類型來執(zhí)行不同的函數(shù)。
class Car:def __init__(self, name):self.name = namedef drive(self):raise NotImplementedError("Subclass must implement abstract method")def stop(self):raise NotImplementedError("Subclass must implement abstract method")class SportsCar(Car):def drive(self):return 'SportsCar driving!'def stop(self):return 'SportsCar breaking!'class Truck(Car):def drive(self):return 'Truck driving slowly because heavily loaded.'def stop(self):return 'Truck breaking!'cars = [Truck('Bananatruck'),Truck('Orangetruck'),SportsCar('Z3')]for car in cars:print(car.name + ': ' + car.drive())?但是,如果你仔細(xì)看完上面的代碼,似乎你覺得并沒有什么特殊之處。因為,Python 沒有覆寫(override)的概念,也就是說嚴(yán)格來講,Python 并不支持多態(tài)。相反,個人覺得,上述代碼表現(xiàn)的行為,用帶太語言編程里的鴨子類型也許更合適。
?在動態(tài)語言中經(jīng)常提到鴨子類型,所謂鴨子類型就是:If it walks like a duck and quacks like a duck, it must be a duck。鴨子類型是編程語言中動態(tài)類型語言中的一種設(shè)計風(fēng)格,一個對象的特征不是由父類決定,而是通過對象的方法決定的。
特殊方法&屬性
??python類中有很多特殊方法、屬性或則約定,熟悉這些特性,能讓你編寫更加健壯的代碼,主要如下:
- __XXX
?該類變量是一個私有變量(private),只有內(nèi)部可以訪問,外部不能訪問。
- _XXX_
?該類變量是特殊變量,特殊變量是可以直接訪問的,不是private。
變量。
- _XXX
?該類變量外部是可以訪問的,但是,按照約定俗成的規(guī)定,當(dāng)你看到這樣的變量時,意思
就是,“雖然我可以被訪問,但是,請把我視為私有變量,不要隨意訪問”。
-
XXX_
定義的一個變量和某個保留關(guān)鍵字沖突,這時候可以使用單下劃線作為后綴, 以示區(qū)分。
特殊屬性
# 用類名和對象名效果會不同,請自己嘗試MasterStudent(obj).__doc__ # 類注釋 MasterStudent(obj).__name__ # 類名(可能帶命名空間) MasterStudent(obj).__bases__ # 直接父類 MasterStudent(obj).__dict__ # 類信息,字典類型 MasterStudent(obj).__class__ # 一般實例調(diào)用__class__屬性時會指向該實例對應(yīng)的類,然后可以再去調(diào)用其它類屬性(注意是類屬性,不是實例屬性) MasterStudent(obj).__module__ # 模塊名 MasterStudent(obj).__mro__ # 多重繼承init時的解析順序,(<class '__main__.MasterStudent'>, <class '__main__.Student'>, <class 'object'>) MasterStudent(obj).__qualname__ # qualified refernece name,見PEP3155 MasterStudent(obj).__slots__ # 見下面解釋 MasterStudent(obj).method.__annotations__ # 注解,見下面解釋-
__slot__
?用于限制類中的屬性(只能是這些屬性),使用__slots__要注意,__slots__定義的屬性僅對當(dāng)前類實例起作用,對繼承的子類是不起作用的。如果父類與子類中都定義了__slot__,則結(jié)果為父類與子類__slot__的合集。
-
__annotations__
?__annotations__是python3引入的類型檢查機(jī)制,是一種推薦性寫法,如果要強(qiáng)制runtime進(jìn)行類型檢查,可以使用enforce包,并用裝飾器@enforce.runtime_validation裝飾相應(yīng)的方法。查看annotations使用MasterStudent.__init__.__annotations__
魔法方法
-
__init__(),__del__(),__new__()
?這個方法是一個類的構(gòu)造函數(shù),與之對應(yīng)的__del__()是析構(gòu)函數(shù),通過此方法我們可以定義一個對象的初始操作。但實際上,新式類的__new__()才是真正的初始化函數(shù)。
?首先,我們驗證一下這三個方法的執(zhí)行順序:
class A(object):def __init__(self):print('__init__')def __new__(cls, *args, **kwargs):print('__new__')return super().__new__(cls, *args, **kwargs) # cls表示一個類,一個當(dāng)前要被實例化的類,參數(shù)由py解釋器自動提供def __del__(self):print('__del__')a = A() print('do something') # __new__ # __init__ # do something # __del__# 實際上,__new__()負(fù)責(zé)創(chuàng)建一個對象,__init__負(fù)責(zé)定制化該對象,即是在對象創(chuàng)建好之后初始化變量?既然知道了__new__()方法,我們是不是可以考慮一下,如何應(yīng)用它呢?最常見的就是單例模式了,下面給出實現(xiàn)實例。
class Singleton(object):_instance = Nonedef __new__(cls, *args, **kwargs):"""注意這實際上是一個類方法, cls 表示當(dāng)前類:param args::param kwargs::return:"""if cls._instance is None:cls._instance = super().__new__(cls, *args, **kwargs)return cls._instances1 = Singleton() s2 = Singleton() if s1 is s2:print('yeah') -
__repr__(),__str__()
?分別為str()、repr()函數(shù)提供接口,打印的更好看。
class Pair:def __init__(self, x, y):self.x = xself.y = ydef __repr__(self):return 'Pair({0.x!r}, {0.y!r})'.format(self)def __str__(self):return '({0.x!s}, {0.y!s})'.format(self)def __eq__(self, other):if self.x == other.x and self.y == other.y:return Trueelse:return Falsep = Pair(3, 4) pp = eval(repr(p)) if pp == p:print('true') -
__getitem__(),__setitem__(),__delitem__()
?__getitem__()將對象當(dāng)作list使用,如obj = ACLASS(), obj_first=obj[0]
class A(object):def __init__(self):self['B'] = "BB"self['D'] = "DD"self.jj = 'jj'del self['D']def __setitem__(self, name, value):"""每當(dāng)屬性被賦值的時候都會調(diào)用該方法"""print("__setitem__:Set %s Value %s" % (name, value))self.__dict__[name] = valuedef __getitem__(self, name):"""當(dāng)訪問屬性時會調(diào)用該方法"""print("__getitem__:Try to get attribute named '%s'" % name)if hasattr(self, name):return getattr(self, name)return Nonedef __delitem__(self, name):"""當(dāng)刪除屬性時調(diào)用該方法"""print("__delitem__:Delect attribute '%s'" % name)del self.__dict__[name]print(self.__dict__)if __name__ == "__main__":X = A()b = X['jj']print(b) -
__getattr__(),__setattr__(),__delattr__()
?為getattr(),del obj.attr,setattr(),hasattr()提供接口,這是魔法方法中的一個難點,并且發(fā)現(xiàn)不少其他博客居然講錯了,但也許是py3和py2的差別,請參考官方文檔-py3屬性設(shè)置。需要銘記實例對象屬性尋找的順序如下:
- 首先訪問 __getattribute__() 魔法方法(隱含默認(rèn)調(diào)用,無論何種情況,均會調(diào)用此方法
- 去實例對象t中查找是否具備該屬性: t.__dict__ 中查找,每個類和實例對象都有一個 __dict__ 的屬性
- 若在 t.__dict__ 中找不到對應(yīng)的屬性, 則去該實例的類中尋找,即 t.__class__.__dict__
- 若在實例的類中也招不到該屬性,則去父類中尋找,即 t.__class__.__bases__.__dict__中尋找
- 若以上均無法找到,則會調(diào)用 __getattr__ 方法,執(zhí)行內(nèi)部的命令(若未重載 __getattr__ 方法,則直接報錯:AttributeError) #! /usr/bin/python # _*_ coding: utf-8 _*_class Test(object):def __init__(self, count):self.count = countdef __getattr__(self, name):print('__getattr__')raise AttributeError('no such field')def __getattribute__(self, item):"""一旦重載了 __getattribute__() 方法, 如果找不到屬性,則必須要手動加入第④步, 否則無法進(jìn)入到 第⑤步 (__getattr__)的:param item::return:"""print('__getattribute__')return super().__getattribute__(item)def __setattr__(self, name, value):print('__setattr__')super().__setattr__(name, value)def __delattr__(self, name):print('__delattr__')super().__delattr__(name)# 希望讀者好好體會下面測試代碼的輸出,并思考為什么 t = Test(3) print(t.count) print('*'*40)t.x = 10 # __setattr__ a = t.x # __getattribute__ print(a) # 10print('**', t.__dict__) # __getattribute__ # ** {'x': 10}if 'x' in t.__class__.__bases__[0].__dict__:print('** x in base class dict') else:print('** x not in base class dict') # __getattribute__ # ** x not in base class dictdel t.x # __delattr__a = t.x # __getattribute__ # __getattr__
-
__gt__(),__lt__(),__eq__(),__ne__(),__ge__()
?定義對象比較方法,為關(guān)系運算符>,>=,<,==等提供接口,這就類似于C++里面的運算符重載。
class SavingsAccount(object):def __init__(self, name, pin, balance=0.0):self._name = nameself._pin = pinself._balance = balancedef __lt__(self, other):return self._balance < other._balances1 = SavingsAccount("Ken", "1000", 0) s2 = SavingsAccount("Bill", "1001", 30) print(s1 < s2)?其實,類似的數(shù)值運算的魔法方法非常非常之多,好吧,我真的用了兩個,現(xiàn)在是三個非常來形容,以下列出部分,希望讀者根據(jù)上面的例子舉一反三:
class Point(object):def __init__(self):self.x = -1self.y = 5# 正負(fù),取反、絕對值def __pos__(self):passdef __neg__(self):passdef __invert__(self):passdef __abs__(self):pass# 加減乘除、取余,指數(shù)def __add__(self, other):passdef __sub__(self, other):passdef __divmod__(self, other):passdef __mul__(self, other):passdef __mod__(self, other):passdef __pow__(self, power, modulo=None):pass# 邏輯運算符def __and__(self, other):passdef __or__(self, other):passdef __xor__(self, other):pass# 位移def __lshift__(self, other):passdef __rshift__(self, other):pass# 賦值語句def __iadd__(self, other):passdef __imul__(self, other):passdef __isub__(self, other):passdef __idiv__(self, other):passdef __imod__(self, other):passdef __ipow__(self, other):passdef __ilshift__(self, other):passdef __irshift__(self, other):pass -
__get__(),__set__(),__delete__()
?實現(xiàn)了這些方法的類,稱之為描述器類,這里不展開講,僅給出一個實例
class Integer:"""資料描述器和非資料描述器會產(chǎn)生不一樣的優(yōu)先級,"""def __init__(self, name):self.name = namedef __get__(self, instance, cls):if instance is None:return selfelse:return instance.__dict__[self.name]def __set__(self, instance, value):if not isinstance(value, int):raise TypeError('Expected an int')instance.__dict__[self.name] = valuedef __delete__(self, instance):del instance.__dict__[self.name]class Point:x = Integer('x')y = Integer('y')def __init__(self, x, y):"""特別注意因為Interger是資料描述器,所以self.x 其實指的就是x = Integer('x')"""self.x = xself.y = yp = Point(10, 11) -
__iter__()
? 為for … in提供接口,返回一個迭代對象,并調(diào)用對象next()方法,直到StopIteration。以下這個例子來源廖雪峰python教程
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 > 10: # 退出循環(huán)的條件raise StopIteration()return self.a # 返回下一個值for i in Fib():print(i) -
__dir__()
?為dir()函數(shù)提供接口,查看一個對象中有哪些屬性和方法, 返回值應(yīng)該是iterable。【關(guān)于Iterable放在后面討論】,如返回一個list,一般而言我們不去重寫這個方法。
首先我們看看dir函數(shù):
>>> res = dir(str(123)) >>> print(type(res)) <class 'list'> >>> print(res) ['__add__', '__class__', '__contains__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getnewargs__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mod__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__rmod__', '__rmul__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'capitalize', 'casefold', 'center', 'count', 'encode', 'endswith', 'expandtabs', 'find', 'format', 'format_map', 'index', 'isalnum', 'isalpha', 'isdecimal', 'isdigit', 'isidentifier', 'islower', 'isnumeric', 'isprintable', 'isspace', 'istitle', 'isupper', 'join', 'ljust', 'lower', 'lstrip', 'maketrans', 'partition', 'replace', 'rfind', 'rindex', 'rjust', 'rpartition', 'rsplit', 'rstrip', 'split', 'splitlines', 'startswith', 'strip', 'swapcase', 'title', 'translate', 'upper', 'zfill']我們自定義一個__dir__()方法試試:
class A(object):def __dir__(self):print('不給你看這個函數(shù)有什么屬性和方法,哼、、哼哼')return []if __name__ == "__main__":a = A()result = dir(a)print(result)# 不給你看這個函數(shù)有什么屬性和方法,哼、、哼哼 # <class 'list'> -
__call__()
?假設(shè)有對象obj, 則obj()可以調(diào)用__call__()方法
class A(object):def _pre_process(self):print('preoprocess data')def __call__(self, *args, **kwargs):self._pre_process()print('object called over')A()() # 調(diào)用了類中__call__()中的邏輯 -
數(shù)據(jù)類型轉(zhuǎn)換
# 類似數(shù)值計算的魔法方法,實現(xiàn)以下方法就可以實現(xiàn)自定義的數(shù)據(jù)類型轉(zhuǎn)換 class A(object):def __init__(self, num):self.x = numdef __int__(self):passdef __float__(self):passdef __long__(self):passdef __complex__(self):passdef __bytes__(self):passdef __oct__(self):passdef __hex__(self):passdef __str__(self):passdef __nonzero__(self):"""這里僅僅實現(xiàn)bool轉(zhuǎn)換,其他的讀者可舉一反三"""if self.x != 0:return Trueelse:return Falseif __name__ == '__main__':print(bool(A(5))) -
__enter__(),__exit__()
?這兩個魔法方法使對象是用于With會話管理,一般用于需要打開關(guān)閉的場景,如文件讀寫、數(shù)據(jù)庫連接。
#! /usr/bin/python # _*_ coding: utf-8 _*_class OpenTextFile(object):"""實現(xiàn)打開一個文件使用with,這里僅僅是個例子因為 open()本身就可以實現(xiàn) with context"""def __init__(self, file_path):self.file = file_pathdef __enter__(self):self.f = open(self.file, 'r', encoding='utf-8')return self.fdef __exit__(self, exc_type, exc_val, exc_tb):self.f.close()with OpenTextFile('./with_context.py') as f:print(f.readlines()) -
其他魔法方法
# 獲取對象大小 __sizeof__()# 獲取對象哈希值 __hash__()# 用于對象序列化和反序列化 __reduce__() __reduce_ex__()__instancecheck__(self, instance) # 檢查一個實例是不是你定義的類的實例 __subclasscheck__(self, subclass) # 檢查一個類是不是你定義的類的子類__contains__(self,value) # 使用in操作測試關(guān)系時 __concat__(self,value) # 連接兩個對象時 __index__(self) # 對象被當(dāng)作list索引時
summary:
__getattr__(self, name): 訪問不存在的屬性時調(diào)用
__getattribute__(self, name):訪問存在的屬性時調(diào)用(先調(diào)用該方法,查看是否存在該屬性,若不存在,接著去調(diào)用①)
__setattr__(self, name, value):設(shè)置實例對象的一個新的屬性時調(diào)用
__delattr__(self, name):刪除一個實例對象的屬性時調(diào)用
輔助知識
OOP實用函數(shù)
- type() 查看對象類型
- issubclass() 查看一個對象是否屬于某個類的子類
- is vs == vs isinstance 前者查看兩個對象是否嚴(yán)格相同,后者查看某對象是否屬于某類
- id用于獲取對象的內(nèi)存地址
迭代器生成器
- 列表生成式
??List Comprehensions, 是python內(nèi)置的用來生成list的一種快捷高效的方式。
# 舉例 [x * x for x in range(1, 11)] [x * x for x in range(1, 11) if x % 2 == 0]- 生成器
??Generator,是列表生成式的一種優(yōu)化方案,列表生成式的list會直接放在內(nèi)存中,因此其大小必然會受到內(nèi)存的限制;而生成器就是為了解決這種資源耗費的情況,能夠做到先定義,邊循環(huán)邊計算。
# 注意區(qū)分生成器和列表生成式的定義方式 # 生成器是用()、列表生成式是用[]>>> g = (x * x for x in range(10)) >>> g <generator object <genexpr> at 0x0000020A6184D0A0># 如果要一個一個打印出來,可以通過next()或則__next__()獲得generator的下一個返回值 >>> next(g) 0 >>> next(g) 1 >>> next(g) 4 >>> next(g) 9 >>> g.__next__() 16# 在實際編程中,我們一般這樣使用 for n in g:print(n)??在實際應(yīng)用中,我們經(jīng)常會借助yield關(guān)鍵字構(gòu)造生成器函數(shù),如斐波那契函數(shù):
def fib(max):n, a, b = 0, 0, 1while n < max:yield ba, b = b, a + bn = n + 1>>> g = fib(6) >>> g <generator object fib at 0x0000020A619C6BF8> >>> next(g) 1 >>> next(g) 1 >>> next(g) 2 >>> next(g) 3 >>> next(g) 5 >>> next(g) 8 >>> next(g) Traceback (most recent call last):File "<stdin>", line 1, in <module> StopIteration- 迭代器
??可以直接作用于for循環(huán)的對象統(tǒng)稱為可迭代對象(Iterable),包括集合數(shù)據(jù)類型(如list、tuple、dict、set、str)和 generator(生成器、帶yield的generator function)。但是集合數(shù)據(jù)類型和generator有一個很大的區(qū)別:generator可以使用next()不斷調(diào)用,直至StopIteration。在python中,可以被next()函數(shù)調(diào)用并不斷返回下一個值的對象稱為迭代器:Iterator。
?
Tips: 如果一個對象是 Iterator(迭代器), 那么它必然是一個Iterable(可迭代對象);而Iterable不一定是一個Iterator,但是Iterable可以通過iter()函數(shù)變成Iterator;特殊的是generator既是Iterable又是Iterator。
>>> from collections import Iterator >>> isinstance((x for x in range(10)), Iterator) True>>> isinstance([], Iterator) False >>> isinstance('abc', Iterator) False>>> isinstance(iter([]), Iterator) True >>> isinstance(iter('abc'), Iterator) True總結(jié)
以上是生活随笔為你收集整理的Python面向对象、魔法方法的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python函数详解
- 下一篇: python装饰器详细剖析