1. 程式人生 > >python(七):元類與抽象基類

python(七):元類與抽象基類

imp 匿名 exec int 上下文 增加 abstract 分割 als

一、實例創建

  在創建實例時,調用__new__方法和__init__方法,這兩個方法在沒有定義時,是自動調用了object來實現的。python3默認創建的類是繼承了object。

class A(object):
    def __init__(self, *args, **kwargs):
        self.name, self.age, self.gender = args[:3]
    def __new__(cls, *args, **kwargs):
        print("__new__ has called.")
        return
super(A, cls).__new__(cls) # 可寫為 super().__new__(cls) 或 object.__new__(cls) a = A("Li", 27, "male") print(a.name, a.age, a.gender) """ __new__ has called. Li 27 male """

二、類的創建

  以class關鍵字開頭的上下文在定義時就已經被解釋執行。而函數(包括匿名函數)在沒被調用時是不執行的。這個過程本質上做了一件事情:從元類type那裏創建了一個名為A的類,開辟類內存空間,並讀取class語句的上下文,將類屬性和方法寫進去。

print("--解釋器開始執行--")
def func():
    print("what the hell?")

print("--開始讀取class關鍵字的上下文--")
class A:
    name = "A"
    func()
    print("--上下文結束--")
    
def fn1():
    print("--開始讀取匿名函數--")
    def fn2():
        pass
    pass
    print("--讀取結束--")
print("--解釋器執行結束--")

"""
--解釋器開始執行--
--開始讀取class關鍵字的上下文--
what the hell?
--上下文結束--
--解釋器執行結束--
"""

  " 使用class語句定義新類時,將會發生很多事情。首先,類主體將為作其自己的私有字典內的一系列語句來執行。其內容裏語句的執行與正常代碼中的執行過程相同,只是增加了會在私有成員(名稱以__開頭)上發生的名稱變形。然後,類的名稱、基類列表和字典將傳遞給元類的解構函數,以創建相應的類對象。最後,調用元類type(),這裏可以自定義。在python3中,使用class Foo(metaclass=type)來顯式地指定元類。如果沒有找到任何__metaclass__值,Python將使用默認的元類type。" -- <<python 參考手冊(第四版)>>

class_name = "Foo"    # 類名
class_parents = (object, )   # 基類
# 類主體
class_body = """
name = "Foo"
def __init__(self, x):
    self.x = x
def hello(self):
    print("Hello")
"""
class_dict = {}
# 在局部字典class_dict中執行類主體
exec(class_body, globals(), class_dict)
# 創建類對象Foo
Foo = type(class_name, class_parents, class_dict)  # type可以指定
Foo("X").hello()
# Hello

  type類創建類時,指定了類的三個部分: class_name, class_parent, class_dict。這一步是在底層實現的。

技術分享圖片
string = """name = ‘Li‘
age = 2712
"""
# 字符串必須是換行符或分號分割
dic = {}
exec(string, globals())  # globals表示執行字符串後的結果保存到全局命名空間中
print(name, age)
print(dic)
exec(string, globals(), dic)  # locals表示執行字符串後的結果保存到局部一個映射對象中
print(dic)

"""
Li 2712
{}
{‘name‘: ‘Li‘, ‘age‘: 2712}
"""
exec函數用法

  我們可以用type動態地創建類。你可以用上面的方式去實現類的上下文,也可以直接定義函數並給到字典裏,盡管它看起來有些"汙染"全局空間:

class_name = "A"
class_parent = ()

label = "hello world"

def init(self, name, age):
    self.name = name
    self.age = age
def hello(self):
    print("Hello, i‘m %s, %s." % (self.name, self.age))
    
A = type(class_name, class_parent, {"__init__": init, "hello": hello, "label": label})

a = A("Li", 18)
a.hello()
print(a.label)

"""
Hello, i‘m Li, 18.
hello world
"""

三、元類的實現過程

  

復制代碼

print("First...")
class MyType(type):
    print("MyType begin ...")
    def __init__(self, *args, **kwargs):
        print("Mytype __init__", self, *args, **kwargs , sep="\r\n", end="\r\n\r\n")
        type.__init__(self, *args, **kwargs)  # 調用type.__init__
        
    def __call__(self, *args, **kwargs):
        print("Mytype __call__", *args, **kwargs)
        obj = self.__new__(self)   # 第一個self是Foo,第二個self是F("Alex")
        print("obj ",obj, *args, **kwargs)
        print(self)
        self.__init__(obj,*args, **kwargs)
        return obj
    
    def __new__(cls, *args, **kwargs):
        print("Mytype __new__", cls, *args, **kwargs, sep="\r\n", end="\r\n\r\n")
        return type.__new__(cls, *args, **kwargs)
    print("MyType end ...")
    
