一 python特殊屬性

1 總述

屬性 含義
_ name_ 類,函數(shù),方法等的名字
_ module_ 類定義所現(xiàn)在的模塊名
_ class_ 對象或類所屬的類
_ bases_ 類的基類的元素,順序為他們在基類列表中出現(xiàn)的順序
_ doc_ 類/函數(shù)的文檔字符傳,如果沒有定義則為None
_ mro_ 類的mro,class.mro()返回
_ dict_ 類或?qū)嵗膶傩裕蓪懙淖值?
_ dir_ 返回了類或者對象所有成員列表,dir()函數(shù)調(diào)用的是_ dir_ (),如果提供了_ dir_ (),則返回屬性列表,否則盡可能從__dict__屬性中收集信息

2 name

獲取類和函數(shù)的名字

            
              #!/usr/local/bin/python3.6
#coding:utf-8
class  A:
    pass
class  B(A):
    pass
def  C():
    pass
print (A.__name__,B.__name__,C.__name__,sep='\n')
            
          

python類之特殊屬性和魔術(shù)方法

3 module

類定義所在的模塊名

            
              #!/usr/local/bin/python3.6
#coding:utf-8
class  A:
    pass
class B:
    pass
print  (A.__module__,B.__module__)
            
          

python類之特殊屬性和魔術(shù)方法

            
              #!/usr/local/bin/python3.6
#coding:utf-8
import  disp
print (disp.A.__module__,disp.B.__module__,sep='\n')
            
          

python類之特殊屬性和魔術(shù)方法_第1張圖片

4 class

對象或類所屬的類

            
              #!/usr/local/bin/python3.6
#coding:utf-8
class  A:
    pass
class B(A):
    pass
a=A()
b=B()
print (A.__class__,B.__class__,sep='\n')  #類所屬的類是class
print (a.__class__,b.__class__,sep='\n')  # 對象所屬的類是實實在在的類
            
          

python類之特殊屬性和魔術(shù)方法

5 bases

類的基類的元組,順序是他們在基類列表中出現(xiàn)的順序

            
              #!/usr/local/bin/python3.6
#coding:utf-8
class  A:
    pass
class B(A):
    pass
class C(B):
    pass
class E:
    pass
class D(E,C):
    pass
print (A.__bases__,B.__bases__,C.__bases__,D.__bases__,sep='\n')
            
          

結(jié)果如下

python類之特殊屬性和魔術(shù)方法_第2張圖片

6 DOC

文檔字符串,針對類和函數(shù)有效,若不存在,則返回為None

            
              #!/usr/local/bin/python3.6
#coding:utf-8
class  A:
    '''this  is  class'''
    pass
def B():
    '''this is  function'''
    pass
class C:
    pass
print (A.__doc__,B.__doc__,C.__doc__,sep='\n')
            
          

結(jié)果如下
python類之特殊屬性和魔術(shù)方法

7 mro

類的mro。返回多繼承中的查找順序

            
              #!/usr/local/bin/python3.6
#coding:utf-8
class  A:
    pass
class B(A):
    pass
class C(B):
    pass
class E:
    pass
class D(E,C):
    pass
print (A.__mro__,B.__mro__,C.__mro__,D.__mro__,sep='\n')
            
          

結(jié)果如下

python類之特殊屬性和魔術(shù)方法_第3張圖片

8 dict

類或者實例的屬性,可寫的字典

            
              #!/usr/local/bin/python3.6
#coding:utf-8
class  A:
    a=10
    def  __init__(self,x):
        self.x=5
a=A(3)

print (A.__dict__)
print (a.__dict__)
            
          

結(jié)果如下
python類之特殊屬性和魔術(shù)方法

9 dir

dir 返回了類或者對象所有成員名稱列表,dir()函數(shù)調(diào)用的是_ dir_ (),如果提供了_ dir_ () ,則返回屬性的列表,否則會盡量從__dict__屬性中收集

dir() 對于不同類型的對象具有不同的行為:
1 如果對象是模塊對象,則列表包含模塊的屬性名

            
              #!/usr/local/bin/python3.6
#coding:utf-8
import  re
def  foo(x):
    y=1
print (dir())  # 輸出當(dāng)前模塊信息,此處會打印當(dāng)前導(dǎo)入的模塊和導(dǎo)入的函數(shù)
print (dir(re))
print ('+'*20)
print (dir(foo))
            
          

結(jié)果如下
python類之特殊屬性和魔術(shù)方法_第4張圖片

2 如果對象是類型或者類對象,列表包含類的屬性名,以及其他基類的屬性名

            
              #!/usr/local/bin/python3.6
#coding:utf-8
class A:
    a='1234'
    def __init__(self):
        pass
class  B(A): # 此處調(diào)用父類,其dir中會包含父類的屬性
    pass
print (dir())  # 輸出當(dāng)前模塊信息,此處會打印當(dāng)前導(dǎo)入的模塊和導(dǎo)入的函數(shù)
print ('*'*40)
print (dir(A),dir(B),sep='\n')  # 此中DIR屬性父類和子類是完全相同的,但dict中卻是不同的
print (A.__dict__,B.__dict__,sep='\n')
            
          

python類之特殊屬性和魔術(shù)方法_第5張圖片

3 如果是對象,列表包含對象的屬性名,它的類的屬性名和基類的屬性名

            
              #!/usr/local/bin/python3.6
#coding:utf-8
class A:
    a='1234'
    def __init__(self):
        self.x=10
class  B(A): # 此處調(diào)用父類,其dir中會包含父類的屬性
    pass
a=A()
print (dir())  # 輸出當(dāng)前模塊信息,此處會打印當(dāng)前導(dǎo)入的模塊和導(dǎo)入的函數(shù)
print ('*'*40)
print (dir(A),dir(B),dir(a),sep='\n') #此處若是打印實例的屬性,則會吧類的屬性也打印上來
            
          

結(jié)果如下
python類之特殊屬性和魔術(shù)方法_第6張圖片

4此處對屬性名進行了重寫操作

            
              #!/usr/local/bin/python3.6
#coding:utf-8
class A:
    a='1234'
    def __init__(self):
        self.x=10
class  B(A): # 此處調(diào)用父類,其dir中會包含父類的屬性
    def __dir__(self): # 此處是針對實例設(shè)置的,和類本身并無關(guān)系
        return ['this is class A '] # 此處是dir返回是列表,若使用字符串,則會處理成列表進行返回
a=A()
b=B()
print (dir())  # 輸出當(dāng)前模塊信息,此處會打印當(dāng)前導(dǎo)入的模塊和導(dǎo)入的函數(shù),以及實例后的對象
print ('*'*40)
print (dir(A),dir(B),dir(a),dir(b),sep='\n') #此處若是打印實例的屬性,則會吧類的屬性也打印上來
            
          

結(jié)果如下
python類之特殊屬性和魔術(shù)方法_第7張圖片

10 _ slots_ 槽位

1 問題引出

都是字典惹的禍
字典為了提升查詢效率,必須用空間換時間
一般來說一個對象,屬性多一點,都存儲在字典中便于查詢,問題不大,但是數(shù)百萬個對象,那么字典就占得有點大了,這個時候,python便提供了_ slots_

2 實驗代碼

            
              #!/usr/bin/poython3.6
#conding:utf-8
# this is test
class A:
    x=123
    __slots__ = ('p1',)  # 此處只放置了一個槽位,則只能使用p1變量,不能使用其他
    def __init__(self):
        self.p1=1
        self.p2=2
    def show(self):
        print ('this is  test1,{}'.format(self.p1))
print (A().__dict__)
            
          

結(jié)果如下
python類之特殊屬性和魔術(shù)方法_第8張圖片

此處的p2屬性不能使用

            
              #!/usr/bin/poython3.6
#conding:utf-8
# this is test
class A:
    x=123
    __slots__ = ('p1','p2')  # 此處只放置了一個槽位,則只能使用p1變量,不能使用其他
    def __init__(self):
        self.p1=1
        self.p2=2
    def show(self):
        print ('this is  test1,{}'.format(self.p1))
print ('slots',A().__slots__)  #實例的此屬性可以被打印出來,但實例的字典屬性卻不存在
print (A.__dict__)  # 類的字典屬性不受影響
print ('dict',A().__dict__)
            
          

結(jié)果如下
python類之特殊屬性和魔術(shù)方法_第9張圖片

添加實例屬性如下

            
              #!/usr/bin/poython3.6
#conding:utf-8
# this is test
class A:
    x=123
    __slots__ = 'p1','p2'  # 此處只放置了一個槽位,則只能使用p1變量,不能使用其他
    def __init__(self):
        self.p1=1
        self.p2=2
    def show(self):
        print ('this is  test1,{}'.format(self.p1))
print ('slots',A().__slots__)  #實例的此屬性可以被打印出來,但實例的字典屬性卻不存在
print (A.__dict__)  # 類的字典屬性不受影響
A.p4=300  #類添加屬性,只會影響類的__dict__,實例中不會顯示,而__slots__ 不會對類造成影響
try:
    setattr(A(),'p5',30)
except  AttributeError  as  a:
    print (A(),'不能添加屬性')
finally:
    print (A().__slots__) #查看

try:
    A().P3=300
except  AttributeError  as  a:
    print (A(),'不能添加屬性')
finally:
    print (A().__slots__) #查看
            
          

結(jié)果如下

python類之特殊屬性和魔術(shù)方法_第10張圖片

_ slots_ 告訴解釋器,實例的屬性都叫什么,一般來說,既要節(jié)約內(nèi)存,最好還是使用元祖來存儲__slots__梳理,這樣就阻止了實例產(chǎn)生__dict__來保存實例的屬性

