1. 程式人生 > >[Python]第九章 魔法方法、特性和迭代器

[Python]第九章 魔法方法、特性和迭代器

文章目錄


類似__future__以兩個下劃線開頭和結尾的名稱擁有特殊意義,因此在程式建立中不能使用這樣的名稱,這樣的名稱中,很大一部分是特殊名稱
魔法名稱: init
特性property:以前通過魔法方法處理,現在通過函式property處理
迭代器iterator:使用魔法方法__iter__,可讓其用於for迴圈之中

9.1如果你使用的不是Python3

Python2有些功能(如特性和函式super)不適合舊式類,如果要讓類是最新的,要麼在模組開頭包含賦值語句__metaclass__=type,要麼直接或者間接的繼承內建類object或其他新式類。
如下:

class NewStyle(object):
	more_code_here
class OldStyle:
	more_code_here

在這兩個類中,NewStyle是一個新式類,而OldStyle是一箇舊式類。如果檔案開頭包含賦值語句__metaclass__ = type,這兩個類都將是新式類。
在Python 3中沒有舊式類,因此無需顯式地繼承object或將__metaclass__設定為type。所有的類都將隱式地繼承object。如果沒有指定超類,將直接繼承它,否則將間接地繼承它。

9.2建構函式

即初始化方法,只是命名__init__,建構函式特殊之處在於,將在物件建立後自動呼叫他們,因此無需顯式呼叫

class Person:
    def __init__(self,name):
        self.name=name
p=Person('jack')#這裡直接在構造物件的時候呼叫了__init__方法
p.name
'jack'

PS:解構函式__del__

9.2.1重寫普通方法和特殊的建構函式

重寫普通方法,方法名相同

class A:
    def hello(self):
        print('hello,I am A')
class B(A):
    pass
class Son(A):
    def hello(self,name):
        self.name=name
        print('hello,I am the son of A,I am ',self.name)
>>> a=A()
>>> hello()
hello,I am A
>>>b=B()
>>>hello()
hello,I am A
>>>son=Son()
>>> son.hello('jack')
hello,I am the son of A,I am  jack

重寫建構函式時,必須呼叫超類(繼承的類)的建構函式,否則可能無法正確的初始化物件

class Bird:
    def __init__(self):
        self.hungry=True
    def eat(self):
        if self.hungry:
            print('dying...')
            self.hungry=False
        else:
            print('full')
>>>bird=Bird()
>>> bird.eat()
dying...

寫一個子類,建構函式沒有重寫父類

class SongBird(A):
    def __init__(self):
        self.sound='jiujiu'
    def sing(self):
        print(self.sound)
>>>songbird=SongBird()
>>>songbird.sing()
jiujiu
>>>songbird.eat()#eat是父類的方法,這個方法呼叫了父類的建構函式
報錯

解決方法有見如下兩節

9.2.2呼叫未關聯的超類建構函式

在子類的建構函式中新增一行程式碼:父類.__init__(self)

class SongBird(Bird):
    def __init__(self):
        Bird.__init__(self)
        self.sound='jiujiu'
    def sing(self):
        print(self.sound)
songbird=SongBird()
songbird.eat()
dying..

通過類呼叫方法(如Bird.__init__),就沒有例項與其相關聯。在這種情況下,可隨便設定引數self。這樣的方法稱為未關聯的。
通過將這個未關聯方法的self引數設定為當前例項,將使用超類的建構函式來初始化SongBird物件。這意味著將設定其屬性hungry。

9.2.3使用函式super(更好)

super函式適用於新式類,在子類的構造方法中新增super().__init__()

class SongBir(Bird):
    def __init__(self):
        super().__init__()
        self.sound='jiujiu'
    def sing(self):
        print(self.sound)
>>>songbir=SongBir()
>>>songbir.eat()
dying...

9.3元素訪問

用魔法方法建立行為類似於序列或對映的物件

9.3.1基本的序列和對映協議

