HTML與CSS佈局技巧
阿新 • • 發佈:2020-08-17
一:元類介紹
“元類就是深度的魔法,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}