您的位置:

将 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 开发人员,他们可以为此使用元类。

结论

在本教程中,我们讨论了元类,如何定制元类,以及用户如何使用它们来解决问题和复杂的编程及其替代方案。