面向對象進階

isinstance(obj,cls) 檢查obj是不是cls的實例python

issubclass(obj,cls) 檢查obj是不是cls的繼承類程序員

反射

經過字符串的形式操做對象的相關的屬性。app

主要是指程序能夠訪問、檢測和修改它自己狀態或行爲的一種能力(自省)this

hasattr(obj,name) 檢查obj下是否有叫name屬性,name必須字符串

getattr(obj,name,default=None)  查詢obj下的屬性,有返回name對應的參數,沒有返回default

setattr(obj,x,y)  給obj設置屬性,x必須字符串

delattr(obj,x)    刪除obj下的屬性,x必須字符串

示例:code

class BlackMedium:
    feature='Ugly'
    def __init__(self,name,addr):
        self.name=name
        self.addr=addr

    def sell_house(self):
        print('%s 黑中介賣房子啦,傻逼纔買呢,可是誰能證實本身不傻逼' %self.name)
    def rent_house(self):
        print('%s 黑中介租房子啦,傻逼才租呢' %self.name)

b1=BlackMedium('萬成置地','回龍觀天露園')

#檢測是否含有某屬性
print(hasattr(b1,'name'))
print(hasattr(b1,'sell_house'))

#獲取屬性
n=getattr(b1,'name')
print(n)
func=getattr(b1,'rent_house')
func()

# getattr(b1,'aaaaaaaa') #報錯
print(getattr(b1,'aaaaaaaa','不存在啊'))

#設置屬性
setattr(b1,'sb',True)
setattr(b1,'show_name',lambda self:self.name+'sb')
print(b1.__dict__)
print(b1.show_name(b1))

#刪除屬性
delattr(b1,'addr')
delattr(b1,'show_name')
delattr(b1,'show_name111')#不存在,則報錯

print(b1.__dict__)

反射的好處

好處一:實現可插拔機制orm

有倆程序員,一個lili,一個是egon,lili在寫程序的時候須要用到egon所寫的類,
可是egon去跟女友度蜜月去了,尚未完成他寫的類,lili想到了反射,使用了反射機制lili能夠繼續完成本身的代碼,
等egon度蜜月回來後再繼續完成類的定義而且去實現lili想要的功能。

總之反射的好處就是,能夠事先定義好接口,接口只有在被完成後纔會真正執行,這實現了即插即用,
這實際上是一種‘後期綁定’,什麼意思?即你能夠事先把主要的邏輯寫好(只定義接口),而後後期再去實現接口的功能

好處二:動態導入模塊(基於反射當前模塊成員)對象

在模塊中導入本身:

    this_modul=sys.modules[__name__]


使用字符串導入模塊:

    一、__import__("time")

    二、import importlib
    
        importlib.import_module("time")

    推薦使用第二種

setattr,getattr,delattr 操做屬性,在類中的用法和意義

setattr

設置屬性的時候會觸發。實質是操做類.__dict__

delattr

刪除類的屬性時會觸發,實質是操做類.__dict__

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

setitem,getitem,__delitem__的使用方法

做用同上。只不過是調用的時候__setattr__那中方法是用 . 去調用。而這種是用["值"]的方式去調用。遞歸

class Foo:
    x = 1

    def __init__(self, y):
        self.y = y

    def __getitem__(self, item):   #執行對象[屬性],找不到屬性的時候會執行這個代碼。
        print('----> from getattr:你找的屬性不存在')

    def __setitem__(self, key, value):  #執行對象[屬性]=值,的時候會執行這個代碼
        self.__dict__[key]=value
 
    def __delitem__(self, item):        #執行 del 對象[屬性] 的時候會執行這個代碼。
        self.__dict__.pop(item)

f1 = Foo(10)   #初始化觸發init
print(f1.__dict__)

f1["z"]=3      #賦值觸發setitem
print(f1.__dict__)

f1["a"]        #查找不存在的屬性,觸發getitem

f1.__dict__['a'] = 3
del f1["a"]      #刪除觸發delitem
print(f1.__dict__)

定製本身的數據類型:二次加工標準類型

基於類繼承實現

