千家信息网

Python 面向对象高级编程——使用枚举和元类

发表于:2024-11-14 作者:千家信息网编辑
千家信息网最后更新 2024年11月14日,1.1 使用枚举基于Enum类实现的枚举>>> fromenum import Enum>>> Month = Enum('Month', ('Jan','Feb', 'Mar', 'Apr', 'M
千家信息网最后更新 2024年11月14日Python 面向对象高级编程——使用枚举和元类

1.1 使用枚举

基于Enum实现的枚举

>>> fromenum import Enum

>>> Month = Enum('Month', ('Jan','Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'))

>>> for name, member inMonth.__members__.items():

... print(name, '=>', member, ',', member.value)

...

Jan => Month.Jan , 1

Feb => Month.Feb , 2

Mar => Month.Mar , 3

Apr => Month.Apr , 4

May => Month.May , 5

Jun => Month.Jun , 6

Jul => Month.Jul , 7

Aug => Month.Aug , 8

Sep => Month.Sep , 9

Oct => Month.Oct , 10

Nov => Month.Nov , 11

Dec => Month.Dec , 12

value属性则是自动赋给成员的int常量,默认1开始计数

>>> Month.Jun

>>> Month.Jun.value

6

精确地控制枚举类型,可以从Enum派生出自定义类

>>> from enum import Enum, unique

>>> @unique --装饰器检查是否重复

... class Weekday(Enum):

... Sun = 0

... Mon = 1

... Tue = 2

... Wed = 3

... Thu = 4

... Fri = 5

... Sat = 6

...

>>> Weekday(1)

>>> Weekday(5)

>>>Weekday['Sun']

>>> Weekday.Sun

>>>Weekday.Sun.value

0

>>> Weekday(7)

Traceback (most recent call last):

File "", line 1, in

File "/usr/local/lib/python3.5/enum.py", line 235, in __call__

return cls.__new__(cls, value)

File "/usr/local/lib/python3.5/enum.py", line 470, in __new__

raise ValueError("%r is not a valid %s" % (value,cls.__name__))

ValueError: 7 is not a valid Weekday

1.2 使用元类

1.2.1 type()

动态语言和静态语言最大的不同,就是函数和类的定义,不是编译时定义的,而是运行时动态创建的。

定义一个hello.py的模块

class Hello(object):

def hello(self, name='world'):

print('Hello, %s.' % name)

python解释器中调用

>>> from hello import Hello

>>> h = Hello()

>>> h.hello()

Hello, world.

>>> print(type(Hello)) --type类型

>>> print(type(h)) --class hello类型

class的定义是运行时动态创建的,而创建class的方法就是使用type()函数

>>> deffn(self, name = 'world'):

... print('Hello, %s' % name)

...

>>> Hello= type('Hello', (object,), dict(hello = fn))

>>> h = Hello()

>>> h.hello()

Hello, world

>>> print(type(Hello))

>>> print(type(h))

要创建一个class对象,type()函数依次传入3个参数:

1.class的名称

2.继承的父类集合,注意Python支持多重继承,如果只有一个父类,别忘了tuple的单元素写法

>>> T = (1)

>>> type(T)

>>> T = (1,)

>>> type(T)

3.class的方法名称与函数绑定,这里我们把函数fn绑定到方法名hello上。

1.2.2 metaclass

metaclass,直译为元类,简单的解释就是:metaclass是类的前提。

先定义metaclass,然后创建类,最后创建实例。

正常情况下,很少使用到metaclass

首先定义ListMetaclass

>>> class ListMetaclass(type): #metaclass是类的模板,必须从type派生

... def __new__(cls, name, bases, attrs):

... attrs['add'] = lambda self, vealue:self.append(value) --方法的定义

... return type.__new__(cls, name, bases, attrs)

...

>>>

>>> class Mylist(list, metaclass = ListMetaclass):

... pass

...

>>> L = Mylist()

>>> L

[]

>>> L.add(1)

>>> L

[1]

Python解释器在创建MyList时,要通过ListMetaclass.__new__()来创建;在此我们可以修改metaclass的定义如加上新的方法达到新增功能。

__new__()方法接收到的参数依次是:

1.当前准备创建的类的对象;

2.类的名字;

3.类继承的父类集合;

4.类的方法集合。

1.2.2.1 ORM

表示这部分内容比较难理解

开始介绍metaclass就提到:metaclass使用的场景很少,而ORM场景便是一个典型的例子。作为DBA,我认为这个技能需要掌握。

ORM全称"Object RelationalMapping",即对象-关系映射,就是把关系数据库的一行映射为一个对象,也就是一个类对应一个表,这样,写代码更简单,不用直接操作SQL语句。

要编写一个ORM框架,所有的类都只能动态定义,因为只有使用者才能根据表的结构定义出对应的类来。

让我们来尝试编写一个ORM框架。

编写底层模块的第一步,就是先把调用接口写出来。比如,使用者如果使用这个ORM框架,想定义一个User类来操作对应的数据库表User,我们期待他写出这样的代码:

class User(Model):

#定义类的属性到列的映射:

id = IntegerField('id')

name = StringField('username')

email = StringField('email')

password = StringField('password')

# 创建一个实例:

u = User(id=12345, name='Michael',email='test@orm.org', password='my-pwd')

# 保存到数据库:

u.save()

其中,父类Model和属性类型StringFieldIntegerField是由ORM框架提供的,剩下的魔术方法比如save()全部由metaclass自动完成。虽然metaclass的编写会比较复杂,但ORM的使用者用起来却异常简单。

现在,我们就按上面的接口来实现该ORM

首先来定义Field类,它负责保存数据库表的字段名和字段类型:

class Field(object):

def __init__(self, name, column_type):

self.name = name

self.column_type = column_type

def __str__(self):

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

Field的基础上,进一步定义各种类型的Field,比如StringFieldIntegerField等等:

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')

下一步,就是编写最复杂的ModelMetaclass了:

class ModelMetaclass(type):

def __new__(cls, name, bases, attrs):

if name=='Model':

return type.__new__(cls, name, bases, attrs)

print('Found model: %s' % name)

mappings = dict()

for k, v in attrs.items():

if isinstance(v, Field):

print('Found mapping: %s ==>%s' % (k, v))

mappings[k] = v

for k in mappings.keys():

attrs.pop(k)

attrs['__mappings__'] = mappings # 保存属性和列的映射关系

attrs['__table__'] = name # 假设表名和类名一致

return type.__new__(cls, name, bases, attrs)

以及基类Model

class Model(dict,metaclass=ModelMetaclass):

def __init__(self, **kw):

super(Model, self).__init__(**kw)

def__getattr__(self, key):

try:

return self[key]

except KeyError:

raise AttributeError(r"'Model' object has no attribute '%s'" %key)

def __setattr__(self, key, value):

self[key] = value

def save(self):

fields = []

params = []

args = []

for k, v in self.__mappings__.items():

fields.append(v.name)

params.append('?')

args.append(getattr(self, k, None))

sql = 'insert into %s (%s) values (%s)' % (self.__table__,','.join(fields), ','.join(params))

print('SQL: %s' % sql)

print('ARGS: %s' % str(args))

当用户定义一个class User(Model)时,Python解释器首先在当前类User的定义中查找metaclass,如果没有找到,就继续在父类Model中查找metaclass,找到了,就使用Model中定义的metaclassModelMetaclass来创建User类,也就是说,metaclass可以隐式地继承到子类,但子类自己却感觉不到。

ModelMetaclass中,一共做了几件事情:

排除掉对Model类的修改;

在当前类(比如User)中查找定义的类的所有属性,如果找到一个Field属性,就把它保存到一个__mappings__dict中,同时从类属性中删除该Field属性,否则,容易造成运行时错误(实例的属性会遮盖类的同名属性);

把表名保存到__table__中,这里简化为表名默认为类名。

Model类中,就可以定义各种操作数据库的方法,比如save()delete()find()update等等。

我们实现了save()方法,把一个实例保存到数据库中。因为有表名,属性到字段的映射和属性值的集合,就可以构造出INSERT语句。

编写代码试试:

u = User(id=12345, name='Michael',email='test@orm.org', password='my-pwd')

u.save()

输出如下:

Found model: User

Found mapping: email ==>

Found mapping: password ==>

Found mapping: id ==>

Found mapping: name ==>

SQL: insert into User(password,email,username,id) values (?,?,?,?)

ARGS: ['my-pwd', 'test@orm.org', 'Michael',12345]

可以看到,save()方法已经打印出了可执行的SQL语句,以及参数列表,只需要真正连接到数据库,执行该SQL语句,就可以完成真正的功能。

不到100行代码,我们就通过metaclass实现了一个精简的ORM框架。


属性 方法 数据 数据库 就是 类型 函数 框架 对象 代码 动态 实例 语句 解释 使用者 参数 字段 解释器 运行 复杂 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 国泰君安后端软件开发工资 中国软件开发人员有多少钱 盐城网络营销软件开发诚信服务 河北菠萝网络技术有限公司 软件开发者和手机厂商关系 软件开发人员奖项名称有哪些 中拓互联网络科技 软件开发行业的发展趋势如何 实况经理人数据库 分布式如何同步数据库 网络安全连环画四格简单 梁溪区计算机网络技术特点 重庆服务器虚拟化系统云主机 战友招募 不同服务器 中国网络安全大赛与电子竞技 河南畜牧兽医万方数据库 数据库安全机制的设计目标 ns暗黑2登录不上服务器 软件开发规模的发展趋势 异构数据库同步 瀚高数据库如何初始化 服务器启动一直显示红叉 网络安全宣讲内容 猎人919人软件开发 上海质量网络技术转让产品 仙桃哪里有软件开发团队 中小学校网络安全应急演练总结 mysql 数据库分析 怎么把数据库的数据传回前端 数据库与网站分离技术
0