python面向對象高級

__str__,__repr__

  • __str__定義在類內部,必須返回一個字符串類型
  • __repr__定義在類內部,必須返回一個字符串類型
  • 打印由這個類產生的對象時,會觸發執行__str__,若是沒有__str__會觸發__repr__
class Bar:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    # def __str__(self):
    #     print("this str")
    #     return "名字是%s age%s" % (self.name, self.age)

    def __repr__(self):  # 轉換字符串,在解釋器中執行
        print("thsi repr")
        return "%s" % self.name

test = Bar("alex", 18)
print(test)
#__str__定義在類內部,必須返回一個字符串類型,
#__repr__定義在類內部,必須返回一個字符串類型,
#打印由這個類產生的對象時,會觸發執行__str__,若是沒有__str__會觸發__repr__
__str__ 和 __repr__

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

  • isinstance(obj,cls)檢查對象(obj)是不是類(類的對象)
  • issubclass(sub, super)檢查字(sub)類是不是父( super) 類的派生類
class Bar:
    pass
class Foo(Bar):
    pass

obj=Foo()

print(isinstance(obj,Foo))
print(Foo.__bases__)
print(issubclass(Foo,Bar))

"""
True
(<class '__main__.Bar'>,)
True
"""
isinstance issubclass

attr系列

  • __getattr__只有在使用點調用屬性且屬性不存在的時候纔會觸發
  • __setattr__添加/修改屬性會觸發它的執行
  • __delattr__刪除屬性的時候會觸發
class Foo:
    def __init__(self,x):
        self.name=x

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

    def __setattr__(self, key, value): #這的key類型是str
        print('----> from setattr')
        # if not isinstance(value,str):
        #     raise TypeError('must be str')
        #setattr(self,key,value)和 self.key=value  #這就無限遞歸了,你好好想一想
        self.__dict__[key]=value

    def __delattr__(self, item): #這的item類型是str
        print('----> from delattr')
        # delattr(self,item)和 del self.item #這就無限遞歸了
        self.__dict__.pop(item)

# __setattr__添加/修改屬性會觸發它的執行
f1 = Foo('alex') #設置屬性 觸發__setattr__
print(f1.__dict__)  # 由於你重寫了__setattr__,凡是賦值操做都會觸發它的運行,你啥都沒寫,就是根本沒賦值,除非你直接操做屬性字典,不然永遠沒法賦值
f1.z = 3  # 添加屬性 觸發__setattr__
print(f1.__dict__)
setattr(f1,'nn',55) #添加屬性 觸發__setattr__
print(f1.__dict__)
f1.__dict__['a'] = 3  # 能夠直接修改屬性字典,來完成添加/修改屬性的操做,這樣的操做不觸發__setattr__
print(f1.__dict__)

# __delattr__刪除屬性的時候會觸發
del f1.a             #觸發__delattr__
print(f1.__dict__)
delattr(f1,'nn')    #觸發__delattr__
print(f1.__dict__)
f1.__dict__.pop('z')    #不觸發__delattr__
print(f1.__dict__)

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

__getattr__和__getattribute__

  • 當__getattribute__與__getattr__同時存在,只會執行__getattrbute__,除非__getattribute__在執行過程當中拋出異常AttributeError
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__
"""
10
執行的是我
"""

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

    def __getattribute__(self, item):
        print('無論是否存在,我都會執行')

f1=Foo(10)
f1.x
f1.xxxxxx
"""
無論是否存在,我都會執行
無論是否存在,我都會執行
"""

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__

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

一、基於繼承的原理定製本身的數據類型

包裝:python爲你們提供了標準數據類型,以及豐富的內置方法,其實在不少場景下咱們都須要基於標準數據類型來定製咱們本身的數據類型,新增/改寫方法,這就用到了咱們剛學的繼承/派生知識(其餘的標準類型都可以經過下面的方式進行二次加工)javascript

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)
繼承實現

二、受權的方式實現定製本身的數據類型

受權:受權是包裝的一個特性, 包裝一個類型一般是對已存在的類型的一些定製,這種作法能夠新建,修改或刪除原有產品的功能。其它的則保持原樣。受權的過程,便是全部更新的功能都是由新類的某部分來處理,但已存在的功能就受權給對象的默認屬性。 實現受權的關鍵點就是覆蓋__getattr__方法java

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()
受權實現Open
class List:
    def __init__(self,x):
        self.seq=list(x)

    def append(self,value):
        if not isinstance(value,str):
            raise TypeError('must be str')
        self.seq.append(value)
    @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])

l.append('1')

print(l.mid)
l.insert(0,123123123123123123123123123)
# print(l.seq)
print(l)
受權實現List

item系列

  • 經過字典方式,查看修改,類或者對象的屬性
  • __setitem__,__getitem,__delitem__
class Foo:
    def __init__(self,name):
        self.name=name

    # 參數類型是字符串

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

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

    def __setitem__(self, key, value):
        print('setitem',type(key))
        self.__dict__[key]=value

    def __setattr__(self, key, value):
        print('----> from setattr',type(key))
        self.__dict__[key] = value

    def __delitem__(self, key):
        print('del obj[key]時,我執行',type(key))
        self.__dict__.pop(key)

    def __delattr__(self, item):
        print('----> from delattr del obj.key時,我執行',type(item))
        self.__dict__.pop(item)

#觸發__setattr__
f1=Foo('sb')
f1.gender='male'
setattr(f1,'level','high')

#觸發__setitem__
f1['age']=18
f1['age1']=19

#觸發__delattr__
del f1.age1
delattr(f1,'level')

#觸發__delitem__
del f1['age']

#觸發__getattr__
f1.xxxxxx

#觸發__getitem__
f1['name']

#什麼都不觸發
f1.__dict__['test']='aa'
f1.__dict__['test']
f1.__dict__.pop('test')
print(f1.__dict__)
item系列

__format__

  • 自定製格式化字符串__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): # format_spec指定格式化的類型
        if not format_spec or format_spec not in date_dic:
            format_spec='ymd'
        fmt=date_dic[format_spec] # 經過date_dic字典格式化成相應格式的類型
        return fmt.format(self) #把對象傳入,格式化的內容

d1=Date(2016,12,29)
print(format(d1))
print('{:mdy}'.format(d1))
"""
2016-12-26
20161226
"""
__format__