print(Second...)
class Foo(metaclass=MyType):
    print("begin...")
    def __init__(self, name):
        self.name = name
        print("Foo __init__")
        
    def __new__(cls, *args, **kwargs):
        print("Foo __new__", end="\r\n\r\n")
        return object.__new__(cls)
    print("over...")
    
    def __call__(self, *args, **kwargs):
        print("Foo __call__", self, *args, **kwargs, end="\r\n\r\n")
    
print("third...")
f = Foo("Alex")
print("f",f, end="\r\n\r\n")
f()
print("fname",f.name)

"""
First...
MyType begin ...
MyType end ...
Second...
begin...
over...
Mytype __new__
<class ‘__main__.MyType‘>
Foo
()
{‘__module__‘: ‘__main__‘, ‘__qualname__‘: ‘Foo‘, ‘__init__‘: <function Foo.__init__ at 0x10ad89268>, ‘__new__‘: <function Foo.__new__ at 0x10ad89488>, ‘__call__‘: <function Foo.__call__ at 0x10ad86ae8>}

Mytype __init__
<class ‘__main__.Foo‘>
Foo
()
{‘__module__‘: ‘__main__‘, ‘__qualname__‘: ‘Foo‘, ‘__init__‘: <function Foo.__init__ at 0x10ad89268>, ‘__new__‘: <function Foo.__new__ at 0x10ad89488>, ‘__call__‘: <function Foo.__call__ at 0x10ad86ae8>}

third...
Mytype __call__ Alex
Foo __new__

obj  <__main__.Foo object at 0x10ae2ac88> Alex
<class ‘__main__.Foo‘>
Foo __init__
f <__main__.Foo object at 0x10ae2ac88>

Foo __call__ <__main__.Foo object at 0x10ae2ac88>

fname Alex

"""

  假設MyType是type類,type有三個特殊方法__init__、__call__、__new__。

  首先, First請忽略掉吧。假設底層就這樣搞了一個type類,它的名字叫MyType。

  其次,Second這一步。解釋器發現class和Foo(),會知道要從元類MyType中"實例化"一個類對象。

    它會首先掃描class Foo()的整個上下文,並分成三部分,類名、基類元組,和私有字典。

    然後它會告訴解釋器,馬上調用MyType(就是Type)類來創建一個名為Foo的類,來開辟內存空間,把這個Foo的私有字典(包括屬性和方法)給放進去。

    於是解釋器執行了MyType.__new__,並繼續執行MyType.__init__。來創建一個名為Foo的類對象。

  再次,Third這一步。

    首先通過Foo()來調用MyType.__call__,來實例化一個Foo類。它相當於Foo = Type()

    然後依次執行Foo.__new__和Foo.__init__,來實例化一個實例對象。

    Foo()相當於: MyType()(),而MyType()就是F。於是,在a = Foo(),實際上執行了MyType()()。前面說過,實例+()會調用所屬類的__call__方法,同樣地,類 + ()會調用類所屬元類(MyType)的__call__方法。

    至此,一個實例就算創建完成了。

四、抽象基類

  抽象基類有兩個特點:

    1.規定繼承類必須具有抽象基類指定的方法

    2.抽象基類無法實例化

  基於上述兩個特點,抽象基類主要用於接口設計

  實現抽象基類可以使用內置的abc模塊

import abc
class Human(metaclass=abc.ABCMeta):
    @abc.abstractmethod   # 規定子類必須有名為introduce的實例方法
    def introduce(self):
        pass
    
    @abc.abstractproperty  # 規定子類必須有名為country的裝飾器方法
    def country(self):
        pass
    
    @abc.abstractclassmethod  # 規定子類必須有名為gender的類方法
    def gender(cls):
        pass
    @abc.abstractstaticmethod  # 規定子類必須有名為hello的靜態方法
    def hello():
        pass
class Person(Human):
    __country = "China"
    def __init__(self, name, age):
        self.name = name
        self.age = age
    def introduce(self):
        return "I‘m {}, {}.".format(self.name, self.age)
    
    @property
    def country(self):
        return Person.__country
    
    @classmethod
    def gender(cls):
        return "female"
    
    @staticmethod
    def hello():
        print("What the hell?")

person = Person("Li", 24)
print(person.introduce())
print(person.country)
print(Person.gender())
person.hello()

# I‘m Li, 24.
# China
# female
# What the hell?

  collections.abc模塊收集了常用的抽象基類。感興趣的話可以打開collections.abc查看源碼。

__all__ = ["Awaitable", "Coroutine",
           "AsyncIterable", "AsyncIterator", "AsyncGenerator",
           "Hashable", "Iterable", "Iterator", "Generator", "Reversible",
           "Sized", "Container", "Callable", "Collection",
           "Set", "MutableSet",
           "Mapping", "MutableMapping",
           "MappingView", "KeysView", "ItemsView", "ValuesView",
           "Sequence", "MutableSequence",
           "ByteString",
           ]

python(七):元類與抽象基類