1. 程式人生 > 實用技巧 >HTML與CSS佈局技巧

HTML與CSS佈局技巧

一:元類介紹

“元類就是深度的魔法,99%的使用者應該根本不必為此操心。

如果你想搞清楚究竟是否需要用到元類,那麼你就不需要它。

那些實際用到元類的人都非常清楚地知道他們需要做什麼,而且根本不需要解釋為什麼要用元類。”

—— Python界的領袖 Tim Peters


一切都源自一句話:在Python中,一切皆物件

元類:類他爹

定義類,可以控制物件的產生

元類,可以控制類的產生

# 元類 ==> People類 ==> obj
class People(object):
    school = 'Tinghua'

    def __init__(self, name, age):
        self.name = name
        self.age = age

    def say(self):
        print(f'Hello {self.name}!')


obj = People('xxq', 18)
# 呼叫People類 ==> 物件obj
# 呼叫元類 ==> People類

# 檢視類(型別)
print(type(obj))  # <class '__main__.People'>
print(type(People))  # <class 'type'>   # 預設的元類是:type


結論:

預設的元類是:type,預設情況下,我們用class關鍵字定義的類 都是由type產生的

二:class關鍵字 底層做了哪些事?

1.先拿到1個類名

class_name = 'People'

2.拿到 類的父類

class_bases = (object,)

3.再拿到類的名稱空間(執行類體程式碼得到的)

class_dic = {}
class_body = '''
    school = 'Tinghua'

    def __init__(self, name, age):
        self.name = name
        self.age = age

    def say(self):
        print(f'Hello {self.name}!')
'''
exec(class_body, {}, class_dic)
# print(class_dic)

補充:exec的使用

dic = {}    # 空字典
s = '''
a = 1
b = 2
c = 3
'''

exec(s, {}, dic)
print(dic)

4.呼叫元類(傳入類的3大要素:類名、基類、類的名稱空間) 得到1個元類的物件,然後將元類的變數賦值給People(這個People就是我們用class自定義的類)

People = type(class_name, class_bases, class_dic)

5.完整思路

# 1.先拿到1個類名
class_name = 'People'

# 2.拿到 類的父類
class_bases = (object,)

# 3.再拿到類的名稱空間(執行類體程式碼得到的)
class_dic = {}
class_body = '''
    school = 'Tinghua'

    def __init__(self, name, age):
        self.name = name
        self.age = age

    def say(self):
        print(f'Hello {self.name}!')
'''
exec(class_body, {}, class_dic)
# print(class_dic)

# 4.呼叫元類(傳入類的3大要素:類名、基類、類的名稱空間) 得到1個元類的物件,然後將元類的變數賦值給People(這個People就是我們用class自定義的類)
People = type(class_name, class_bases, class_dic)

三:自定義元類

1.先拿到1個類名:'People'

2.拿到 類的父類:(object,)

3.再拿到類的名稱空間(執行類體程式碼得到的){...}

4.呼叫元類(傳入類的3大要素:類名、基類、類的名稱空間) 得到1個元類的物件,然後將元類的變數賦值給People(這個People就是我們用class自定義的類)

People = Mymeta('People', (object,), {...})

1.自定義元類

只有繼承了type的類 才是自定義的元類

可以通過自定義的元類來規範類的產生

import re


class Mymeta(type):  # 只有繼承了type的類 才是自定義的元類
    def __init__(self, class_name, class_bases, class_dic):
        print(self)  # 類
        # print(class_name)
        # print(class_bases)
        # print(class_dic)

        # 規範類名
        if not re.match('[A-Z]', class_name):
            raise BaseException('類名必須用駝峰體')

        # 至少繼承一個類
        if len(class_bases) == 0:
            raise BaseException('至少繼承一個類')

        # 文件註釋
        # print('文件註釋:', class_dic.get('__doc__'))
        doc = class_dic.get('__doc__')
        if not (doc and len(doc.strip()) > 0):
            raise BaseException('必須要有文件註釋,並且註釋內容不能為空')


# People = Mymeta('People', (object,), {...})
class People(object, metaclass=Mymeta):
    '''
    這是我的文件註釋
    '''
    school = 'Tinghua'

    def __init__(self, name, age):
        self.name = name
        self.age = age

    def say(self):
        print(f'Hello {self.name}!')

2.探究是否可呼叫

①自定義的類例項化的物件和其他int、list都不可呼叫

class Foo:
    pass


obj1 = Foo()

obj2 = int(10)

obj3 = list([1, 2, 3])


# obj1()
# TypeError: 'Foo' object is not callable

# obj2()
# TypeError: 'int' object is not callable

# obj3()
# TypeError: 'list' object is not callable

②自定義的類加上__call__方法後,例項化的物件,是可以直接通過加()被呼叫的

呼叫物件 其實就是在呼叫物件類中 定義的繫結方法:__call__

class Foo:
    def __call__(self, *args, **kwargs):
        print('=========')
        print(self)
        print(args)
        print(kwargs)


obj1 = Foo()
obj1(1, 2, 3, a=1, b=2) # 呼叫物件 其實就是在呼叫物件類中 定義的繫結方法:__call__
# (1, 2, 3)
# {'a': 1, 'b': 2}

3.自定義元類 控制類的呼叫

