將 Python 中的元類用於元編程

元編程聽起來可能是新的,但是如果用戶曾經使用過 Decorators 或元類,他們已經在他們的項目中使用了元編程。因此,我們可以說元編程是用於操縱程序的程序。

在本教程中,我們將討論元類及其用途,以及它們的替代方法。由於這是 Python 的高級話題,建議用戶在開始本教程之前,先修改一下 Python 中的裝飾者和 Python 中的 OOPS 概念的基本概念。

元類

在 Python 中,每個模塊或函數都與某種類型相關聯。例如,如果用戶有一個整數值的變數,那麼它就與一個「int」類型相關聯。用戶可以通過使用 type()方法知道任何東西的類型。

示例:


number = 13
print("Type of number is:", type(num))

list = [3, 7, 8]
print("Type of list is:", type(list))

name = "Mark Jackson"
print("Type of name is:", type(name))

輸出:

Type of number is: <class 'int'>
Type of list is: <class 'list'>
Type of name is: <class 'str'>

解釋-

Python 中的每種類型都是由類定義的。因此,在上面的例子中,它們是「int」、「list」或「str」類類型的對象,不像 C 語言或 Java 語言中 int、char 和 float 是主要的數據類型。用戶可以通過創建該類型的類來創建新類型。例如,我們可以按城市類創建一個新的對象類型。

示例:


class City:
    pass
City_object = City()

# now, we will print the type of object of City class
print("Type of City_object is:", type(City_object))

輸出:

Type of City_object is: <class '__main__.City'>

Python 中的類也是一個對象,因此,像其他對象一樣,它是元類的實例。元類是一種特殊的類類型,負責創建類和類對象。因此,例如,如果用戶想要找到「City」類的類型,他們會發現它是「type」

示例:


class City:
    pass

# now, we will print the type of the City class
print("Type of City class is:", type(City))

輸出:

Type of City class is: <class 'type'>

由於類也是一個對象,我們可以修改它們。例如,用戶可以像處理其他對象一樣,在類中添加或減去欄位或函數。

示例:


# First, we will define the class without any using any class methods or variables.
class City:
    pass

# now, we will define the method variables
City.a = 65

# we will now, define the class methods
City.foo = lambda self: print('Pune')

# then, we will create the object
userobject = City()

print(userobject.a)
userobject.foo()

輸出:

65
Pune

我們可以將整個元類總結為:

元類用於創建類,這些類可以創建對象。

元類負責創建類,因此用戶可以通過插入代碼或額外的操作來修改類的創建方式,從而編寫自己定製的元類。通常,用戶不需要定製的元類,但是在特殊情況下,它是必要的。

有一些問題可以通過使用元類或非元類來解決,但是也有一些情況下只使用元類來解決它們。

如何創建定製的元類

為了創建定製的元類,用戶定製的元類必須繼承類型元類,並且通常重寫,例如:

  • new (): new ()函數是 int()函數之前的類。這用於創建對象並返回它。用戶可以覆蓋此功能來控制對象的創建方式。
  • int(): 函數用於初始化創建的對象,該對象作為參數傳遞。

用戶可以直接使用 type()方法創建類。可以通過以下方式調用 type()方法:

  1. 如前面的例子所示,用戶可以用一個參數調用它,它將返回類型。
  2. 用戶可以用三個參數調用它。它將創建類。其中傳遞了以下參數:
    • 班級名稱
    • 傳遞具有由類繼承的基類的元組
    • 類字典:這將作為類的本地命名空間,用函數和變數填充。

示例:


def City_method(self):
    print("This is City class method!")

# we will create the base class
class Base:
    def userfunction(self):
        print("This is a inherited method!")

# we will create the city class dynamically by using
# type() function.
City = type('City', (Base, ), dict(a = "Mark Jackson", user_method = City_method))

# now, we will print the type of City
print("The Type of City class: ", type(City))

# we will create the instance of City class
City_object = City()
print(" The Type of City_object: ", type(City_object))

# we will now call the inherited method
City_object.userfunction()

# we will call the City class method
City_object.user_method()

# at last we will print the variable
print(City_object.a)

輸出:

The Type of City class:  <class 'type'>
 The Type of City_object:  <class '__main__.City'>
This is a inherited method!
This is City class method!
Mark Jackson

