ITPub博客

首页 > 数据库 > 数据库开发技术 > python面向对象编程篇(20):元类初识

python面向对象编程篇(20):元类初识

原创 数据库开发技术 作者:程序员小钟同学 时间:2021-03-23 16:03:00 0 删除 编辑

之前只说过,在python是一个动态语言,动态语言本身支持运行期动态创建类,且python奉行的理念是:一切皆对象,包括类,函数方法等等都是对象。作为一个对象在python世界里,你可以为对象做的事情就很多,如:

  • 给对象赋予一个变量
  • 给对象添加属性,修改拷贝
  • 作为参数进行传递
  • ·····························

那类既然是对象,那是由谁创造出类的呐?它的父类又是谁呐?

带着这些疑问和思考,有必要需要去了解一下关于《元类》。

元类

1、什么是元类(metaclass)?

再了解什么是元类,之前我们梳理一下之前学到的知识点。追本溯源,首先,在python我们场景的几个数据结构类型,他们其实也是对象,那既然是对象,那肯定有对应的创建对象的类(相对数据类型来说)。

1.1几种数据类型分析:

python查看对象所属类型方式:

  • type函数
  • 对象自带的__class__属性


对应的对象的类型:

print(type(1)) # <class 'int'>
# PS:说明:1的类型是int,1是int一个实例

print(type('a'))# <class 'str'>
# PS:说明:'a'的类型是str,'a'是str一个实例

print(type([1,'a']))#<class 'list'>
# PS:说明:[1,'a']的类型是list,它是list一个实例

print(type({1:1,2:3}))# <class 'dict'>
# PS:说明:{1:1,2:3}的类型是dict,它是dict一个实例

print(type(True))# <class 'bool'>
# PS:说明:True的类型是bool,它是bool一个实例

再深入,int是类的话,那么类的类型是什么?

print(type(int))# <class 'type'>
print(type(str))# <class 'type'>
print(type(list))# <class 'type'>
print(type(dict))# <class 'type'>
print(type(bool))# <class 'type'>


对比使用__class__查看对象类型:

print(int.__class__)# <class 'type'>
print(str.__class__)# <class 'type'>
print(list.__class__)# <class 'type'>
print(dict.__class__)# <class 'type'>
print(bool.__class__)# <class 'type'>

再深入查看具体的类型:
print(int.__class__.__class__)# <class 'type'>
print(str.__class__.__class__)# <class 'type'>
print(list.__class__.__class__)# <class 'type'>
print(dict.__class__.__class__)# <class 'type'>
print(bool.__class__.__class__)# <class 'type'>

从上面看,我们的类的类型都是:type

那我们的type的类的类型是什么?

print(type.__class__)# <class 'type'>

其他的补充:

自定义方法:

def ceshi(): pass 
print(type(ceshi)) # <class 'function'>
print(type(lambda x:x)) # <class 'function'>

自定义类:

class ceshi: pass
print(type(ceshi)) # <class 'type'>

自定义类的对象的类型:

class ceshi: pass

a = ceshi()
print(type(a)) # <class '__main__.ceshi'>

从上面可以看:type 和 class的关系:

  • a 的类型是:__main__模块下的ceshi类
  • ceshi类 的类型是:type

1.2查看所有类的基类:

print(int.__bases__)# (<class 'object'>,)
print(str.__bases__)# (<class 'object'>,)
print(list.__bases__)# (<class 'object'>,)



class ceshi: pass
print(ceshi.__bases__)# (<class 'object'>,)


class ceshi_fun: pass
print(ceshi_fun.__bases__)# (<class 'object'>,)

print(type.__bases__)# (<class 'object'>,)

上面可以看出 type、class和object的关系图为:

综上可以总结为:

  • Python当中的每一个类都是type的实例。
  • 元类metaclass就是用来创建类的,type 就是创建所有类的「元类」
  • 普通类型、方法、实例,还是类 等在python里统统都是对象,它们的都是元类创建出来的。
  • 可以动态的定制或修改继承它的子类。
  • 元类相当于是类的模板,也就是类的类,可以给类预装属性,预装函数
  • 元类可以让子类隐式的继承一些方法或属性

