學習python,從入門到放棄(28)
學習python,從入門到放棄(28)
反射實際案例
利用面向物件編寫系統終端功能,反射提供了一種不需要考慮程式碼的前提下,操作資料和功能。
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() obj1 = LinuxCmd() def run(obj): while True: cmd = input('請輸入您的指令>>>:') if hasattr(obj, cmd): func_name = getattr(obj, cmd) func_name() else: print('cmd command not found') run(obj) # windows系統 run(obj1) # Linux系統
面向物件的雙下方法
面向物件中的雙下方法也有一些人稱之為是魔法方法,有些雙下方法不需要刻意呼叫,到達某個條件會自動觸發,比如__init__
方法就是物件例項化的時候自動觸發。
-
__str__
物件被執行列印(print、前端展示)操作的時候自動觸發,必須返回字串型別的資料,很多時候用來更加精準的描述物件。
-
__del__
物件被執行(被動、主動)刪除操作之後自動執行。
-
__getattr__
物件查詢不存在名字的時候自動觸發。
-
__setattr__
物件在執行新增屬性操作的時候自動觸發。 obj.變數名=變數值
-
__call__
物件被加括號呼叫的時候自動觸發。
-
__enter__
物件被執行 with 上下文管理語法開始自動觸發,該方法返回什麼 as 後面的變數名就會得到什麼。
-
__exit__
物件被執行 with 上下文管理語法結束之後自動觸發。
-
__getattribute__
只要物件查詢名字無論名字是否存在都會執行該方法
如果類中有__getattribute__
方法 那麼就不會去執行__getattr__
方法。
讓字典具備句點符查詢值的功能
-
定義一個類繼承字典
class MyDict(dict): def __getattr__(self, item): return self.get(item) def __setattr__(self, key, value): self[key] = value '''要區別是名稱空間的名字還是資料k:v鍵值對''' obj = MyDict({'name': 'jason', 'age': 18})
-
給字典名稱空間新增名字
obj.pwd = 123 # 給字典名稱空間新增名字 不是資料k:v print(obj)
元類簡介
元類即產生類的類,type 本來它用來能讓你瞭解一個物件的型別,還可以動態地建立類:type 可以把對於類的描述作為引數,並返回一個類。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'>
class Teacher(MyClass):
pass
print(type(Teacher)) # <class 'type'>
產生類的兩種表現形式
-
class關鍵字
class C1(object): pass print(C1) # <class '__main__.C1'>
-
type元類
res = type('C1', (), {}) print(res) # <class '__main__.C1'>
兩種方法本質是一種,元類能夠控制類的建立,也就意味著我們可以高度定製類的行為。
元類的基本使用
元類是不能通過繼承的方式直接指定的,需要通過關鍵字引數的形式修改。
class MyTypeClass(type):
def __init__(cls, cls_name, cls_bases, cls_dict):
# print(cls, cls_name, cls_bases, cls_dict)
if not cls_name.istitle():
raise Exception("類名的首字母必須大寫")
super().__init__(cls_name, cls_bases, cls_dict)
class C1(metaclass=MyTypeClass):
school = '清華大學'
class a(metaclass=MyTypeClass):
school = '清華大學'
由上可知,在我們定義類的時候,給出了一個.istitle()
函式,來限定類名,當類 C1 建立時不會有問題,但是當建立類 a 的時候,會丟擲異常,要求首字母必須大寫。
元類進階操作
之前我們學過,物件加括號會自動執行產生該物件的類裡面的__call__
,並且該方法返回什麼物件加括號就會得到什麼。那麼由此可推導至類,類加括號會執行元類的裡面的__call__
該方法返回什麼其實類加括號就會得到什麼。
-
類裡面的
__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 __init__ run '''
__call__
方法先於__init__
方法執行。 -
定製物件的產生過程
定製,類在例項化產生物件的時候,物件的獨有資料必須採用關鍵字引數。
class MyTypeClass(type): def __call__(self, *args, **kwargs): print('__call__ run') print(args,kwargs) if args: raise Exception('必須全部採用關鍵字引數') super().__call__(*args, **kwargs) class MyClass(metaclass=MyTypeClass): def __init__(self, name): print('__init__ run') self.name = name # obj1 = MyClass('jason') obj2 = MyClass(name='jason')
編寫元類裡面的
__init__
方法,可以高度定製類的產生過程。編寫元類裡面的
__call__
方法,可以高度定製物件的產生過程。
__new__
方法
__new__
用於產生空物件(類),__init__
用於例項化物件(類),對比來說,__new__相當於骨架,__init__
相當於血肉。
但是由於__new__
方法過於底層,並不是所有的地方都可以直接呼叫__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