序列和對映基本上是元素的集合,要實現他們的基本行為(協議),不可變物件需要實現2個方法,可變物件需要實現4個:
__len__(self):返回集合的項數,序列的元素個數,對映的鍵值對數,返回0,視為假
__getitem__(self,key):返回指定鍵對應的值,對序列來說鍵是0~n-1的整數(n是序列長度),對映的鍵可以是任何型別的
__setitem__(self,key,value):以與鍵相關的方式儲存值,以便以後使用__getitem__來獲取,適用可變物件
__delitem__(self,key):在物件組成部件使用__del__語句時被呼叫,刪除與鍵相關的值,適用於可變物件且允許被刪除
額外要求:
1.對於序列,如果鍵為負整數,應從末尾往前數。x[-n]等同於x[len(x)-n]
2.如果鍵的型別不合適(如對序列使用字串鍵),可能引發TypeError異常
3.對於序列,如果索引的型別是正確的,但不在允許的範圍內,應引發IndexError異常
CASE:建立一個無窮序列
建立一個序列,可以自己設定開始值和步長,沒有設定的話,預設以0開始步長是1
這個序列的長度是無窮的,因此不能從後往前,key不能是負數,而且得是整數
先寫一個引數檢查函式,在之後類的方法裡被呼叫即可

def check_index(key):
    if not isinstance(key,int):raise TypeError#isinstance(例項,類)判斷該例項是否屬於該類
    if key<0:raise IndexError

這個類除了構造方法,還要有__getitem__(self,key)和__setitem__(self,key,value)方法用於獲取和傳入引數

class ArithmeticSequence:
    def __init__(self,start=0,step=1):
        self.start=start#序列中的第1個值,儲存起始值
        self.step=step#兩個相鄰值的差,儲存步長值
        self.changed={}#一個字典,包含使用者修改後的值
        
    def __getitem__(self,key):#這個方法用來根據KEY返回值
        check_index(key)#首先要檢查傳入的key是否符合要求,不符合就會在檢查函數了報錯
        try:
            return self.changed[key]#如果該字典中含有該key的鍵值對,就返回值
        except KeyError:#KeyError在字典中不含該key時引發
            return self.start+key*self.step#如果不存在就返回計算後的值
        finally:#可用該子句檢查字典changed的狀態
            print(self.changed)#
    def __setitem__(self,key,value):#該方法用來傳入鍵值
        check_index(key)#先檢查傳入的key是否符合要求
        self.changed[key]=value#將鍵值傳入字典
     
>>>a=ArithmeticSequence()
>>>a[4]#按照預設起始值和步長,self.start+key*self.step=0+4*1=4 
4
>>>s=ArithmeticSequence(1,2)
>>>s[4]#按照設定起始值和步長,self.start+key*self.step=1+4*2=9
9

這兩個輸出都是呼叫了__getitem__的結果,這兩個操作都沒有改變字典

>>>s[5]=2
>>>s[5]
2

這個賦值實際上調動的是__setitem__方法,字典裡傳入新的鍵值對

>>>del s[5]
報錯AttributeError,該類沒有__del__方法,因此不能被del語句呼叫
>>>s[-1]
報錯IndexError,檢查函式中報對應的錯
>>>s['num']
報錯TypeError,檢查函式中報對應的錯

9.3.2從list、dict和str派生

通過繼承去實現序列/對映的其他有用的魔法方法,標準庫中,模組collections補充了抽象和具體的基類,但也可以繼承內建型別
CASE:帶訪問計數器的列表

class Counterlist(list):
    def __init__(self,*args):
        super().__init__(*args)
        self.counter=0
    def __getitem__(self,index):
        self.counter+=1
        return super(Counterlist,self).__getitem__(index)#呼叫父類的	 __getitem__方法
# super(Counterlist,self):找到Counterlist的父類(即list),再把Counterlist類的物件轉換為list的物件
>>>a=[11,12,1,2,3,4,5]
>>>c=Counterlist(a)
>>>c
[11, 12, 1, 2, 3, 4, 5]
>>>c[0]#呼叫了__getitem__方法
11
>>>c.counter
1
>>>c[0]+c[2]#呼叫了__getitem__方法2次
12
>>>c.counter
3

Counterlist也繼承了list的一下方法

>>>c.reverse()
>>>c
[5, 4, 3, 2, 1, 12, 11]
>>>del c[2:3]
>>>c
[5, 4, 2, 1, 12, 11]

9.4其他魔法方法

可參閱“Python Reference Manual”的Special method names一節

9.5特性

class Rectangle:
    def __init__(self):
        self.width=0
        self.height=0
    def set_size(self,size):
        self.width,self.height=size#size引數以元組的形式分別給屬性width\height賦值
    def get_size(self):
        return self.width,self.height
    