2、使用type动态的创建类

type动态创建类的参数说明:

type(class_name, (base_class, ...), {class_attr_key: attr_value, ...})
  • class_name :类名
  • base_class :由父类名称组成的元组(针对继承的情况,可以为空)
  • class_attr_key :包含类属性的字典(名称和值)(一般仅限于类属性,但是其实也当做实例属性,不过实例属性是动态的附加)

PS:class_attr_key 是类属性的创建,不属于实例的属性(但也可以当做实例属性)

示例:

#运用type创建类、添加属性
Test = type("Test",(),{'age':1988,'name':"xiaozhong"})
test = Test()
test.age= 56
print(test.age) # 对象实例属性:56
print(Test.age) # 类属性:1988


Test.age = 6767 # 修改类的属性
test2 = Test() # 创建新的对象
test2.age= 233452 # 给新对象的属性修改新的值
print(test2.age)  # 233452
print(Test.age) # 修改类的属性后 --6767

附加类的方法:

#实例对象的方法
def show_age(self):
    print("---返回年龄---%d" % self.age)
    return self.age


#创建类方法
@classmethod
def run(cls):
    print('调用类方法:',cls.__name__)
    return cls.__name__

#创建静态方法
@staticmethod
def eat():
    print('调用类静态方法:')
    return "吃饭"

# 使用元类(type)创建类
Test = type("Test", (), {'age':13,'show_age': show_age,'name':'小钟','run':run,'eat':eat})

test = Test()
test.age= 56


print(test.age) # 对象实例属性:56
print(test.show_age()) # 调用方法
print(Test.run()) # 调用类方法
print(Test.eat()) # 调用类静态方法

输出:

56
---返回年龄---56
56
调用类方法: Test
Test
调用类静态方法:
吃饭

实现继承的方式:

#实例对象的方法
def show_age(self):
    print("---返回年龄---%d" % self.age)
    return self.age


#创建类方法
@classmethod
def run(cls):
    print('调用类方法:',cls.__name__)
    return cls.__name__

#创建静态方法
@staticmethod
def eat():
    print('调用类静态方法:')
    return "吃饭"


class Base(object):
    pass
    basename = '基类名称'

# 使用元类(type)创建类
Test = type("Test", (Base,), {'age':13,'show_age': show_age,'name':'小钟','run':run,'eat':eat})

test = Test()
test.age= 56


print(test.age) # 对象实例属性:56
print(test.show_age()) # 调用方法
print(Test.run()) # 调用类方法
print(Test.eat()) # 调用类静态方法
print(Test.basename) # 调用基类属性

PS:新增:

class Base(object):
   pass
   basename = '基类名称'

输出:

56
---返回年龄---56
56
调用类方法: Test
Test
调用类静态方法:
吃饭
基类名称

3、结合metaclass使用继承type的中的__new__方式进行定制类的创建

metaclass属性:作用正是去指示某个新的类所套用的类模板(元类)是什么。

#实例对象的方法
def show_age(self):
    print("---返回年龄---%d" % self.age)
    return self.age


#创建类方法
@classmethod
def run(cls):
    print('调用类方法:',cls.__name__)
    return cls.__name__

#创建静态方法
@staticmethod
def eat():
    print('调用类静态方法:')
    return "吃饭"



# metaclass是创建类,所以必须从`type`类型派生:
class TestMetaclass(type):

    # __new__是真正的构造函数,在这里传入的cls对象,再这里返回这个对象
    # __new__是再__init__之前调用,分配对象
    def __new__(cls, name, bases, attrs):
        attrs['age'] = 12
        attrs['eat'] = eat
        attrs['run'] = run
        attrs['show_age'] = show_age
        return type.__new__(cls, name, bases, attrs)


class Test(metaclass=TestMetaclass):
    pass


test = Test()



print(test.age) # 对象实例属性:56
print(test.show_age()) # 调用方法
print(Test.run()) # 调用类方法
print(Test.eat()) # 调用类静态方法

注意:class Test(metaclass=TestMetaclass): 是PY3的写法,PY3需要这样写

输出:

