元編程聽起來可能是新的,但是如果用戶曾經使用過 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()方法:
- 如前面的例子所示,用戶可以用一個參數調用它,它將返回類型。
 - 用戶可以用三個參數調用它。它將創建類。其中傳遞了以下參數:
- 班級名稱
 - 傳遞具有由類繼承的基類的元組
 - 類字典:這將作為類的本地命名空間,用函數和變數填充。
 
 
示例:
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
微信掃一掃 
支付寶掃一掃