有Python不愁没对象

什么是元类?

什么是元类?

学习了python的面向对象编程知识后,都知道实例对象是由类创建的,那么类是由什么创建的呢?元类。

虽然平时接触到元类的机会不多,但也有必要认识一下。

类也是对象

类同样也是一种对象。是的,没错,就是对象。只要你使用关键字class,Python解释器在执行的时候就会创建一个对象。

  • 你可以将它赋值给一个变量
  • 你可以为它增加属性
  • 你可以将它作为函数参数进行传递

 

class Girl:
    pass

#增加属性
Girl.boy = "me"
#检查是否有boy属性
print("Does the girl have a boy? ", hasattr(Girl, 'boy'))
#查看boy是你还是老王
print("the girl‘s boy friend is", Girl.boy)
#查看girl是不是一个类
print(Girl)
#创建Girl的实例对象
girl = Girl
print(girl)

控制台打印结果:

Does the girl have a boy? True
the girl's boy friend is me
<class '__main__.Girl'>
<class '__main__.Girl'>

Process finished with exit code 0

当你使用class关键字时,Python解释器自动创建这个对象。但就和Python中的大多数事情一样,Python仍然提供给你手动处理的方法。

 

不知道你是否还有印象,就是第一天学习python的时候都会写的一个函数——type()。

num = 1
print(type(num))
string = 'python'
print(type(string))
float_ = 1.10
print(type(float_))
class Girl:
    pass

print(type(Girl))

console result of printing:

<class 'int'>
<class 'str'>
<class 'float'>
<class 'type'>

Process finished with exit code 0

 

使用type创建类

type还有一种完全不同的功能,动态的创建类。接受一个类的描述作为参数,然后返回一个类。

type实现格式:

type(类名, 由父类名称组成的元组(针对继承的情况,可以为空),包含属性的字典(名称和值))

示例:

# 使用type创建带有属性的类
girl = type('Girl', (), {'boy': 'me'})
# 检查是否有boy属性
print("Does the girl have a boy? ", hasattr(girl, 'boy'))
# 查看boy是你还是老王
print("the girl's boy friend is", girl.boy)

console result of printing:

Does the girl have a boy? True
the girl's boy friend is me

Process finished with exit code 0
有控制台打印结果可以发现,type的第一个参数是类名,第二个是元组是用来填写继承父类名称,第三个是设置类属性。
注意:添加的属性是类属性,并不是实例属性
使用type()创建类方法
def say():
    print("I have a handsome boy~")

girl = type('Girl', (), {'say': say})
girl.say()
console result of printing :
I have a handsome boy~

Process finished with exit code 0
什么是元类?
其实呢?实例对象就是由类(对象)创建的,而这个类(对象)就是由元类创建的。
Girl = MetaClass()   #使用元类创建出一个对象,这个对象称为“类”
girl = Girl()     #使用“类”创建出实例对象
函数type实际上是一个元类。type就是Python在背后用来创建所有类的元类。现在你想知道那为什么type会全部采用小写形式而不是Type呢?好吧,我猜这是为了和str保持一致性,str是用来创建字符串对象的类,而int是用来创建整数对象的类。type就是创建类对象的类。你可以通过检查class属性来看到这一点。Python中所有的东西,注意,我是指所有的东西——都是对象。这包括整数、字符串、函数以及类。它们全部都是对象,而且它们都是从一个类创建而来,这个类就是type。
查看示例对象的类(对象)属性__class__:
class Girl:
    pass


str = 'python'
print(str.__class__)
num = 1
print(num.__class__)
girl = Girl()
print(girl.__class__)
console result of printing :
<class 'str'>
<class 'int'>
<class '__main__.Girl'>

Process finished with exit code 0
查看类(对象)的类属性__class__:
class Girl:
    pass


str = 'python'
print(str.__class__.__class__)
num = 1
print(num.__class__.__class__)
girl = Girl()
print(girl.__class__.__class__)
console result of printing :
<class 'type'>
<class 'type'>
<class 'type'>

Process finished with exit code 0