>>>r=Rectangle()
>>>r.width=5#外部直接訪問
>>>r.height=10
>>>r.get_size()
(5, 10)
>>>r.set_size((55,111))
>>>r.get_size()
(55, 111)

這裡size不是真正的屬性,get_size和set_size是假想屬性size的存取方法。如果有一天你想修改,讓size成為真正的屬性,而width和height是動態計算出來的,就需要提供用於訪問width和height的存取方法,使用這個類的程式也必須重寫。
所幸Python能夠替你隱藏存取方法,讓所有的屬性看起來都一樣。通過存取方法定義的屬性通常稱為特性(property)。

9.5.1函式property

class Rectangle:
    def __init__(self):
        self.width=0
        self.height=0
    def set_size(self,size):
        self.width,self.height=size#size引數以元組的形式分別給屬性width\height賦值
    def get_size(self):
        return self.width,self.height
    size=property(get_size,set_size)#將size定義成了特性,獲取方法在前,設定方法在後

>>>r=Rectangle()
>>>r.width=10
>>>r.height=50
>>>r.size#不需要呼叫get_size方法了,因為size已經變成了屬性(特性)
(10, 50)
>>>r.size=55,111#不需要呼叫set_size方法了,因為size已經變成了屬性(特性)
>>>r.size
(55, 111)

這裡property(fget,fset,fdel,doc)函式中,獲取(get)方法在前。當沒有引數時,預設沒有方法,不可獲取也不可設定,當只有1個引數時,只可獲取,兩個引數可獲取可設定,三個引數的最後一個引數指定用於刪除屬性的方法,四個引數的最後一個引數,指定一個文件字串。

9.5.2靜態方法和類方法

靜態方法和類方法是這樣建立的:將它們分別包裝在staticmethod和classmethod類的物件中。
靜態方法的定義中沒有引數self,可直接通過類來呼叫。
類方法的定義中包含類似於self的引數,通常被命名為cls。對於類方法,也可通過物件直接呼叫,但引數cls將自動關聯到類。
手工包裝和替換(方法繁瑣):

class MyClass:
	def smeth():
		print('This is a static method')
	smeth = staticmethod(smeth)

	def cmeth(cls):
		print('This is a class method of', cls)
	cmeth = classmethod(cmeth)

修飾器語法:

class MyClass:
	@staticmethod
	def smeth():
		print('This is a static method')
	@classmethod
	def cmeth(cls):
		print('This is a class method of', cls)
>>> MyClass.smeth()
This is a static method
>>> MyClass.cmeth()
This is a class method of <class '__main__.MyClass'>

9.5.3__getattr__、__setattr__等方法

可以攔截對物件屬性的所有訪問企圖,在舊式類中實現特性,要在屬性被訪問時執行一段程式碼,必須使用一些魔法方法:
__getattribute__(self,name):在屬性被訪問時自動呼叫(只適用新式類)
__getattr__(self,name):在屬性被訪問而物件沒有這樣的屬性時自動呼叫
__setattr__(self,name,value):試圖給屬性複製時自動呼叫
__delattr__(self,name):試圖刪除屬性時動呼叫
相比property,這些方法更棘手效率更低,但可在這些方法中編寫處理多個特性的程式碼。
__dict__參考:http://www.cnblogs.com/alvin2010/p/9102344.html

class Rectangle:
	def __init__ (self):
		self.width = 0
		self.height = 0
	def __setattr__(self, name, value):
		if name == 'size':
			self.width, self.height = value  #這是的value一定是一個元組
		else:
			self. __dict__[name] = value #儲存屬性
	def __getattr__(self, name):
		if name == 'size':
			return self.width, self.height
		else:
			raise AttributeError()
>>>rect2=Rectangle()
>>>rect2.__setattr__('size',(51,11))
>>>rect2.size
(51, 11)
>>>rect2.__setattr__('length',555)
>>>rect2.length
555
>>>rect2.color
報錯:AttributeError

9.6迭代器

iter__迭代器的基礎
9.6.1迭代器協議
迭代以為著重複多次,就像迴圈那樣。
方法__iter__返回一個迭代器,他是包含__next__方法的物件,而呼叫這個方法可不傳遞引數,迭代器盈返回下一個值,如果迭代器沒有可供返回的值,引發StopIteration異常。
內建的便利函式next(it)等同於it.__next

斐波那契數列

class Fibs:
    def __init__(self):
        self.a=0
        self.b=1
    def __next__(self):
        self.a,self.b=self.b,self.a+self.b
        return self.a
    def __iter__(self):
        return self