繼承

            
              #!/usr/bin/poython3.6
#conding:utf-8
# this is test
class A:
    x=123
    __slots__ = 'p1','p2'  # 此處只放置了一個槽位,則只能使用p1變量,不能使用其他
    def __init__(self):
        self.p1=1
        self.p2=2
    def show(self):
        print ('this is  test1,{}'.format(self.p1))
class B(A):
    def __init__(self):
        super().__init__()
        self.b1=200
b=B()
b.b2=300
print (b.__dict__)  # 繼承則失效 
            
          

結(jié)果如下
python類之特殊屬性和魔術(shù)方法

3 應(yīng)用場景

使用需要構(gòu)建在數(shù)百萬以上對象,且內(nèi)存容量較為緊張的場景

二 python 實例屬性之魔術(shù)方法

1 分類

描述 方法
初始化和銷毀 _ init__和_ del\
在字典和set中使用 _ hash_
布爾類型,常用于判斷語句 _ bool_
可視化,用于輸出對應(yīng)的類型 _ str__和_ repr\
運算符重載 _ eq_ ,_ ne_ ,_ gt_ ,__lt__等
容器和大小相關(guān)和操作相關(guān)屬性 _ getitem_ ,__setitem__等
可調(diào)用對象,將實例化的對象當(dāng)成一個函數(shù)去調(diào)用,一旦可以當(dāng)函數(shù)調(diào)用 _ call_
上下文管理(with open(x) as f 等形式 _ enter_ ,_ exit_
反射 _ getattr_ , _ setattr_ ,_ delattr_
描述器 Object._ get_ (self,instance,owner)Object._ set_ (self,instance,value)Object._ delete_ (self,instance)

2 初始化和銷毀

            
              #!/usr/local/bin/python3.6
#coding:utf-8
class X:
    def __init__(self,name):
        self.name=name
        self.x=10
        print ("init  instance")
    def __del__(self):
        print ('delete {}'.format(self.name))
a=X('a')
del  a  # 因為python自身的垃圾回收機制,而del刪除實例的操作不確定何時執(zhí)行,因此需要使用del進行清除處理
            
          

結(jié)果如下
python類之特殊屬性和魔術(shù)方法

3 hash

1 簡介

hash 中最基礎(chǔ)的hash就是取模運算。
list 不能hash的原因
list 源碼: 其中hash=None,在調(diào)用None的時候自然是不能hash的

python類之特殊屬性和魔術(shù)方法_第11張圖片

判斷是否可hash

            
              #!/usr/local/bin/python3.6
#coding:utf-8
from   collections import Hashable
class X:
    def __init__(self,x):
        self.x=x
    def __hash__(self):
        return 1
print (isinstance(X(1),Hashable))
print (isinstance([],Hashable))
            
          

結(jié)果如下
python類之特殊屬性和魔術(shù)方法

2 定義不可哈希類型

            
              #!/usr/local/bin/python3.6
#coding:utf-8
from collections  import  Hashable
class X:
    def __init__(self):
        self.x=10
    __hash__=None

print (isinstance(X(),Hashable))  #判斷是否可hash,返回為bool類型
print (hash(X()))
            
          

結(jié)果如下
python類之特殊屬性和魔術(shù)方法_第12張圖片

3 實例

實例如下

            
              #!/usr/local/bin/python3.6
#coding:utf-8
class X:
    def __init__(self):
        self.x=10
    def __hash__(self):  # 此處是定義的是實例的hash,和類沒關(guān)系
        return  None  # 此處設(shè)置hash的返回為None,模擬列表
        # 另一種方式定義不可哈希類型  __hash__=None
class Y:  #此類未設(shè)置相關(guān)的hash屬性
    def __init__(self):
        pass
class Z: # 此類定義了hash的返回值為1 ,則實例化后調(diào)用hash返回為1
    def __hash__(self):
        return  1
print (hash(Y())) # 此處返回整數(shù)
print (hash(Z())) # 此處返回為固定數(shù)
print (hash(X()))  #進行判斷是否能夠進行hash操作,未進行hash,直接拋出異常
            
          

結(jié)果如下
python類之特殊屬性和魔術(shù)方法_第13張圖片

            
              #!/usr/local/bin/python3.6
#coding:utf-8
class X:
    def __init__(self):
        self.x=10
    def __hash__(self):
        return 1
lst=[X(),X()] # 此處進行實例化并裝載到列表中
print  (lst) # 此處打印列表的值,指定的是內(nèi)存地址

s=set(lst)  # 此處定義一個集合,集合本身具有去重功能,上述的hash的返回值是相同的,則說明其hash相同,則表明其key相同,常理推論可得
# 其會走set的去重屬性進行處理
print (len(s))
for  i in  s:  # 打印其中的值,如下
    print (hash(i))
            
          

結(jié)果如下
python類之特殊屬性和魔術(shù)方法_第14張圖片

此處s集合中的元素hash后的結(jié)果是相同的,但是其卻沒有進行去重操作,則此處表明hash相等的函數(shù)不一定就是相同的,此處沒有直接的相等關(guān)系

            
              #!/usr/local/bin/python3.6
#coding:utf-8
class X:
    def __init__(self):
        self.x=10
    def __hash__(self):
        return 1
    def __eq__(self, other):  # 此處是判斷是否相等的原因,及就是其值必須相等的情況下才能說是同一個,而不是hash相同就認為是同一個,此處返回bool值,當(dāng)然可以是0或非0的數(shù)
        return True
lst=[X(),X()] # 此處進行實例化并裝載到列表中
print  (lst) # 此處打印列表的值,指定的是內(nèi)存地址

s=set(lst)  # 此處定義一個集合,集合本身具有去重功能,
# 其會走set的去重屬性進行處理
print (len(s))
for  i in  s:  # 打印其中的值,如下
    print (hash(i))
            
          

結(jié)果如下
python類之特殊屬性和魔術(shù)方法

            
              #!/usr/local/bin/python3.6
#coding:utf-8
class X:
    def __init__(self):
        self.x=10
    def __hash__(self):
        return 1
    def __eq__(self, other):  # 此處是判斷是否相等的原因,及就是其值必須相等的情況下才能說是同一個,而不是hash相同就認為是同一個
        return False
lst=[X(),X()] # 此處進行實例化并裝載到列表中
print  (lst) # 此處打印列表的值,指定的是內(nèi)存地址

s=set(lst)  # 此處定義一個集合,集合本身具有去重功能,上述的hash的返回值是相同的,則說明其hash相同,則表明其key相同,常理推論可得
# 其會走set的去重屬性進行處理
print (len(s))
for  i in  s:  # 打印其中的值,如下
    print (hash(i))
            
          

結(jié)果如下
python類之特殊屬性和魔術(shù)方法

            
              #!/usr/local/bin/python3.6
#coding:utf-8
class X:
    def __init__(self):
        self.x=10
    def __hash__(self):
        return 1
    def __eq__(self, other):  # 此處是判斷是否相等的原因,及就是其值必須相等的情況下才能說是同一個,而不是hash相同就認為是同一個
        return False
a=X()
lst=[a,a]
print (lst)
s=set(lst)
print (len(s))
for x  in  s:
    print  (hash(x))
            
          

結(jié)果如下
python類之特殊屬性和魔術(shù)方法

4 結(jié)論

set判斷是否是同一個的步驟:
1 先進行內(nèi)存地址的判斷,及is判斷,若內(nèi)存地址相同,則肯定是同一個
2 若內(nèi)存地址不同,再進行eq 判斷,及就是==進行判斷,若相同,則是同一個,若不同,則不是同一個,此處和hash沒有任何關(guān)系


及就是: 同假為假,一真一假為真,同真為真

默認的能夠使用hash的原因是由于在基類object中實現(xiàn)了hash方法,一般的,不同的內(nèi)存地址的hash是不相同的,兩個對象的hash相同,叫hash沖突

hash 相同不代表一樣

            
              #!/usr/local/bin/python3.6
#coding:utf-8
class X:
    def __init__(self,x):
        self.x=x
    def __hash__(self):
        return 1
    def __eq__(self, other):  # 此處是判斷是否相等的原因,及就是其值必須相等的情況下才能說是同一個,而不是hash相同就認為是同一個
        return self.x==other.x#  此處表示二元操作,前面調(diào)用方法self.x,后面的作為參數(shù)傳遞進去進行處理,other表示第二個對象對應(yīng)的方法

print  (hash(X(4)))
lst=[X(4),X(6)]
t=tuple(lst)
s=set(lst)
print (s)
for x  in  s:
    print  (hash(x))
            
          

結(jié)果如下

python類之特殊屬性和魔術(shù)方法_第15張圖片

__hash__方法只是返回一個hash值作為set的key,但是去重,還需要__eq__來判斷2個對象是否相等,

5 練習(xí):

設(shè)計二維坐標(biāo)類Point,比較2個坐標(biāo)是否相等

            
              #!/usr/local/bin/python3.6
#coding:utf-8
class Point:
    def  __init__(self,x,y):
        self.x=x
        self.y=y
    def __hash__(self):
        return  hash((self.x,self.y))   # 此處返回是一個元組,是不可變數(shù)據(jù)類型,此處可直接進行hash
    def  __eq__(self, other):
        return self.x==other.x  and  self.y == other.y
a1=Point(3,4)
a2=Point(3,4)
print ( a1 is a2 )
print (a1==a2)
            
          

結(jié)果如下
python類之特殊屬性和魔術(shù)方法

4 bool

1簡介

_ bool_ 內(nèi)建函數(shù)bool(), 或者對象放在邏輯表達式的位置,調(diào)用這個函數(shù)返回布爾值,沒有定義_ bool_ ,就找_ len_ 返回長度,非0為真,如果__len__也沒有定義,則所有的實例都返回是真。

2 實例

            
              #!/usr/local/bin/python3.6
#coding:utf-8
class Point:  # 此類未定義len和bool,因此其返回值為恒真
    def  __init__(self):
        self.x=3
        self.y=4
    # def __bool__(self):
    #     return False

print (bool(Point()))
            
          

結(jié)果如下
python類之特殊屬性和魔術(shù)方法

            
              #!/usr/local/bin/python3.6
#coding:utf-8
class Point:
    def  __init__(self):
        self.x=3
        self.y=4
    def __bool__(self): # 此處定義了bool的返回值為False,則調(diào)用bool()返回結(jié)果應(yīng)該為False
        return False
print (bool(Point()))
            
          

python類之特殊屬性和魔術(shù)方法

            
              #!/usr/local/bin/python3.6
#coding:utf-8
class Point:
    def  __init__(self):
        self.x=3
        self.y=4
    # def __bool__(self): # 此處定義了bool的返回值為False,則調(diào)用bool()返回結(jié)果應(yīng)該為False
    #     return False
    def __len__(self): # 此處用于當(dāng)bool不存在時的找尋位置,為0則表示為空,則為False
        return 0
print (bool(Point()))
            
          

python類之特殊屬性和魔術(shù)方法

            
              #!/usr/local/bin/python3.6
#coding:utf-8
class Point:
    def  __init__(self):
        self.x=3
        self.y=4
    def __bool__(self): # 同時存在,則以bool為準 
        return False
    def __len__(self): 
        return 1
print (bool(Point()))
            
          

結(jié)果如下
python類之特殊屬性和魔術(shù)方法

這也就是為啥空的字典和空集合以及空列表為False的原因了,因為其沒有定義bool,因此其只能通過訪問len來實現(xiàn)了 。

5 可視化

1 簡介

方法 意義
_ repr_ 內(nèi)建函數(shù)repr()對一個對象獲取字符串表達式,如果一個類定義了_ repr__但沒有定義_ str\ ,那么在請求該類的實例的"非正式"的字符串也將調(diào)用_ repr_ ()
_ str_ str() 函數(shù),內(nèi)建函數(shù)format,print()函數(shù)調(diào)用,需要返回對象的字符串表達式
_ bytes_ bytes 的時候,返回一個獨享的bytes表達,及返回bytes對象

2 基礎(chǔ)實例

            
              #!/usr/local/bin/python3.6
#coding:utf-8
class Point:
    def  __init__(self):
        self.x=3
        self.y=4
    def  __repr__(self):
        return str([self.x,self.y])  #此處的返回必須使用字符串進行包裹,否則會報錯
print (Point())
            
          

結(jié)果如下
python類之特殊屬性和魔術(shù)方法

            
              #!/usr/local/bin/python3.6
#coding:utf-8
class Point:
    def  __init__(self):
        self.x=3
        self.y=4
    def  __repr__(self):
        return str([self.x,self.y])  #此處的返回必須使用字符串進行包裹,否則會報錯
    def  __str__(self):  # 若存在此屬性,則上述的表達式將不會被調(diào)用
        return  'abcdefg'
print (Point())
            
          

結(jié)果如下

python類之特殊屬性和魔術(shù)方法

            
              #!/usr/local/bin/python3.6
#coding:utf-8
class Point:
    def  __init__(self):
        self.x=3
        self.y=4
    def  __repr__(self):
        return str([self.x,self.y])  #此處的返回必須使用字符串進行包裹,否則會報錯
    def  __str__(self):  # 若存在此屬性,則上述的表達式將不會被調(diào)用
        return  'abcdefg'
print (Point())
p1=Point()
p2=Point()
lst=[p1,p2]
for x in  lst:
    print (x)
print (lst)
            
          

python類之特殊屬性和魔術(shù)方法

            
              #!/usr/local/bin/python3.6
#coding:utf-8
class Point:
    def  __init__(self):
        self.x=3
        self.y=4
    def  __repr__(self):
        return str([self.x,self.y])  #此處的返回必須使用字符串進行包裹,否則會報錯
    def  __str__(self):  # 若存在此屬性,則上述的表達式將不會被調(diào)用
        return  'abcdefg'
print (Point())
p1=Point()
p2=Point()
lst=(p1,p2)
for x in  lst:
    print (x)
print (lst)
print (*lst)  #進行解包處理,此時是針對于對象上的,此時應(yīng)該調(diào)用的是str
            
          

python類之特殊屬性和魔術(shù)方法_第16張圖片

3 結(jié)論

上述實例證明,當(dāng)str和repr同時存在時,如果輸出結(jié)果直接作用于對象上,則調(diào)用str方法,否則將調(diào)用repr方法

6 運算符重載

1 簡介

operator 模塊提供以下的特殊方法,可以將類的實例使用下面操作符來進行操作

運算符 特殊方法 含義
<,<=,==,>,>=,!= _ lt_ ,_ le_ ,_ eq_ ,_ gt_ ,_ ge_ ,_ ne_ 比較運算符
+,-,*,/,%,//,**,divmod _ add_ ,_ sub_ ,_ mul_ ,_ truediv_ ,_ mod_ ,_ floordiv_ ,_ pow_ ,_ divmod_ 算數(shù)運算符,移位,位運算也有對應(yīng)的方法
+=,-=,*=,/=,%=,//=,**= _ iadd_ ,_ isub_ ,_ imul_ ,_ itruediv_ ,_ imod_ ,_ ifloordiv_ ,_ ipow_

2 實驗

            
              #!/usr/local/bin/python3.6
#coding:utf-8
class  A:
    def  __init__(self,x):
        self.x=x
    def __lt__(self, other):
        return  self.x  < other.x
    def __eq__(self, other):
        return  self.x == other.x
    def __ne__(self, other):
        return  self.x  != other.x
    def __sub__(self, other):
        return  self.x - other.x

print (A(10)
              
              
            
          

結(jié)果如下
python類之特殊屬性和魔術(shù)方法

            
              #!/usr/local/bin/python3.6
#coding:utf-8
class  A:
    def  __init__(self,x):
        self.x=x
    def __iadd__(self, other):  # 此處定義的是+= 返回的是self=self+other
        self.x += other.x
        print ('__iadd__')
        return  self  # 此處返回的是一個實例,可通過調(diào)用其方法來實現(xiàn)此方法是否執(zhí)行

a1=A(10)
a2=A(20)
print ('*'*30)
a1+=a2
print (a1.x,a2.x)
            
          

python類之特殊屬性和魔術(shù)方法

            
              #!/usr/local/bin/python3.6
#coding:utf-8
class  A:
    def  __init__(self,x):
        self.x=x
    def __iadd__(self, other):  # 此處定義的是+= 返回的是self=self+other
        # self.x += other.x
        print ('__iadd__')
        return  A(self.x+other.x )  #此處方法相同

a1=A(10)
a2=A(20)
print ('*'*30)
a1+=a2
print (a1.x,a2.x)
            
          

python類之特殊屬性和魔術(shù)方法

            
              #!/usr/local/bin/python3.6
#coding:utf-8
class  A:
    def  __init__(self,x):
        self.x=x
    def __iadd__(self, other):  # 此處定義的是+= 返回的是self=self+other
        # self.x += other.x
        print ('__iadd__')
        return  A(self.x+other.x )  #此處方法相同
    def  __isub__(self, other):
        print ('__isub__')
        self.x -= other.x
        return  self

a1=A(10)
a2=A(20)
print ('*'*30)
a1+=a2
print (a1.x,a2.x)
a1-=a2
print (a1.x,a2.x)
            
          

python類之特殊屬性和魔術(shù)方法_第17張圖片

functools.total_ordering 的應(yīng)用

默認的

            
              #!/usr/local/bin/python3.6
#coding:utf-8
class  A:
    def  __init__(self,x):
        self.x=x
    def __lt__(self, other):  # 此處定義的是小于,現(xiàn)需要使用大于等于,則默認會報錯
        return self.x  < other.x

print (A(1) >= A(2))
            
          

結(jié)果如下
python類之特殊屬性和魔術(shù)方法_第18張圖片

            
              #!/usr/local/bin/python3.6
#coding:utf-8
from  functools  import total_ordering
@total_ordering
class  A:
    def  __init__(self,x):
        self.x=x
    def __lt__(self, other):  # 此處定義的是小于,現(xiàn)需要使用大于等于,則默認會報錯
        return self.x  < other.x

print (A(1) >= A(2))
print (A(1)==A(2))
print (A(1) != A(2))
            
          

結(jié)果如下
python類之特殊屬性和魔術(shù)方法

3 運算符重載中的反向方法

            
              #!/usr/bin/poython3.6
#conding:utf-8
# this is test
class Add:
    def __init__(self,x:int):
        self.x=x
    def __add__(self, other):
        print ('add',self)
        return self.x+other.x
    def __iadd__(self, other):
        print ('iadd',self)
        return  self.x+other.x
    def __radd__(self, other):
        print ('radd',self)
        return  self.x+other.x
class B:
    def __init__(self,x):
        self.x=x
a=Add(3)
b=B(4)
print (a+b)
print (b+a) 
            
          

結(jié)果如下

python類之特殊屬性和魔術(shù)方法

            
              #!/usr/bin/poython3.6
#conding:utf-8
# this is test
class Add:
    def __init__(self,x:int):
        self.x=x
    def __add__(self, other):
        print ('add',self)
        return self.x+other.x
    def __iadd__(self, other):
        print ('iadd',self)
        return  self.x+other.x
    def __radd__(self, other):
        print ('radd',self)
        return  self+other # 此處self調(diào)用了add,其相當(dāng)于實例self.__add__(other)進行處理的
class B:
    def __init__(self,x):
        self.x=x
a=Add(3)
b=B(4)
print (a+b)
print (b+a)
            
          

結(jié)果如下
python類之特殊屬性和魔術(shù)方法

            
              #!/usr/bin/poython3.6
#conding:utf-8
# this is test
class Add:
    def __init__(self,x):
        self.x=x
    def __add__(self, other):
        print ('add',self)
        return self.x+other
    def __iadd__(self, other):
        print ('iadd',self)
        return  self.x+other.x
    def __radd__(self, other):
        print ('radd',self)
        return  self+other # 此處self調(diào)用了add,其相當(dāng)于實例self.__add__(other)進行處理的
class B:
    def __init__(self,x):
        self.x=x
a=Add(3)
b=B(4)

print (1+a)
print (1+b)
            
          

結(jié)果如下
python類之特殊屬性和魔術(shù)方法_第19張圖片

結(jié)論如下:

            
              b+a 等價于 b.add(a),但是B類沒有實現(xiàn)add方法,就去找a的__radd__方法 

1+a 等價于1.add(a),而int 類型實現(xiàn)了__add__放方法,不過這個方法對于這種加法的返回值是NotImplemented,解釋器發(fā)現(xiàn)了這個值,就會對第二個操作對象執(zhí)行__radd__進行調(diào)用。
            
          

4 總結(jié)

運算符重載的應(yīng)用場景
往往是面向?qū)ο髮崿F(xiàn)的類,需要做大量的運算,而運算符是這種運算在數(shù)學(xué)上最常見的表達方式,int 類中,幾乎實現(xiàn)了所有操作符,可以作為參考

7 容器相關(guān)方法

1 簡介

內(nèi)建方法 含義
_ len_ 內(nèi)建函數(shù)len(),返回對象的長度(>=0的整數(shù)),其實即使吧對象當(dāng)作容器類型來看,就如同list或dict,bool()函數(shù)調(diào)用的時候,如果沒有_ bool_ ()方法,則會看_ len_ ()方法是否存在,存在返回非0為真,第三方庫中可能存在size,其和len的含義相同
_ iter_ 迭代器時,調(diào)用,返回一個新的迭代器對象
_ contains_ in成員運算符,沒有實現(xiàn),就調(diào)用__iter__方法遍歷
_ getitem_ 實現(xiàn)self[key]訪問,序列對象,key接受整數(shù)為索引,或者切片,對于set和dict,key為hashable,key不存在時引KeyError異常
_ setitem_ 和__getitem__的訪問相似,是設(shè)置值的方法
_ missing_ 字典使用_ getitem_ ()調(diào)用時,key不存在執(zhí)行該方法

2 實例

            
              #!/usr/local/bin/python3.6
#coding:utf-8
class  Item:
    def __init__(self,name,*args):
        self.name=name
        self.lst=list(args)
    def  __len__(self):
        return len(self.lst)
    def __iter__(self):
        return iter(self.lst)  # 此處返回是一個迭代器,必須是一個迭代器
    def __add__(self, other):  # 此處使用+ 號返回一個列表
        self.lst.append(other)
        return self
    def __getitem__(self, index):  # 此處應(yīng)用于列表時,表示為索引,此處應(yīng)用于字典時,表示key
        if  index   > len(self.lst):
            print ('Key Error')
        else:
            return self.lst[index]
    def __setitem__(self, index, value): # 此處表示修改屬性列表中的值
        if  index   > len(self.lst):
            print ('Key Error')
        else:
            self.lst[index]=value
            return   self
    # def __missing__(self, key): # 此方法只能適用于字典的處理
    #     pass
    def  __repr__(self):
        return str(self.lst)  # 此處對其進行可視化處理
a=Item('mysql',12,3,45,678,8909)
print (len(a))
# 此處調(diào)用了__iter__方法
for i  in a:
    print (i)
print ('++++++++++++++++')
print (a[2])  # 此處調(diào)用了__getitem__方法,用于獲取值
a+10  # 此處使用__add__方法進行加入,此處追加到列表的末尾
print (a[-1])  # 獲取列表的最后一個元素,則得到此值
a[1]=20  # 使用__setitem__方法修改屬性
print (a[1])  #返回對應(yīng)位置的值
a+10+20+30+40  # 此處進行連加操作,因為其add方法返回是self,因此每次賦值后都會增加
print (a)
            
          

結(jié)果如下
python類之特殊屬性和魔術(shù)方法_第20張圖片

8 可調(diào)用對象

1 簡介

在python中一切皆對象,函數(shù)也不例外
可調(diào)用對象
方法
__call__類中出現(xiàn)該方法,實例就可以像函數(shù)一樣調(diào)用,
可調(diào)用對象: 定義一個類,并實例化得到其實例,將實例像函數(shù)一樣調(diào)用。調(diào)用是實例的,不是類的。

2 實例

            
              #!/usr/local/bin/python3.6
#coding:utf-8
def  foo():
    print (foo.__module__,foo.__name__)

foo.__call__()# 此處的方法和下面的相同,皆是調(diào)用該函數(shù)
foo()
print (dir(foo))
            
          

結(jié)果如下

python類之特殊屬性和魔術(shù)方法_第21張圖片

函數(shù)的可調(diào)用原因是函數(shù)實現(xiàn)了\call()方法

            
              #!/usr/local/bin/python3.6
#coding:utf-8
def  foo():
    print (foo.__module__,foo.__name__)

print (foo.__call__) # 此處返回一個函數(shù)對象是一個wrapper
foo.__call__()# 此處的方法和下面的相同,皆是調(diào)用該函數(shù)
foo()
            
          

結(jié)果如下
python類之特殊屬性和魔術(shù)方法

            
              #!/usr/local/bin/python3.6
#coding:utf-8
class  A:
    def __init__(self):
        self.x=1
    def __call__(self, *args):  # 此處的第一個是self,表明其是給實例使用的,并不是給類使用的
        return   args  # 此處返回一個元組

print (A()(12344))  # 此處第一個括號是實例化,第二個是傳遞參數(shù)并調(diào)用實例
            
          

python類之特殊屬性和魔術(shù)方法

3 練習(xí)

利用封裝完成斐波那契額數(shù)列

方法1

            
              #!/usr/local/bin/python3.6
#coding:utf-8
class  A:
    def  __call__(self,num):
        a,b=0,1
        for  i in  range(num):
            print (b)
            a,b=b,a+b
A()(10)
            
          

結(jié)果如下
python類之特殊屬性和魔術(shù)方法_第22張圖片

方法2

            
              #!/usr/local/bin/python3.6
#coding:utf-8
class  A:
    def __init__(self):
        self.lst=[1,1,2]
    def  __call__(self,num):
        if  num < 3:
            return   self.lst[:num]
        else:
            for  i in range(num-3):
                self.lst.append(self.lst[-1]+self.lst[-2])
            return  self.lst
print (A()(10))
            
          

結(jié)果如下

python類之特殊屬性和魔術(shù)方法

添加功能如下

            
              #!/usr/bin/poython3.6
#conding:utf-8
class  A:
    def __init__(self):
        self.lst=[1,1,2]
    def  __len__(self):
        return len(self.lst)
    def  __call__(self,x):
        if  len(self.lst)  >  x:
            return  self.lst[:x]
        for  i  in   range(2,x):
            self.lst.append(self.lst[i]+self.lst[i-1])
        return  self.lst
    def __getitem__(self, item):
        if item < 0:
            return  None
        if  len(self) > item:
            return self.lst[item]
    def __iter__(self):
        return  iter(self.lst)

a=A()
print (a(10))
print (a[4])
for x in a:
    print (x)
            
          

結(jié)果如下

python類之特殊屬性和魔術(shù)方法_第23張圖片

9 上下文管理

1 推導(dǎo)過程

文件IO操作可以對文件對象進行上下文管理,使用with...as語法
推導(dǎo)過程

            
              #!/usr/bin/poython3.6
#conding:utf-8
# 此處為默認的上下文管理
# with   open('test')  as f:
#     pass

class A:
    pass

with  A()  as  f:
    pass
            
          

結(jié)果如下
python類之特殊屬性和魔術(shù)方法_第24張圖片

提示需要添加 __enter__屬性
添加如下

            
              #!/usr/bin/poython3.6
#conding:utf-8
# 此處為默認的上下文管理
# with   open('test')  as f:
#     pass

class A:
    def  __enter__(self):
        pass

with  A()  as  f:
    pass
            
          

結(jié)果如下
python類之特殊屬性和魔術(shù)方法_第25張圖片

提示需要添加 __exit__屬性

            
              #!/usr/bin/poython3.6
#conding:utf-8
# 此處為默認的上下文管理
# with   open('test')  as f:
#     pass

class A:
    def  __enter__(self):
        pass
    def  __exit__(self, exc_type, exc_val, exc_tb):
        pass
with  A()  as  f:
    pass
            
          

結(jié)果如下
python類之特殊屬性和魔術(shù)方法

            
              #!/usr/bin/poython3.6
#conding:utf-8
class A:
    def  __enter__(self):
        print ('__enter__')
    def  __exit__(self, exc_type, exc_val, exc_tb):
        print ('__exit__')
with  A()  as  f:
    pass
            
          

由此圖可知,其調(diào)用順序是先調(diào)用_ enter_ ,后調(diào)用_ exit_

2 屬性

方法 意義
_ enter_ 進入于此對象相關(guān)的上下文,如果存在該方法,with語法會把該方法的返回值作為綁定到as字句中指定的變量上
_ exit_ 退出與此對象的上下文

exit 中變量的含義:
1 exc_type: 異常類型,如果沒有異常,則返回是None
2 exc_tb:異常追蹤信息,如果沒有異常,則是None
3 exc_va :異常對應(yīng)的值,如果沒異常,則是None
此處的return 用于壓制異常,若此處是False,則會拋出異常,等效True 或 False

缺少了enter 進不去,缺少了exitc出不來

            
              #!/usr/bin/poython3.6
#conding:utf-8
class A:
    def  __init__(self):
        print ('init instance')
    def  __enter__(self):
        print ('__enter__')
        return 1
    def  __exit__(self, exc_type, exc_val, exc_tb):
        print ('__exit__')
p=A()
with  p  as  f:  # 此處的p是__enter__的返回值,是f的參數(shù),若此處__enter__無return,則默認返回為None,無意義
    print (p==f) # 此處用于比較p和f的關(guān)系 
    print (p is f)
    print (p)
    print (f)
            
          

python類之特殊屬性和魔術(shù)方法_第26張圖片

上述結(jié)論如下:
實例化對象的時候,并不會調(diào)用enter,進入with語句塊會調(diào)用__enter__方法,然后執(zhí)行語句體,最后離開with語句塊的時候,調(diào)用__exit__方法

with 可以開啟一個上下文運行環(huán)境,在執(zhí)行前做一些準備工作,執(zhí)行后做一些收尾工作。

3 上下文管理對異常的處理方式

            
              #!/usr/bin/poython3.6
#conding:utf-8
class A:
    def  __init__(self):
        print ('init instance')
    def  __enter__(self):
        print ('__enter__')
        return 1
    def  __exit__(self, exc_type, exc_val, exc_tb):
        print ('__exit__')
p=A()
with  p  as  f:  # 此處的p是__enter__的返回值,是f的參數(shù),若此處__enter__無return,則默認返回為None,無意義
    raise Exception('Error')  # 此處拋出異常,一般的,拋出異常后,語句將不會再次執(zhí)行
    print (p==f) # 此處用于比較p和f的關(guān)系
    print (p is f)
    print (p)
    print (f)
            
          

結(jié)果如下

python類之特殊屬性和魔術(shù)方法_第27張圖片

由此證明,當(dāng)異常拋出時,exit對應(yīng)的語句仍然會被執(zhí)行。

            
              #!/usr/bin/poython3.6
#conding:utf-8
import  sys
class A:
    def  __init__(self):
        print ('init instance')
    def  __enter__(self):
        print ('__enter__')
        return 1
    def  __exit__(self, exc_type, exc_val, exc_tb):
        print ('__exit__')
p=A()
with  p  as  f:  # 此處的p是__enter__的返回值,是f的參數(shù),若此處__enter__無return,則默認返回為None,無意義
    sys.exit()  # 此處的是直接退出
    print (p==f) # 此處用于比較p和f的關(guān)系
    print (p is f)
    print (p)
    print (f)
            
          

結(jié)果如下
python類之特殊屬性和魔術(shù)方法

上述證明,此處滿足上述清理工作,上下文管理非常安全,能夠保證變量的順利清除工作。

            
              #!/usr/bin/poython3.6
#conding:utf-8
import  sys
class A:
    def  __init__(self):
        print ('init instance')
    def  __enter__(self):
        print ('__enter__')
        return self
    def  __exit__(self, exc_type, exc_val, exc_tb):
        print ('__exit__')
        print (exc_tb) #追蹤信息
        print (exc_type)  # 類型
        print (exc_val)  # 值
        return  1  # 此處設(shè)置為1 是壓制異常,不讓其出現(xiàn)
p=A()
with  p  as  f:  # 此處的p是__enter__的返回值,是f的參數(shù),若此處__enter__無return,則默認返回為None,無意義
    raise   Exception('Error1234454')
    print (p==f) # 此處用于比較p和f的關(guān)系
    print (p is f)
    print (p)
    print (f)
            
          

結(jié)果如下
python類之特殊屬性和魔術(shù)方法_第28張圖片

4 通過此方法進行函數(shù)執(zhí)行時長計算

之前的計算時長方式

            
              #!/usr/bin/poython3.6
#conding:utf-8
import  datetime
import  time
import  sys
def  wapper(fn):
    def _wapper(*args,**kwargs):
        start_time=datetime.datetime.now()
        ret  =  fn(*args,**kwargs)
        delta=(datetime.datetime.now()-start_time).total_seconds()
        print ("{} 函數(shù)的執(zhí)行時間為: {}".format(fn.__name__,delta))
        return  ret
    return  _wapper
@wapper
def  add(x,y):
    time.sleep(2)
    return  x+y

add(4,5)
            
          

結(jié)果如下
python類之特殊屬性和魔術(shù)方法

使用上下文管理的方式統(tǒng)計函數(shù)執(zhí)行時間

            
              #!/usr/bin/poython3.6
#conding:utf-8
import  datetime
import  time
class Timer:
    def  __init__(self,fn):
        self.fn=fn
    def  __enter__(self):
        self.start_time=datetime.datetime.now()
        return  self.fn  # 此處對應(yīng)的是as前面的值
    def __exit__(self, exc_type, exc_val, exc_tb):
        delat=(datetime.datetime.now()-self.start_time).total_seconds()
        print ("函數(shù){} 的執(zhí)行時間為: {}".format(self.fn.__name__,delat))
        return  1
def  add(x,y):
    return  x+y
p=Timer(add)
with   p  as f:  # 此處調(diào)用的是__enter__的返回值,重命名為f
    time.sleep(2)
    print (f(4,5))
            
          

結(jié)果如下
python類之特殊屬性和魔術(shù)方法

5 類裝飾器的應(yīng)用

            
              #!/usr/bin/poython3.6
#conding:utf-8
import  datetime
import time
from functools import  wraps
class A:
    def __init__(self,fn):
        self.fn=fn
    def __call__(self,*args,**kwargs):  #實例調(diào)用支持的方法
        self.start_time=datetime.datetime.now()
        ret = self.fn(*args,**kwargs)
        delta=(datetime.datetime.now()-self.start_time).total_seconds()
        print ("{} 函數(shù)的執(zhí)行時間為: {}".format(self.fn.__name__,delta))
        return  ret
@A   #add=A(add)
def add(x,y):
    time.sleep(2)
    return  x+y
print  (add(10,20))
            
          

結(jié)果如下

python類之特殊屬性和魔術(shù)方法

6 進行屬性覆蓋如下

            
              #!/usr/bin/poython3.6
#conding:utf-8
import  datetime
import time
from functools import  wraps
class A:
    def __init__(self,fn):
        self.fn=fn
    def __call__(self,*args,**kwargs):  #實例調(diào)用支持的方法
        self.start_time=datetime.datetime.now()
        ret = self.fn(*args,**kwargs)
        delta=(datetime.datetime.now()-self.start_time).total_seconds()
        print ("{} 函數(shù)的執(zhí)行時間為: {}".format(self.fn.__name__,delta))
        return  ret
@A   #add=A(add)
def add(x,y):
    '''this is function'''
    time.sleep(2)
    return  x+y
print  (add(10,20))
print  (add.__doc__) # 此處打印出文檔
            
          

結(jié)果如下

python類之特殊屬性和魔術(shù)方法

            
              #!/usr/bin/poython3.6
#conding:utf-8
import  datetime
import time
from functools import  wraps
class A:
    def __init__(self,fn):
        self.__doc__=fn.__doc__  # 此處只能進行部分的屬性覆蓋操作
        self.__name__=fn.__name__
        self.fn=fn
    def __call__(self,*args,**kwargs):  #實例調(diào)用支持的方法
        self.start_time=datetime.datetime.now()
        ret = self.fn(*args,**kwargs)
        delta=(datetime.datetime.now()-self.start_time).total_seconds()
        print ("{} 函數(shù)的執(zhí)行時間為: {}".format(self.fn.__name__,delta))
        return  ret
@A   #add=A(add)
def add(x,y):
    '''this is function'''
    time.sleep(2)
    return  x+y
print  (add(10,20))
print  (add.__doc__) # 此處打印出文檔
            
          

結(jié)果如下
python類之特殊屬性和魔術(shù)方法

            
              #!/usr/bin/poython3.6
#conding:utf-8
import  datetime
import time
from functools import  wraps
class A:
    def __init__(self,fn):
        wraps(fn)(self)  # 調(diào)用此方法完成屬性的覆蓋操作,此處第一個是原函數(shù),后面是現(xiàn)在的函數(shù)
        self.fn=fn
    def __call__(self,*args,**kwargs):  #實例調(diào)用支持的方法
        self.start_time=datetime.datetime.now()
        ret = self.fn(*args,**kwargs)
        delta=(datetime.datetime.now()-self.start_time).total_seconds()
        print ("{} 函數(shù)的執(zhí)行時間為: {}".format(self.fn.__name__,delta))
        return  ret
@A   #add=A(add)
def add(x,y):
    '''this is function'''
    time.sleep(2)
    return  x+y
print  (add(10,20))
print  (add.__doc__) # 此處打印出文檔
            
          

結(jié)果如下
python類之特殊屬性和魔術(shù)方法

7 contextlib.contextmanager

是一個裝飾器實現(xiàn)上下文管理,裝飾一個函數(shù),而不像類一樣可以實現(xiàn)__enter__和__exit__方法

對下面的函數(shù)有要求,必須有yield,也就是這個函數(shù)必須返回一個和生成器,且只有一個yield的值。

            
              #!/usr/bin/poython3.6
#conding:utf-8
import   contextlib
@contextlib.contextmanager
def foo():
    print ('enter')
    yield
    print ('exit')

with  foo() as f:
    pass
            
          

結(jié)果如下

python類之特殊屬性和魔術(shù)方法

            
              #!/usr/bin/poython3.6
#conding:utf-8
import   contextlib
@contextlib.contextmanager
def foo():
    print ('enter')
    yield  [1,2,34,56,5867,856,867,]  # 此處相當(dāng)分界線,用于分割上面和下面的執(zhí)行
    print ('exit')

with  foo() as f:
    print (f)
            
          

python類之特殊屬性和魔術(shù)方法

            
              #!/usr/bin/poython3.6
#conding:utf-8
import   contextlib
@contextlib.contextmanager
def foo():
    print ('enter')
    yield  [1,2,34,56,5867,856,867,]  # 此處相當(dāng)分界線,用于分割上面和下面的執(zhí)行
    print ('12344exit')

with  foo() as f:
    try:
        raise Exception
    finally:
        print ('exit')
    print (f)
            
          

結(jié)果如下

python類之特殊屬性和魔術(shù)方法_第29張圖片

修改異常捕獲如下

            
              #!/usr/bin/poython3.6
#conding:utf-8
import   contextlib
@contextlib.contextmanager
def foo():
    print ('enter')
    try:
        yield  [1,2,34,56,5867,856,867,]  # 此處相當(dāng)分界線,用于分割上面和下面的執(zhí)行
    finally:  # 放置在此處能夠執(zhí)行后面的相關(guān)操作
        print ('12344exit')

with  foo() as f:
    raise  Exception
    print (f)
            
          

結(jié)果如下
python類之特殊屬性和魔術(shù)方法_第30張圖片

總結(jié): 如果業(yè)務(wù)邏輯簡單可以使用函數(shù)加裝飾器的方式,如果業(yè)務(wù)復(fù)雜,用類的方式加__enter__和__exit__更方便

8 上下文的應(yīng)用場景

1 增強功能
在代碼執(zhí)行的前后增加代碼,以增強其功能,類似裝飾器的功能

2 資源管理
打開了資源需要關(guān)閉,例如文件對象,網(wǎng)絡(luò)鏈接,數(shù)據(jù)庫鏈接等

3 權(quán)限驗證

在執(zhí)行代碼之前,做權(quán)限的驗證,在 enter 中處理
在代碼進入的時候進行處理,在權(quán)限出去則不管

10 反射

1 概述

運行時:區(qū)別于編譯時,指的是程序被加載到內(nèi)存中執(zhí)行的時候。


反射:python中,能夠通過一個對象,找出其type,class,attribute或method的能力,成為反射或自醒。


具有反射能力的函數(shù)type(),isinstance(),callable()(查看對象能否被調(diào)用),dir(),getattr()

2 內(nèi)建函數(shù)

            
              object 可以是類或?qū)嵗?
