python元类 orm_Python3 元类与ORM

元类

exec方法

exec()方法可以执行字符串形式的python代码块

使用方法exec(object, global=None, local=None)

object: 字符串类型的python代码块

global: 代表全局名称空间, 必须是字典, 默认为None, 如传参则表明该代码块在全局名称空间中运行

local: 代表局部名称空间, 可以是任何映射, 默认为None, 如传参则表明该代码块在局部空间中运行

code = '''

x = 0

sum = x + y + z

print(sum)

'''

y = 1

z = 2

global_dict = {'y': 2, 'z': 3}

local_dict = {'y': 3, 'z': 4}

exec(code)

'''

y = 1

z = 2

x = 0

sum = x + y + z

print(sum)

'''

exec(code, global_dict)

'''

相当于

y = 2

z = 3

x = 0

sum = x + y + z

print(sum)

'''

exec(code, global_dict, local_dict)

'''

相当于

y = 2

z = 3

def exec_func():

y = 3

z = 4

x = 0

sum = x + y + z

print(sum)

exec_func()

'''

'''

3

5

7

'''

type元类

类本身也是对象, 是通过实例化元类得到的对象, 元类是类的类

python默认的元类是type, 也可以自定义元类来实现控制类的创建

通过type元类来创建类 MyClass = type(class_name, class_bases, class_dict)

class_name 类名

class_bases 父类, 以元祖形式传入

class_dict 类的属性, 以字典的形式传入 (类的名称空间)

# 用type元类来创建一个Chinese类

def __init__(self, name, gender, age):

self.name = name

self.gender = gender

self.age = age

class_name = 'Chinese'

class_bases = (object,)

class_dict = {'country': 'China', '__init__': __init__}

Chinese = type(class_name, class_bases, class_dict)

c1 = Chinese('bigb', 'male', 18)

print(c1.name) # bigb

print(c1.country) # China

自定义元类

自定义元类控制类的创建

我们可以通过自定义元类来实现控制类的创建过程

自定义的元类必须继承type, 并且覆盖type的__init__的方法

'''

通过自定义元类来实现:

1. 类名首字母必须大写

2. 类中必须有文档注释

'''

class MyMeta(type):

def __init__(self, class_name, class_bases, class_dict):

print(class_name) # chinese

print(class_bases) # (,)

print(class_dict) # {'__module__': '__main__', '__qualname__': 'chinese', 'country': 'China', '__init__': , 'kongfu': }

# 类名首字母必须大写

if not class_name.istitle():

raise TypeError('类的首字母必须大写!')

# 类中必须有注释

if not class_dict.get('__doc__'):

raise TypeError('类中必须有文档注释!')

# 调用type中的__init__方法初始化对象

super().__init__(class_name, class_bases, class_dict)

class chinese(object, metaclass=MyMeta): # foo = MyMeta('foo', (object, ) {...})

country = 'China'

def __init__(self, name, gender, age):

self.name = name

self.gender = gender

self.age = age

def kongfu(self):

print('降龙十八掌!')

'''

raise TypeError('类的首字母必须大写!')

TypeError: 类的首字母必须大写!

'''

# 将类名大写, 再运行

class Chinese(object, metaclass=MyMeta): # foo = MyMeta('foo', (object, ) {...})

country = 'China'

def __init__(self, name, gender, age):

self.name = name

self.gender = gender

self.age = age

def kongfu(self):

print('降龙十八掌!')

'''

raise TypeError('类中必须有文档注释!')

TypeError: 类中必须有文档注释!

'''

自定义元类控制类的调用

调用一个对象时, 会触发对象的类当中的 __call__ 方法

类本身也是个对象, 因此在调用类实例化对象的时候, 就会触发元类当中的__call__ 方法

class MyMeta(type):

def __call__(self, *args, **kwargs):

# 产生一个空对象

obj = self.__new__(self) # self是类对象

# 初始化空对象

self.__init__(obj, *args, **kwargs)

# 返回初始化好的对象

return obj

class Chinese(object, metaclass=MyMeta): # foo = MyMeta('foo', (object, ) {...})

country = 'China'

def __init__(self, name, gender, age):

self.name = name

self.gender = gender

self.age = age

def kongfu(self):

print('降龙十八掌!')

# 这里调用了类对象Chinese, 因此会触发Chinese的类(元类)中的__call__方法

c1 = Chinese('bigb', 'male', 18)

print(c1.name)

'''

1. __call__中的__new__生成了一个空对象

2. __call__中的__init__初始化这个空对象

3. __call__返回了这个对象,并赋值给了c1

'''

现在 我们可以在此基础上通过修改 __call__ 的逻辑从而控制类的调用过程

# 通过元类让Chinese类实例化出来的对象的属性变为私有属性

class MyMeta(type):

def __call__(self, *args, **kwargs):

# 产生一个空对象

obj = self.__new__(self) # self是类对象

# 初始化空对象

self.__init__(obj, *args, **kwargs)

# 将对象的属性变成私有属性(对象._类__属性名)

obj.__dict__ = {f'_{self.__name__}__{k}': v for k, v in obj.__dict__.items()}

# 返回初始化好的对象

return obj

class Chinese(object, metaclass=MyMeta): # foo = MyMeta('foo', (object, ) {...})

country = 'China'

def __init__(self, name, gender, age):

self.name = name

self.gender = gender

self.age = age

def kongfu(self):

print('降龙十八掌!')

# 这里调用了类对象Chinese, 因此会触发Chinese的类(元类)中的__call__方法

c1 = Chinese('bigb', 'male', 18)

print(c1._Chinese__name) # bigb

ORM

对象关系映射 Object Relational Mapping

表 ---> 类

字段 ---> 属性

记录 ---> 对象

# 定义字段类

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

# 定义整数字段类

class IntegerField(Field):

def __init__(self, name, column_type='int', primary_key=False, default=0):

super().__init__(name, column_type, primary_key, default)

# 定义字符字段类

class StringField(Field):

def __init__(self, name, column_type='varchar(64)', primary_key=False, default=None):

super().__init__(name, column_type, primary_key, default)

# 定义元类

class OrmMetaClass(type):

def __new__(cls, class_name, class_bases, class_dict):

print(class_name)

print(class_dict)

# Models类中什么都不做, 将类原路返回

if class_name == 'Models':

return type.__new__(cls, class_name, class_bases, 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

# 判断该字段对象中的primary key是否为True

if value.primary_key:

# 判断主键是否已经存在

if primary_key:

raise TypeError('只能有一个主键!')

# 若主键不存在, 则给primary key赋值

primary_key = value.name

# 删除mappings与类名称空间中重复的属性, 节省空间

for key in mappings.keys():

class_dict.pop(key)

if not primary_key:

raise TypeError('必须有一个主键!')

# 给类的名称空间添加表名

class_dict['table_name'] = table_name

# 给类的名称空间添加主键名

class_dict['primary_key'] = primary_key

# 给类的名称空间中添加mappings字典, 字典中拥有所有字段属性

class_dict['mappings'] = mappings

return type.__new__(cls, class_name, class_bases, class_dict)

class Models(dict, metaclass=OrmMetaClass):

# 让表对象(字典)可以通过 对象.属性的方式获取值

def __getattr__(self, item):

return self.get(item)

# 让表对象(字典)可以通过 对象.属性 = 值 的方式进行赋值

def __setattr__(self, 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')

原文:https://www.cnblogs.com/bigb/p/11796023.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值