由此可见,不管是什么类型的类,最终的创建元类都是type

因此,元类就是创建类这个对象的东西。type就是Python的内建元类,当然了,你也可以自定义元类。

 

自定义元类

需要创建自己的想要的元类就要用到__metaclass__属性,下面说一下Python创建对象时的一些原理:

class motherInLaw(object):
    __metaclass__ = ''


class Gril(motherInLaw):
    pass
  • Girl这个有__metaclass__属性吗?如果有,那么Python就会通过__metaclass__创建一个名为Girl的类对象。
  • 如果没有找到__metaclass__,它就会继续往上找,找它的父类motherInLow,然后和上面一步的操作一样。
  • 如果父类都找不到就会尝试往它的模块层次找__metaclass__,并尝试做上面的同样操作
  • 如果都找不到就会使用python的内置type来创建对象。

 

元类的主要目的就是为了当创建类时能够自动地改变类,假如你想要你的类的属性都应该是大写形式,那么就可以使用元类进行实现。__metaclass__可以任意调用,不仅仅可以调用指定的类还可以调用指定函数。

指定函数进行调用:

def toUpper(class_name, class_parents, class_attr):
    # class_name 会保存类的名字 Foo
    # class_parents 会保存类的父类 object
    # class_attr 会以字典的方式保存所有的类属性

    # 遍历属性字典,把不是__开头的属性名字变为大写
    new = {}
    for name, value in class_attr.items():
        new[name.upper()] = value
        print("name.upper():", name.upper())
        print("value:", value)

    # 调用type来创建一个类
    return type(class_name, class_parents, new)


class Girl(object, metaclass=toUpper):  # 指定metaclass属性
    boy = 'me'


print("+" * 30)
print("Does the girl have the boy? ", hasattr(Girl, 'boy'))
print("Does the girl have the boy? ", hasattr(Girl, 'BOY'))

girl = Girl()
print("the girl's boy friend is", girl.BOY)

console result of printing :

name.upper(): __MODULE__
value: __main__
name.upper(): __QUALNAME__
value: Girl
name.upper(): BOY
value: me
++++++++++++++++++++++++++++++
Does the girl have the boy? False
Does the girl have the boy? True
the girl's boy friend is me

Process finished with exit code 0

 

调用指定类实现:

class UpperAttrMetaClass(type):

    # __new__ 是在__init__之前被调用的特殊方法
    # __new__是用来创建对象并返回之的方法
    # 而__init__只是用来将传入的参数初始化给对象
    # 你很少用到__new__,除非你希望能够控制对象的创建
    # 这里,创建的对象是类,我们希望能够自定义它,所以我们这里改写__new__
    # 如果你希望的话,你也可以在__init__中做些事情
    # 还有一些高级的用法会涉及到改写__call__特殊方法,但是我们这里不用
    def __new__(cls, class_name, class_parents, class_attr):
        # 遍历属性字典,把不是__开头的属性名字变为大写
        new = {}
        for name, value in class_attr.items():
            new[name.upper()] = value
            print("name.upper():", name.upper())
            print("value:", value)

        # 调用type来创建一个类
        return type(class_name, class_parents, new)


class Girl(object, metaclass=UpperAttrMetaClass):  # 指定metaclass属性
    boy = 'me'


print("+" * 30)
print("Does the girl have the boy? ", hasattr(Girl, 'boy'))
print("Does the girl have the boy? ", hasattr(Girl, 'BOY'))

girl = Girl()
print("the girl's boy friend is", girl.BOY)

console result of printing :

name.upper(): __MODULE__
value: __main__
name.upper(): __QUALNAME__
value: Girl
name.upper(): BOY
value: me
++++++++++++++++++++++++++++++
Does the girl have the boy? False
Does the girl have the boy? True
the girl's boy friend is me

Process finished with exit code 0

元类的作用其实很简单:

  • 修改类
  • 返回修改之后的类

 

点赞

发表评论

昵称和uid可以选填一个,填邮箱必填(留言回复后将会发邮件给你)
tips:输入uid可以快速获得你的昵称和头像

Title - Artist
0:00