語法格式:
getattr(object,name[,default]) : 通過name 返回object的屬性值,當(dāng)屬性不存在時,將使用default返回,如果沒有default,則拋出attributeError,name 必須位字符串 

setattr(object,name,value) object 的屬性存在,則覆蓋,若不存在,則新增。

hasattr(object,name)  判斷對象是否有這個名字屬性,name必須時字符串
            
          

3 實例

            
              #!/usr/bin/poython3.6
#conding:utf-8
class  A:
    x=10
    def __init__(self):
        self.x=5

setattr(A,'y',20)  # 動態(tài)添加類屬性位y=20
print (A.__dict__)  # 打印屬性信息列表
a=A()
setattr(a,'z',100)  # 實例動態(tài)增加屬性
print (getattr(A,'y')) # 查看增加的屬性是否存在
print (getattr(A,'x'))  # 定義屬性是否存在
print (getattr(a,'y'))  # 查看實例中是否存在該屬性
print (a.__dict__) # 查看實例屬性信息中是否具有'z'屬性
print (A.__dict__)  # 打印屬性信息列表,此處查看當(dāng)實例屬性信息增加后,類屬性信息是否增加

if hasattr(a,'z'):
    print ("{} 函數(shù)存在此屬性 {}".format('a','y'))
            
          