__slots__

  • __slots__是什麼:是一個類變量,變量值能夠是列表,元祖,或者可迭代對象,也能夠是一個字符串(意味着全部實例只有一個數據屬性)
  • 引子:使用點來訪問屬性本質就是在訪問類或者對象的__dict__屬性字典(類的字典是共享的,而每一個實例的是獨立的)
  • 爲什麼使用__slots__:字典會佔用大量內存,若是你有一個屬性不多的類,可是有不少實例,爲了節省內存可使用__slots__取代實例的__dict__ 當你定義__slots__後,__slots__就會爲實例使用一種更加緊湊的內部表示。實例經過一個很小的固定大小的數組來構建,而不是爲每一個實例定義一個 字典,這跟元組或列表很相似。在__slots__中列出的屬性名在內部被映射到這個數組的指定小標上。使用__slots__一個很差的地方就是咱們不能再給 實例添加新的屬性了,只能使用在__slots__中定義的那些屬性名。
  • 注意事項:__slots__的不少特性都依賴於普通的基於字典的實現。另外,定義了__slots__後的類再也不 支持一些普通類特性了,好比多繼承。大多數狀況下,你應該 只在那些常常被使用到 的用做數據結構的類上定義__slots__好比在程序中須要建立某個類的幾百萬個實例對象 。 關於__slots__的一個常見誤區是它能夠做爲一個封裝工具來防止用戶給實例增長新的屬性。儘管使用__slots__能夠達到這樣的目的,可是這個並非它的初衷。
class Poo:
    __slots__ = 'x'

p1 = Poo()
p1.x = 1
# p1.y = 2  # 報錯
print(p1.__slots__)  #打印x  p1再也不有__dict__


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


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

f1=Foo()
f1.name='alex'
f1.age=18
print(f1.__slots__) #['name', 'age']

f2=Foo()
f2.name='egon'
f2.age=19
print(f2.__slots__) #['name', 'age']
print(Foo.__dict__)#f1與f2都沒有屬性字典__dict__了,統一歸__slots__管,節省內存
"""
{
'age': <member 'age' of 'Foo' objects>, 
'name': <member 'name' of 'Foo' objects>, 
'__module__': '__main__', '__doc__': None, 
'__slots__': ['name', 'age']}ame', 'age']
}
"""
__slots__

__next__和__iter__實現迭代器協議

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)

"""
True
1
2
3
4
"""
__iter__,__next__
class Range:
    def __init__(self,start,end,step):
        self.start=start
        self.end=end
        self.step=step

    def __next__(self):
        if self.start >= self.end:
            raise StopIteration
        x=self.start
        self.start+=self.step
        return x

    def __iter__(self):
        return self

for i in Range(1,7,2):
    print(i) #1  3   5
簡單模擬range
class Fib:
    def __init__(self):
        self._a=0
        self._b=1

    def __iter__(self):
        return self

    def __next__(self):
        self._a,self._b=self._b,self._a + self._b
        return self._a

f1=Fib()

print(f1.__next__())
print(next(f1))
print(next(f1))

for i in f1:
    if i > 100:
        break
    print('%s ' %i,end='')

'''
1
1
2
3 5 8 13 21 34 55 89 
'''
斐波那契數列

__doc__

  • 它類的描述信息
  • 該屬性沒法繼承給子類
class Foo:
    #我是描述信息
    pass

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

__module__和__class__

  • __module__ 表示當前操做的對象在那個模塊
  • __class__ 表示當前操做的對象的類是什麼
#test.py
class C:
    def __init__(self):
        self.name = '測試'


#aa.py
from test import C
class Foo:
    pass

f1 = Foo()
print(f1.__module__) #__main__
print(f1.__class__)  #<class '__main__.Foo'>

c1 = C()
print(c1.__module__) #test  即:輸出模塊
print(c1.__class__)  #<class 'test.C'>  即:輸出類
__module__,__class__

__del__析構方法

  • 狀況1:當對象在內存中被釋放時,自動觸發執行。
  • 狀況2:人爲主動del f對象時(若是引用計數爲零時),自動觸發執行。
  • 注:若是產生的對象僅僅只是python程序級別的(用戶級),那麼無需定義__del__,若是產生的對象的同時還會向操做系統發起系統調用,即一個對象有用戶級與內核級兩種資源,好比(打開一個文件,建立一個數據庫連接),則必須在清除對象的同時回收系統資源,這就用到了__del__
  • 典型的應用場景:
    1. 數據庫連接對象被刪除前向操做系統發起關閉數據庫連接的系統調用,回收資源
    2. 在del f以前保證f.close()執行
class Foo:
    def __del__(self):
        print('執行我啦')
f1=Foo()
del f1
print('------->')
"""
執行我啦
------->
"""


class Foo:
    def __del__(self):
        print('執行我啦')
f1=Foo()
# del f1
print('------->')
"""
------->
執行我啦
"""
__del__
"""
典型的應用場景:
建立數據庫類,用該類實例化出數據庫連接對象,對象自己是存放於用戶空間內存中,而連接則是由操做系統管理的,存放於內核空間內存中
當程序結束時,python只會回收本身的內存空間,即用戶態內存,而操做系統的資源則沒有被回收,這就須要咱們定製__del__,
在對象被刪除前向操做系統發起關閉數據庫連接的系統調用,回收資源
這與文件處理是一個道理:

f=open('a.txt') #作了兩件事,在用戶空間拿到一個f變量,在操做系統內核空間打開一個文件
del f #只回收用戶空間的f,操做系統的文件還處於打開狀態
#因此咱們應該在del f以前保證f.close()執行,即使是沒有del,程序執行完畢也會自動del清理資源,因而文件操做的正確用法應該是
f=open('a.txt')
#讀寫...
f.close()
#不少狀況下你們都容易忽略f.close,這就用到了with上下文管理
"""
import time
class Open:
    def __init__(self,filepath,mode='r',encode='utf-8'):
        self.f=open(filepath,mode=mode,encoding=encode)

    def write(self):
        pass

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

    def __del__(self):
        print('----->del')
        self.f.close()  ##

f=Open('a.txt','w')
f1=f
del f  #f1=f 引用計數不爲零
print('=========>')

"""
=========>
----->del
"""
應用場景

