1.實戰一:加載配置文件純大寫的配置
# 配置文件加載:獲取配置文件中所有大寫的配置 小寫的直接忽略 組織成字典 import settings new_dict = { } # print(dir(settings)) # dir獲取括號中對象可以調用的名字 # ['AGE', 'INFO', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'desc', 'name'] for i in dir(settings): if i.isupper(): # 如果名字是純大寫 那麼獲取該大寫名字對應的值 'AGE' 'INFO' v = getattr(settings, i) new_dict[i] = v print(new_dict)
2.實戰二:模擬操作系統cmd終端執行用戶命令
class WinCmd(object): def dir(self): print('dir獲取當前目錄下所有的文件名稱') def ls(self): print('ls獲取當前路徑下所有的文件名稱') def ipconfig(self): print('ipconfig獲取當前計算機的網卡信息') obj = WinCmd() while True: cmd = input('請輸入您的命令>>>:') if hasattr(obj, cmd): cmd_name = getattr(obj, cmd) cmd_name() else: print('%s 不是內部或外部命令,也不是可運行的程序或批處理文件' % cmd)
1.魔法方法概念
魔法方法就是類中定義雙下方法,之所以會叫魔法方法原因是這些方法都是到達某個條件自動觸發,無需調用。
eg:__init__方法再給對象設置獨有數據的時候自動觸發(實例化)
2.常見的魔法方法及觸發條件
(1)__init__
class MyClass(object): def __init__(self,name): """實例化對象的時候自動觸發""" print('__init__方法') obj = MyClass
(2)__str__
class MyClass(object): def __str__(self): """ 對象被執行打印操作的時候會自動觸發 該方法必須返回一個字符串 返回什麼字符串打印對象之後就展示什麼字符串 """ # print('__str__方法') print('這是類:%s 產生的一個對象') # return '對象:%s'%self # return '對象:%s'%self return '' obj = MyClass() print(obj)
(3)__call__
class MyClass(object): def __call__(self, *args, **kwargs): """對象加括號調用 自動觸發該方法""" print('__call__方法') print(args) print(kwargs) obj = MyClass() obj(1,2,name='jaosn',age = 18) ''' __call__方法 (1, 2) {'name': 'jaosn', 'age': 18} '''
(4)__getattr__
class MyClass(object): def __getattr__(self, item): """當對象獲取一個不存在的屬性名 自動觸發 該方法返回什麼 對象獲取不存在的屬性名就會得到什麼 形參item就是對象想要獲取的不存在的屬性名 """ print('__getattr__', item) return '您想要獲取的屬性名:%s不存在' % item obj = MyClass() print(obj.age) ''' __getattr__ age 您想要獲取的屬性名:age不存在 '''
(5)__setattr__
class MyClass(object): def __init__(self,name): """實例化對象的時候自動觸發""" print('__init__方法') self.name = name def __setattr__(self, key, value): """對象操作屬性值的時候自動觸發>>>: 對象.屬性名=屬性值""" print("__setattr__") print(key) print(value) # super().__setattr__(key, value) __str__重寫之後才需要寫這個 obj = MyClass('jason') print(obj.__dict__) # {'name': 'jason'} obj.name = 'kevin' print(obj.__dict__) # {'name': 'kevin'}
(6)__del__
class MyClass(object): def __del__(self): """對象在被刪除(主動 被動)的時候自動觸發""" print('__del__') obj = MyClass() del obj # __del__
(7)__gatattribute__
class MyClass(object): def __getattribute__(self, item): print('__getattribute__') """對象獲取屬性的時候自動觸發 無論這個屬性存不存在 當類中既有__getattr__又有__getattribute__的時候 只會走後者 """ # 補充之回到__getattr__方法: return super(MyClass, self).__getattribute__(item) 復雜寫法 return super().__getattribute__(item) # 簡便寫法 obj = MyClass() obj.name # __getattribute__ print(obj.name) # None
(8)__enter__與__exit__
class MyClass(object): def __enter__(self): """對象被with語法執行的時候自動觸發 該方法返回什麼 as關鍵字後面的變量名就能得到什麼""" print('__enter__') return 123 def __exit__(self, exc_type, exc_val, exc_tb): """對象被with語法執行並運行完with子代碼之後 自動觸發""" print('__exit__') obj = MyClass() with obj as f: pass print(f) # 123 ''' __enter__ __exit__ '''
3.表格說明
項目 Value __ init__對象實例化的時候自動觸發__ str__對象被執行打印(print、前端展示)操作的時候自動觸發,該方法必須返回字符串類型的數據,很多時候用來更加精准的描述對象__ del__對象被執行(被動、主動)刪除操作之後自動執行__ getattr__對象查找不存在名字的時候自動觸發__ setattr__對象在執行添加屬性操作的時候自動觸發 >>> obj.變量名=變量值__ call__對象被加括號調用的時候自動觸發__ enter__對象被執行with上下文管理語法開始自動觸發 ,該方法返回什麼as後面的變量名就會得到什麼__ exit__對象被執行with上下文管理語法結束之後自動觸發__ getattribute__只要對象查找名字無論名字是否存在都會執行該方法,如果類中有__getattribute__方法 那麼就不會去執行__getattr__方法__ new__在類被實例化前,最先被執行的方法,主要用來去創建一個完全空白的對象
需求:補全下面代碼,執行之後不報錯
class Context: pass with Context() as f: f.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 f: f.do_something()
1.元類的引出
(1)基礎階段我們使用type來查找數據的數據類型;
(2)但是學了面向對象之後,發現查看的不是數據類型,而是數據所屬的類;s1 = '哈哈哈 今天下午終於可以敲代碼了!!!' l2 = [60, 80, 100, 120, 150, 200] d = { 'name': '死給我看', 'age': 18} print(type(s1)) # <class 'str'> print(type(l2)) # <class 'list'> print(type(d)) # <class 'dict'>
(3)而是數據所屬的類型,其實本質還是通過各個類產生了對象。
class str: pass h = 'hello' str('hello')
(4)我們可以理解為type用於查看產生當前對象的類是誰
class MyClass: pass obj = MyClass() print(type(obj)) # 查看產生對象obj的類:<class '__main__.MyClass'> print(type(MyClass)) # 查看產生對象MyClass的類:<class 'type'>
(5)通過上述推導,得出最後結論,自定義的類都是由type類產生的,我們將產生類的類稱之為“元類!!!”
1.方式一:class關鍵字
class MyClass: pass
2. 利用元類type
type(類名,類的父類,類的名稱空間)
class MyClass1: pass print(MyClass1.__dict__,MyClass1) # {'__module__': '__main__', '__dict__': <attribute '__dict__' of 'MyClass1' objects>, '__weakref__': <attribute '__weakref__' of 'MyClass1' objects>, '__doc__': None} <class '__main__.MyClass1'> res = type('MyClass2',(),{ }) print(res.__dict__,res) # {'__module__': '__main__', '__dict__': <attribute '__dict__' of 'MyClass2' objects>, '__weakref__': <attribute '__weakref__' of 'MyClass2' objects>, '__doc__': None} <class '__main__.MyClass2'> obj = res() print(obj) # <__main__.MyClass2 object at 0x00000209EA66D390>
3.類的補充
學習元類其實就是掌握了類的產生過程,我們就可以在類的產生過程中高度定制化類的行為。
eg:
類名必須首字母大寫
上述需求就需要使用元類來控制類的產生過程 在過程中校驗
1.自定義指定元類
class MyMetaClass(type): pass """只有繼承了type的類才可以稱之為是元類""" class MyClass(metaclass=MyMetaClass): pass """如果想要切換產生類的元類不能使用繼承 必須使用關鍵字metaclass聲明"""
2.元類中雙下init用於實例化類
類中的__init__用於實例化對象;元類中__init__用於實例化類.
class MyMetaClass(type): def __init__(self, what, bases=None, dict=None): # print('別暈') # print('what', what) 類名 # print('bases', bases) 類的父類 # print('dict', dict) 類的名稱空間 if not what.istitle(): # print('首字母必須大寫 你會不會寫python 面向對象學過嗎 lowB') raise Exception('首字母必須大寫 你會不會寫python 面向對象學過嗎 lowB') super().__init__(what, bases, dict) class Aaa(metaclass=MyMetaClass): pass # 沒有報錯,說明實例化類成功
元類不單單可以控制類的產生過程,其實也可以控制對象的!!!
(1)對象加括號執行產生該對象類裡面的雙下call
(2)類加括號執行產生該類的元類裡面的雙下call
回想__call__方法:
對象加括號會自動執行產生該對象的類裡面的__call__,並且該方法返回什麼對象加括號就會得到什麼
推導:類加括號會執行元類的裡面的__call__該方法返回什麼其實類加括號就會得到什麼class MyMetaClass(type): def __call__(self, *args, **kwargs): print('__call__') if args: raise Exception('必須用關鍵字參數傳參') super().__call__(*args, **kwargs) class MyClass(metaclass=MyMetaClass): def __init__(self, name, age): self.name = name self.age = age print('__init__') # 需求:實例化對象 所有的參數都必須采用關鍵字參數的形式 obj = MyClass('jason', 18) # obj = MyClass(name='jason', age=18)
總結:
如果我們想高度定制對象的產生過程:可以操作元類裡面的__call__。
如果我們想高度定制類的產生過程:可以操作元類裡面的__init__。
1.類產生對象的步驟
(1)產生一個空對象
(2)自動觸發__init__方法實例化對象
(3)返回實例化好的對象
2. __new__和__init__
__new__方法專門用於產生空對象 骨架
__init__方法專門用於給對象添加屬性 血肉
1.自定義字典並且讓字典具備
d.key = value 修改鍵值對
d.key = value 添加鍵值對
class MyClass(dict):
def __setattr__(self, key, value):
self[key] = value
super().__setattr__(key, value)
obj = MyClass()
obj.name = 'k'
obj.age = 18
obj.gender = 'male'
print(obj)
''' {'name': 'k', 'age': 18, 'gender': 'male'} '''