結(jié)果如下
python類之特殊屬性和魔術(shù)方法_第31張圖片
進階

            
              #!/usr/bin/poython3.6
#conding:utf-8
class  A:
    x=10
    def __init__(self,y):
        self.x=5
        self.y=y
a=A(20)
setattr(A,'printf',lambda  self: 1 ) #此處增加一個類的屬性,并通過參數(shù)調(diào)用

setattr(a,'myclass',lambda  : 10 ) # 此處增加一個實例屬性
print (a.printf())  # 打印此類的屬性結(jié)果如下
print (getattr(a,'printf')())  # 因為此處是函數(shù),因此需要通過()來進行調(diào)用
print (getattr(a,'myclass')())
if not  hasattr(A,'sub'):  # 此處添加一個類的函數(shù)屬性,實現(xiàn)函數(shù)之前的差運算
    setattr(A,'sub',lambda self,other : A(self.y- other.y) )
if not  hasattr(A,'add'): # 此處添加一個類的屬性,實現(xiàn)函數(shù)之間的和的計算
    setattr(A,'add',lambda self,other: (self.y + other.y))
print (a.__dict__)
print (A.__dict__)
b1=A(10)
b2=A(20)
print (b2.sub(b1))
print (b1.add(b2))
            
          

結(jié)果如下

