雙下方法和元類
阿新 • • 發佈:2022-04-11
總結
一、反射實際案例
二、面向物件的雙下方法
三、元類
四、元類進階
五、雙下new方法
一、反射實際案例
# 反射提供了使用者不需要考慮程式碼,作業系統的資料和功能的方法 # 利用面向物件編寫系統終端功能 class WinCmd(object): def ls(self): print('windows系統正在執行ls命令') def dir(self): print('windows系統正在執行dir命令') def cd(self): print('windows系統正在執行cd命令') obj = WinCmd() def run(obj): while True: cmd = input('請輸入您的指令>>>:').strip() if hasattr(obj, cmd): func_name = getattr(obj,cmd) func_name() else: print('cmd command not found') # 指令沒有找到 run(obj) # 通過輸入指令,來進行執行 # 同理我們也可以模擬linux系統 class LinuxCmd(object): def ls(self): print(' Linux系統正在執行ls命令') def dir(self): print(' Linux系統正在執行dir命令') def cd(self): print(' Linux系統正在執行cd命令') obj = LinuxCmd() def run(obj): while True: cmd = input('請輸入您的指令>>>:').strip() if hasattr(obj, cmd): func_name = getattr(obj,cmd) func_name() else: print('cmd command not found') # 指令沒有找到 run(obj) # 只需要增加物件就可以
二、面向物件的雙下方法
"""
面向物件中的雙下方法很神奇,而且有些雙下方法不需要可以的呼叫,到達某個條件就會自動觸發
eg:
__init__ 在物件例項化的時候 就會自動觸發
"""
- 雙下str方法(str)
__srt__ 物件被執行列印(print、前端展示)操作的時候自動觸發 這個方法必須返回字串型別的資料,如果不是直接報錯 '用來更加精準的描述物件' class student(object): def __init__(self, name): self.name = name def __str__(self): print('哎嘿哎嘿') return '蕪湖' obj = student('owen') print(obj) # 圖為: def __str__(self): print('哎嘿哎嘿') return 123 # 當返回不是字串時,報錯
- 雙下del(del)
__del__
物件被執行(被動、主動)刪除操作之後自動執行
# 被動操作
class student(object):
def __init__(self, name):
self.name = name
def __str__(self):
print('哎嘿哎嘿')
return '蕪湖'
def __del__(self):
print('del執行')
obj = student('owen')
print('起飛')
# 主動操作 class student(object): def __init__(self, name): self.name = name def __str__(self): print('哎嘿哎嘿') return '蕪湖' def __del__(self): print('del執行') obj = student('owen') del obj print('起飛')
- 雙下gettattr(getattr)
物件查詢不存在名字的時候自動觸發
class Student(object):
def __init__(self, name):
self.name = name
def __getattr__(self, item):
print('執行getattr',item)
obj = Student('owen')
print(obj.name)
print(obj.age)
- 雙下setattr(setattr)
class Student(object):
def __init__(self, name):
self.name = name
def __setattr__(self, key, value):
print('觸發setattr')
print(key, value)
obj = Student('owen')
obj.age = 10
- 雙下call(call)
# 可以支援物件加括號,可以傳值,還可以返回值
class Student(object):
def __init__(self, name):
self.name = name
def __call__(self, *args, **kwargs):
print('執行call', args, kwargs)
return '起飛'
# obj()
obj = Student('owen')
print(obj(111))
- 雙下enter(enter) 和 雙下exit(exit)
# 之所以放他們在一塊是因為他們固定在一起,要不然直接報錯
class Student(object):
def __init__(self, name):
self.name = name
def __enter__(self):
# 物件被執行with上下文管理語法開始自動觸發
# 該方法返回什麼as後面的變數名就會得到什麼
print('執行enter')
def __exit__(self, exc_type, exc_val, exc_tb):
# 物件被執行with上下文管理語法結束之後自動觸發
print('執行exit')
obj = Student('owen')
with obj as f:
print(111)
print(222)
- 雙下getattribute(getattribute)
# 只要物件查詢名字無論名字是否存在都會執行該方法
# 如果類中有__getattribute__方法 那麼就不會去執行__getattr__方法
class Student(object):
def __init__(self, name):
self.name = name
def __getattribute__(self, item):
print('執行getattribute')
obj = Student('owen')
print(obj.name)
筆試題講解
1.讓字典具備句點符查詢值的功能
# 我們要區分名稱空間的名字還是資料k:v鍵值對
class MyDict(dict):
def __getattr__(self, item):
return self.get(item)
def __setattr__(self, key, value):
self[key] = value
obj = MyDict({'name':'owen', 'age': 20})
print(obj.name)
print(obj.age)
obj.gender = 'male'
print(obj)
2.補全下列程式碼 使其執行不報錯
原題:
"""
class Context:
pass
with Context() as ctx:
ctx.do_something()
"""
# 書寫
1. 看到with方法就知道定義雙下enter和雙下exit
2.變數名加點繫結方法增加一個do_something方法
class Context:
def __enter__(self):
return self
def __exit__(self, exc_type, exc_val, exc_tb):
pass
def do_something(self):
pass
with Context() as ctx:
ctx.do_something()
三、元類
1、含義
# 元類——產生類的類
print(type(111)) # <class 'int'>
print(type([11, 22, 33])) # print(type([11, 22, 33]))
我們這裡使用type檢視就是當前物件所屬的類名稱
class MyClass(object):
pass
obj = MyClass()
print(type(obj)) # <class '__main__.MyClass'>
print(type(MyClass)) # <class 'type'>
class Student:
pass
print(type(Student)) # <class 'type'>
# 所以我們在這裡得到
""" type 就是所有類的預設的元類 """
2、產生類的兩種表現形式
1.class關鍵字
class MyClass(object):
pass
print(MyClass)
# <class '__main__.MyClass'>
2.type元類
type(類名,父類,類的名稱空間)
res = type(MyClass,(),{})
print(res)
# <class '__main__.MyClass'>
"""
我們學習元類主要因為
元類能夠控制類的建立,相當於我們可以高度定製類的行為。
eg:我們掌握了物品的生產過程,就可以在生產過程中做任何的額外操作
實際應用:要求類的名字必須首字母大寫
因為我們現在對類的產生過程還沒有那麼瞭解,所以我們思考物件的產生過程為>>>>>類裡面的__init__方法,所以我們判斷類的產生過程為>>>>>元類裡面的__init__方法
"""
3、元類的基本使用
""" 元類是不能通過繼承的方式來直接指定 ,需要通過關鍵字引數(metaclass)的形式來修改"""
class MyTypeClass(type):
def __init__(self,cls_name, cls_bases, cls_dict): # 雙下init黃色就是表達這個init方法被修改
print(self,cls_name, cls_bases, cls_dict) # <class '__main__.M1'> M1 () {'__module__': '__main__', '__qualname__': 'M1', 'school': '安徽大學'}
if not cls_name.istitle(): # 判斷首字母是不是小寫
raise Exception('類名首字母要大寫呀')
super().__init__(cls_name, cls_bases, cls_dict)
class M1(metaclass=MyTypeClass):
school = '安徽大學'
四、元類進階操作
# 思考__call__操作方法
物件加括號會制動執行產生這個物件的類裡面的__call__,並且這個方法返回什麼物件加括號就會得到什麼
so:類加括號會執行元類的裡面的__call__該方法返回什麼,類加括號就會得到什麼
class MyTypeClass(type):
def __call__(self,*args, **kwargs):
return 'bibibibi'
class MyClass(metaclass=MyTypeClass):
pass
obj = MyClass()
print(obj)
"""類裡面的__init__方法和元類裡面的__call__方法執行的先後順序"""
class MyTypeClass(type):
def __call__(self,*args, **kwargs):
print('__call__ run')
# super().__init__(*args, **kwargs)
class MyClass(metaclass=MyTypeClass):
def __init__(self, name):
print('__init__ run')
self.name = name
obj = MyClass()
# __call__ run
這裡就是先走雙下call,所以一個物件的產生先經歷元類裡面的雙下call
"""強制規定:類在例項化產生物件的時候 物件的獨有資料必須採用關鍵字引數"""
class MyTypeClass(type):
def __call__(self,*args, **kwargs):
if args:
raise Exception('必須全部用關鍵字引數')
super().__call__(*args, **kwargs)
class MyClass(metaclass=MyTypeClass):
def __init__(self, name):
self.name = name
# obj = MyClass('owen') # 報錯
obj = MyClass(name= 'owen')
"""
如果想高度定製類的產生過程
那麼編寫元類裡面的__init__方法
如果想高度定製物件的產生過程
那麼編寫元類裡面的__call__方法
"""
五、雙下new方法
"""
__new__用來產生空物件(類) 相當於人的骨架
__init__用來例項化物件(類) 相當於人的血肉
"""
class MyTypeClass(type):
def __new__(cls, *args, **kwargs):
obj = type.__new__(cls, *args, **kwargs)
return obj
class MyClass(metaclass=MyTypeClass):
def __init__(self,name):
self.name = name
obj = MyClass('owen')
print(obj) # <__main__.MyClass object at 0x0000021373C891D0>
完整版本:
"""
注意:並不是所有的地方都可以直接呼叫__new__ 該方法過於底層
如果是在元類的__new__裡面 可以直接呼叫
class Meta(type):
def __new__(cls, *args, **kwargs):
obj = type.__new__(cls,*args,**kwargs)
return obj
如果是在元類的__call__裡面 需要間接呼叫
class Mate(type):
def __call__(self, *args, **kwargs):
obj = object.__new__(self) # 建立一個空物件
self.__init__(obj,*args,**kwargs) # 讓物件去初始化
return obj
"""