__enter__和__exit__實現上下文管理協議

  • with語句(也叫上下文管理協議),爲了讓一個對象兼容with語句,必須在這個對象的類中聲明__enter__和__exit__方法
  • __exit__()中的三個參數分別表明異常類型(exc_type),異常值(exc_val)和追溯信息(exc_tb),即with語句中代碼塊出現異常,則with後的代碼都沒法執行
  • 若是__exit()返回值爲True,那麼異常會被清空,就好像啥都沒發生同樣,with後的語句正常執行
  • 用途或者說好處:
    1. 使用with語句的目的就是把代碼塊放入with中執行,with結束後,自動完成清理工做,無須手動干預
    2. 在須要管理一些資源好比文件,網絡鏈接和鎖的編程環境中,能夠在__exit__中定製自動釋放資源的機制
"""
with open('a.txt') as f:
    '代碼塊'
上述叫作上下文管理協議,即with語句,爲了讓一個對象兼容with語句,必須在這個對象的類中聲明__enter__和__exit__方法
__exit__的運行完畢就表明了整個with語句的執行完畢
"""

class Open:
    def __init__(self,name):
        self.name=name
    def __enter__(self):
        print('出現with語句,對象的__enter__被觸發,有返回值則賦值給as聲明的變量')
        return self
    def __exit__(self, exc_type, exc_val, exc_tb):
        print('with中代碼塊執行完畢時執行我啊')


with Open('a.txt') as f:
    print('=====>執行代碼塊')
    # print(f,f.name) #__enter__方法的返回值賦值給as聲明的變量

"""
出現with語句,對象的__enter__被觸發,有返回值則賦值給as聲明的變量
=====>執行代碼塊
with中代碼塊執行完畢時執行我啊
"""


#__exit__()中的三個參數分別表明異常類型,異常值和追溯信息,with語句中代碼塊出現異常,則with後的代碼都沒法執行
class Open:
    def __init__(self,name):
        self.name=name

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

    def __exit__(self, exc_type, exc_val, exc_tb):
        print('with中代碼塊執行完畢時執行我啊')
        print(exc_type)
        print(exc_val)
        print(exc_tb)

with Open('a.txt') as f:
    print('=====>執行代碼塊')
    raise AttributeError('***着火啦,救火啊***') #遇到raise,with裏代碼就結束,with代碼塊裏raise後面的代碼就不會執行,with代碼結束就會執行__exit__方法
    print('11111111')  ####永遠不會打印
print('0'*100) #------------------------------->不會執行


#若是__exit()返回值爲True,那麼異常會被清空,就好像啥都沒發生同樣,with後的語句正常執行
class Open:
    def __init__(self,name):
        self.name=name

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

    def __exit__(self, exc_type, exc_val, exc_tb):
        print('with中代碼塊執行完畢時執行我啊')
        print(exc_type)
        print(exc_val)
        print(exc_tb)
        return True     #有返回值

with Open('a.txt') as f:
    print('=====>執行代碼塊')
    raise AttributeError('***着火啦,救火啊***')
    print('11111111')  ####永遠不會打印
print('0'*100) #------------------------------->會執行   #__exit__方法返回布爾值爲true的值時,with代碼塊後面的代碼會執行
__enter__, __exit__
class Open:
    def __init__(self,filepath,mode,encode='utf-8'):
        self.f=open(filepath,mode=mode,encoding=encode)
        self.filepath=filepath
        self.mode=mode
        self.encoding=encode

    def write(self,line):
        print('write')
        self.f.write(line)

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

    def __enter__(self):
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        self.f.close()
        return True

with Open('a.txt','w') as f: #f=Open('a.txt','w')
    f.write('11\n')
    f.write('22\n')
    print(sssssssssssssss) #拋出異常,交給__exit__處理
    f.write('33\n') # 這不執行

print('aaaaa')#執行
模擬Open

property

  • 一個靜態屬性property本質就是實現了get,set,delete三種方法
#被property裝飾的屬性會優先於對象的屬性被使用,只有在屬性sex定義property後才能定義sex.setter,sex.deleter
class People:
    def __init__(self,name,SEX):
        self.name=name
        # self.__sex=SEX  #這不調用@sex.setter  def sex(self,value):方法  由於設置的是__sex 不是 sex,或者說__sex沒有被@property裝飾
        self.sex=SEX      #①由於sex被@property裝飾,因此self.sex=SEX是去找@sex.setter  def sex(self,value): 方法,而不是給對象賦值
    @property
    def sex(self):
        return self.__sex #p1.__sex
    @sex.setter
    def sex(self,value):
        print('...')
        if not isinstance(value,str):
            raise TypeError('性別必須是字符串類型')
        self.__sex=value         #② male給了value ,  self.__sex='male'
    @sex.deleter                      #del p1.sex的時候調用
    def sex(self):
        del self.__sex #del p1.__sex

p1=People('alex','male')  #會調用 @sex.setter  def sex(self,value): 方法是由於__init__中 self.sex=SEX 是給sex設置值了
p1.sex='female' #會調用@sex.setter  def sex(self,value): 方法
del p1.sex
print(p1.sex) #AttributeError: 'People' object has no attribute '_People__sex'
用法1
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
"""
get的時候運行我啊
set的時候運行我啊
delete的時候運行我啊
"""
用法2
class Goods:
    def __init__(self):
        # 原價
        self.original_price = 100
        # 折扣
        self.discount = 0.8
    @property
    def price(self):
        # 實際價格 = 原價 * 折扣
        new_price = self.original_price * self.discount
        return new_price
    @price.setter
    def price(self, value):
        self.original_price = value
    @price.deleter
    def price(self):
        del self.original_price

obj = Goods()
print(obj.price)  #  80.0
obj.price = 200   # 修改商品原價
print(obj.price)  # 160.0
del obj.price     # 刪除商品原價
修改價格
#實現類型檢測功能

#第一關:
class People:
    def __init__(self,name):
        self.name=name
    @property
    def name(self):
        return self.name

p1=People('alex') #property自動實現了set和get方法屬於數據描述符,比實例屬性優先級高,因此你這寫會觸發property內置的set,拋出異常


#第二關:修訂版
class People:
    def __init__(self,name):
        self.name=name #實例化就觸發property

    @property
    def name(self):
        # return self.name #無限遞歸
        print('get------>')
        return self.DouNiWan

    @name.setter
    def name(self,value):
        print('set------>')
        self.DouNiWan=value

    @name.deleter
    def name(self):
        print('delete------>')
        del self.DouNiWan

