1. 程式人生 > 程式設計 >深入瞭解python中元類的相關知識

深入瞭解python中元類的相關知識

類也是物件

在大多數程式語言中,類就是一組用來描述如何生成一個物件的程式碼段,在python中也是成立的。

class ObjectCreator:
  pass  
my_object = ObjectCreator()
print(my_object)
"""
輸出結果:
<__main__.ObjectCreator object at 0x037DACD0>
"""

但是,python的類不止於此,類同樣也是一種物件。

class ObjectCreator:
  pass

上面的程式碼段將在記憶體中建立一個物件,名字就叫做ObjectCreator。這個物件(類物件ObjectCreator)擁有建立物件(例項物件)的能力,但它本質上仍然還是一個物件,於是你就可以對它做如下的操作:

  • 給它複製一個變數
  • 拷貝它
  • 給它增加屬性
  • 將它作為函式引數傳遞

示例程式碼:

class ObjectCreator:
  pass
# 把它賦值給一個變數
a = ObjectCreator
print(a) # <class '__main__.ObjectCreator'>
# 作為函式引數傳遞
def echo(o):
  print(o)
  
echo(ObjectCreator) # <class '__main__.ObjectCreator'>

動態的建立類

因為類也是物件,所以可以在執行時動態的建立它們,使用class關鍵字即可。

def choose_class(name):
  if name == 'foo':
    class Foo(object):
      pass
    return Foo   # 返回的是類,不是類的例項
  else:
    class Bar(object):
      pass
    return Bar
MyClass = choose_class("foo")
print(MyClass) # 列印類物件
# 輸出結果
<class '__main__.choose_class.<locals>.Foo'>
print(MyClass()) # 列印例項物件
# 輸出結果
<__main__.choose_class.<locals>.Foo object at 0x0368CFD0>

使用type建立類

我們知道通過type()可以知道這個物件的型別是什麼,他還有一個完全不同的功能,動態的建立類。
type可以接受一個類的描述作為引數,然後返回一個類。

語法:

type(類名,由父類名稱構成的元組(針對繼承的情況可以為空),包含屬性的字典)

MyClass = type("MyClass",(),{})
print(MyClass)
# 輸出結果:
<class '__main__.MyClass'>

使用type建立帶屬性的類

type 接受一個字典來為類定義屬性,如下所示:

Foo = type("Foo",{'bar':True})

等價於

class Foo:
  bar = True

使用type建立繼承的子類

接著上面的程式碼,我們已經建立了一個Foo類,現在來建立一個它的子類。

FooChild = type("FooChild",(Foo,),{})
print(FooChild.bar) # # bar屬性是由Foo繼承而來
# 輸出結果:
True

注意:

  • type的第二個引數,元組中是父類的名字,不是字串。
  • 新增的屬性是類屬性,不是例項屬性。

使用type建立帶有方法的類

最終你會希望為你的類增加方法。只需要定義一個有著恰當簽名的函式並將其作為屬性賦值就可以了。

新增例項方法

def test_f(self):
  print("新增的例項方法")
Foo = type("Foo",{"test_f":test_f})
f = Foo()
f.test_f()
# 輸出結果:
新增的例項方法

新增靜態方法

@staticmethod
def test_static():
  print("新增的靜態方法")
Foo = type("Foo",{"test_static":test_static})
Foo.test_static()
Foo.test_static()
# 輸出結果:
新增的靜態方法

新增類方法

@classmethod
def test_class(cls):
  print("新增的類方法")
Foo = type("Foo",{"test_class":test_class})
Foo.test_class()
# 輸出的結果:
新增的類方法

什麼是元類

元類就是用來建立類的“東西”。元類就是就是用來建立類物件的,元類就是類的類。
可以這樣理解:

MyClass =  MetaClass() # 使用元類建立類物件
MyObject = MyClass() # 使用類物件建立例項物件

type函式其實就是元類。type就是在Python在背後建立所有類的元類,可以通過__class __屬性來檢視,__class __的功能是檢視物件所在的類,它可以巢狀使用。

class A:
  pass
print(A.__class__)
a = A()
print(a.__class__)
print(a.__class__.__class__)
# 輸出結果:
<class 'type'>
<class '__main__.A'>
<class 'type'>

可以看出,最後物件的類都是type元類。

Python中所有的東西,注意,我是指所有的東西——都是物件。這包括整數、字串、函式以及類。它們全部都是物件,而且它們都是從一個類建立而來,這個類就是type。

整數:

age = 18
print(age.__class__)
print(age.__class__.__class__)
# 輸出結果:
<class 'int'>
<class 'type'>

字串:

name = "張三"
print(name .__class__)
print(name .__class__.__class__)
# 輸出結果:
<class 'str'>
<class 'type'>

函式:

def f():
  pass
print(f.__class__)
print(f.__class__.__class__)
# 輸出結果:
<class 'function'>
<class 'type'>

自定義元類

首先的瞭解一下metaclass屬性,用它來指定一個元類,python會在定義的類中尋找metaclass屬性,如果沒找到,就到它的父類找以此類推。如果找到了,python就會用它來建立類物件,如果實在沒有找到就會用內建的type來建立這個類。
metaclass中可以放type或者任何使用到type或者子類化type的東東都可以。

自定義類的主要目的:

  • 攔截類的建立
  • 修改類

使用函式實現一個自定義的元類

功能:把不是__開頭的類屬性名字變為大寫

def upper_attr(future_class_name: str,future_class_parents: tuple,future_class_attr: dict):
  newAttr = {}
  for key,value in future_class_attr.items():
    if not key.startswith("__"):
      newAttr[key.upper()] = value
  return type(future_class_name,future_class_parents,newAttr)
class Foo(metaclass=upper_attr):
  name = "張三"
  age = 18

hasattr(Foo,"name") # 判斷是否有該類屬性 False
hasattr(Foo,"NAME") # True
hasattr(Foo,"age") # False
hasattr(Foo,"AGE") # True

繼承type實現一個自定義元類

功能:同上

class MyMetaClass(type):
  def __new__(cls,class_name: str,class_parents: tuple,class_attr: dict):
    newAttr = {}
    for key,value in class_attr.items():
      if not key.startswith("__"):
        newAttr[key.upper()] = value
    # 方法1:通過'type'來做類物件的建立
    # return type(class_name,class_parents,newAttr)  
    # 方法2:複用type.__new__方法
    # 這就是基本的OOP程式設計,沒什麼魔法
    # return type.__new__(cls,class_name,newAttr)  
    # 方法3:使用super方法
    return super(MyMetaClass,cls).__new__(cls,newAttr)
class Foo(metaclass=MyMetaClass):
  name = "張三"
  age = 18
hasattr(Foo,"AGE") # True

效果和上面是一樣的。

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支援我們。