python類之特殊屬性和魔術(shù)方法_第32張圖片

注意:
這種動態(tài)增加屬性的方式是運行時改變類或者實例的方式,比裝飾器和Mixin更加靈活,其具有更大的使用用途

4 實例應(yīng)用

實現(xiàn)分發(fā)器
簡單雛形

            
              #!/usr/bin/poython3.6
#conding:utf-8
class  Dispatcher:
    def cmd1(self):  # 此處在內(nèi)部定義一個方法
        print ('cmd10')
    def run(self):  # 此處用于執(zhí)行
        while True:
            cmd=input("plase  input str:")  #退出程序命令
            if  cmd.strip() == 'q'  or  cmd.strip() == 'quit':
                return
            getattr(self,cmd.strip(),self.__defaltfun)()  # 此處用于獲取該方法,若不存在,則執(zhí)行默認程序
    def __defaltfun(self):
        print ('default')
a=Dispatcher()
a.run()
            
          

結(jié)果如下
python類之特殊屬性和魔術(shù)方法_第33張圖片

            
              #!/usr/bin/poython3.6
#conding:utf-8
class  Dispatcher:
    def cmd1(self):  # 此處在內(nèi)部定義一個方法
        print ('cmd10')
    def  reg(self,cmd,fn):
        if isinstance(cmd,str):
            setattr(self.__class__,cmd.strip(),fn)  # 此處使用的是類,若是實例化,則需要進行下面將self傳入進去的方式進行調(diào)用
        else:
            print ('TypeError')
    def run(self):
        while True:
            cmd = input("plase  input str:")
            if  cmd.strip()  == 'q'  or  cmd.strip() == 'quit':
                return
            getattr(self,cmd.strip(),self.defaultfun)()
    def defaultfun(self):
        print ('default')