class List(list): #繼承list全部的屬性,也能夠派生出本身新的,好比append和mid
    def append(self, p_object):
        
        if not isinstance(p_object,int):  #' 派生本身的append:加上類型檢查'
            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)

受權的方式

import time
class FileHandle:
    def __init__(self,filename,mode='r',encoding='utf-8'):
        self.file=open(filename,mode,encoding=encoding)
    def write(self,line):
        t=time.strftime('%Y-%m-%d %T')
        self.file.write('%s %s' %(t,line))

    def __getattr__(self, item):
        return getattr(self.file,item)

f1=FileHandle('b.txt','w+')
f1.write('你好啊')
f1.seek(0)
print(f1.read())
f1.close()


#重寫list
class List:
    def __init__(self,seq):
        self.seq=seq

    def append(self, p_object):
        ' 派生本身的append加上類型檢查,覆蓋原有的append'
        if not isinstance(p_object,int):
            raise TypeError('must be int')
        self.seq.append(p_object)

    def clear(self):
        if not self.permission:
            raise PermissionError('not allow the operation')
        self.seq.clear()    

    @property
    def mid(self):
        '新增本身的方法'
        index=len(self.seq)//2
        return self.seq[index]

    def __getattr__(self, item):
        return getattr(self.seq,item)

    def __str__(self):
        return str(self.seq)

l=List([1,2,3])
print(l)
l.append(4)
print(l)
# l.append('3333333') #報錯,必須爲int類型

print(l.mid)

#基於受權,得到insert方法
l.insert(0,-123)
print(l)

自定製格式化字符串__format__

date_dic={
    'ymd':'{0.year}:{0.month}:{0.day}',
    'dmy':'{0.day}/{0.month}/{0.year}',
    'mdy':'{0.month}-{0.day}-{0.year}',
}
class Date:
    def __init__(self,year,month,day):
        self.year=year
        self.month=month
        self.day=day

    def __format__(self, format_spec):
        if not format_spec or format_spec not in date_dic: 若是輸入的爲空或者輸入的格式不在規定的字典中,賦予默認格式

            format_spec='ymd'

        fmt=date_dic[format_spec]
        return fmt.format(self)


d1=Date(2016,12,29)
print(format(d1))           #默認輸出模式
print('{:mdy1}'.format(d1)) #沒有mdy1這個模式。因此默認ymd模式輸出


print('{:mdy}'.format(d1))  #mdy輸出模式


print(format(d1,"dmy"))     #dmy輸出模式
print("{:dmy}".format(d1))  #dmy輸出模式,同上

__next__和__iter__實現迭代器協議

from collections import Iterable(可迭代對象),Iterator(迭代器)

class Foo:
    def __init__(self,start=0,stop=0):
        self.start=start
        self.stop=stop
        self.con=0


    def __iter__(self):
        return self


    def __next__(self):
        if self.con >= self.start and self.start >=self.stop :
            raise StopIteration
        if self.stop==0:
            if self.con < self.start:
                n = self.con
                self.con += 1
                return n
        else:
            self.con=self.stop
            n=self.start
            self.start+=1
            return n

f=Foo(1,6)
print(isinstance(f,Iterator))    #判斷f是不是迭代器

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

for i in Foo(6):
    print(i)

doc

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

print(Foo.__doc__)

輸出:'我是描述信息'

這個屬性沒法被繼承接口

__enter__和exit

class Open:
    def __init__(self,name,mode="w"):
        self.x=open(name,mode=mode)
        self.name=name
        self.mode=mode

    def __enter__(self): #出現with語句,對象的__enter__被觸發,有返回值則賦值給as聲明的變量
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):  #with中代碼塊執行完畢後執行
        print("exc_type",exc_type)                  #捕獲異常。獲取異常的各個信息。
        print("exc_val",exc_val)
        print("exc_tb",exc_tb)
        return True



    def write(self,value):
        self.x.write(value)

    def __getattr__(self, item):
        return getattr(self.x,item)

# a=Open("a.txt","r")
# print(a.read())
# a.seek(0)
# print(a.read())

