1. 程式人生 > >python之面向對象的高級進階

python之面向對象的高級進階

attr 演示 not dict 屬性字典 查看 int函數 打開 重寫

一 、isinstance(obj,cls)和issubclass(sub,super)

  isinstance(obj,cls)檢查是否obj是否是類 cls 的對象

class Foo(object):
     pass
  
 obj = Foo()
  
 isinstance(obj, Foo)

  issubclass(sub, super)檢查sub類是否是 super 類的派生類

class Foo(object):
    pass
 
class Bar(Foo):
    pass
 
issubclass(Bar, Foo)

  二 反射

    1 什麽是反射

      主要是指程序可以訪問、檢測和修改它本身狀態或行為的一種能力(自省)

     2 python面向對象中的反射:通過字符串的形式操作對象相關的屬性。python中的一切事物都是對象(都可以使用反射)

  三 __setattr__,__delattr__,__getattr__

class Foo:
    x=1
    def __init__(self,y):
        self.y=y

    def __getattr__(self, item):
        print(‘----> from getattr:你找的屬性不存在‘)


    def __setattr__(self, key, value):
        print(‘----> from setattr‘)
        # self.key=value #這就無限遞歸了,你好好想想
        # self.__dict__[key]=value #應該使用它

    def __delattr__(self, item):
        print(‘----> from delattr‘)
        # del self.item #無限遞歸了
        self.__dict__.pop(item)

#__setattr__添加/修改屬性會觸發它的執行
f1=Foo(10)
print(f1.__dict__) # 因為你重寫了__setattr__,凡是賦值操作都會觸發它的運行,你啥都沒寫,就是根本沒賦值,除非你直接操作屬性字典,否則永遠無法賦值
f1.z=3
print(f1.__dict__)

#__delattr__刪除屬性的時候會觸發
f1.__dict__[‘a‘]=3#我們可以直接修改屬性字典,來完成添加/修改屬性的操作
del f1.a
print(f1.__dict__)

#__getattr__只有在使用點調用屬性且屬性不存在的時候才會觸發
f1.xxxxxx

三者的用法演示

  四 二次加工標準類型(包裝)

  

class List(list): #繼承list所有的屬性,也可以派生出自己新的,比如append和mid
    def append(self, p_object):
        ‘ 派生自己的append:加上類型檢查‘
        if not isinstance(p_object,int):
            raise TypeError(‘must be int‘)
        super().append(p_object)

    @property
    def mid(self):
        ‘新增自己的屬性‘
        index=len(self)//2
        return self[index]

l=List([1,2,3,4])
print(l)
l.append(5)
print(l)
# l.append(‘1111111‘) #報錯,必須為int類型

print(l.mid)

#其余的方法都繼承list的
l.insert(0,-123)
print(l)
l.clear()
print(l)

二次加工標準類型(基於繼承實現)

  五 __getattribute__

class Foo:
    def __init__(self,x):
        self.x=x

    def __getattr__(self, item):
        print(‘執行的是我‘)
        # return self.__dict__[item]

f1=Foo(10)
print(f1.x)
f1.xxxxxx #不存在的屬性訪問,觸發__getattr__

回顧__getattr__

  

class Foo:
    def __init__(self,x):
        self.x=x

    def __getattribute__(self, item):
        print(‘不管是否存在,我都會執行‘)

f1=Foo(10)
f1.x
f1.xxxxxx

__getattribute__

  

#_*_coding:utf-8_*_
__author__ = ‘Linhaifeng‘

class Foo:
    def __init__(self,x):
        self.x=x

    def __getattr__(self, item):
        print(‘執行的是我‘)
        # return self.__dict__[item]
    def __getattribute__(self, item):
        print(‘不管是否存在,我都會執行‘)
        raise AttributeError(‘哈哈‘)

f1=Foo(10)
f1.x
f1.xxxxxx

#當__getattribute__與__getattr__同時存在,只會執行__getattrbute__,除非__getattribute__在執行過程中拋出異常AttributeError

二者同時出現

  六 描述符(__get__,__set__,__delete__)

   描述符是什麽:描述符本質就是一個新式類,在這個新式類中,至少實現了__get__(),__set__(),__delete__()中的一個,這也被稱為描述符協議
  __get__():調用一個屬性時,觸發
  __set__():為一個屬性賦值時,觸發
  __delete__():采用del刪除屬性時,觸發 

class Foo: #在python3中Foo是新式類,它實現了三種方法,這個類就被稱作一個描述符
    def __get__(self, instance, owner):
        pass
    def __set__(self, instance, value):
        pass
    def __delete__(self, instance):
        pass

定義一個描述符

  2 描述符是幹什麽的:描述符的作用是用來代理另外一個類的屬性的(必須把描述符定義成這個類的類屬性,不能定義到構造函數中)  

class Foo:
    def __get__(self, instance, owner):
        print(‘觸發get‘)
    def __set__(self, instance, value):
        print(‘觸發set‘)
    def __delete__(self, instance):
        print(‘觸發delete‘)