a=Dispatcher()
a.reg('cmd2',lambda  self :print (2)) 
a.reg('cmd3',lambda  self :print (3))
a.run()
            
          

結(jié)果如下

python類之特殊屬性和魔術(shù)方法_第34張圖片

5 反射相關(guān)魔術(shù)方法

魔術(shù)方法 意義
_ getattr_ () 當(dāng)通過搜索實例,實例的類以及祖先類查不到的屬性,就會調(diào)用此方法
_ setattr_ () 通過訪問實例屬性,進行增加,修改都要調(diào)用它
_ delattr_ () 當(dāng)通過實例刪除屬性的時候調(diào)用此方法
_ getattribute_ 實例所有的屬性調(diào)用都是從政方法開始

實例如下:

            
              #!/usr/bin/poython3.6
#conding:utf-8

class  Base:
    a=5

class A(Base):
    m=6
    def __init__(self,x):
        print ('init')
        self.x=x  #此處定義了屬性,所以下面的__setattr__被執(zhí)行了一次,初始化先執(zhí)行,之后__setattr__這個屬性再執(zhí)行一次
    def __getattr__(self, item):#針對上述無法查找到的屬性,則執(zhí)行此屬性,可配置其值為None來彌補此屬性值
        print ('__getattr__',item)
        self.__dict__[item]=None
    def __setattr__(self, key, value): #設(shè)置一個屬性時,一定要執(zhí)行,至于是否生效,則另當(dāng)別論
        print ('__setattr__',key,value)
    def __delattr__(self, item): #此處在刪除一個實例的屬性進行的操作,只要實例能找到,都能夠刪除
        print ('__delattr__',item)
A.n=50 # 此處是正常的添加類屬性,不會產(chǎn)生其他的報錯
a=A(20)
print (a.__dict__)
a.b  # 針對不存在的屬性進行調(diào)用
a.x=30 # 設(shè)置實例的屬性變化
a.c=200 #添加一個不存在的屬性
del  a.a # 刪除一個實例的屬性
print (a.__dict__)
            
          

結(jié)果如下

python類之特殊屬性和魔術(shù)方法_第35張圖片

            
              #!/usr/bin/poython3.6
#conding:utf-8

class  Base:
    a=5