p1=People('alex') #self.name實際是存放到self.DouNiWan裏
print(p1.name)
print(p1.name)
print(p1.name)
print(p1.__dict__)

p1.name='egon'
print(p1.__dict__)

del p1.name
print(p1.__dict__)


#第三關:加上類型檢查
class People:
    def __init__(self,name):
        self.name=name #實例化就觸發property

    @property
    def name(self):
        # return self.name #無限遞歸
        print('get------>')
        return self.DouNiWan

    @name.setter
    def name(self,value):
        print('set------>')
        if not isinstance(value,str):
            raise TypeError('必須是字符串類型')
        self.DouNiWan=value

    @name.deleter
    def name(self):
        print('delete------>')
        del self.DouNiWan

p1=People('alex') #self.name實際是存放到self.DouNiWan裏
p1.name=1
類型檢查

__call__

  • 對象後面加括號,觸發執行。
  • 構造方法的執行是由建立對象觸發的,即:對象 = 類名()
  • 而對於 __call__ 方法的執行是由對象後加括號觸發的,即:對象() 或者 類()()
class Foo:
    def __init__(self):
        pass
    def __call__(self, *args, **kwargs):
        print('__call__')
obj = Foo()  # 執行 __init__
obj()  # 執行 __call__
__call__
class MyType(type):
    def __call__(cls, *args, **kwargs):
        print(cls)
        obj = cls.__new__(cls, *args, **kwargs)
        print('在這裏面..')
        print('==========================')
        obj.__init__(*args, **kwargs)
        return obj

class Foo(metaclass=MyType):
    def __init__(self):
        self.name = 'alex'
        print('123')

f = Foo()   #調用
print(f.name)

"""
<class '__main__.Foo'>
在這裏面..
==========================
123
alex
"""
__call__, __new__

__annotations__

def test(x:int,y:int)->int:
    return x+y
print(test.__annotations__) 
#{'x': <class 'int'>, 'y': <class 'int'>, 'return': <class 'int'>}
__annotations__

描述符(__get__,__set__,__delete__)

  • 描述符本質:
    1. 就是一個新式類,在這個新式類中,至少實現了__get__(),__set__(),__delete__()中的一個,這也被稱爲描述符協議
    2. __get__()調用一個屬性時,觸發
    3. __set__()爲一個屬性賦值是,觸發
    4. __delete__()採用del刪除屬性時,觸發
  • 描述符的做用:
    1. 是用來代理另一個類的屬性的(必須把描述符定義成這個類的類屬性,不能定義到構造函數中)
  • 描述符分兩種:
    1. 數據描述符:至少實現了__get__()和__set__()
    2. 非數據描述符:沒有實現__set__()
  • 注意事項:
    1. 描述符自己應該定義成新式類,被代理的類也應該是新式類
    2. 必須把描述符定義成這個類的類屬性,不能爲定義到構造函數中
    3. 要嚴格遵循該優先級,優先級由高到底分別是
      1. 類屬性
      2. 數據描述符
      3. 實例屬性
      4. 非數據描述符
      5. 找不到的屬性觸發__getattr__()
  • 描述符總結:
    1. 描述符是能夠實現大部分python類特性中的底層魔法,包括@classmethod,@staticmethd,@property甚至是__slots__屬性
    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
#疑問:什麼時候,何地,會觸發這三個方法的執行

#描述符分兩種
#1:數據描述符:至少實現了__get__()和__set__()
class Foo:
    def __set__(self, instance, value):
        print('set')
    def __get__(self, instance, owner):
        print('get')

#2:非數據描述符:沒有實現__set__()
class Foo:
    def __get__(self, instance, owner):
        print('get')
引子:描述符類產生的實例進行屬性操做並不會觸發三個方法的執行
#描述符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__)
描述符應用之什麼時候?何地?
類屬性>數據描述符
#描述符Str
class Str:
    def __get__(self, instance, owner):
        print('Str調用')
    def __set__(self, instance, value):
        print('Str設置...')
    def __delete__(self, instance):
        print('Str刪除...')

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


p1=People('egon',18)

#若是描述符是一個數據描述符(即有__get__又有__set__),那麼p1.name的調用與賦值都是觸發描述符的操做,於p1自己無關了,至關於覆蓋了實例的屬性
p1.name='egonnnnnn'
p1.name
print(p1.__dict__)#實例的屬性字典中沒有name,由於name是一個數據描述符,優先級高於實例屬性,查看/賦值/刪除都是跟描述符有關,與實例無關了
del p1.name
數據描述符>實例屬性
實例屬性>非數據描述符 
class Foo:
    def __set__(self, instance, value):
        print('set')
    def __get__(self, instance, owner):
        print('get')
class Room:
    name=Foo()
    def __init__(self,name,width,length):
        self.name=name
        self.width=width
        self.length=length


#name是一個數據描述符,由於name=Foo()而Foo實現了get和set方法,於是比實例屬性有更高的優先級
#對實例的屬性操做,觸發的都是描述符的
r1=Room('廁所',1,1)
r1.name
r1.name='廚房'



class Foo:
    def __get__(self, instance, owner):
        print('get')
class Room:
    name=Foo()
    def __init__(self,name,width,length):
        self.name=name
        self.width=width
        self.length=length


#name是一個非數據描述符,由於name=Foo()而Foo沒有實現set方法,於是比實例屬性有更低的優先級
#對實例的屬性操做,觸發的都是實例本身的
r1=Room('廁所',1,1)
r1.name
r1.name='廚房'
再次驗證:實例屬性>非數據描述符
非數據描述符>找不到
class Str:
    def __init__(self,name):
        self.name=name
    def __get__(self, instance, owner):
        print('get--->',instance,owner)
        return instance.__dict__[self.name]

    def __set__(self, instance, value):
        print('set--->',instance,value)
        instance.__dict__[self.name]=value
    def __delete__(self, instance):
        print('delete--->',instance)
        instance.__dict__.pop(self.name)


class People:
    name=Str('name')
    def __init__(self,name,age,salary):
        self.name=name
        self.age=age
        self.salary=salary

p1=People('egon',18,3231.3)

#調用
print(p1.__dict__)
p1.name

#賦值
print(p1.__dict__)
p1.name='egonlin'
print(p1.__dict__)