#包含這三個方法的新式類稱為描述符,由這個類產生的實例進行屬性的調用/賦值/刪除,並不會觸發這三個方法
f1=Foo()
f1.name=‘egon‘
f1.name
del f1.name
#疑問:何時,何地,會觸發這三個方法的執行

引子:描述符類產生的實例進行屬性操作並不會觸發三個方法的執行

  

#描述符Str
class Str:
    def __get__(self, instance, owner):
        print(‘Str調用‘)
    def __set__(self, instance, value):
        print(‘Str設置...‘)
    def __delete__(self, instance):
        print(‘Str刪除...‘)

#描述符Int
class Int:
    def __get__(self, instance, owner):
        print(‘Int調用‘)
    def __set__(self, instance, value):
        print(‘Int設置...‘)
    def __delete__(self, instance):
        print(‘Int刪除...‘)

class People:
    name=Str()
    age=Int()
    def __init__(self,name,age): #name被Str類代理,age被Int類代理,
        self.name=name
        self.age=age

#何地?:定義成另外一個類的類屬性

#何時?:且看下列演示

p1=People(‘alex‘,18)

#描述符Str的使用
p1.name
p1.name=‘egon‘
del p1.name

#描述符Int的使用
p1.age
p1.age=18
del p1.age

#我們來瞅瞅到底發生了什麽
print(p1.__dict__)
print(People.__dict__)

#補充
print(type(p1) == People) #type(obj)其實是查看obj是由哪個類實例化來的
print(type(p1).__dict__ == People.__dict__)

描述符應用之何時?何地?

  3 描述符分兩種
  一 數據描述符:至少實現了__get__()和__set__()

  

class Foo:
     def __set__(self, instance, value):
         print(‘set‘)
      def __get__(self, instance, owner):
        print(‘get‘)

  二 非數據描述符:沒有實現__set__() 

class Foo:
      def __get__(self, instance, owner):
         print(‘get‘)

  4 註意事項:
  一 描述符本身應該定義成新式類,被代理的類也應該是新式類
  二 必須把描述符定義成這個類的類屬性,不能為定義到構造函數中
  三 要嚴格遵循該優先級,優先級由高到底分別是
  1.類屬性
  2.數據描述符
  3.實例屬性
  4.非數據描述符
  5.找不到的屬性觸發__getattr__()

六 再看property

  一個靜態屬性property本質就是實現了get,set,delete三種方法

class Foo:
    @property
    def AAA(self):
        print(‘get的時候運行我啊‘)

    @AAA.setter
    def AAA(self,value):
        print(‘set的時候運行我啊‘)

    @AAA.deleter
    def AAA(self):
        print(‘delete的時候運行我啊‘)

#只有在屬性AAA定義property後才能定義AAA.setter,AAA.deleter
f1=Foo()
f1.AAA
f1.AAA=‘aaa‘
del f1.AAA

用法一

  

class Foo:
    def get_AAA(self):
        print(‘get的時候運行我啊‘)

    def set_AAA(self,value):
        print(‘set的時候運行我啊‘)

    def delete_AAA(self):
        print(‘delete的時候運行我啊‘)
    AAA=property(get_AAA,set_AAA,delete_AAA) #內置property三個參數與get,set,delete一一對應

f1=Foo()
f1.AAA
f1.AAA=‘aaa‘
del f1.AAA

用法二

七 __setitem__,__getitem,__delitem__

class Foo:
    def __init__(self,name):
        self.name=name

    def __getitem__(self, item):
        print(self.__dict__[item])

    def __setitem__(self, key, value):
        self.__dict__[key]=value
    def __delitem__(self, key):
        print(‘del obj[key]時,我執行‘)
        self.__dict__.pop(key)
    def __delattr__(self, item):
        print(‘del obj.key時,我執行‘)
        self.__dict__.pop(item)

f1=Foo(‘sb‘)
f1[‘age‘]=18
f1[‘age1‘]=19
del f1.age1
del f1[‘age‘]
f1[‘name‘]=‘alex‘
print(f1.__dict__)

八 __str__,__repr__,__format__

  改變對象的字符串顯示__str__,__repr__

  自定制格式化字符串__format__

#_*_coding:utf-8_*_
__author__ = ‘Linhaifeng‘
format_dict={
    ‘nat‘:‘{obj.name}-{obj.addr}-{obj.type}‘,#學校名-學校地址-學校類型
    ‘tna‘:‘{obj.type}:{obj.name}:{obj.addr}‘,#學校類型:學校名:學校地址
    ‘tan‘:‘{obj.type}/{obj.addr}/{obj.name}‘,#學校類型/學校地址/學校名
}
class School:
    def __init__(self,name,addr,type):
        self.name=name
        self.addr=addr
        self.type=type

    def __repr__(self):
        return ‘School(%s,%s)‘ %(self.name,self.addr)
    def __str__(self):
        return ‘(%s,%s)‘ %(self.name,self.addr)

    def __format__(self, format_spec):
        # if format_spec
        if not format_spec or format_spec not in format_dict:
            format_spec=‘nat‘
        fmt=format_dict[format_spec]
        return fmt.format(obj=self)