現在,讓我們看看如何在不直接使用 type()函數的情況下創建元類。例如,我們將創建名為 MultiBases 的元類,它將檢查正在創建的類是否繼承自多個基類。如果是這樣,它將引發錯誤。

示例:


# Inside the metaclass
class MultiBases(type):
    # we will override the __new__() function
    def __new__(city, city_name, bases, citydict):
        # if the number of base classes are greator than 1
        # it will raise an error
        if len(bases)>1:
            raise TypeError("There are inherited multiple base classes!!!")

        # else it will execute the __new__() function of super class, that is
        # it will call the __init__() function of type class
        return super().__new__(city, city_name, bases, citydict)

# the metaclass can be specified by using 'metaclass' keyword argument
# now we will use the MultiBase class for creating classes
# this will be propagated to all subclasses of Base
class Base(metaclass = MultiBases):
    pass

# this will raise no error
class P(Base):
    pass

# this will raise no error
class Q(Base):
    pass
# this will raise no error
class R(Base):
    pass
# This will raise an error!
class S(P, Q, R):
    pass

輸出:

---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-2-409c90c285d5> in <module>
     29     pass
     30 # This will raise an error!
---> 31 class S(P, Q, R):
     32     pass

<ipython-input-2-409c90c285d5> in __new__(city, city_name, bases, citydict)
      6         # it will raise an error
      7         if len(bases)>1:
----> 8             raise TypeError("There are inherited multiple base classes!!!")
      9 
     10         # else it will execute the __new__() function of super class, that is

TypeError: There are inherited multiple base classes!!!

如何用元類解決這個問題

有一些問題,用戶可以通過使用元類和裝飾器來解決。但是有些問題只能用元類來解決。比如,用戶想要調試類函數,他們想要的是,每當執行類函數時,它都會在執行它們的主體之前列印出它們的完全限定名。

示例:


from functools import wraps

def debugg(funct):
    '''decorator for debugging passed function'''

    @wraps(funct)
    def wrapperr(*args, **kwargs):
        print("The full name of this Function:", funct.__qualname__)
        return funct(*args, **kwargs)
    return wrapperr

def debug_methods(clas):
    '''class decorator make use of debug decorator
       for debuging the class functions '''

    # now we will check in the class dictionary for any callable(function)
    # if there is any, replace it with debugged version
    for key, value in vars(clas).items():
        if callable(value):
            setattr(clas, key, debugg(value))
    return clas

# sample class
@debugmethods
class Calculator:
    def add(self, p, q):
        return p+q
    def mul(self, p, q):
        return p*q
    def div(self, p, q):
        return p/q

user_cal = Calculator()
print(user_cal.add(5, 8))
print(user_cal.mul(6, 7))
print(user_cal.div(21, 7))

輸出:

The full name of this method: Calculator.add
13
The full name of this method: Calculator.mul
42
The full name of this method: Calculator.div
3.0

解釋-

上面的解決方案運行良好,但是有一個問題,那就是用戶想要將裝飾器方法應用到繼承了「Calculator」類的所有子類。因此在這種情況下,用戶必須將 decorator 方法分別應用於每個子類,就像我們在上面的例子中對 Calculator 類所做的那樣。

現在,實際的問題是這個類可能有很多子類,將 decorator 方法單獨應用於每個子類是一個具有挑戰性和耗時的過程。因此,為了解決這個問題,用戶必須確保每個子類都有這個調試屬性,他們應該尋找基於元類的解決方案。

示例:

我們將正常地創建類,然後立即使用調試方法裝飾器來結束:


from functools import wraps

def debugg(funct):
    '''decorator for debugging passed function'''

    @wraps(funct)
    def wrapperr(*args, **kwargs):
        print("The full name of this Function:", funct.__qualname__)
        return funct(*args, **kwargs)
    return wrapperr

def debug_methods(clas):
    '''class decorator will make use of the debug decorator
       to the debug class '''

    for key, value in vars(clas).items():
        if callable(value):
            setattr(clas, key, debugg(value))
    return clas

class debug_Meta(type):
    '''meta class which feed created class object
       to debug_method for getting debug functionality
       enabled objects'''

    def __new__(clas, clasname, bases, clasdict):
        object = super().__new__(clas, clasname, bases, clasdict)
        object = debug_methods(object)
        return object

# the base class with metaclass 'debug_Meta'
# now all the subclass of this will have the applied debugging function
class Base(metaclass = debug_Meta):pass