#刪除
print(p1.__dict__)
del p1.name
print(p1.__dict__)
類型限制1
class Str:
    def __init__(self,name):
        self.name=name
    def __get__(self, instance, owner):
        print('get--->',instance,owner)
        return instance.__dict__[self.name]

    def __set__(self, instance, value):
        print('set--->',instance,value)
        instance.__dict__[self.name]=value
    def __delete__(self, instance):
        print('delete--->',instance)
        instance.__dict__.pop(self.name)


class People:
    name=Str('name')
    def __init__(self,name,age,salary):
        self.name=name
        self.age=age
        self.salary=salary

#疑問:若是我用類名去操做屬性呢
People.name #報錯,錯誤的根源在於類去操做屬性時,會把None傳給instance

#修訂__get__方法
class Str:
    def __init__(self,name):
        self.name=name
    def __get__(self, instance, owner):
        print('get--->',instance,owner)
        if instance is None:
            return self
        return instance.__dict__[self.name]

    def __set__(self, instance, value):
        print('set--->',instance,value)
        instance.__dict__[self.name]=value
    def __delete__(self, instance):
        print('delete--->',instance)
        instance.__dict__.pop(self.name)


class People:
    name=Str('name')
    def __init__(self,name,age,salary):
        self.name=name
        self.age=age
        self.salary=salary
print(People.name) #完美,解決
類型限制2
class Str:
    def __init__(self,name,expected_type):
        self.name=name
        self.expected_type=expected_type
    def __get__(self, instance, owner):
        print('get--->',instance,owner)
        if instance is None:
            return self
        return instance.__dict__[self.name]

    def __set__(self, instance, value):
        print('set--->',instance,value)
        if not isinstance(value,self.expected_type): #若是不是指望的類型,則拋出異常
            raise TypeError('Expected %s' %str(self.expected_type))
        instance.__dict__[self.name]=value
    def __delete__(self, instance):
        print('delete--->',instance)
        instance.__dict__.pop(self.name)


class People:
    name=Str('name',str) #新增類型限制str
    def __init__(self,name,age,salary):
        self.name=name
        self.age=age
        self.salary=salary

p1=People(123,18,3333.3)#傳入的name因不是字符串類型而拋出異常
類型限制3
類型限制4
類型限制5
def decorate(cls):
    print('類的裝飾器開始運行啦------>')
    return cls

@decorate #無參:People=decorate(People)
class People:
    def __init__(self,name,age,salary):
        self.name=name
        self.age=age
        self.salary=salary

p1=People('egon',18,3333.3)
類的裝飾器:無參
def typeassert(**kwargs):
    def decorate(cls):
        print('類的裝飾器開始運行啦------>',kwargs)
        return cls
    return decorate
@typeassert(name=str,age=int,salary=float) #有參:1.運行typeassert(...)返回結果是decorate,此時參數都傳給kwargs 2.People=decorate(People)
class People:
    def __init__(self,name,age,salary):
        self.name=name
        self.age=age
        self.salary=salary

p1=People('egon',18,3333.3)
類的裝飾器:有參
本身作一個@property
實現延遲計算功能
一個小的改動,延遲計算的好夢就破碎了  
class ClassMethod:
    def __init__(self,func):
        self.func=func

    def __get__(self, instance, owner): #類來調用,instance爲None,owner爲類自己,實例來調用,instance爲實例,owner爲類自己,
        def feedback():
            print('在這裏能夠加功能啊...')
            return self.func(owner)
        return feedback

class People:
    name='linhaifeng'
    @ClassMethod # say_hi=ClassMethod(say_hi)
    def say_hi(cls):
        print('你好啊,帥哥 %s' %cls.name)

People.say_hi()

p1=People()
p1.say_hi()
#疑問,類方法若是有參數呢,好說,好說

class ClassMethod:
    def __init__(self,func):
        self.func=func

    def __get__(self, instance, owner): #類來調用,instance爲None,owner爲類自己,實例來調用,instance爲實例,owner爲類自己,
        def feedback(*args,**kwargs):
            print('在這裏能夠加功能啊...')
            return self.func(owner,*args,**kwargs)
        return feedback

class People:
    name='linhaifeng'
    @ClassMethod # say_hi=ClassMethod(say_hi)
    def say_hi(cls,msg):
        print('你好啊,帥哥 %s %s' %(cls.name,msg))

People.say_hi('你是那偷心的賊')

p1=People()
p1.say_hi('你是那偷心的賊')
本身作一個@classmethod
class StaticMethod:
    def __init__(self,func):
        self.func=func

    def __get__(self, instance, owner): #類來調用,instance爲None,owner爲類自己,實例來調用,instance爲實例,owner爲類自己,
        def feedback(*args,**kwargs):
            print('在這裏能夠加功能啊...')
            return self.func(*args,**kwargs)
        return feedback

class People:
    @StaticMethod# say_hi=StaticMethod(say_hi)
    def say_hi(x,y,z):
        print('------>',x,y,z)

People.say_hi(1,2,3)

p1=People()
p1.say_hi(4,5,6)
本身作一個@staticmethod  

metaclass

  • 元類是類的類,是類的模板
  • 元類是用來控制如何建立類的,正如類是建立對象的模板同樣
  • 元類的實例爲類,正如類的實例爲對象(f1對象是Foo類的一個實例,Foo類是 type 類的一個實例)
  • type是python的一個內建元類,用來直接控制生成類,python中任何class定義的類其實都是type類實例化的對象。
#type元類,是全部類的類,利用type模擬class關鍵字的建立類的過程

class Bar2:
    x=1
    def run(self):
        print('%s is runing' %self.name)
print(Bar2.__bases__)
print(Bar2.__dict__)

#用type模擬Bar2
def run(self):
    print('%s is runing' %self.name)
class_name='Bar'
bases=(object,)
class_dic={
    'x':1,
    'run':run
}
Bar=type(class_name,bases,class_dic)
print(Bar) #<class '__main__.Bar'>
print(type(Bar)) #<class 'type'>
print(Bar.__bases__)
print(Bar.__dict__)


#就是新建了一個空類
class Spam2:
    pass

print(Spam2)
print(Spam2.__bases__)
print(Spam2.__dict__)

#用type模擬Spam2
Spam=type('Spam',(),{})
print(Spam)
print(Spam.__bases__)
print(Spam.__dict__)
test 1
class Poo(metaclass=type):  #就是執行了 type('Poo',(object,),{'x':1,'run':...})
    x=1
    def run(self):
        print('running')

