1. 程式人生 > >面向物件,元類,控制類,物件的建立

面向物件,元類,控制類,物件的建立

"""

call

呼叫的意思 ​ 在在物件被呼叫時 執行

函式 類

自定義元類 的目的 ​ 1.可以通過call 來控制物件的建立過程 ​ 2.可用控制類的建立過程 """

自定義一個元類 元類也是一個類 但是需要繼承type
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)







要控制類的建立過程 只要找到類所屬的類 中的init即可

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走一遍,才能返回到類裡使用。因此在第一次經過元類時,元類就通過賦值給固定死了,以後都不會再初始化了。

 

 

 

瞭解:

程式碼1:

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的區 newinit先執行 其作用是建立一個空的類物件 作為一個類物件 必須具備是三個組成部分 所以呼叫type中的new來完成組裝 得到這個類物件後需要將其返回 以供init來使用

"""