2022.4.11面向物件雙下方法及元類
阿新 • • 發佈:2022-04-11
2022.4.11面向物件雙下方法及元類
- 反射實際案例
- 面向物件雙下方法
- 元類
- 雙下new方法
一、反射實際案例
利用面向物件編寫終端功能
class WinCmd(object): def ls(self): print('windows系統正在執行ls命令') def dir(self): print('windows系統正在執行dir命令') def cd(self): print('windows系統正在執行cd命令') class LinuxCmd(object): def ls(self): print('Linux系統正在執行ls命令') def dir(self): print('Linux系統正在執行dir命令') def cd(self): print('Linux系統正在執行cd命令') obj = WinCmd() # 獲取物件obj操作win obj1 = LinuxCmd() # 獲取物件obj2操作linux 然後就可以使用物件利用反射方法操作資料和功能 def run(obj): while True: cmd = input('請輸入您的指令>>>:') if hasattr(obj,cmd): # 判斷物件有沒有cmd的命令 func_name = getattr(obj,cmd) # 獲取cmd對應的值 func_name() # 加括號執行函式 else: print('cmd command not found') run(obj) run(obj1)
二、面向物件的雙下方法
面向物件中的雙下方法也有一些人稱之為是魔法方法
有些雙下方法不需要刻意呼叫 到達某個條件會自動觸發
eg:雙下init, 物件例項化的時候自動觸發
1.__str__ 物件被執行列印(print、前端展示)操作的時候自動觸發 注意:該方法必須返回字串型別的資料,很多時候用來更加精準的描述物件 2.__del__ 物件被執行(被動、主動)刪除操作之後自動執行 #主動:程式碼刪除 #被動:垃圾回收 3.__getattr__ 物件查詢不存在名字的時候自動觸發 4.__setattr__ 物件在執行新增屬性操作的時候自動觸發 >>> obj.變數名=變數值 5.__call__ 物件被加括號呼叫的時候自動觸發 6.__enter__ 物件被執行with上下文管理語法開始自動觸發 該方法返回什麼as後面的變數名就會得到什麼 7.__exit__ 物件被執行with上下文管理語法結束之後自動觸發 8.__getattribute__ 只要物件查詢名字無論名字是否存在都會執行該方法 如果類中有__getattribute__方法 那麼就不會去執行__getattr__方法
筆試題講解
1、需求:讓字典具備句點符查詢值的功能
# 定義一個類繼承字典 class MyDict(dict): def __getattr__(self,item) # 查不到名字執行的程式碼 return sef.get(item) def __serattr__(self,key.value): # 新增資料時執行的程式碼 self[key] = value obj = MyDict({'name':'jason','age':18}) obj.pwd = 123 # 類中沒有這個資料,則走__setattr__,返回值是新增鍵值對 print(obj.name) # 類中沒有這個資料則走__getattr__,返回值是獲取字典v值
2、需求:補全下列程式碼,使其執行不報錯
class Context:
pass
with Context() as ctx:
ctx.do_something()
分析這幾行程式碼,首先是定義了類Context,然後使用with語句,這裡as後面的ctx就是類Context中的雙下enter的返回值,這樣我們就可以想到在類中補全這個函式
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、元類簡介
概念:即產生類的類
引入:type(123)的底層原理
print(type(123)) # <class 'int'>
print(type([12, 33, 44])) # <class 'list'>
print(type({'name':'jason','pwd':123})) # <class 'dict'>
type檢視的其實是當前物件所屬的類名稱
# 為什麼type可以識別不同資料型別呢,其實type底層是一個類,且這個類稱為元類,因此 他可以識別不同的資料型別資料什麼類,這些內建的方法就是提前定義好的類
class MyClass(object):
pass
obj = MyClass()
print(type(obj))
print(type(MyClass)) # <class 'type'>
class Student:
pass
print(type(Student)) # <class 'type'>
class Teacher(MyClass):
pass
print(type(Teacher)) # <class 'type'>
'''type就是所有類預設的元類!!!'''
2、產生類的兩種表現形式(本質是一種)
(1)class關鍵字
class C1(object):
pass
print(C1) # <class '__main__.C1'>
(2)type元類
type(類名,父類,類的名稱空間)
res = type('C1', (), {}) # 定義;類C1,沒有父類,名稱空間為空
print(res) # <class '__main__.C1'>
學習元類的目的:
元類能夠控制類的建立 也就意味著我們可以高度定製類的行為
eg:掌握了物品的生產過程 就可以在過程中做任何的額外操作
3、元類基本使用
"""元類是不能通過繼承的方式直接指定的"""
需要通過關鍵字引數的形式修改
class C1(metaclass=MyTypeClass):
pass
需求比如:要求類的名字必須首字母大寫
思考在哪裡編寫定製化程式碼?
首先要知道類名是元類type的哪裡產生的,即雙下init,因此我們就需要修改雙下init的產生過程,如下
class MyTypeClass(type):
def __init__(cls, cls_name, cls_bases, cls_dict): # 括號裡引數分別是:類名、父類、名稱空間
if not cls_name.istitle():
raise Exception("類名的首字母必須大寫 你個SD")
super().__init__(cls_name, cls_bases, cls_dict)
class C1(metaclass=MyTypeClass):
school = '清華大學'
class a(metaclass=MyTypeClass): # 類名的首字母必須大寫 你個SD
school = '清華大學'
4、元類進階操作
(1)類裡面的__init__方法和元類裡面的__call__方法執行的先後順序
class MyTypeClass(type): # 自定義元類
def __call__(self, *args, **kwargs):
print('__call__ run')
super().__call__(*args, **kwargs)
class MyClass(metaclass=MyTypeClass): # 繼承自定義元類
def __init__(self, name):
print('__init__ run')
self.name = name
obj = MyClass('jason') # __call__ run
我們知道雙下call的作用是物件加括號呼叫時才會執行的程式碼,那麼這裡自定義的元類就是重寫雙下call,可見在加括號生成物件的時候是先執行元類中的雙下call,
這樣看來,我們是否可以通過重寫元類,在元的生產過程做修改來操作產生什麼樣的類呢?
(2)定製物件的產生過程
需求:
強制規定:類在例項化產生物件的時候 物件的獨有資料必須採用關鍵字引數
class MyTypeClass(type):
def __call__(self, *args, **kwargs):
if args: # args不為空主動報錯,因為args屬於位置引數
raise Exception('必須全部採用關鍵字引數')
super().__call__(*args, **kwargs)
class MyClass(metaclass=MyTypeClass):
def __init__(self, name):
self.name = name
obj1 = MyClass('jason') # 報錯:必須全部採用關鍵字引數
obj2 = MyClass(name='jason')
因此,從這個例子可以看出來,
如果需要高度定製類的生產過程,那麼就可以重新編寫元類裡面的:雙下init方法
同理,如果想高度定製物件的產生過程,可以重新編寫元類裡面的:雙下call方法
四、雙下new方法
引入:
__new__用於產生空物件(類) 骨架
__init__用於例項化物件(類) 血肉
"""
注意:並不是所有的地方都可以直接呼叫__new__ 該方法過於底層
(1)如果是在元類的__new__裡面 可以直接呼叫
class Meta(type):
def __new__(cls, *args, **kwargs):
obj = type.__new__(cls,*args,**kwargs)
return obj
(2)如果是在元類的__call__裡面 需要間接呼叫
class Mate(type):
def __call__(self, *args, **kwargs):
obj = object.__new__(self) # 建立一個空物件
self.__init__(obj,*args,**kwargs) # 讓物件去初始化
return obj
"""