#__init__
class Mymeta(type):
     def __init__(self,class_name,class_bases,class_dic): #這的self就是Foo類
         for key in class_dic:
            if not callable(class_dic[key]):continue
            if not class_dic[key].__doc__:
                raise TypeError('你沒寫註釋,趕忙去寫')
         # type.__init__(self,class_name,class_bases,class_dic)

class Foo(metaclass=Mymeta):  #就是執行了 Foo=Mymeta('Foo',(object,),{'x':1,'run':...})
    x=1
    def run(self):
        'run function'
        print('running')

#__call__
class Mymeta(type):
     def __init__(self,class_name,class_bases,class_dic):
            pass
     def __call__(self, *args, **kwargs):# print(self) 這的self就是Foo類
        obj=self.__new__(self) #建一個空對象
        self.__init__(obj,*args,**kwargs) #注意參數obj   obj.name='tom'
        return obj                        #把對象返回
class Foo(metaclass=Mymeta):
    x=1
    def __init__(self,name):
        self.name=name #obj.name='tom'
    def run(self):
        print('running')

f=Foo('tom')
print(f)
print(f.name)
test 2
"""
exec:3個參數
參數 1:包含一系列python代碼的字符串
參數 2:全局做用域(字典形式),若是不指定,默認爲globals()
參數 3:局部做用域(字典形式),若是不指定,默認爲locals()
能夠把exec命令的執行當成是一個函數的執行,會將執行期間產生的名字存放於局部名稱空間中
"""
g={
    'x':1,
    'y':2
}
l={}

exec('''
global x,z
x=100
z=200

m=300
''',g,l)

print(g) #{'x': 100, 'y': 2,'z':200,......}
print(l) #{'m': 300}
exec的用法
class Foo:
    def __call__(self, *args, **kwargs):
        print(self)
        print(args)
        print(kwargs)

obj=Foo()
#一、要想讓obj這個對象變成一個可調用的對象,須要在該對象的類中定義一個方法__call__方法,該方法會在調用對象時自動觸發
#二、調用obj的返回值就是__call__方法的返回值
res=obj(1,2,3,x=1,y=2)
"""
<__main__.Foo object at 0x0000000000B0BEB8>
(1, 2, 3)
{'x': 1, 'y': 2}
"""
__call__
class OldboyTeacher(object):
    school='oldboy'

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

    def say(self):
        print('%s says welcome to the oldboy to learn Python' %self.name)

t1=OldboyTeacher('egon',18)
print(type(t1)) #查看對象t1的類是<class '__main__.OldboyTeacher'>
print(type(OldboyTeacher)) # 結果爲<class 'type'>,證實是調用了type這個元類而產生的OldboyTeacher,即默認的元類爲type

"""
一:
python中一切皆爲對象。
全部的對象都是實例化或者說調用類而獲得的(調用類的過程稱爲類的實例化),好比對象t1是調用類OldboyTeacher獲得的
元類-->實例化-->類OldboyTeacher-->實例化-->對象t1


二:
class關鍵字建立類的流程分析:
用class關鍵字定義的類自己也是一個對象,負責產生該對象的類稱之爲元類(元類能夠簡稱爲類的類),內置的元類爲type
class關鍵字在幫咱們建立類時,必然幫咱們調用了元類OldboyTeacher=type(...),那調用type時傳入的參數是什麼呢?
必然是類的關鍵組成部分,一個類有三大組成部分,分別是
一、類名class_name='OldboyTeacher'
二、基類們class_bases=(object,)
三、類的名稱空間class_dic,類的名稱空間是執行類體代碼而獲得的
調用type時會依次傳入以上三個參數
綜上,class關鍵字幫咱們建立一個類應該細分爲如下四個過程:
一、拿到類名:class_name='OldboyTeacher'
二、拿到類的基類們:class_bases=(object,)
三、執行類體代碼,拿到類的名稱空間:class_dic={...}
四、調用元類獲得類:OldboyTeacher=type(class_name,class_bases,class_dic)


三:
自定義元類控制類OldboyTeacher的建立:
一個類沒有聲明本身的元類,默認他的元類就是type,除了使用內置元類type,
咱們也能夠經過繼承type來自定義元類,而後使用metaclass關鍵字參數爲一個類指定元類
"""
class Mymeta(type): #只有繼承了type類才能稱之爲一個元類,不然就是一個普通的自定義類
    pass

class OldboyTeacher(object,metaclass=Mymeta): # OldboyTeacher=Mymeta('OldboyTeacher',(object),{...})
    school='oldboy'

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

    def say(self):
        print('%s says welcome to the oldboy to learn Python' %self.name)

"""
四:
自定義元類能夠控制類的產生過程,類的產生過程其實就是元類的調用過程,
即OldboyTeacher=Mymeta('OldboyTeacher',(object),{...}),
調用Mymeta會先產生一個空對象OldoyTeacher,
而後連同調用Mymeta括號內的參數一同傳給Mymeta下的__init__方法,完成初始化,因而咱們能夠
"""

class Mymeta(type): #只有繼承了type類才能稱之爲一個元類,不然就是一個普通的自定義類
    def __init__(self,class_name,class_bases,class_dic):
        # print(self) #<class '__main__.OldboyTeacher'>
        # print(class_bases) #(<class 'object'>,)
        # print(class_dic) #{'__module__': '__main__', '__qualname__': 'OldboyTeacher', 'school': 'oldboy', '__init__': <function OldboyTeacher.__init__ at 0x102b95ae8>, 'say': <function OldboyTeacher.say at 0x10621c6a8>}
        super(Mymeta, self).__init__(class_name, class_bases, class_dic)  # 重用父類的功能

        if class_name.islower():
            raise TypeError('類名%s請修改成駝峯體' %class_name)

        if '__doc__' not in class_dic or len(class_dic['__doc__'].strip(' \n')) == 0:
            raise TypeError('類中必須有文檔註釋,而且文檔註釋不能爲空')

class OldboyTeacher(object,metaclass=Mymeta): # OldboyTeacher=Mymeta('OldboyTeacher',(object),{...})
    """
    類OldboyTeacher的文檔註釋
    """
    school='oldboy'

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

    def say(self):
        print('%s says welcome to the oldboy to learn Python' %self.name)


"""
五:
自定義元類控制類OldboyTeacher的調用:
儲備知識:__call__
調用一個對象,就是觸發對象所在類中的__call__方法的執行,若是把OldboyTeacher也當作一個對象,
那麼在OldboyTeacher這個對象的類中也必然存在一個__call__方法
"""

