1. 程式人生 > 其它 >學習python,從入門到放棄(28)

學習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__方法。

讓字典具備句點符查詢值的功能

  1. 定義一個類繼承字典

    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})
    
  2. 給字典名稱空間新增名字

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

產生類的兩種表現形式

  1. class關鍵字

    class C1(object):
        pass
    
    
    print(C1)  # <class '__main__.C1'>
    
  2. 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