注意到這個迭代器實現了方法__iter__,而這個方法返回迭代器本身。在很多情況下,都在另一個物件中實現返回迭代器的方法__iter__,並在for迴圈中使用這個物件。但推薦在迭代器中也實現方__iter__(並像剛才那樣讓它返回self),這樣迭代器就可直接用於for迴圈中。
實現了方法__iter__的物件是可迭代的,而實現了方法__next__的物件
是迭代器。
fibs是一個無窮大的‘數列’

fibs=Fibs()
for f in fibs:
    if f>100:
        print(f)
        break

也可以通過迭代物件呼叫內建函式iter,返回一個迭代器

it=iter([1,2,3])
next(it)
1
next(it)
2

9.6.2從迭代器生成序列

將迭代出來的物件轉換成序列

class TestIterator:
	value = 0
	def __next__(self):
		self.value += 1
		if self.value > 10: raise StopIteration
			return self.value
	def __iter__(self):
		return self
>>> ti = TestIterator()
>>> list(ti)
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

9.7生成器

生成器是一種使用普通函式語法定義的迭代器

9.7.1建立生成器

CASE:將巢狀列表展開的函式

nested = [[1, 2], [3, 4], [5]]
def faltten(nested):
    for aa in nested:
        for a in aa:
            yield a

包含yield語句的函式都被稱為生成器
生成器不是使用return返回一個值,而是可以生成多個值,每次一個。
每次使用yield生成一個值後,函式都將凍結,即在此停止執行,等待被重新喚醒。被重新喚醒後,函式將從停止的地方開始繼續執行。
每執行到一個 yield 語句就會中斷,並返回一個迭代值,下次執行時從 yield 的下一個語句繼續執行。看起來就好像一個函式在正常執行的過程中被 yield 中斷了數次,每次中斷都會通過 yield 返回當前的迭代值。

list(faltten(nested))
[1, 2, 3, 4, 5]

或者

for i in faltten(nested):
	print(i)
1
2
3
4
5

生成器推導:
類似於列表推導

[i ** 2 for i in range(10)]
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

生成器推導

(i ** 2 for i in range(10))
<generator object <genexpr> at 0x0000000004AD8678>

返回的就是一個生成器,可以逐步計算

g=(i ** 2 for i in range(10))
next(g)
0
next(g)
1

如果要包裝可迭代物件(大量的值),迭代器優於列表推導
另外圓括號可使該生成器直接被函式呼叫

sum(i ** 2 for i in range(10))
285

9.7.2遞迴生成器

遞迴:呼叫自身
基線條件(針對最小的問題):滿足這種條件是函式將直接返回一個值
遞迴條件:包含一個或多個呼叫,這些呼叫旨在解決問題的一部分

 def faltten(nested):
    try:
        for aa in nested:
            for a in faltten(aa):#呼叫自身
                yield a#返回,中斷
    except TypeError:#當需要展開的是一個單個元素,就會觸發該異常,執行後面的語句
        yield nested