12
---返回年龄---12
12
调用类方法: Test
Test
调用类静态方法:
吃饭

通过__dict__查看我们的当前的类拥有的属性:

for i in Test.__dict__:
    print(i)
输出结果:
__module__
age
eat
run
show_age
__dict__
__weakref__
__doc__

示例代码梳理上述的步骤:

  • 1 创建一个元类TestMetaclass
  • 2 使用元类TestMetaclass创建了一个Test类(Test类的属性__metaclass__就变成了TestMetaclass)

示例代码内部执行流程分析:

- 1 对于Test,首先Python会在类的定义中寻找__metaclass__属性

> PS: 我们已经把class Test(metaclass=TestMetaclass),所以它存在__metaclass__属性

- 2 如果找到__metaclass__属性则使用它来创建类Test,

- 3 如果没有找到,继续往父类寻找,如果父类还是没有,则默认就会用内建的type来创建这个类。

PS:类创建过程:

Metaclass.__new__方法返回的是类的对象

1: MetaMetaclass.__call__返回Metaclass.__new_的返回值

2:如果它返回的是Metaclass的一个实例,同时会调用Metaclass.__init__

4、基于元类实现的单例模式示例

class SingleInstanceMetaClass(type):

    def __init__(self, name, bases, dic):
        self.__single_instance = None
        super().__init__(name, bases, dic)

    def __call__(cls, *args, **kwargs):
        if cls.__single_instance:
            return cls.__single_instance
        single_obj = cls.__new__(cls)
        single_obj.__init__(*args, **kwargs)
        cls.__single_instance = single_obj
        return single_obj

 # 测试SingleInstanceMetaClass
class SingleTest(metaclass=SingleInstanceMetaClass):

    def __init__(self):
        self.name = 'xiaozhong'
        self.age = 12


a = SingleTest()
b = SingleTest()

print(a is b)
a.age = 89
print(a.name, b.age)
b.age = 11289
print(a.name, b.age)

PS:单例模式,没必要重复产生对象,避免内存浪费。

5、补充关于是类属性还是实例属性的演示:

#实例对象的方法
def show_age(self):
    print("---返回年龄---%d" % self.age)
    return self.age


#创建类方法
@classmethod
def run(cls):
    print('调用类方法:',cls.__name__)
    return cls.__name__

#创建静态方法
@staticmethod
def eat():
    print('调用类静态方法:')
    return "吃饭"



# metaclass是创建类,所以必须从`type`类型派生:
class TestMetaclass(type):
    def __new__(cls, name, bases, attrs):
        attrs['age'] = 12
        attrs['eat'] = eat
        attrs['run'] = run
        attrs['show_age'] = show_age
        return type.__new__(cls, name, bases, attrs)


class Test(metaclass=TestMetaclass):
    pass


test = Test()

Test.age = 458458
print(Test.age) # 对象实例属性:56
test.age = 12
print(test.age) # 对象实例属性:56
print(test.show_age()) # 调用方法
print(Test.run()) # 调用类方法
print(Test.eat()) # 调用类静态方法

输出:

458458
12
---返回年龄---12
12
调用类方法: Test
Test
调用类静态方法:
吃饭

但是如果没有

test.age = 12

则默认的实例的属性都是类属性的值。

Test.age = 458458
print(Test.age) # 对象实例属性:56
# test.age = 12
print(test.age) # 对象实例属性:56
print(test.show_age()) # 调用方法
print(Test.run()) # 调用类方法
print(Test.eat()) # 调用类静态方法

输出的结果为:

458458
458458
---返回年龄---458458
458458
调用类方法: Test
Test
调用类静态方法:
吃饭

使用元类演示一个简易版的ORM框架

以上是对元类的简单总结和介绍,以下ORM示例参考廖雪峰老师讲述的ORM的进行元类的场景实战:

PS:廖雪峰老师博客地址

  • https://www.liaoxuefeng.com/wiki/897692888725344/923030550637312

首先理解一下什么是ORM,”Object Relational Mapping”,即对象-关系映射,简称ORM.

在python常见几个的ORM框架:

  • 同步:Django’s ORM、peewee、SQLAlchemy
  • 异步:tortoise-orm,GINO

