面向物件,元類,控制類,物件的建立
call
呼叫的意思 在在物件被呼叫時 執行
函式 類
自定義元類 的目的 1.可以通過call 來控制物件的建立過程 2.可用控制類的建立過程 """
class MyMeta(type):
self 表示要建立物件的那個類(Person) *args是呼叫Person類時傳入的引數
• def __call__(self, *args, **kwargs):
• print("MyMte中的 call run'")
print(*args) #Person的引數
print(self) #person 類
下面的三步是固定寫法 一個模板 只要你需要控制物件的建立過程 就應該先把模板寫出來
1.建立空物件
• obj = object.__new__(self)
2.呼叫初始化方法
• self.__init__(obj,*args,**kwargs)
3.得到一個完整的物件
• return obj
修改Person類的元類為MyMeta
class Person(metaclass=MyMeta):
• def __init__(self,name,age):
• self.name = name
• self.age = age
• def __call__(self, *args, **kwargs): #程式執行的就會自動執行
• print("call run...")
呼叫Person這個物件時 執行的是 Person的類(type)中__call__ 方法
p = Person("張三",80)
print(p)
當呼叫物件時 會執行該物件所屬類中的__call__方法
p()
print(p.name)
print(p.age)
練習:
class My(type):
obj = None
def __call__(self, *args, **kwargs):
if not My.obj:
print('yes')
obj = object.__new__(self)
self.__init__(obj,*args, **kwargs)
My.obj = obj
return My.obj
class Orange(metaclass=My):
def __init__(self,name,type_1,price):
self.name = name
self.type = type_1
self.price = price
def forma(self):
print(self.__dict__)
o1 = Orange('金水橘','橘子',40)
o1.forma()
練習2:
class CarMeta(type):
• def __call__(self, *args, **kwargs):
if len(args) < 3:
raise ValueError("必須包含三個引數.....")
obj = object.__new__(self)
self.__init__(obj,*args,**kwargs)
if not("production_date" in obj.__dict__
and "engine_number" in obj.__dict__
and "capacity" in obj.__dict__):
raise ValueError("必須包含 生產日期,發動機編號,載客容量")
return obj
class BigCar(metaclass=CarMeta):
• def __init__(self,production_date,engine_number,capacity):
• self.production_date = production_date
• self.engine_number = engine_number
• self.capacity = capacity
c = BigCar("2018 12 21","e-1-3-q-f",5)
print(c)
class MyMeta(type):
1. self 剛建出來的類
2. 第二個 類的名字
3. 第三個 類的父類們 元組
4. 第四個 這個類傳進來的名稱空間
def __init__(self ,class_name ,bases ,namespace):
print("============================")
# print(self.__dict__)
# 我要控制 類的名字 必須 是大寫開頭
if not class_name.istitle():
print("類名 必須大寫開頭...... ")
# 該程式碼是主動丟擲異常
raise TypeError("類名 必須大寫開頭...... ")
# 要空類的建立 必須包含__doc__這個屬性
if not self.__doc__:
raise TypeError("類中必須有文件註釋.....")
pass
class Student(metaclass=MyMeta): # Student = MyMeta("Student",(object,),{})
"""
這是文件註釋 可以通過__doc__來獲取
這是一個學生類
"""
# 在類的__init__中可以控制該類物件的建立過程
def __init__(self ,name):
print("-----------------------")
print(self.__dict__)
self.name = name
print(Student.__doc__)
元類使用總結:
""" 元類是用於建立類的類 學習元類是為了 能控制類的建立過程 以及 類例項化物件的過程
一. 控制類的建立過程 1.建立一個元類 (需要繼承type) 2.覆蓋init方法 該方法 會將新建的類物件 類名 父類們 名稱空間 都傳進來 , 可以利用這些資訊在做處理 3.對於需要被控制的類 需要指定metaclass 為上面的元類
二. 控制類例項化物件的過程 1.建立一個元類 (需要繼承type) 2.覆蓋call方法 會將 正在例項化物件的類 呼叫類是傳入的引數 都傳進來 3.在call方法中 必須要先編寫模板程式碼 3.1建立空物件 3.2呼叫類的init方法來初始化這個空物件 3.3返回該物件 4.加入你需要控制的邏輯
類的三個組成部分 類名 父類們 名稱空間
元類 -> 例項化產生 -> 類 -> 例項化產生 -> 物件
"""
練習:
組合建立類的建立物件的技巧
class My(type):
def __init__(self,a,b,c):
if not a.istitle():
print('首字母注意大寫')
raise TypeError('類名開頭必須大寫')
if not a.__doc__:
print('註釋為空')
raise TypeError('請添加註釋')
obj = None
def __call__(self, *args, **kwargs):
print('yes')
if not My.obj:
obj = object.__new__(self)
self.__init__(obj,*args, **kwargs)
My.obj = obj
return My.obj
class Tree(metaclass=My):
def __init__(self,name,age):
self.name = name
self.age = age
def breath(self):
print('這顆%s的%s的大樹很粗壯'%(self.age,self.name))
t1=Tree('衫樹',30)
print(t1)
t2=Tree('衫樹',30)
print(t2)
t1.breath()
""" 單例模式
單個例項 一個類如果只有一個例項 那麼該類稱之為單例
為什麼需要單例
當要處理的資料全部相同時,比如印表機影印檔案時
"""
程式碼1:
class Printer():
"""
這是一個單例類 請不要直接例項化 使用get方法來獲取例項
"""
obj = None
def __init__(self,name,brand,type):
self.name = name
self.brand = brand
self.type = type
def printing(self,text):
print("正在列印 %s" % text)
以下三個物件 的資料完全相同 但是卻 佔用三分記憶體空間
p1 = Printer("ES005","愛普生","彩色印表機")
p2 = Printer("ES005","愛普生","彩色印表機")
p3 = Printer("ES005","愛普生","彩色印表機")
用類呼叫:
obj = None #定義了類的屬性
@classmethod
def get_printer(cls):
if not cls.obj:
obj = cls("ES005","愛普生","彩色印表機")
cls.obj = obj
print("建立了新的物件")
return cls.obj
通過該方法來獲取物件 可以保證只有一個物件
p = Printer.get_printer()
print(p)
p = Printer.get_printer()
print(p)
p = Printer.get_printer()
print(p)
p = Printer.get_printer()
print(p)
# 記憶體地址都相同
通過該方法來獲取物件 可以保證只有一個物件
但是這還不夠 因為 還是可以通過呼叫類產生新物件
就應該使用元類 來控制例項化的過程 call
在call 中編寫程式碼 保證每次呼叫call 都返回同一個例項 即可
現在要處理問題就是 如何能夠限制該類 只能例項化一個物件
元類
程式碼2:
class My(type):
obj = None
def __call__(self, *args, **kwargs):
if not My.obj:
obj = object.__new__(self)
self.__init__(obj, *args, **kwargs)
My.obj = obj
return My.obj
class Printer(metaclass=My):
"""
這是一個單例類 請不要直接例項化 使用get方法來獲取例項
"""
def __init__(self,name,brand,type):
self.name = name
self.brand = brand
self.type = type
def printing(self,text):
print("正在列印 %s" % text)
p1 = Printer("ES005","愛普生","彩色印表機")
p2 = Printer("ES005","愛普生","彩色印表機")
print(p1)
print(p2)
#記憶體地址全部一樣
p1.printing("一本小說....")
元類的思想就是控制物件,所有物件全部都要在call走一遍,才能返回到類裡使用。因此在第一次經過元類時,元類就通過賦值給固定死了,以後都不會再初始化了。
瞭解:
class MyMeta(type):
# 用於新建類物件
def __new__(cls, *args, **kwargs):
print("new run")
print(MyMeta)
print(*args)
呼叫type通過的__new__方法來建立一個空的類物件 已經將三個組成部分都放到類物件中了
res = type.__new__(cls,*args)
return res
def __init__(self,class_name,bases,namespace):
print("init run")
print(self)
class Student(metaclass=MyMeta):
pass
print(Student)
""" new 與 init的區 new 比init先執行 其作用是建立一個空的類物件 作為一個類物件 必須具備是三個組成部分 所以呼叫type中的new來完成組裝 得到這個類物件後需要將其返回 以供init來使用