import re


class Mymeta(type):  # 只有繼承了type的類 才是自定義的元類
    def __init__(self, class_name, class_bases, class_dic):
        print(self)  # 類
        # print(class_name)
        # print(class_bases)
        # print(class_dic)

        # 規範類名
        if not re.match('[A-Z]', class_name):
            raise BaseException('類名必須用駝峰體')

        # 至少繼承一個類
        if len(class_bases) == 0:
            raise BaseException('至少繼承一個類')

        # 文件註釋
        # print('文件註釋:', class_dic.get('__doc__'))
        doc = class_dic.get('__doc__')
        if not (doc and len(doc.strip()) > 0):
            raise BaseException('必須要有文件註釋,並且註釋內容不能為空')

    def __call__(self, *args, **kwargs):
        # 1.先建立一個空物件
        # 2.呼叫類的__init__方法,將空物件連同括號內的引數 一同傳給__init__
        # 3.將初始化好的物件 賦值給變數名obj
        return 123


# People = Mymeta('People', (object,), {...})
class People(object, metaclass=Mymeta):
    '''
    這是我的文件註釋
    '''
    school = 'Tinghua'

    def __init__(self, name, age):
        self.name = name
        self.age = age

    def say(self):
        print(f'Hello {self.name}!')


people_obj = People()  # <class '__main__.People'>

print(people_obj)  # 123

4.自定義元類 控制類的呼叫 - 進階

import re


class Mymeta(type):  # 只有繼承了type的類 才是自定義的元類
    def __init__(self, class_name, class_bases, class_dic):
        print(self)  # 類
        # print(class_name)
        # print(class_bases)
        # print(class_dic)

        # 規範類名
        if not re.match('[A-Z]', class_name):
            raise BaseException('類名必須用駝峰體')

        # 至少繼承一個類
        if len(class_bases) == 0:
            raise BaseException('至少繼承一個類')

        # 文件註釋
        # print('文件註釋:', class_dic.get('__doc__'))
        doc = class_dic.get('__doc__')
        if not (doc and len(doc.strip()) > 0):
            raise BaseException('必須要有文件註釋,並且註釋內容不能為空')

    # people_obj = People() 觸發了下面的__call__
    def __call__(self, *args, **kwargs):
        # 1.先建立一個人的空物件
        people_obj = object.__new__(self)
        # 2.呼叫類的__init__方法,將空物件連同括號內的引數 一同傳給__init__
        # People.__init__
        self.__init__(people_obj, *args, **kwargs)
        # 3.將初始化好的人物件 賦值給變數名people_obj
        return people_obj


# People = Mymeta('People', (object,), {...})
class People(object, metaclass=Mymeta):
    '''
    這是我的文件註釋
    '''
    school = 'Tinghua'

    #      people_obj,'xxq',18
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def say(self):
        print(f'Hello {self.name}!')


people_obj = People('xxq', 18)  # <class '__main__.People'>

print(people_obj)  # <__main__.People object at 0x00000200F400DD90>

print(people_obj.name)  # xxq
print(people_obj.age)  # 18
print(people_obj.say)  # <bound method People.say of <__main__.People object at 0x000001E2AE41DD90>>

# 呼叫People類的時候,發生了什麼
# 1.先建立一個人(people)的空物件
# 2.呼叫類的__init__方法,將空獨享連同括號內的引數 一同傳給__init__
# 3.將初始化好的人物件 賦值給變數名people_obj

5.自定義元類 將類的屬性隱藏

import re


class Mymeta(type):  # 只有繼承了type的類 才是自定義的元類
    def __init__(self, class_name, class_bases, class_dic):
        print(self)  # 類
        # print(class_name)
        # print(class_bases)
        # print(class_dic)

        # 規範類名
        if not re.match('[A-Z]', class_name):
            raise BaseException('類名必須用駝峰體')

        # 至少繼承一個類
        if len(class_bases) == 0:
            raise BaseException('至少繼承一個類')

        # 文件註釋
        # print('文件註釋:', class_dic.get('__doc__'))
        doc = class_dic.get('__doc__')
        if not (doc and len(doc.strip()) > 0):
            raise BaseException('必須要有文件註釋,並且註釋內容不能為空')

    # people_obj = People() 觸發了下面的__call__
    def __call__(self, *args, **kwargs):
        # 1.先建立一個人的空物件
        people_obj = object.__new__(self)
        # 2.呼叫類的__init__方法,將空物件連同括號內的引數 一同傳給__init__
        # People.__init__
        self.__init__(people_obj, *args, **kwargs)
        print(people_obj.__dict__)
        people_obj.__dict__ = {'_%s__%s' % (self.__name__, k): v for k, v in people_obj.__dict__.items()}
        # 3.將初始化好的人物件 賦值給變數名people_obj
        return people_obj


# People = Mymeta('People', (object,), {...})
class People(object, metaclass=Mymeta):
    '''
    這是我的文件註釋
    '''
    school = 'Tinghua'

    #      people_obj,'xxq',18
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def say(self):
        print(f'Hello {self.name}!')


people_obj = People('xxq', 18)
# <class '__main__.People'>
# {'name': 'xxq', 'age': 18}

print(people_obj.__dict__)
# {'_People__name': 'xxq', '_People__age': 18}