# now, we will inherite the Base
class Calculator(Base):
    def add(self, p, q):
        return p+q

#and then, we will inherite the Calculator
class Calculator_adv(Calculator):
    def mult(self, p, q):
        return p*q

# Now Calculator_adv object will show
# the behaviour og debugging

user_cal = Calculator_adv()
print(user_cal.add(3, 7))
user_cal = Calculator_adv()
print(user_cal.mult(3, 7))

輸出:

The full name of this Function: Calculator.add
10
The full name of this Function: Calculator_adv.mult
21

用戶何時應該使用元類

用戶不經常使用元類,因為元類主要用於複雜的情況。但是很少有用戶可以使用元類的情況:

  • 如上例所示,元類用於向下生成繼承層次結構。這也將影響所有的子類。如果用戶有這樣的情況,那麼他們可以使用元類。
  • 如果用戶想自動改變類,他們可以在創建元類時使用它。
  • 如果用戶是 API 開發人員,他們可以為此使用元類。

結論

在本教程中,我們討論了元類,如何定製元類,以及用戶如何使用它們來解決問題和複雜的編程及其替代方案。


原創文章,作者:小藍,如若轉載,請註明出處:https://www.506064.com/zh-tw/n/240292.html

(0)
打賞 微信掃一掃 微信掃一掃 支付寶掃一掃 支付寶掃一掃
小藍的頭像小藍
上一篇 2024-12-12 12:21
下一篇 2024-12-12 12:21

相關推薦

  • Python列表中負數的個數

    Python列表是一個有序的集合,可以存儲多個不同類型的元素。而負數是指小於0的整數。在Python列表中,我們想要找到負數的個數,可以通過以下幾個方面進行實現。 一、使用循環遍歷…

    編程 2025-04-29
  • Python中引入上一級目錄中函數

    Python中經常需要調用其他文件夾中的模塊或函數,其中一個常見的操作是引入上一級目錄中的函數。在此,我們將從多個角度詳細解釋如何在Python中引入上一級目錄的函數。 一、加入環…

    編程 2025-04-29
  • Python周杰倫代碼用法介紹

    本文將從多個方面對Python周杰倫代碼進行詳細的闡述。 一、代碼介紹 from urllib.request import urlopen from bs4 import Bea…

    編程 2025-04-29
  • 如何查看Anaconda中Python路徑

    對Anaconda中Python路徑即conda環境的查看進行詳細的闡述。 一、使用命令行查看 1、在Windows系統中,可以使用命令提示符(cmd)或者Anaconda Pro…

    編程 2025-04-29
  • Python計算陽曆日期對應周幾

    本文介紹如何通過Python計算任意陽曆日期對應周幾。 一、獲取日期 獲取日期可以通過Python內置的模塊datetime實現,示例代碼如下: from datetime imp…

    編程 2025-04-29
  • Python程序需要編譯才能執行

    Python 被廣泛應用於數據分析、人工智慧、科學計算等領域,它的靈活性和簡單易學的性質使得越來越多的人喜歡使用 Python 進行編程。然而,在 Python 中程序執行的方式不…

    編程 2025-04-29
  • 蝴蝶優化演算法Python版

    蝴蝶優化演算法是一種基於仿生學的優化演算法,模仿自然界中的蝴蝶進行搜索。它可以應用於多個領域的優化問題,包括數學優化、工程問題、機器學習等。本文將從多個方面對蝴蝶優化演算法Python版…

    編程 2025-04-29
  • Python編程二級證書考試相關現已可以上網購買

    計算機二級Python考試是一項重要的國家級認證考試,也是Python編程的入門考試。與其他考試一樣,Python編程二級證書的考生需要進入正式考試,而為了備考,這篇文章將詳細介紹…

    編程 2025-04-29
  • python強行終止程序快捷鍵

    本文將從多個方面對python強行終止程序快捷鍵進行詳細闡述,並提供相應代碼示例。 一、Ctrl+C快捷鍵 Ctrl+C快捷鍵是在終端中經常用來強行終止運行的程序。當你在終端中運行…

    編程 2025-04-29
  • Python清華鏡像下載

    Python清華鏡像是一個高質量的Python開發資源鏡像站,提供了Python及其相關的開發工具、框架和文檔的下載服務。本文將從以下幾個方面對Python清華鏡像下載進行詳細的闡…

    編程 2025-04-29

發表回復

登錄後才能評論