简单的定义一个ORM示例 首先梳理思路:

  • 1.定义一个Field基类
  • 2.定义基于Field基类上扩展出来的:StringField和IntegerField等
  • 3.定义model模型元类
  • 4.基于model模型元类是定制出具体的类
  • 5.基于定制出具体类映射对应的表
  • 6.基于对应的表的实例执行具体的SQL语句

1.首先定义:Field基类

class Field():

    def __init__(self, name, colnum_type):
        self.name =name
        self.colnum_type = colnum_type

    def __str__(self):
        return  '<%s:%s>' % (self.__class__.__name__, self.name)

2.定义基于Field基类上扩展出来的:StringField和IntegerField

class StringField(Field):

    def __init__(self, name):
        super(StringField, self).__init__(name, 'varchar(100)')


class IntegerField(Field):

    def __init__(self, name):
        super(IntegerField, self).__init__(name, 'bigint')

3.定义model模型元类


class ModeMetaclass(type):

    def __new__(cls, name, bases, attrs):
        if name == 'Model':
            return type.__new__(cls, name, bases, attrs)
        maps = dict()
        # 把attr属性内的与Field类相关的k-v对儿,集中放到mappings内,mappings放到attrs内。
        for k, v in attrs.items():# 找到值是Field类对象的k-v对,然后复制到mappings字典内。
            if isinstance(v, Field):
                print('>>>>Found mapping: %s ==> %s' % (k, v))
                maps[k] = v
                
        for k in maps.keys():# 从attr中删除值是Field类的对象的k-v对。
            attrs.pop(k) 
        attrs['__maps__'] = maps #保存属性和列的映射关系
        attrs['__table__'] = name #假设表的名字等于类的名字
        return type.__new__(cls, name, bases, attrs)

4.基于model模型元类是定制出具体的类

class Model(dict, metaclass=ModeMetaclass):
    def __init__(self, **kw):
        super(Model, self).__init__(**kw)

    def __getattr__(self, key):
        return self[key]

    def __setattr__(self, key, value):
        self[key] = value

    def save(self):
        fields = []
        params = []
        values = []
        for k, v in self.__maps__.items():
            fields.append(v.name)
            params.append('?')
            values.append(self[k])
        sql = 'insert into %s (%s) values (%s)' % (self.__table__, ','.join(fields), ','.join(params))
        print('SQL: %s' % sql)
        print('values: %s' % values)

5.基于定制出具体类映射对应的表

#user表
class User(Model):
    __table__ = 'user'
    # 定义类的属性到列的映射:
    id = IntegerField('id')
    username = StringField('username')


#score表
class Score(Model):
    __table__ = 'score'
    # 定义类的属性到列的映射:
    id = IntegerField('id')
    score = IntegerField('score')

6.基于对应的表的实例执行具体的SQL语句

u = User(id=1, username='xiaoxiao')
u.save()

s = Score(id=1, score=100)
s.save()

输出结果:

>>>>Found mapping: id ==> <IntegerField:id>
>>>>Found mapping: username ==> <StringField:username>
>>>>Found mapping: id ==> <IntegerField:id>
>>>>Found mapping: score ==> <IntegerField:score>
SQL: insert into User (id,username) values (?,?)
values: [1, 'xiaoxiao']
SQL: insert into Score (id,score) values (?,?)
values: [1, 90]

以上是对元类的简单应用和实践操作。

个人其他博客地址

简书:https://www.jianshu.com/u/d6960089b087

掘金:https://juejin.cn/user/2963939079225608

小钟同学 | 文 【原创】| QQ:308711822

  • 1:本文相关描述主要是个人的认知和见解,如有不当之处,还望各位大佬指正。
  • 2:关于文章内容,有部分内容参考自互联网,如有链接会声明标注;如没有及时标注备注的链接的,如有侵权请联系,我会立即删除处理哟。


原文作者:程序员小钟同学;如有侵权,请联系管理员删除。

请登录后发表评论 登录
全部评论

注册时间:2021-02-20

  • 博文量
    224
  • 访问量
    111708