python高级用法技巧-Python高级用法总结--元类

type()

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

比方说我们要定义一个 Hello 的 class ,就写一个hello.py 模块:

class Hello(object):

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

print("Hello, %s" %name)

当 Python 解释器载入 hello 模块时,就会依次执行该模块的所有语句,执行结果就是动态创建出一个 Hello 的 class 对象,测试如下:

fromhello import Hello

h=Hello()

h.hello() # Hello,world

print(type(Hello)) #print(type(h)) #

type() 函数可以查看一个类型或变量的类型,Hello 是一个class,它的类型就是 type ,而 h 是一个实例,它的类型就是 class Hello。

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

type() 函数既可以返回一个对象的类型,又可以创建出新的类型,比如,我们可以通过 type() 函数创建出 Hello 类,而无需通过 class Hello(object) 来定义。

def fn(self, name="world"): # 先定义函数

print("Hello, %s"%name)

Hello= type("Hello", (object,), dict(hello=fn)) # 创建Hello classh=Hello()

h.hello() # Hello, world

print(type(Hello)) #print(type(h)) #

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

class 的名称。

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

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

通过 type() 函数创建的类和直接写 class 是完全一样的,因为Python 解释器遇到 class 定义时,仅仅是扫描一下 class 定义的语法,然后调用 type() 函数创建出 class。

正常情况下,我们都用 class xxx 来定义类,但是, type() 函数也允许我们动态创建出类来,也就是说,动态语言本身支持运行期动态创建类,这和静态语言有非常大的不同,要在静态语言运行期创建类,必须构造源代码字符串再调用编译器,或者借助一些工具生成字节码实现,本质上都是动态编译,非常复杂。

metaclass

除了使用 type() 动态创建类以外,要控制类的创建行为,还可以使用 metaclass。

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

当我们定义了类以后,就可以根据这个类创建出实例,所以:先定义类,然后创建实例。

但是如果我们想创建出类呢?那就必须根据 metaclass 创建出类,所以:先定义 metaclass ,然后创建类。

连接起来就是:先定义metaclass ,就可以创建类,最后创建实例。

所以,metaclass 允许你创建类或者修改类。换句话说,你可以把类看出是 metatclass 创建出来的实例。

metaclass 是 Python 面向对象里最难理解,也是最难使用的魔术代码。支持正常情况下,我们不会碰到。

我们先看一个简单的例子,这个metaclass 的类名总是 Metaclass 结尾,以便清楚地表示这是一个 metaclass:

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

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

attrs["add"] =lambda self, value: self.paaned(value)returntype.__new__(cls, name, bases, attrs)classMyList(list):

__etaclass__= ListMetaclass # 指示使用ListMetaclass 来定制类

当我们写下 __metaclass__ = ListMetaclass 语句时,魔术就生效了,它指示 Python 解释器在创建 MyList 时,要通过ListMetaclass.__new__() 来创建,在此,我们可以修改类的定义,比如加上新的方法,然后,返回修改后的定义。

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

当前准备创建的类的对象。

类的名字。

类继承的父类集合。

类的方法集合

测试一下 MyList 是否可以调用 add() 方法:

L=MyList()

L.add(1)

print L # [1]

而普通的 list 没有 add() 方法

l =list()

l.add()

Traceback (most recent call last):

File"", line 1, in AttributeError:"list" object has no attribute "add"

动态修改有什么意义? 直接在MyList 定义中写上 add() 方法不是更加简单吗? 正常情况先,确实应该直接写,通过metaclass 修改纯属变态。

但是,总会遇到需要通过 metaclass 修改类的定义的。

ORM 就是一个典型的例子

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

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

实例:编写 ORM 框架

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

classUser(Model):

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

id= IntegerField("id")

name= StringField("username")

email= StringField("email")

password= StringField("password")

# 创建一个实例

u= User(id=123, name="Michael", email="xxx@qq.com", password="pwd")

# 保存到数据库

u.save()

其中,父类 Model 和属性类型 StringField 、IntegerField 是由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 ,比如 StringFielf、IntegerField 等等:

classStringField(Field):

def __init__(self, name):

super(StringField, self).__init__(name,"varchar(100)")classIntegerField(Field):

def __init__(self, name):

super(IntaegerField, self).__init__(name,"bigint")

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

classModelMetaclass(type):

def __new__(cls, name, bases, attrs):if name=="Model":returntype.__new__(cls, name, bases, attrs)

mappings=dict()for k, v inattrs.iteritems():ifisinstance(v, Field):

print("Fund mapping: %s==>%s" %(k, v))

mappings[k]=vfor k inmappings.iterkeys():

attrs.pop(k)

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

attrs["__mappings__"] =mappings # 保存属性和列的映射关系return type.__new__(cls, name, bases, attrs)

以及基类 Model:

classModel(dict):

__metaclass__=ModelMetaclass

def __init__(slef,**kw):

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

def __getattr(self, key):try:returnself[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 inself.__mappings__.iteritems():

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 中定义的 __metaclass__ 的ModelMetaclass 来创建 User 类,也就是说, metaclass 可以隐式地继承到子类,但子类自己却感觉不到。

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

排除掉对 Model 类的修改。

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

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

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

编写测试代码:

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,uid) values (?,?,?,?)

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

最后解释一下类属性和实例属性。直接在class中定义的是类属性:

class Student(object):

name= "Student"

实例属性必须通过实例来绑定,比如self.name = "xxx"。来测试一下:

>>># 创建实例s:>>> s =Student()>>># 打印name属性,因为实例并没有name属性,所以会继续查找class的name属性:>>>print(s.name)

Student>>># 这和调用Student.name是一样的:>>>print(Student.name)

Student>>># 给实例绑定name属性:>>> s.name = "Michael"

>>># 由于实例属性优先级比类属性高,因此,它会屏蔽掉类的name属性:>>>print(s.name)

Michael>>># 但是类属性并未消失,用Student.name仍然可以访问:>>>print(Student.name)

Student>>># 如果删除实例的name属性:>>>del s.name>>># 再次调用s.name,由于实例的name属性没有找到,类的name属性就显示出来了:>>>print(s.name)

Student

因此,在编写程序的时候,千万不要把实例属

性和类属性使用相同的名字。

在我们编写的ORM中,ModelMetaclass会删除掉User类的所有类属性,目的就是避免造成混淆。

摘抄至https://www.liaoxuefeng.com/wiki/001374738125095c955c1e6d8bb493182103fac9270762a000/001386820064557c69858840b4c48d2b8411bc2ea9099ba000

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值