class A(Base):
    m=6
    def __init__(self,x):
        print ('init')
        self.x=x  #此處定義了屬性,所以下面的__setattr__被執(zhí)行了一次,初始化先執(zhí)行,之后__setattr__這個屬性再執(zhí)行一次
    def __getattr__(self, item):#針對上述無法查找到的屬性,則執(zhí)行此屬性,可配置其值為None來彌補此屬性值
        print ('__getattr__',item)
        self.__dict__[item]=None
    def __setattr__(self, key, value): #設(shè)置一個屬性時,一定要執(zhí)行,至于是否生效,則另當(dāng)別論
        print ('__setattr__',key,value)
    def __delattr__(self, item): #此處在刪除一個實例的屬性進行的操作,只要實例能找到,都能夠刪除
        print ('__delattr__',item)

    def __getattribute__(self, item):  #此處是在字典屬性之前進行攔截執(zhí)行
        print ('__getattribute__',item)
a=A(20)

print (a.x)
            
          

結(jié)果如下
python類之特殊屬性和魔術(shù)方法

實例的所有屬性的訪問,第一個都會調(diào)用__getattribute__方法,其阻止了屬性查找,該方法應(yīng)該返回(計算后)值或者拋出一個attributeError 異常
他的return 值將作為屬性查找的結(jié)果,如果拋出attributeError 異常,則會直接調(diào)用__getattr__方法,因為表示屬性沒有找到

            
              #!/usr/bin/poython3.6
#conding:utf-8

class  Base:
    a=5

class A(Base):
    m=6
    def __init__(self,x):
        print ('init')
        self.x=x  #此處定義了屬性,所以下面的__setattr__被執(zhí)行了一次,初始化先執(zhí)行,之后__setattr__這個屬性再執(zhí)行一次
    def __getattr__(self, item):#針對上述無法查找到的屬性,則執(zhí)行此屬性,可配置其值為None來彌補此屬性值
        print ('__getattr__',item)
        # self.__dict__[item]=None
    def __setattr__(self, key, value): #設(shè)置一個屬性時,一定要執(zhí)行,至于是否生效,則另當(dāng)別論
        print ('__setattr__',key,value)
    def __delattr__(self, item): #此處在刪除一個實例的屬性進行的操作,只要實例能找到,都能夠刪除
        print ('__delattr__',item)

    def __getattribute__(self, item):  #此處是在字典屬性之前進行攔截執(zhí)行
        print ('__getattribute__',item)
        raise  AttributeError(item)  # 此處若屬性不存在,拋出異常,則直接進入getattr中機型處理
a=A(20)

print (a.x)
            
          

python類之特殊屬性和魔術(shù)方法

            
              #!/usr/bin/poython3.6
#conding:utf-8

class  Base:
    a=5

class A(Base):
    m=6
    def __init__(self,x):
        print ('init')
        self.x=x  #此處定義了屬性,所以下面的__setattr__被執(zhí)行了一次,初始化先執(zhí)行,之后__setattr__這個屬性再執(zhí)行一次
    def __getattr__(self, item):#針對上述無法查找到的屬性,則執(zhí)行此屬性,可配置其值為None來彌補此屬性值
        print ('__getattr__',item)
        # self.__dict__[item]=None
    def __setattr__(self, key, value): #設(shè)置一個屬性時,一定要執(zhí)行,至于是否生效,則另當(dāng)別論
        print ('__setattr__',key,value)
    def __delattr__(self, item): #此處在刪除一個實例的屬性進行的操作,只要實例能找到,都能夠刪除
        print ('__delattr__',item)

    def __getattribute__(self, item):  #此處是在字典屬性之前進行攔截執(zhí)行
        print ('__getattribute__',item)
        # raise  AttributeError(item)  # 此處若屬性不存在,拋出異常,則直接進入getattr中機型處理
        return object.__getattribute__(self,item)  #此處表示若不存在,則直接去object中進行查找,并得到其訪問的值
a=A(20)

print (a.b)
            
          

結(jié)果如下
python類之特殊屬性和魔術(shù)方法

注意:\getattribute\ 方法中為了避免在該方法中無限遞歸,實現(xiàn)了應(yīng)該永久調(diào)用基類的同名方法以訪問需要的任何屬性,除非你明確知道\getattribute\方法用來做什么,否則不要使用它。

6 總結(jié):

屬性查找順序
實例調(diào)用----> \getattribute()----> instance.\dict---->instance.\class----> 繼承的祖先類(知道object)的\dict\調(diào)用\getattr()

11 描述器

1 定義

在python中,一個類實現(xiàn)了一下三種方式中的任何一種,就是描述器

            
              object.__get__(self,instance,owner)
object.__set__(self,instance,value)
object.__delete__(self,instance)
            
          

如果僅實現(xiàn)了\get\,就是非數(shù)據(jù)描述器 non-data descriptor
同時實現(xiàn)了\get\和\set\或者\get\和\delete\ 或者三個都實現(xiàn),則稱為數(shù)據(jù)描述符 data descriptor
如果一個類的類屬性設(shè)置為描述器,那么那被稱為owner屬主。

2 基本實踐

            
              #!/usr/bin/poython3.6
#conding:utf-8
class A:
    def  __init__(self):
        print ('A,init')
        self.a1='a1'

class B:
    x=A()  # 調(diào)用上述的類形成實例
    def __init__(self):
        print ('B,init')
        self.x=100  # 此處B類實例的屬性為x=100

print  (B.x.a1)  # 此處通過調(diào)用B類而調(diào)用B類的類屬性x,進而調(diào)用A類的實例的a1方法.必須是先初始化,然后再進行相關(guān)的調(diào)用

b=B()  # 此處調(diào)用從類開始,會執(zhí)行A和B的init方法
print  (b.x)   #此處調(diào)用的是實例B的x屬性,其值是100,此處對x.a1沒有屬性,因為其被self.x=100覆蓋了
            
          

結(jié)果如下

python類之特殊屬性和魔術(shù)方法

默認查找順序: 類加載時,類變量需要先生成,而類B的x屬性是類A的實例,因此需要先執(zhí)行類A的初始化,進而執(zhí)行B的初始化操作。

            
              #!/usr/bin/poython3.6
#conding:utf-8
class A:
    def  __init__(self):
        print ('A,init')
        self.a1='a1'
    def __get__(self, instance, owner): #加入此方法,行為被攔截,執(zhí)行了init后執(zhí)行了此方法,返回為None,因此后面調(diào)用的None
        return (self,instance,owner)

class B:
    x=A()  # 調(diào)用上述的類形成實例
    def __init__(self):
        print ('B,init')
        self.x=100  # 此處B類實例的屬性為x=100

print (B.x)  # 此處x對應(yīng)的a1的屬性被攔截,上述返回為x實例,instance為B類實例的返回,owner為B類,及就是屬性所屬的類,self為A類的實例
b=B()  # 對類B進行實例化
print (b.x)  # 對類b的屬性進行調(diào)用
            
          

結(jié)果如下
python類之特殊屬性和魔術(shù)方法

            
              屬性中的值:
self : 類A對應(yīng)的實例
owner: 類B 
instance 說明,及類B的實例
            
          
            
              #!/usr/bin/poython3.6
#conding:utf-8
class A:
    def  __init__(self):
        print ('A,init')
        self.a1='a1'
    def __get__(self, instance, owner): #加入此方法,行為被攔截,執(zhí)行了init后執(zhí)行了此方法,返回為None,因此后面調(diào)用的None
        return (self,instance,owner)

class B:
    x=A()  # 調(diào)用上述的類形成實例
    def __init__(self):
        print ('B,init')
        self.x=A()  # 此處B類實例的屬性為調(diào)用A類的屬性

b=B()  # 對類B進行實例化
print (b.x.a1)  # 對類b的屬性進行調(diào)用,此處調(diào)用的是A類的屬性,此處沒有觸動__get__魔術(shù)方法,進而說明__get__和實例無關(guān)
            
          

python類之特殊屬性和魔術(shù)方法

結(jié)論: _ get_ ()魔術(shù)方法只對調(diào)用的類有攔截作用,對類B下的實例無任何作用,此get是在調(diào)用子類的類屬性時會執(zhí)行此方法。

通過屬性描述器操作屬主的狀態(tài)

            
              #!/usr/bin/poython3.6
#conding:utf-8
class A:
    def  __init__(self):
        print ('A,init')
        self.a1='a1'
    def __get__(self, instance, owner): #加入此方法,行為被攔截,執(zhí)行了init后執(zhí)行了此方法,返回為None,因此后面調(diào)用的None
        return   self # 此處返回self,則表示A的實例,A的實例當(dāng)然可以調(diào)用a1方法
class B:
    x=A()  # 調(diào)用上述的類形成實例
    def __init__(self):
        print ('B,init')
        self.x=A()  # 此處B類實例的屬性為調(diào)用A類的屬性

print (B.x.a1)# 此處因為返回的是self,及A的實例,因此此處可以調(diào)用A實例的a1方法,自然是成功的
B.x.a1=30 #通過描述器來修改屬主的狀態(tài)
print (B.x.a1)  # 打印狀態(tài)
            
          

結(jié)果如下
python類之特殊屬性和魔術(shù)方法

此處通過返回為self的方式來達到調(diào)用類B的屬性來調(diào)用類A的實例屬性的目的。

            
              #!/usr/bin/poython3.6
#conding:utf-8
class A:
    def  __init__(self):
        print ('A,init')
        self.a1='a1'
    def __get__(self, instance, owner): #加入此方法,行為被攔截,執(zhí)行了init后執(zhí)行了此方法,返回為None,因此后面調(diào)用的None
        print ('__get__',self,instance,owner)
        return   self # 此處返回self,則表示A的實例,A的實例當(dāng)然可以調(diào)用a1方法
    # def  __set__(self, instance, value): #實例化B類時需要調(diào)用此方法,
    #     print ('__set__',self,instance,value)
class B:
    x=A()  # 調(diào)用上述的類形成實例
    def __init__(self):
        print ('B,init')
        self.x=100  # 此處B類實例的屬性為調(diào)用A類的屬性