s1=School(‘oldboy1‘,‘北京‘,‘私立‘)
print(‘from repr: ‘,repr(s1))
print(‘from str: ‘,str(s1))
print(s1)

‘‘‘
str函數或者print函數--->obj.__str__()
repr或者交互式解釋器--->obj.__repr__()
如果__str__沒有被定義,那麽就會使用__repr__來代替輸出
註意:這倆方法的返回值必須是字符串,否則拋出異常
‘‘‘
print(format(s1,‘nat‘))
print(format(s1,‘tna‘))
print(format(s1,‘tan‘))
print(format(s1,‘asfdasdffd‘))

  九 __slots__

  

‘‘‘
1.__slots__是什麽:是一個類變量,變量值可以是列表,元祖,或者可叠代對象,也可以是一個字符串(意味著所有實例只有一個數據屬性)
2.引子:使用點來訪問屬性本質就是在訪問類或者對象的__dict__屬性字典(類的字典是共享的,而每個實例的是獨立的)
3.為何使用__slots__:字典會占用大量內存,如果你有一個屬性很少的類,但是有很多實例,為了節省內存可以使用__slots__取代實例的__dict__
當你定義__slots__後,__slots__就會為實例使用一種更加緊湊的內部表示。實例通過一個很小的固定大小的數組來構建,而不是為每個實例定義一個
字典,這跟元組或列表很類似。在__slots__中列出的屬性名在內部被映射到這個數組的指定小標上。使用__slots__一個不好的地方就是我們不能再給
實例添加新的屬性了,只能使用在__slots__中定義的那些屬性名。
4.註意事項:__slots__的很多特性都依賴於普通的基於字典的實現。另外,定義了__slots__後的類不再 支持一些普通類特性了,比如多繼承。大多數情況下,你應該
只在那些經常被使用到 的用作數據結構的類上定義__slots__比如在程序中需要創建某個類的幾百萬個實例對象 。
關於__slots__的一個常見誤區是它可以作為一個封裝工具來防止用戶給實例增加新的屬性。盡管使用__slots__可以達到這樣的目的,但是這個並不是它的初衷。           更多的是用來作為一個內存優化工具。

‘‘‘
class Foo:
    __slots__=‘x‘


f1=Foo()
f1.x=1
f1.y=2#報錯
print(f1.__slots__) #f1不再有__dict__

class Bar:
    __slots__=[‘x‘,‘y‘]
    
n=Bar()
n.x,n.y=1,2
n.z=3#報錯

__slots__使用

  

class Foo:
    __slots__=[‘name‘,‘age‘]

f1=Foo()
f1.name=‘alex‘
f1.age=18
print(f1.__slots__)

f2=Foo()
f2.name=‘egon‘
f2.age=19
print(f2.__slots__)

print(Foo.__dict__)
#f1與f2都沒有屬性字典__dict__了,統一歸__slots__管,節省內存

刨根問底

  十 __next__和__iter__實現叠代器協議

#_*_coding:utf-8_*_
__author__ = ‘Linhaifeng‘
class Foo:
    def __init__(self,x):
        self.x=x

    def __iter__(self):
        return self

    def __next__(self):
        n=self.x
        self.x+=1
        return self.x

f=Foo(3)
for i in f:
    print(i)

簡單示範

  

class Foo:
    def __init__(self,start,stop):
        self.num=start
        self.stop=stop
    def __iter__(self):
        return self
    def __next__(self):
        if self.num >= self.stop:
            raise StopIteration
        n=self.num
        self.num+=1
        return n

f=Foo(1,5)
from collections import Iterable,Iterator
print(isinstance(f,Iterator))

for i in Foo(1,5):
    print(i)

十一 __doc__

class Foo:
    ‘我是描述信息‘
    pass

print(Foo.__doc__)

  

class Foo:
    ‘我是描述信息‘
    pass

class Bar(Foo):
    pass
print(Bar.__doc__) #該屬性無法繼承給子類

該屬性無法被繼承

十二 __module__和__class__

  __module__ 表示當前操作的對象在那個模塊

  __class__ 表示當前操作的對象的類是什麽

#!/usr/bin/env python
# -*- coding:utf-8 -*-

class C:

    def __init__(self):
        self.name = ‘SB‘

lib/aa.py 
from lib.aa import C

obj = C()
print obj.__module__  # 輸出 lib.aa,即:輸出模塊
print obj.__class__      # 輸出 lib.aa.C,即:輸出類

十三 __del__

析構方法,當對象在內存中被釋放時,自動觸發執行。

註:如果產生的對象僅僅只是python程序級別的(用戶級),那麽無需定義__del__,如果產生的對象的同時還會向操作系統發起系統調用,即一個對象有用戶級與內核級兩種資源,比如(打開一個文件,創建一個數據庫鏈接),則必須在清除對象的同時回收系統資源,這就用到了__del__

python之面向對象的高級進階