Python-元类
exec模块
什么是exec模块?
exec 模块 是Python内置的一个模块
exec模块的作用?
exec 模块可以把 “字符串形式的” Python代码 添加到全局名称空间或局部名称空间中
exec模块怎么用?
直接调用 exec()
需要传三个参数:
- 参数1:字符串形式的Python代码
- 参数2:全局名称空间 字典
- 参数3:局部名称空间 字典
# 全局名称空间
'''
文本形式的python代码:
如下:
'''
code = '''
global x
global y
x = 10
y = 20
def func():
pass
'''
# 全局名称空间
global_dict = {"x":200}
# 局部名称空间
local_dict = {}
# 传三个参数:
# 参数1:字符串形式的Python代码
# 参数2:全局名称空间
# 参数3:局部名称空间
exec(code,global_dict,local_dict)
print(global_dict) # {'x': 10,....}
print(local_dict) # {'func': <function func at 0x0000000001D01E18>}
创建类的两种方式
1、 用 class 关键字创建
# 创建类的第一种方式:
class Test:
country = "china"
def __init__(self,name,age):
self.name = name
self.age = age
def speak(self):
print("speak chinese")
p1 = Test("qinyj",18)
print(Test)
2、 手动调用 type() 实例化出来得到一个自定义的类
# 创建类的第二种方式:
class_name = "Test"
class_base = (object,)
class_dict = {}
code = '''
name = "Test"
def __init__(self,name,age):
self.name = name
self.age = age
def test(self):
print("from Test.test...")
'''
# 使用exec 模块,目的:将字符串形式的Python代码执行封装到类的名称空间中
exec(code,{},class_dict)
'''
type源码:
def __init__(cls, what, bases=None, dict=None): # known special case of type.__init__
"""
type(object_or_name, bases, dict)
type(object) -> the object's type
type(name, bases, dict) -> a new type
# (copied from class doc)
"""
参数说明:
what --> 类名
bases --> 基类/父类
dict --> 类的名称空间
'''
Test = type(class_name,class_base,class_dict)
print(Test)
元类介绍
什么是元类?
元类就是类的类,我们自定义的类的类是type,type就是所有类的类,type就是一个元类
元类的作用?
元类可以帮我们控制类的创建
元类可以帮我们控制类的调用
怎么用元类?
1、 自定义一个元类,继承type,派生出自己的属性和方法
2、 给需要使用的类,通过metaclass 指定自定义的元类
自定义一个元类
首先我们自定义一个元类必须要继承type类,然后重写里面的方法。
# 自定义一个元类
class MyMeta(type):
# 子类的方法与父类的方法一样,优先用子类的,子类覆盖父类的__init__方法
# 控制了子类的定义方式
def __init__(self,class_name,class_base,class_dict):
if not class_name.istitle():
raise TypeError("类的首字母必须大写")
if not class_dict.get("__doc__"):
raise TypeError("类的内部必须要写注释")
super().__init__(class_name, class_base, class_dict)
# 模拟type元类内部做的事情
# 元类触发的__call__可以控制类的调用,调用__call__会触发以下两点:
# 1、会调用__new__产生一个空对象
# 2、会执行__init__(),把参数传过去,再将实例化出来的对象返回给自定义的类
def __call__(self, *args, **kwargs):
obj = object.__new__(self)
obj.__init__(*args, **kwargs)
return obj
# 可以通过元类内部的__new__控制对象的创建
# def __new__(cls, *args, **kwargs):
# pass
# 首先自定义一个类,
# 因为Foo类继承了元类,必须手动继承object
class Foo(object,metaclass=MyMeta):
'''
如果定义的类名首字母没有大写则会报错:
TypeError: 类的首字母必须大写
如果不写注释则会报错:
TypeError: 类的内部必须要写注释
注释:这是一个Foo类
'''
x = 10
def __init__(self,name,age):
self.name = name
self.age = age
def f1(self):
print("from Foo.f1")
foo = Foo("qinyj",18) # 调用Foo对象,会触发type的__call__方法
# print(foo.f1())
ORM
什么是ORM?
ORM:对象关系映射 --> 映射到数据库MySQL中的数据表
Python中 | MySQL数据库中 |
---|---|
类名 | 表名 |
对象 | 一条记录 |
对象.属性 | 字段 |
这里模拟Django的ORM,为了将数据库的增、删、改、查,全部封装成一个个方法,比如:save,delete,update,select
在优酷项目中实现元类
'''
ORM:对象关系映射:----》映射到数据MySQL中的数据表
类名--》表名
对象--》一条记录
对象.属性--》字段
模拟Django的ORM,为了将数据库的增、删、改、查全部封装成一个个的方法:
比如:save、delete、uptate、select
'''
class Field:
def __init__(self,name,column_type,primary_key,default):
self.name = name
self.column_type = column_type
self.primary_key = primary_key
self.default = default
# int类型
class IntegerField(Field):
def __init__(self,name,column_type="int",primary_key=False,default=0):
super().__init__(name,column_type,primary_key,default)
# str类型
class StringField(Field):
def __init__(self,name,column_type="varchar(64)",primary_key=False,default=None):
super().__init__(name,column_type,primary_key,default)
'''
问题1:解决代码冗余问题:比如有100张表,需要写100个__init__
解决1:使用继承,继承父类,继承一个dict
问题2:无法预测每一张表的字段是什么,无法通过父类的__init__解决问题
解决2:通过继承字典,内部的__init__,可以接受任意个数的关键字参数
问题3:继承字典的类实例化的对象,无法通过对象.属性的方式存值
解决3:对象和字典的属性时两个不同的名称空间,通过在类内部实现魔法方法
__getattr__、__setattr__ 实现字典与对象的名称空间的属性相通,一模一样,
并且具备字典原有的特性,取值方式和字典一样
'''
'''
创建元类,元类需要做的事情:
1、一张表必须有一个表名
2、一张数据表必须有一个主键,并且主键必须是唯一的
3、将数据表中所有的字段对象,都存放在一个独立的字典中
存不是目的,取才是目的
'''
class OrmMetaClass(type):
# 元类 实现__new__方法
def __new__(cls, class_name,class_base,class_dict):
# 过滤 Models 类
if class_name == "Models":
# 什么事情都不做,原路返回
return type.__new__(cls, class_name,class_base,class_dict)
# 获取数据表的表名
table_name = class_dict.get("table_name",class_name)
# 定义主键的中间变量
primary_key = None
# 定义字典,存放数据表的字段对象
mappings = {}
# 遍历类名称空间中的所有属性
for key,value in class_dict.items():
# 过滤不想要的属性
if isinstance(value,Field):
mappings[key] = value
# 判断是否是添加了主键
if value.primary_key:
# 判断主键的中间变量是否存在
# 如果已经有了就抛异常,只能有一个主键
if primary_key:
raise TypeError("只能有一个主键")
# 若主键中间的变量没有值,则给中间变量赋值
primary_key = value.name
# 如果上述遍历 发现没有定义主键,则抛异常必须有一个主键
if not primary_key:
raise TypeError("必须有一个主键")
# 循环遍历 把类的名称空间中多余重复的属性删除掉,节省内存资源
for key in mappings.keys():
class_dict.pop(key)
# 给类的名称空间添加表名、主键、存放字段对象 属性。
class_dict["table_name"] = table_name
class_dict["primary_key"] = primary_key
class_dict["mappings"] = mappings
return type.__new__(cls, class_name,class_base,class_dict)
class Models(dict,metaclass=OrmMetaClass):
def __getattr__(self, item):
# print(item,"在调用对象.属性没有属性值得时候触发")
return self.get(item)
def __setattr__(self, key, value):
# print(key,value)
self[key] = value
# 创建用户表类
class User(Models):
user_id = IntegerField(name="user_id",primary_key=True)
user_name = StringField(name="name")
pwd = StringField(name="pwd")
# 创建电影类
class Movies(Models):
movie_id = IntegerField(name="movie_id",primary_key=True)
movie_name = StringField(name="movie_name")
user = User(id="001",name="qinyj",pwd="123")
print(user)
# 通过在类内部实现魔法方法,让对象.属性得到的值和用字典得到的值名称空间相通,取到的值一模一样,
# print(user.get("id"))
# user.age = 18
# print(user.age)
movie = Movies(id="002",movie_name="真实写真")
print(movie)