程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式師世界 >> 編程語言 >  >> 更多編程語言 >> Python

小白學python系列————【Day45】面向對象魔法方法及元類詳細

編輯:Python

今日內容概要

  • 反射實戰案例
  • 面向對象的魔法方法(雙下方法)
  • 魔法方法實戰演練
  • 元類簡介
  • 創建類的兩種方式
  • 元類的實際應用
  • 元類之雙下new方法

反射實戰案例

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__。

魔法方法之雙下new方法

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'} '''

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式師世界 All Rights Reserved