nested=[[[1
            
           

相關推薦

[Python] 魔法方法特性

文章目錄 9.1如果你使用的不是Python3 9.2建構函式 9.2.1重寫普通方法和特殊的建構函式 9.2.2呼叫未關聯的超類建構函式 9.2.3使用函式super(更好)

python基礎——9 魔法方法特性

目錄 9.2.1 建構函式: __init__(self) 9.2.2 呼叫未關聯的超類建構函式 9.3 元素訪問 9.3.1 基本的序列和對映協議 9.3.2 從

.魔法方法特性

一個 self delet item 調用 大數 包含 默認 fir 0.Python中雙下劃線__有特殊意義。 1.構造函數和析構函數: class Test: def __init__(self): #構造函數,創造實例默認執行 pa

魔法方法特性

類定義 刪除 魔法方法 oob ron 對數 元素 indexer ont 構造函數 在python中,創建構造函數和容易,只需將方法init的名稱從普通的init改為魔法版__init__即可.1 class FooBar: 2 def __init__(

python3自學筆記4-切片列表生成式生成器

目錄切片迭代列表生成式生成器迭代器 切片 1、Python提供了切片(Slice)操作符:; 2、list可以進行切片操作: # 生成列表 >>> L = list(range(100)) >>> L [0, 1, 2, 3,

java 中 for foreach 的學習筆記

開發十年,就只剩下這套架構體系了! >>>   

21天學python——3.33.4

3.3.1標誌符 沒什麼新的,和c語言一樣,只能是數字、英文和下劃線,並且大小寫敏感。用來設定變數和函式名。   3.3.2 =  賦值也是很普通的內容 平時只要x = 2 或者 x = ‘a' 就能將字元或者數字賦值給變量了   3.4

21天學python——4.14.2

4.1.1 if基礎 看了之前的基礎起始已經懂得if的基本用法了 if 《條件》:     《語句》 栗子:   然後if是可以有分支的,其實就是else 和else if,但是else if 只能寫成 elif ,在來個栗子:

python,計算生態概念

1,從資料處理到人工智慧。 資料表示 ->資料清洗->資料統計->資料視覺化->資料探勘->人工智慧 資料表示:採用合適方式用程式表達資料。 資料清理:資料歸一化,資料轉換,異常值處理。 資料統計:資料的概要理解,數量。分佈。中位數等。

Python

9.1建立和使用類 動手試一試 9-1 class Restaurant(): def __init__(self,name,type): self.name=name self.type=type def describ

python元件檔案

批量操作具有相同型別的檔案(python實現自動化) shutil 或者稱為shell工具,該模組中包含一些函式,用於實現python程式中複製,移動、改名和刪除檔案 shutil.copy(source, destination) 將路徑source處的檔案複製到路徑destination處

小甲魚Python視頻041課(魔法方法:構造析構 )課後題及參考解答

oat 構造 all 魔法 繼承 return shc 公式 none # -*- coding: utf-8 -*- """ Created on Sun Mar 17 21:13:58 2019 @author: fengs """ """ 測試

Javascript權威指南閱讀筆記--3類型變量(1)

分享圖片 自動 局部變量 清理 ace defined define 原型 未在   之前一直有個想法,好好讀完JS權威指南,便於自己對於JS有個較為全面的了解。畢竟本人非計算機專業出生,雖然做著相關行業的工作,但總覺得對於基礎的掌握並沒有相關專業學者紮實,正好因為辭職待業

5:介面實現多型

介面 介面是軟體資源使用者可用的一組操作 介面中的內容是函式頭和方法頭,以及它們的文件 設計良好的軟體系統會將介面與其實現分隔開來 多型 多型是在兩個或

資料庫系統概論(: 關係查詢處理查詢優化)

第9章    關係查詢處理和查詢優化 查詢優化分類 : 代數優化:指關係代數表示式的優化  物理優化:指存取路徑和底層操作演算法的選擇9.1 關係資料庫系統的查詢處理 9.1.1  查詢處理步驟 ※關係資料庫管

【資料庫視訊】 操作架構索引檢視

一.建立檢視 1.語法格式 CREATE VIEW view_name(view_column_name) AS query_expression [WITH CHECK OPTION] 方法:①通過圖形化介面建立檢視 新建檢視,新增表,定義需要檢視

15 友元異常其他

本章內容包括: 友元類 友元類方法 巢狀類 引發異常,try塊和catch塊 異常類 執行階段型別識別(RTTI) dynamic_cast和typeid static_cast,const_cast和reiterpret_cast RTT

5 操作架構索引檢視

一、建立檢視: 1、語法格式: create view 檢視名 as 查詢表示式 with check option 2、新建檢視 在資源管理器中選擇檢視——>右擊新建 二、刪除和修改檢視 1、圖形化介面: alter view 檢視名 as select

Vulkan Cookbook 11 對映更新取消對映主機可見記憶體

對映、更新和取消對映主機可見記憶體 譯者注:示例程式碼點選此處 對於渲染期間使用的影象和緩衝區,建議繫結位於影象硬體(裝置本地記憶體)上的記憶體。這會產生最好的表現。但我們不能直接訪問這樣的記憶體,需要使用中間(暫存)資源來調節GPU(裝置)和CPU(主機)之間的資料傳輸。 另一方面,暫

C Primer Plus (五版)中文版—— 12 儲存類連結記憶體管理

12.1  儲存類 12.1.1  作用域 定義:作用域描述了程式中可以訪問一個識別符號的一個或多個區域。 分類: 程式碼塊作用域:在程式碼塊中定義的變數具有程式碼塊作用域,從定義處到包含該定義的程式碼塊的末尾,該變數可見。 函式原型作用域:在函式原型