首页 > 数据库 > 数据库开发技术 > python面向对象编程篇(20):元类初识
之前只说过,在python是一个动态语言,动态语言本身支持运行期动态创建类,且python奉行的理念是:一切皆对象,包括类,函数方法等等都是对象。作为一个对象在python世界里,你可以为对象做的事情就很多,如:
那类既然是对象,那是由谁创造出类的呐?它的父类又是谁呐?
带着这些疑问和思考,有必要需要去了解一下关于《元类》。
再了解什么是元类,之前我们梳理一下之前学到的知识点。追本溯源,首先,在python我们场景的几个数据结构类型,他们其实也是对象,那既然是对象,那肯定有对应的创建对象的类(相对数据类型来说)。
python查看对象所属类型方式:
对应的对象的类型:
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的关系:
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里统统都是对象,它们的都是元类创建出来的。 可以动态的定制或修改继承它的子类。 元类相当于是类的模板,也就是类的类,可以给类预装属性,预装函数 元类可以让子类隐式的继承一些方法或属性
type动态创建类的参数说明:
type(class_name, (base_class, ...), {class_attr_key: attr_value, ...})
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
调用类静态方法:
吃饭
基类名称
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 对于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__
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:单例模式,没必要重复产生对象,避免内存浪费。
#实例对象的方法
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的进行元类的场景实战:
PS:廖雪峰老师博客地址
https://www.liaoxuefeng.com/wiki/897692888725344/923030550637312
首先理解一下什么是ORM,”Object Relational Mapping”,即对象-关系映射,简称ORM.
在python常见几个的ORM框架:
简单的定义一个ORM示例 首先梳理思路:
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)
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')
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)
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)
#user表
class User(Model):
__table__ = 'user'
# 定义类的属性到列的映射:
id = IntegerField('id')
username = StringField('username')
#score表
class Score(Model):
__table__ = 'score'
# 定义类的属性到列的映射:
id = IntegerField('id')
score = IntegerField('score')
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:关于文章内容,有部分内容参考自互联网,如有链接会声明标注;如没有及时标注备注的链接的,如有侵权请联系,我会立即删除处理哟。
原文作者:程序员小钟同学;如有侵权,请联系管理员删除。