with Open("a.txt","r") as f:
    print(f.read())
    raise TypeError("error!")

print("-------------------------")

call

對象後面加括號,觸發執行。

注:構造方法的執行是由建立對象觸發的,即:對象 = 類名() ;而對於 call 方法的執行是由對象後加括號觸發的,即:對象() 或者 類()()

class Foo:

    def __init__(self):
        pass
    
    def __call__(self, *args, **kwargs):

        print('__call__')


obj = Foo() # 執行 __init__
obj()       # 執行 __call__

元類:

什麼是元類?

元類是類的類,是類的模板

元類是用來控制如何建立類的,正如類是建立對象的模板同樣

type是python的一個內建元類,用來直接控制生成類,python中任何class定義的類其實都是type類實例化的對象

建立類的兩種方法

方式一:

1 class Foo:
2     def func(self):
3         print('from func')

方式二:

1 def func(self):
2         print('from func')
3 x=1
4 Foo=type('Foo',(object,),{'func':func,'x':1})

一個類沒有聲明本身的元類,默認他的元類就是type,除了使用元類type,用戶也能夠經過繼承type來自定義元類

自定製元類

class B(type):
    def __init__(self,name,bases=None,dict=None):
        print("init B")
        self.name=name

    def __call__(self, *args, **kwargs):
        print("call")
        obj=self.__new__(self)
        self.__init__(obj,*args,**kwargs)
        return obj

class A(object,metaclass=B):
    def __init__(self,name):
        print("init A")
        self.name=name

    def __new__(cls, *args, **kwargs):
        print("new A")
        return super().__new__(cls)

a=A("name1")
print(a.__dict__)

精簡版

class Mytype(type):

    def __init__(self,what,bases=None,dict=None):
        print(what,bases,dict)

    def __call__(self, *args, **kwargs):
        print('--->')
        obj=object.__new__(self)
        self.__init__(obj,*args,**kwargs)
        return obj


class Room(metaclass=Mytype):

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

r1=Room('alex')
print(r1.__dict__)

總結

元類總結

class Mymeta(type):
    def __init__(self,name,bases,dic):
        print('===>Mymeta.__init__')


    def __new__(cls, *args, **kwargs):
        print('===>Mymeta.__new__')
        return type.__new__(cls,*args,**kwargs)

    def __call__(self, *args, **kwargs):
        print('aaa')
        obj=self.__new__(self)
        self.__init__(self,*args,**kwargs)
        return obj

class Foo(object,metaclass=Mymeta):
    def __init__(self,name):
        self.name=name
    def __new__(cls, *args, **kwargs):
        return object.__new__(cls)

'''
須要記住一點:名字加括號的本質(即,任何name()的形式),都是先找到name的爹,而後執行:爹.__call__

而爹.__call__通常作兩件事:
1.調用name.__new__方法並返回一個對象
2.進而調用name.__init__方法對兒子name進行初始化
'''

'''
class 定義Foo,並指定元類爲Mymeta,這就至關於要用Mymeta建立一個新的對象Foo,因而至關於執行
Foo=Mymeta('foo',(...),{...})
所以咱們能夠看到,只定義class就會有以下執行效果
===>Mymeta.__new__
===>Mymeta.__init__
實際上class Foo(metaclass=Mymeta)是觸發了Foo=Mymeta('Foo',(...),{...})操做,
遇到了名字加括號的形式,即Mymeta(...),因而就去找Mymeta的爹type,而後執行type.__call__(...)方法
因而觸發Mymeta.__new__方法獲得一個具體的對象,而後觸發Mymeta.__init__方法對對象進行初始化
'''

'''
obj=Foo('egon')
的原理同上
'''

'''
總結:元類的難點在於執行順序很繞,其實咱們只須要記住兩點就能夠了
1.誰後面跟括號,就從誰的爹中找__call__方法執行
type->Mymeta->Foo->obj
Mymeta()觸發type.__call__
Foo()觸發Mymeta.__call__
obj()觸發Foo.__call__
2.__call__內按前後順序依次調用兒子的__new__和__init__方法
'''
相關文章
相關標籤/搜索