b=B()
print (b.__dict__)  # 打印實例b對應(yīng)的屬性字典
print ('+'*30)
print (b.x.a1)  #此處默認的a1方法是不存在于b實例中,使用set方法將跳過b中定義的self.x方法
            
          

結(jié)果如下
python類之特殊屬性和魔術(shù)方法_第36張圖片

使用_ set_ ()方法如下

            
              #!/usr/bin/poython3.6
#conding:utf-8
class A:
    def  __init__(self):
        print ('A,init')
        self.a1='a1'
    def __get__(self, instance, owner): #加入此方法,行為被攔截,執(zhí)行了init后執(zhí)行了此方法,返回為None,因此后面調(diào)用的None
        print ('__get__',self,instance,owner)
        return   self # 此處返回self,則表示A的實例,A的實例當(dāng)然可以調(diào)用a1方法
    def  __set__(self, instance, value): #實例化B類時需要調(diào)用此方法,此處是將B的實例和A的實例一起送進了set方法中,value及就是B類定義的實例的屬性對應(yīng)的值
        print ('__set__',self,instance,value)
class B:
    x=A()  # 調(diào)用上述的類形成實例
    def __init__(self):
        print ('B,init')
        self.x=100  # 此處B類實例的屬性為調(diào)用A類的屬性
b=B()
print (b.__dict__)  # 打印實例b對應(yīng)的屬性字典
print ('+'*30)
print (b.x.a1)

            
          

結(jié)果如下

python類之特殊屬性和魔術(shù)方法_第37張圖片

如下

            
              #!/usr/bin/poython3.6
#conding:utf-8
class A:
    def  __init__(self):
        print ('A,init')
        self.a1='a1'
    def __get__(self, instance, owner): #加入此方法,行為被攔截,執(zhí)行了init后執(zhí)行了此方法,返回為None,因此后面調(diào)用的None
        print ('__get__',self,instance,owner)
        return   self # 此處返回self,則表示A的實例,A的實例當(dāng)然可以調(diào)用a1方法
    def  __set__(self, instance, value): #實例化B類時需要調(diào)用此方法,
        print ('__set__',self,instance,value)
        self.a1=value  # 若此處定義a1的返回值為value,及類B對應(yīng)的實例屬性x的值,則此處在訪問時,其結(jié)果便是100 
class B:
    x=A()  # 調(diào)用上述的類形成實例
    def __init__(self):
        print ('B,init')
        self.x=100  # 此處B類實例的屬性為調(diào)用A類的屬性
b=B()
print (b.__dict__)  # 打印實例b對應(yīng)的屬性字典
print ('+'*30)
print (b.x.a1)  # 此處最終訪問__get__的原因是此處調(diào)用的是類的屬性,而不是實例的屬性,因此__get__會生效
            
          

python類之特殊屬性和魔術(shù)方法_第38張圖片

3 結(jié)論如下:

當(dāng)一個類的類屬性是一個數(shù)據(jù)描述器時(及除了\get\方法外還有至少一種方法),對他的實例屬性描述器的操作相當(dāng)與對應(yīng)的類的屬性進行操作,及實例的字典優(yōu)先級會降低,而類的字典的優(yōu)先級會升高,實際的結(jié)果是當(dāng)其成為數(shù)據(jù)屬性描述器時,其對應(yīng)的實例的字典中定義的實例屬性將會消失


屬性查找順序:
實例的dict優(yōu)先于非數(shù)據(jù)描述器(只有\(zhòng)get\方法),數(shù)據(jù)描述器優(yōu)先于實例的\dict\
及 數(shù)據(jù)描述器---> 實例的_ dict_ ---> 非數(shù)據(jù)描述器---> 類的_ dict_

4 python中描述器的應(yīng)用

描述器在python中應(yīng)用廣泛
python的方法包括(staticmethod)和classmethod() 都是通過非數(shù)據(jù)描述器來實現(xiàn)。因此實例可以重新定義和覆蓋,這允許單個實例獲取同一類的與其他實例不同的行為
property() 函數(shù)實現(xiàn)為一個數(shù)據(jù)描述器。因此實例不能覆蓋其行為
init也是非數(shù)據(jù)描述器,基本上的魔術(shù)方法都是飛數(shù)據(jù)描述器

5 練習(xí)

1 實現(xiàn) StaticMethod 裝飾器,實現(xiàn)staticmethod的部分功能

            
              #!/usr/bin/poython3.6
#conding:utf-8
class  StaticMethod:
    def __init__(self,fn):
        self.fn=fn
    def __get__(self, instance, owner): # 此方法是在調(diào)用類的屬性時存在的,是在實例字典之后調(diào)用,類字典之前調(diào)用,及相當(dāng)于類字典
        return self.fn

class A:
    @StaticMethod  #  a=StaticMethod(a)
    def  a():
        print ('123456')

x=A() #類的實例化
x.a()  # 調(diào)用實例化的函數(shù)
            
          

結(jié)果如下
python類之特殊屬性和魔術(shù)方法

2 實現(xiàn)ClassMethod 裝飾器,完成 classmethod裝飾器的功能

            
              #!/usr/bin/poython3.6
#conding:utf-8
class  ClassMethod:
    def __init__(self,fn):
        print (fn)
        self.fn=fn
    def __get__(self, instance, owner):
        print (self,instance,owner)
        return  self.fn
class A:
    @ClassMethod
    def  bar(cls):
        print (cls.__name__)

f=A.bar
print (f)
f(A) # A.bar(A) #此處需要傳入函數(shù)參數(shù)A進行處理
            
          

結(jié)果如下
python類之特殊屬性和魔術(shù)方法_第39張圖片

            
              #!/usr/bin/poython3.6
#conding:utf-8
from  functools import partial
class  ClassMethod:
    def __init__(self,fn):
        print (fn)
        self.fn=fn
    def __get__(self, instance, owner):
        print (self,instance,owner)
        return  partial(self.fn,owner)  # 偏函數(shù),使用此函數(shù)進行構(gòu)建新函數(shù)的操作,此處相當(dāng)于將self.fn替換成了owner,而owner為A類
class A:
    @ClassMethod
    def  bar(cls):
        print (cls.__name__)

A.bar()
            
          

結(jié)果如下
python類之特殊屬性和魔術(shù)方法

3 對實例的數(shù)據(jù)進行校驗

            
              #!/usr/bin/poython3.6
#conding:utf-8

class Typed:
    def __init__(self, type):
        self.type = type
    def __get__(self, instance, owner):
        pass
    def __set__(self, instance, value):  # 此處的value是類Person定義的類屬性中的類對應(yīng)的值,及就是name和age對應(yīng)的值
        if not isinstance(value, self.type):
            print ("{} is  not {} this is  type {}".format(value,self.type,type(value)))
            raise ValueError(value)
class  Person:
    name=Typed(str)  #通過此處定義type的方式完成對類型的檢測
    age=Typed(int)
    def __init__(self,name:str,age:int):
        self.name=name
        self.age=age
Person('TOM',20)
Person('jerry','30')

            
          

結(jié)果如下
python類之特殊屬性和魔術(shù)方法_第40張圖片

使用函數(shù)獲取參數(shù)簽名的方式來進行相關(guān)的判斷

            
              #!/usr/bin/poython3.6
#conding:utf-8
import  inspect
class  Typed:
    def __init__(self,type):
        self.type=type
    def __get__(self, instance, owner):
        pass
    def __set__(self, instance, value):
        if  not  isinstance(value,self.type):
            print("{} is  not {} this is  type {}".format(value, self.type, type(value)))
            raise ValueError(value)
class TypeAssert:
    def __init__(self, cls):
        self.cls = cls

    def __call__(self,name,age):
        param = inspect.signature(self.cls).parameters
        for _, v in param.items():  # 此處獲取參數(shù)屬性
            # print(v.name, v.annotation)
            if  v.annotation  !=v.empty:
                setattr(self.cls,name,Typed(v.annotation))  #此處加入?yún)?shù)數(shù)據(jù)屬性至類中
                setattr(self.cls, age, Typed(v.annotation))
@TypeAssert
class  Person:
    def __init__(self,name:str,age:int):
        self.name=name
        self.age=age

Person('tom',40)
Person('tom1','50')
Person(40,60)
            
          

結(jié)果如下

python類之特殊屬性和魔術(shù)方法_第41張圖片

4 模擬Property的功能

            
              #!/usr/bin/poython3.6
#conding:utf-8
class Property:   # 數(shù)據(jù)描述器
    def __init__(self,fget,fset=None):  #此處的fget傳遞的是data
        self.fget=fget
        self.fset=fset
    def __get__(self, instance, owner):  # instance 是A類的實例
        if instance  is not None:
            return self.fget(instance) # 此處是將data寫入
        return  self
    def __set__(self, instance, value):
        if  callable(self.fset):  #是否是可調(diào)用的
            self.fset(instance,value)
        else:
            raise  AttributeError('屬性異常')
    def  setter(self,fn):  # 此處傳遞的是最底下的data參數(shù),fn,
        self.fset=fn
        # return self.fset  # 此處若使用self.fset則表示返回的都是data.此處的data會做覆蓋,導(dǎo)致最終的結(jié)果是相同的
        return self # 要想其是描述器,必須返回此值,否則返回是類A的屬性
class  A:
    def __init__(self,data):
        self.__data=data

    @Property  #data=Property(data)
    def data(self):
        return self.__data
    @data.setter  #data=data.setter(data),此處對應(yīng)的data是上述PROPERTY 傳入data生成實例的data,此處的方法是其方法
    def data(self,value):
        self.__data=value
        return  self.__data
            
          

結(jié)果如下
python類之特殊屬性和魔術(shù)方法