美文网首页
如何理解python中的类_如何理解Python中的元类meta

如何理解python中的类_如何理解Python中的元类meta

作者: 七喜c4d | 来源:发表于2021-02-07 17:56 被阅读0次

    1、Python中的类

    在理解元类之前,你需要掌握Python中的类。Python中借用了SmallTalk语言特性对类进行实现。

    在大部分的语言中,类一般是一段生成对象的代码,这在Python中也一样。

    >>> class ObjectCreator(object):

    ... pass

    ...

    >>> my_object = ObjectCreator()

    >>> print(my_object)

    <__main__.ObjectCreator object at 0x8974f2c>

    在Python中,类也是一种对象。当执行如下代码时,Python会申请一块内存保存名为ObjectCreator的对象。

    >>> class ObjectCreator(object):

    ... pass

    ...

    生成的对象(类)拥有创造对象(实例)的能力,因此称为类。

    作为对象,因此它能够像对象一样操作:

    可以给变量赋值

    可以复制

    可以增加属性

    可以作为函数参数传递

    >>> print(ObjectCreator) # you can print a class because it's an object

    >>> def echo(o):

    ... print(o)

    ...

    >>> echo(ObjectCreator) # you can pass a class as a parameter

    >>> 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>

    (推荐一个q裙,小伙伴们在学习的过程中遇到了什么问题都可以一起交流解决,点击裙号就可跳转啦610 380 249)

    2.动态生成类

    类可以像对象一样动态生成。

    首先使用class关键字对类进行定义

    >>> 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

    >>> print(MyClass()) # you can create an object from this class

    <__main__.Foo object at 0x89c6d4c>

    以上方式并不是那么动态,因为你需要自己定义整个类。而类本身作为一个对象是可以通过其他来生成的,Python中允许手动定义类----通过使用type关键字完成类的定义

    >>> print(type(1))

    >>> print(type("1"))

    >>> print(type(ObjectCreator))

    >>> print(type(ObjectCreator()))

    以上是通过type关键字进行类型查看,但是type还有另一种完全不同的能力--创建类。输入类的描述参数来生成对应的类(为什么type会根据输入参数的不同而表现出两种完全不同的功能,主要是需要满足Python向后兼容)

    以下为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

    %可以换成另一种方式:

    >>> MyShinyClass = type('MyShinyClass', (), {}) # returns a class object

    >>> print(MyShinyClass)

    >>> print(MyShinyClass()) # create an instance with the class

    <__main__.MyShinyClass object at 0x8997cec>

    在手动创建类时,使用了MyShinyClass作为类的名字,同时也用同样的名字对该类进行引用。这两个名字可以不一样,如a=type('MyShinyClass',....),但没必要搞复杂。

    type接受一个字典来定义类中的属性

    >>> class Foo(object):

    ... bar = True

    %可以转换成:

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

    %可以作为正常的类来使用:

    >>> print(Foo)

    >>> print(Foo.bar)

    True

    >>> f = Foo()

    >>> print(f)

    <__main__.Foo object at 0x8a9b84c>

    >>> print(f.bar)

    True

    这样的类也能继承:

    >>> class FooChild(Foo):

    ... pass

    %可以写为:

    >>> FooChild = type('FooChild', (Foo,), {})

    >>> print(FooChild)

    >>> print(FooChild.bar) # bar is inherited from Foo

    True

    当你需要给你的类添加方法(属性)的时候,通过函数段定义,并将其传递给type输入属性参数即可。

    >>> 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

    这样你就可以为创建的类动态的添加你需要的属性。

    3、那么什么是元类呢?

    元类(Metaclass)是创建类的元素。之前已经了解了可以通过类来创建实例对象,那么元类可以用来创建类对象,是类的类。

    MyClass = MetaClass()

    my_object = MyClass()

    结合上文通过type来创建MyClass,我们可以了解type实际上就是元类,Python中使用type作为所有类的元类。可以通过__class__属性来查看类别。在Python中一切都是对象,不管是整数、字符串、函数还是类。但追根溯源,type是python中所有对象的元类,以下代码解释了这个机制。

    >>> age = 35

    >>> age.__class__

    >>> name = 'bob'

    >>> name.__class__

    >>> def foo(): pass

    >>> foo.__class__

    >>> class Bar(object): pass

    >>> b = Bar()

    >>> b.__class__

    >>> age.__class__.__class__

    >>> name.__class__.__class__

    >>> foo.__class__.__class__

    >>> b.__class__.__class__

    type是內建元类(built-in),但是可以创建自己的元类。

    __metaclass__属性

    在Python2中,你可以在创建类时给类添加一个__metaclass__属性,Python会使用这个元类属性来创建类对象Foo

    class Foo(object):

    __metaclass__ = something...

    当执行class Foo(object)时,Python首先在__metaclass__中寻找类的定义,如果有,就用来创建类对象Foo,如果没有找到,Python会调用type方法创建类对象Foo。

    当执行以下代码时,Python的编译过程如下:

    class Foo(Bar):

    pass

    判断Foo中是否有__metaclass__属性?如果有,分配内存并使用__metaclass__属性创建以Foo为名的类对象;如果没有找到,Python会在当前模块里寻找该属性;如果还没有找到,那就会用父类Bar的元类(可能是默认的type)来创建类对象。这里需要注意,Foo从Bar处继承,但不会继承Bar的__metaclass__属性。如果Bar的使用type()来作为__metaclass__机制创建子类的话,那么子类不会继承这种创建机制。

    而在Python3.x中,元类语法与2.x中不同。

    class Foo(object, metaclass=something):

    ...

    __metaclass__属性换成了关键字参数,传递给类对象,但元类的其他行为大体是相同的。3.x中新增的是可以按照关键字参数将属性传递给元类,如下:

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

    ...

    4、自定义元类

    元类的主要作用是,在创建类时自动改变类的一些属性。当进行类似于调用APIs操作时需要创建对应匹配的类。

    举一个例子,如果你想把模块中的所有类的属性名都使用大写,那其中一个可以实现的方式就是使用元类定义,设置__metaclass__。只需要告诉元类需要把属性转换成大写,这样在创建类对象是就可以自动满足。

    如下一个简单例子:

    # the metaclass will automatically get passed the same argument

    # that you usually pass to `type`

    def upper_attr(future_class_name, future_class_parents, future_class_attr):

    """

    Return a class object, with the list of its attribute turned

    into uppercase.

    """

    # pick up any attribute that doesn't start with '__' and uppercase it

    uppercase_attr = {}

    for name, val in future_class_attr.items():

    if not name.startswith('__'):

    uppercase_attr[name.upper()] = val

    else:

    uppercase_attr[name] = val

    # let `type` do the class creation

    return type(future_class_name, future_class_parents, uppercase_attr)

    __metaclass__ = upper_attr # this will affect all classes in the module注意这一行代码,会影响当前模块中的所有类

    class Foo(): # global __metaclass__ won't work with "object" though

    # but we can define __metaclass__ here instead to affect only this class

    # and this will work with "object" children

    bar = 'bip'

    print(hasattr(Foo, 'bar'))

    # Out: False

    print(hasattr(Foo, 'BAR'))

    # Out: True

    f = Foo()

    print(f.BAR)

    # Out: 'bip'

    现在我们换成另一个方式,定义一个元类:

    # remember that `type` is actually a class like `str` and `int`

    # so you can inherit from it

    class UpperAttrMetaclass(type):

    # __new__ is the method called before __init__

    # it's the method that creates the object and returns it

    # while __init__ just initializes the object passed as parameter

    # you rarely use __new__, except when you want to control how the object

    # is created.

    # here the created object is the class, and we want to customize it

    # so we override __new__

    # you can do some stuff in __init__ too if you wish

    # some advanced use involves overriding __call__ as well, but we won't

    # see this

    def __new__(upperattr_metaclass, future_class_name,

    future_class_parents, future_class_attr):

    uppercase_attr = {}

    for name, val in future_class_attr.items():

    if not name.startswith('__'):

    uppercase_attr[name.upper()] = val

    else:

    uppercase_attr[name] = val

    return type(future_class_name, future_class_parents, uppercase_attr)

    这个方式不太符合面向对象编程的准则,这里我们直接调用了type方法而没有重写或使用父类的__new__方法。我们改变一下:

    class UpperAttrMetaclass(type):

    def __new__(upperattr_metaclass, future_class_name,

    future_class_parents, future_class_attr):

    uppercase_attr = {}

    for name, val in future_class_attr.items():

    if not name.startswith('__'):

    uppercase_attr[name.upper()] = val

    else:

    uppercase_attr[name] = val

    # reuse the type.__new__ method

    # this is basic OOP, nothing magic in there

    return type.__new__(upperattr_metaclass, future_class_name,

    future_class_parents, uppercase_attr)

    这里你会发现多出一个upperattr_metaclass参数,这个是由于__new__方法总是把它定义的类作为第一个参数接受,类似于普通类定义中的self,为简洁可用cls来代替。通常可以使用较短的参数名,如下:

    class UpperAttrMetaclass(type):

    def __new__(cls, clsname, bases, dct):

    uppercase_attr = {}

    for name, val in dct.items():

    if not name.startswith('__'):

    uppercase_attr[name.upper()] = val

    else:

    uppercase_attr[name] = val

    return type.__new__(cls, clsname, bases, uppercase_attr)

    当然我们还可以使用super来使得类的继承更加简洁。

    class UpperAttrMetaclass(type):

    def __new__(cls, clsname, bases, dct):

    uppercase_attr = {}

    for name, val in dct.items():

    if not name.startswith('__'):

    uppercase_attr[name.upper()] = val

    else:

    uppercase_attr[name] = val

    return super(UpperAttrMetaclass, cls).__new__(cls, clsname, bases, uppercase_attr)

    回到Python3.x中对元类的使用:

    class Foo(object, metaclass=Thing, kwarg1=value1):

    ...

    %可以转换成如下:

    class Thing(type):

    def __new__(class, clsname, bases, dct, kwargs1=default):

    ...

    元类的介绍差不多就这些。

    元类在实际中最主要的应用是生成API,典型应用是Django的ORM

    (转载:csdn)

    相关文章

      网友评论

          本文标题:如何理解python中的类_如何理解Python中的元类meta

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