元编程可能听起来很新奇,但如果用户以前使用过装饰器或元类,他们在项目中就已经使用了元编程。因此,我们可以说,元编程是用于操纵程序的程序。

在本教程中,我们将讨论元类及其用途以及它们的替代方法。由于这是Python的高级主题,建议用户在开始本教程之前复习Python中的基本概念,如"Python中的装饰器"和"Python中的面向对象编程概念"。

元类

在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是主要的数据类型。用户可以通过创建该类型的类来创建新类型。例如,我们可以通过创建City类来创建新类型的对象。

示例:

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

我们可以总结整个元类如下:

元类用于创建类,而这些类可以创建对象。

231.png

元类负责创建类,因此用户可以通过插入代码或额外操作来编写自定义元类,以修改创建类的方式。通常情况下,用户不需要自定义元类,但在特殊情况下,它是必要的。

有些问题可以使用元类或非元类来解决,但也有一些情况只能使用元类来解决。

如何创建自定义元类

要创建自定义元类,用户的自定义元类必须继承自type元类,并通常覆盖以下方法:

  • new(): new()函数在init()函数之前执行。它用于创建对象并返回它。用户可以覆盖此函数以控制对象的创建方式。
  • init()init()函数用于初始化传递的已创建对象。

用户可以直接使用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"类的所有子类。因此,在这种情况下,用户必须分别将装饰器方法应用于每个子类,就像我们在上面的示例中为Calculator类所做的那样。

现在,实际的问题是可以有大量的类的子类,将装饰器方法单独应用于每个子类是具有挑战性和耗时的过程。因此,为了解决这个问题,用户必须确保每个子类都具有此调试属性,他们应该考虑基于元类的解决方案。

示例:

我们将按常规方式创建类,然后立即使用调试方法装饰器包装:

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)开发人员,可以使用元类来实现这一目的。

结论

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

标签: Tkinter教程, Tkinter安装, Tkinter库, Tkinter入门, Tkinter学习, Tkinter入门教程, Tkinter, Tkinter进阶, Tkinter指南, Tkinter学习指南, Tkinter进阶教程, Tkinter编程