class Mymeta(type): #只有繼承了type類才能稱之爲一個元類,不然就是一個普通的自定義類
    def __call__(self, *args, **kwargs):
        print(self) #<class '__main__.OldboyTeacher'>
        print(args) #('egon', 18)
        print(kwargs) #{}
        return 123

class OldboyTeacher(object,metaclass=Mymeta):
    school='oldboy'

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

    def say(self):
        print('%s says welcome to the oldboy to learn Python' %self.name)



# 調用OldboyTeacher就是在調用OldboyTeacher類中的__call__方法
# 而後將OldboyTeacher傳給self,溢出的位置參數傳給*,溢出的關鍵字參數傳給**
# 調用OldboyTeacher的返回值就是調用__call__的返回值
t1=OldboyTeacher('egon',18)
print(t1) #123


"""
六:
默認地,調用t1=OldboyTeacher('egon',18)會作三件事
一、產生一個空對象obj
二、調用__init__方法初始化對象obj
三、返回初始化好的obj
對應着,OldboyTeacher類中的__call__方法也應該作這三件事
"""
class Mymeta(type): #只有繼承了type類才能稱之爲一個元類,不然就是一個普通的自定義類
    def __call__(self, *args, **kwargs): #self=<class '__main__.OldboyTeacher'>
        #一、調用__new__產生一個空對象obj
        obj=self.__new__(self) # 此處的self是類OldoyTeacher,必須傳參,表明建立一個OldboyTeacher的對象obj

        #二、調用__init__初始化空對象obj
        self.__init__(obj,*args,**kwargs)

        #三、返回初始化好的對象obj
        return obj

class OldboyTeacher(object,metaclass=Mymeta):
    school='oldboy'

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

    def say(self):
        print('%s says welcome to the oldboy to learn Python' %self.name)

t1=OldboyTeacher('egon',18)
print(t1.__dict__) #{'name': 'egon', 'age': 18}

"""
七:
上例的__call__至關於一個模板,咱們能夠在該基礎上改寫__call__的邏輯從而控制調用OldboyTeacher的過程,
好比將OldboyTeacher的對象的全部屬性都變成私有的
"""
class Mymeta(type): #只有繼承了type類才能稱之爲一個元類,不然就是一個普通的自定義類
    def __call__(self, *args, **kwargs): #self=<class '__main__.OldboyTeacher'>
        #一、調用__new__產生一個空對象obj
        obj=self.__new__(self) # 此處的self是類OldoyTeacher,必須傳參,表明建立一個OldboyTeacher的對象obj

        #二、調用__init__初始化空對象obj
        self.__init__(obj,*args,**kwargs)

        # 在初始化以後,obj.__dict__裏就有值了
        obj.__dict__={'_%s__%s' %(self.__name__,k):v for k,v in obj.__dict__.items()}
        #三、返回初始化好的對象obj
        return obj

class OldboyTeacher(object,metaclass=Mymeta):
    school='oldboy'

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

    def say(self):
        print('%s says welcome to the oldboy to learn Python' %self.name)

t1=OldboyTeacher('egon',18)
print(t1.__dict__) #{'_OldboyTeacher__name': 'egon', '_OldboyTeacher__age': 18}

"""
八:
再看屬性查找:
上例中涉及到查找屬性的問題,好比self.__new__
結合python繼承的實現原理+元類從新看屬性的查找應該是什麼樣子呢???

在學習完元類後,其實咱們用class自定義的類也全都是對象(包括object類自己也是元類type的 一個實例,能夠用type(object)查看),
咱們學習過繼承的實現原理,若是把類當成對象去看,將下述繼承應該說成是:對象OldboyTeacher繼承對象Foo,對象Foo繼承對象Bar,對象Bar繼承對象object
"""
class Mymeta(type): #只有繼承了type類才能稱之爲一個元類,不然就是一個普通的自定義類
    n=444

    def __call__(self, *args, **kwargs): #self=<class '__main__.OldboyTeacher'>
        obj=self.__new__(self)
        self.__init__(obj,*args,**kwargs)
        return obj

class Bar(object):
    n=333

class Foo(Bar):
    n=222

class OldboyTeacher(Foo,metaclass=Mymeta):
    n=111

    school='oldboy'

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

    def say(self):
        print('%s says welcome to the oldboy to learn Python' %self.name)


print(OldboyTeacher.n) #自下而上依次註釋各個類中的n=xxx,而後從新運行程序,發現n的查找順序爲OldboyTeacher->Foo->Bar->object->Mymeta->type

"""
九:
因而屬性查找應該分紅兩層,一層是對象層(基於c3算法的MRO)的查找,另一個層則是類層(即元類層)的查找
#查找順序:
#一、先對象層:OldoyTeacher->Foo->Bar->object
#二、而後元類層:Mymeta->type
分析下元類Mymeta中__call__裏的self.__new__的查找
"""
class Mymeta(type):
    n=444

    def __call__(self, *args, **kwargs): #self=<class '__main__.OldboyTeacher'>
        obj=self.__new__(self)
        print(self.__new__ is object.__new__) #True


class Bar(object):
    n=333

    # def __new__(cls, *args, **kwargs):
    #     print('Bar.__new__')

class Foo(Bar):
    n=222

    # def __new__(cls, *args, **kwargs):
    #     print('Foo.__new__')

class OldboyTeacher(Foo,metaclass=Mymeta):
    n=111

    school='oldboy'

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

    def say(self):
        print('%s says welcome to the oldboy to learn Python' %self.name)


    # def __new__(cls, *args, **kwargs):
    #     print('OldboyTeacher.__new__')


OldboyTeacher('egon',18) #觸發OldboyTeacher的類中的__call__方法的執行,進而執行self.__new__開始查找

