美文网首页初学者
python元编程详解

python元编程详解

作者: 我爱学python | 来源:发表于2020-03-24 20:15 被阅读0次

    什么是元编程

    软件开发中很重要的一条原则就是“不要重复自己的工作(Don’t repeat youself)”,也就是说当我们需要复制粘贴代码时候,通常都需要寻找一个更加优雅的解决方案,在python中,这类问题常常会归类为“元编程”

    元编程目的

    是创建函数和类,并用他们操作代码(例如修改,生成,或者包装自己已有的代码)。尽可能的使代码优雅简洁。具体而言,通过编程的方法,在更高的抽象层次上对一种层次的抽象的特性进行修改

    元编程应用

    给函数添加一个包装(装饰器)

    注意:对wraps装饰器的使用进行补充说明,在类装饰器中使用闭包会导致生成的对象不再是被装饰的类的实例,而是在装饰器函数创建的子类的实例,这会影响namedoc等属性,在上篇我们使用@wraps装饰器对函数装饰器进行操作让问题得到解决,但在类装饰器中这一方法无效。

    元类

    在理解元类之前,您需要掌握Python中的类。Python对于从Smalltalk语言借用的类是非常奇怪的。在大多数语言中,类只是描述如何生成对象的代码片段。在Python中也是如此:

    >>> class ObjectCreator(object):
    ...       pass
    ...
     
    >>> my_object = ObjectCreator()
    >>> print(my_object)
    <__main__.ObjectCreator object at 0x8974f2c>
    

    一旦使用关键字class,Python就会执行它并创建一个OBJECT。指示

    '''
    遇到问题没人解答?小编创建了一个Python学习交流QQ群:579817333 
    寻找有志同道合的小伙伴,互帮互助,群里还有不错的视频学习教程和PDF电子书!
    '''
    >>> class ObjectCreator(object):
    ...       pass
    ...
    

    在内存中创建一个名为“ObjectCreator”的对象。这个对象(类)本身能够创建对象(实例),这就是为什么它是一个类。但是,它仍然是一个对象,因此:

    • 您可以将其分配给变量
    • 你可以复制它
    • 你可以添加属性
    • 您可以将其作为函数参数传递

    例如:

    >>> print(ObjectCreator) # you can print a class because it's an object
    <class '__main__.ObjectCreator'>
    >>> def echo(o):
    ...       print(o)
    ...
    >>> echo(ObjectCreator) # you can pass a class as a parameter
    <class '__main__.ObjectCreator'>
    >>> print(hasattr(ObjectCreator, 'new_attribute'))
    False
    >>> ObjectCreator.new_attribute = 'foo' # you can add attributes to a class
    >>> print(hasattr(ObjectCreator, 'new_attribute'))
    True
    >>> print(ObjectCreator.new_attribute)
    foo
    >>> ObjectCreatorMirror = ObjectCreator # you can assign a class to a variable
    >>> print(ObjectCreatorMirror.new_attribute)
    foo
    >>> print(ObjectCreatorMirror())
    <__main__.ObjectCreator object at 0x8997b4c>
    

    动态创建类

    由于类是对象,因此您可以像任何对象一样动态创建它们。首先,您可以使用class以下命令在函数中创建类:

    '''
    遇到问题没人解答?小编创建了一个Python学习交流QQ群:579817333 
    寻找有志同道合的小伙伴,互帮互助,群里还有不错的视频学习教程和PDF电子书!
    '''
    >>> def choose_class(name):
    ...     if name == 'foo':
    ...         class Foo(object):
    ...             pass
    ...         return Foo # return the class, not an instance
    ...     else:
    ...         class Bar(object):
    ...             pass
    ...         return Bar
    ...
    >>> MyClass = choose_class('foo')
    >>> print(MyClass) # the function returns a class, not an instance
    <class '__main__.Foo'>
    >>> print(MyClass()) # you can create an object from this class
    <__main__.Foo object at 0x89c6d4c>
    

    但它还不是我们想要的,创建类应该有更优的方法,由于类是对象,因此它们必须由某些东西生成。

    使用class关键字时,Python会自动创建此对象。但与Python中的大多数内容一样,它为您提供了手动执行此操作的方法。我们可用通过type函数查看对象的类型:

    >>> print(type(1))
    <type 'int'>
    >>> print(type("1"))
    <type 'str'>
    >>> print(type(ObjectCreator))
    <type 'type'>
    >>> print(type(ObjectCreator()))
    <class '__main__.ObjectCreator'>
    

    type除了可以查看数据类型外,还有一个特殊的能力,它也可以动态创建类。type可以将类的描述作为参数,并返回一个类。查看type内部原理:

    type(name of the class,
         tuple of the parent class (for inheritance, can be empty),
         dictionary containing attributes names and values)
    

    例如:

    >>> class MyShinyClass(object):
    ...       pass
    

    可以通过以下方式手动创建:

    '''
    遇到问题没人解答?小编创建了一个Python学习交流QQ群:579817333 
    寻找有志同道合的小伙伴,互帮互助,群里还有不错的视频学习教程和PDF电子书!
    '''
    >>> MyShinyClass = type('MyShinyClass', (), {}) # returns a class object
    >>> print(MyShinyClass)
    <class '__main__.MyShinyClass'>
    >>> print(MyShinyClass()) # create an instance with the class
    <__main__.MyShinyClass object at 0x8997cec>
    

    type接受字典来定义类的属性。所以:

    >>> class Foo(object):
    ...       bar = True
    

    可以翻译成:

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

    并用作普通类:

    >>> print(Foo)
    <class '__main__.Foo'>
    >>> print(Foo.bar)
    True
    >>> f = Foo()
    >>> print(f)
    <__main__.Foo object at 0x8a9b84c>
    >>> print(f.bar)
    True 
    

    当然,你可以继承它,所以:

    '''
    遇到问题没人解答?小编创建了一个Python学习交流QQ群:579817333 
    寻找有志同道合的小伙伴,互帮互助,群里还有不错的视频学习教程和PDF电子书!
    '''
    >>>   class FooChild(Foo):
    ...         pass
    

    会解释成:

    >>> FooChild = type('FooChild', (Foo,), {})
    >>> print(FooChild)
    <class '__main__.FooChild'>
    >>> print(FooChild.bar) # bar is inherited from Foo
    True  
    

    最后,如果想要为我们创建的类添加方法,只需使用正确的签名定义函数并将其指定为属性即可。

    会解释成:

    >>> def echo_bar(self):
    ...       print(self.bar)
    ...
    >>> FooChild = type('FooChild', (Foo,), {'echo_bar': echo_bar})
    >>> hasattr(Foo, 'echo_bar')
    False
    >>> hasattr(FooChild, 'echo_bar')
    True
    >>> my_foo = FooChild()
    >>> my_foo.echo_bar()
    True
    

    在动态创建类之后,您可以添加更多方法,就像向正常创建的类对象添加方法一样。

    '''
    遇到问题没人解答?小编创建了一个Python学习交流QQ群:579817333 
    寻找有志同道合的小伙伴,互帮互助,群里还有不错的视频学习教程和PDF电子书!
    '''
    >>> def echo_bar_more(self):
    ...       print('yet another method')
    ...
    >>> FooChild.echo_bar_more = echo_bar_more
    >>> hasattr(FooChild, 'echo_bar_more')
    True
    

    在Python中,类是对象,您可以动态地动态创建类。这是Python在您使用关键字时所执行的操作class,它通过使用元类来实现。

    什么是元类(终于讲到重点了)

    元类是创建类的“类”。我们可以定义类来创建实例,python一切皆对象,类也不列外,它是通过元类来创建。类是创建实例的蓝图,元类是创建类的蓝图。可以很容易地看出,Python类中也需要是第一类对象才能启用此行为。

    例如:

    MyClass = MetaClass()
    my_object = MyClass()
    

    通过type来创建:

    MyClass = type('MyClass', (), {})
    

    这是因为该函数type实际上是一个元类。type是Python用于在幕后创建所有类的元类。

    为什么是小写type而不是大学Type?

    type与str创建字符串对象int的类创建整数对象的类类似,它也只是创建类对象的类。我们通过检查class属性来查看。

    一切,一切,一切重要的事情说三遍,都是Python中的一个对象。这包括整数,字符串,函数和类。所有这些都是对象。所有这些都是从一个类创建的:

    >>> age = 35
    >>> age.__class__
    <type 'int'>
    >>> name = 'bob'
    >>> name.__class__
    <type 'str'>
    >>> def foo(): pass
    >>> foo.__class__
    <type 'function'>
    >>> class Bar(object): pass
    >>> b = Bar()
    >>> b.__class__
    <class '__main__.Bar'>
    

    那么classclass

    '''
    遇到问题没人解答?小编创建了一个Python学习交流QQ群:579817333 
    寻找有志同道合的小伙伴,互帮互助,群里还有不错的视频学习教程和PDF电子书!
    '''
    >>> age.__class__.__class__
    <type 'type'>
    >>> name.__class__.__class__
    <type 'type'>
    >>> foo.__class__.__class__
    <type 'type'>
    >>> b.__class__.__class__
    <type 'type'>
    

    因此,元类只是创建类对象的东西。我们也称它为类工厂

    type 是Python使用的内置元类,我们也可以创建自己的元类。

    mataClass属性

    在Python 2中,我们在编写类时添加属性:

    class Foo(object):
        __metaclass__ = something...
        [...]
    

    如果引用mataClass属性,python将使用元类类创建Foo,但是这样class Foo(object),类对象Foo并不是在内存中创建的

    Python将会在父类中查找metaclass,如果没有,就继续向父类的父类查找,如果还是没有,就在模块中找,还是没有的话就用缺省的MetaClass即type创建类。

    当你这样做时:

    class Foo(Bar):
        pass
    

    Python会执行以下操作:

    如果有metaclass属性,将会在内存中创建一个类对象,名称Foo使用是metaclass。如果Python找不到metaclass,它将metaclass在MODULE级别查找,并尝试执行相同的操作(但仅适用于不继承任何内容的类,基本上是旧式类)。

    如果还是找不到metaclass,它将使用Bar's(第一个父级)自己的元类(可能是默认的type)来创建类对象。

    在这里插入图片描述
    Python中的元类3

    在Python 3中更改了设置元类的语法:

    class Foo(object, metaclass=something):
        ...
    

    metaclass不再使用该属性,而是支持基类列表中的关键字参数。但是并不会影响元类的功能。

    python3中我们可以将属性作为关键字参数传递给元类,如下所示:

    class Foo(object, metaclass=something, kwarg1=value1, kwarg2=value2):
        ...
    

    自定义元类

    一个类没有声明自己的元类,默认他的元类就是type,除了使用元类type,用户也可以通过继承type来自定义元类

    自定义元类的主要目的是:

    • 拦截类的创建
    • 读取类的信息,可以做修改
    • 返回新的类

    通过传入不同的字符串动态的创建不同的类

    def create_class(name):
        if name == 'user':
            class User:
                def __str__(self):
                    return "user"
            return User
     
        elif name == "company":
            class Company:
                def __str__(self):
                    return "company"
            return Company
     
    if __name__ == '__main__':
        Myclass = create_class("user")
        my_obj = Myclass()
        print(my_obj)    #user
        print(type(my_obj))     #<class '__main__.create_class.<locals>.User'>
    

    用type创建

    '''
    遇到问题没人解答?小编创建了一个Python学习交流QQ群:579817333 
    寻找有志同道合的小伙伴,互帮互助,群里还有不错的视频学习教程和PDF电子书!
    '''
    # 一个简单type创建类的例子
    #type(object_or_name, bases, dict)
    #type里面有三个参数,第一个类名,第二个基类名,第三个是属性
    User = type("User",(),{"name":"derek"})
     
    my_obj = User()
    print(my_obj.name)    #derek
    
    #带方法的创建<br><br>def say(self):     #必须加self
        return "i am derek"
     
    User = type("User",(),{"name":"derek","say":say})
     
    my_obj = User()
    print(my_obj.name)     #derek
    print(my_obj.say())    #i am derek
    

    让type创建的类继承一个基类

    def say(self):     #必须加self
        return "i am derek"
     
    class BaseClass:
        def answer(self):
            return "i am baseclass"
     
    #type里面有三个参数,第一个类名,第二个基类名,第三个是属性
    User = type("User",(BaseClass,),{"name":"derek","say":say})
     
    if __name__ == '__main__':
     
        my_obj = User()
        print(my_obj.name)          #d erek
        print(my_obj.say())         # i am derek
        print(my_obj.answer())      # i am baseclass
    

    但是在实际编码中,我们一般不直接用type去创建类,而是用元类的写法,自定义一个元类metaclass去创建

    '''
    遇到问题没人解答?小编创建了一个Python学习交流QQ群:579817333 
    寻找有志同道合的小伙伴,互帮互助,群里还有不错的视频学习教程和PDF电子书!
    '''
    # 把User类创建的过程委托给元类去做,这样代码的分离性比较好
     
    class MetaClass(type):
        def __new__(cls, *args, **kwargs):
            return super().__new__(cls,*args, **kwargs)
     
    class User(metaclass=MetaClass):
        def __init__(self,name):
            self.name = name
     
        def __str__(self):
            return "test"
     
    if __name__ == '__main__':
        #python中类的实例化过程,会首先寻找metaclass,通过metaclass去创建User类
        my_obj = User(name="derek")
        print(my_obj)    #test
    

    还有一个典型的自定义元类例子就是Django ORM。
    元类的主要用例是创建API。

    它允许您定义如下内容:

    class Person(models.Model):
        name = models.CharField(max_length=30)
        age = models.IntegerField()
    

    但是如果你这样做:

    guy = Person(name='bob', age='35')
    print(guy.age)
    

    它不会返回一个IntegerField对象。它将返回一个int,甚至可以直接从数据库中获取它。

    因为models.Model定义metaclass它会使用一些魔法将Person您刚刚使用简单语句定义的内容转换为数据库字段的复杂sql。

    Django通过公开一个简单的API并使用元类,从这个API中重新创建代码来完成幕后的实际工作,从而使复杂的外观变得简单。

    相关文章

      网友评论

        本文标题:python元编程详解

        本文链接:https://www.haomeiwen.com/subject/zbeayhtx.html