"""
十:
總結,Mymeta下的__call__裏的self.__new__在OldboyTeacher、Foo、Bar裏都沒有找到__new__的狀況下,
會去找object裏的__new__,而object下默認就有一個__new__,因此即使是以前的類均未實現__new__,
也必定會在object中找到一個,根本不會、也根本不必再去找元類Mymeta->type中查找__new__

咱們在元類的__call__中也能夠用object.__new__(self)去造對象
但咱們仍是推薦在__call__中使用self.__new__(self)去創造空對象,
由於這種方式會檢索三個類OldboyTeacher->Foo->Bar,而object.__new__則是直接跨過了他們三個
"""
class Mymeta(type): #只有繼承了type類才能稱之爲一個元類,不然就是一個普通的自定義類
    n=444

    def __new__(cls, *args, **kwargs):
        obj=type.__new__(cls,*args,**kwargs) # 必須按照這種傳值方式
        print(obj.__dict__)
        # return obj # 只有在返回值是type的對象時,纔會觸發下面的__init__
        return 123

    def __init__(self,class_name,class_bases,class_dic):
        print('run。。。')


class OldboyTeacher(object,metaclass=Mymeta): #OldboyTeacher=Mymeta('OldboyTeacher',(object),{...})
    n=111

    school='oldboy'

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

    def say(self):
        print('%s says welcome to the oldboy to learn Python' %self.name)

print(type(Mymeta)) #<class 'type'>
"""
產生類OldboyTeacher的過程就是在調用Mymeta,而Mymeta也是type類的一個對象,那麼Mymeta之因此能夠調用,必定是在元類type中有一個__call__方法
該方法中一樣須要作至少三件事:
class type:
    def __call__(self, *args, **kwargs): #self=<class '__main__.Mymeta'>
        obj=self.__new__(self,*args,**kwargs) # 產生Mymeta的一個對象
        self.__init__(obj,*args,**kwargs)
        return obj
"""
分析
在元類中控制把自定義類的數據屬性都變成大寫
"""
練習二:在元類中控制自定義的類無需__init__方法
  1.元類幫其完成建立對象,以及初始化操做;
  2.要求實例化時傳參必須爲關鍵字形式,不然拋出異常TypeError: must use keyword argument
  3.key做爲用戶自定義類產生對象的屬性,且全部屬性變成大寫
"""
class Mymetaclass(type):
    # def __new__(cls,name,bases,attrs):
    #     update_attrs={}
    #     for k,v in attrs.items():
    #         if not callable(v) and not k.startswith('__'):
    #             update_attrs[k.upper()]=v
    #         else:
    #             update_attrs[k]=v
    #     return type.__new__(cls,name,bases,update_attrs)

    def __call__(self, *args, **kwargs):
        if args:
            raise TypeError('must use keyword argument for key function')
        obj = object.__new__(self) #建立對象,self爲類Foo

        for k,v in kwargs.items():
            obj.__dict__[k.upper()]=v
        return obj

class Chinese(metaclass=Mymetaclass):
    country='China'
    tag='Legend of the Dragon' #龍的傳人
    def walk(self):
        print('%s is walking' %self.name)


p=Chinese(name='egon',age=18,sex='male')
print(p.__dict__)
在元類中控制自定義的類無需__init__方法
class Mymeta(type):
    def __init__(self,class_name,class_bases,class_dic):
        #控制類Foo的建立
        super(Mymeta,self).__init__(class_name,class_bases,class_dic)

    def __call__(self, *args, **kwargs):
        #控制Foo的調用過程,即Foo對象的產生過程
        obj = self.__new__(self)
        self.__init__(obj, *args, **kwargs)
        obj.__dict__={'_%s__%s' %(self.__name__,k):v for k,v in obj.__dict__.items()}

        return obj

class Foo(object,metaclass=Mymeta):  # Foo=Mymeta(...)
    def __init__(self, name, age,sex):
        self.name=name
        self.age=age
        self.sex=sex


obj=Foo('egon',18,'male')
print(obj.__dict__)
在元類中控制自定義的類產生的對象相關的屬性所有爲隱藏屬性
#步驟五:基於元類實現單例模式
# 單例:即單個實例,指的是同一個類實例化屢次的結果指向同一個對象,用於節省內存空間
# 若是咱們從配置文件中讀取配置來進行實例化,在配置相同的狀況下,就不必重複產生對象浪費內存了
#settings.py文件內容以下
HOST='1.1.1.1'
PORT=3306

#方式一:定義一個類方法實現單例模式
import settings

class Mysql:
    __instance=None
    def __init__(self,host,port):
        self.host=host
        self.port=port

    @classmethod
    def singleton(cls):
        if not cls.__instance:
            cls.__instance=cls(settings.HOST,settings.PORT)
        return cls.__instance


obj1=Mysql('1.1.1.2',3306)
obj2=Mysql('1.1.1.3',3307)
print(obj1 is obj2) #False

obj3=Mysql.singleton()
obj4=Mysql.singleton()
print(obj3 is obj4) #True



#方式二:定製元類實現單例模式
import settings

class Mymeta(type):
    def __init__(self,name,bases,dic): #定義類Mysql時就觸發

        # 事先先從配置文件中取配置來造一個Mysql的實例出來
        self.__instance = object.__new__(self)  # 產生對象
        self.__init__(self.__instance, settings.HOST, settings.PORT)  # 初始化對象
        # 上述兩步能夠合成下面一步
        # self.__instance=super().__call__(*args,**kwargs)


        super().__init__(name,bases,dic)

    def __call__(self, *args, **kwargs): #Mysql(...)時觸發
        if args or kwargs: # args或kwargs內有值
            obj=object.__new__(self)
            self.__init__(obj,*args,**kwargs)
            return obj

        return self.__instance




class Mysql(metaclass=Mymeta):
    def __init__(self,host,port):
        self.host=host
        self.port=port



obj1=Mysql() # 沒有傳值則默認從配置文件中讀配置來實例化,全部的實例應該指向一個內存地址
obj2=Mysql()
obj3=Mysql()

print(obj1 is obj2 is obj3)

obj4=Mysql('1.1.1.4',3307)



#方式三:定義一個裝飾器實現單例模式
import settings

def singleton(cls): #cls=Mysql
    _instance=cls(settings.HOST,settings.PORT)

    def wrapper(*args,**kwargs):
        if args or kwargs:
            obj=cls(*args,**kwargs)
            return obj
        return _instance

    return wrapper


@singleton # Mysql=singleton(Mysql)
class Mysql:
    def __init__(self,host,port):
        self.host=host
        self.port=port

obj1=Mysql()
obj2=Mysql()
obj3=Mysql()
print(obj1 is obj2 is obj3) #True

obj4=Mysql('1.1.1.3',3307)
obj5=Mysql('1.1.1.4',3308)
print(obj3 is obj4) #False
基於元類實現單例模式
相關文章
相關標籤/搜索