设为首页 收藏本站
查看: 941|回复: 0

[经验分享] Python中的元类

[复制链接]

尚未签到

发表于 2015-11-29 10:08:24 | 显示全部楼层 |阅读模式
  从前面"Python对象"文章中了解到,在Python中一切都是对象,类可以创建实例对象,但是类本身也是对象。



class C(object):
pass
c = C()
print c.__class__
print C.__class__   
  代码中,通过"__class__"属性来查看对象的类型,对于类C对象本身,它的类型是type。
DSC0000.png
  由于类也是对象,所以就可以在运行时动态的创建类,那么这时候就要用到内建函数type了。

再看type
  从前面的文章了解到,可以通过内建函数type来获取对象的类型。



class C(object):
pass
c = C()
print c.__class__ is type(c)
print C.__class__ is type(C)
  这里,我们就看看内建函数type的另一个强大的功能,动态的创建类。当使用type创建类的时候,有以下形式:



type(类名, 父类的元组(可以为空), 属性的字典)

  • 要创建的class的名字
  • 父类集合,如果只有一个父类,别忘了tuple的单元素写法
  • class的属性字典
  看看type创建类的例子:



def  printInfo(self):
print "%s is %d years old" %(self.name, self.age)
S = type("Student", (object, ), {"name": "Wilber", "age": 28, "printStudentInfo": printInfo})
print type(S)
s = S()
print type(s)
s.printStudentInfo()   
  例子中,通过type动态的创建了一个Studnent类,并且通过这个类可以创建实例:
DSC0001.png

__metaclass__
  函数type实际上是一个元类,元类就是用来创建类的"模板"。我们可以通过类"模板"创建实例对象,同样,也可以使用元类"模板"来创建类对象;也就是说,元类就是类的类。
  在创建一个类的时候,可以设置"__metaclass__"属性来指定元类。
  "__metaclass__"属性对应的代码就是创建类的代码(这段代码可以是一个函数,也可以是一个类);如果这段代码是类,"__metaclass__"的类名总是以Metaclass结尾,以便清楚地表示这是一个元类。
  对于元类的查找,Python有一套规则:


  • Python解释器会在当前类中查找"__metaclass__"属性对于的代码,然后创建一个类对象
  • 如果没有找到"__metaclass__"属性,会继续在父类中寻找"__metaclass__属性",并尝试前面同样的操作
  • 如果在任何父类中都找不到"__metaclass__",就会用内置的type来创建这个类对象



def queueMeta(name, bases, attrs):
attrs['InQueue'] = lambda self, value: self.append(value)
def deQueue(self):
if len(self) > 0:
return self.pop(0)
attrs['DeQueue'] = deQueue
# 直接调用type内建函数
return type(name, bases, attrs)

# 元类从`type`类型派生
class QueueMetaclass(type):
def __new__(cls, name, bases, attrs):
attrs['InQueue'] = lambda self, value: self.append(value)
def deQueue(self):
if len(self) > 0:
return self.pop(0)
attrs['DeQueue'] = deQueue
# 直接调用type内建函数
# return type(name, bases, attrs)
# 通过父类的__new__方法
return type.__new__(cls, name, bases, attrs)
class MyQueue(list):
# 设置metaclass属性,可以使用一个函数,也可以使用一个类,只要是可以创建类的代码
#__metaclass__ = queueMeta
__metaclass__ = QueueMetaclass
pass

q = MyQueue("hello World")
print q
q.InQueue("!")
print q
q.DeQueue()
print q
  代码中的MyQueue类型继承自list,但是通过设置"__metaclass__"属性,可以修改创建的类。也就是说,元类做了下面的事情:


  • 拦截类的创建
  • 根据"__metaclass__"对应的代码修改类
  • 返回修改之后的类
DSC0002.png

元类的__init__和__new__
  当创建元类的时候,为了定制创建出来的类的特性,一般会实现元类的"__init__"和"__new__"方法。



class MyMetaclass(type):
def __new__(meta, name, bases, attrs):
print '-----------------------------------'
print "Allocating memory for class", name
print meta
print bases
print attrs
return super(MyMetaclass, meta).__new__(meta, name, bases, attrs)
def __init__(cls, name, bases, attrs):
print '-----------------------------------'
print "Initializing class", name
print cls
print bases
print attrs
super(MyMetaclass, cls).__init__(name, bases, attrs)
class MyClass(object):
__metaclass__ = MyMetaclass
def foo(self, param):
pass
barattr = 2   
  通过这个例子演示了使用元类的"__init__"和"__new__"方法:
DSC0003.png

元类的__call__
  "__call__"是另外一个经常在实现元类时被重写的方法,与"__init__"和"__new__"不同的是,当调用"__call__"的时候,类已经被创建出来了,"__call__"是作用在类创建的实例过程。
  看下面的代码:



class MyMetaclass(type):
def __call__(cls, *args, **kwds):
print '__call__ of ', str(cls)
print '__call__ *args=', str(args)
return type.__call__(cls, *args, **kwds)
class MyClass(object):
__metaclass__ = MyMetaclass
def __init__(self, a, b):
print 'MyClass object with a=%s, b=%s' % (a, b)
print 'gonna create foo now...'
foo = MyClass(1, 2)   
  代码的输出为:
DSC0004.png

元类使用举例
  前面已经介绍了很多关于元类的知识了,下面看看怎么实际使用元类。
  元类在ORM中是比较常用的,因为需要在运行时创建类型,看下面简单的例子:



class Field(object):
def __init__(self, fname, ftype):
self.fname = fname
self.ftype = ftype
def __str__(self):
return '{%s: (%s, %s)}' % (self.__class__.__name__, self.fname, self.ftype)   
class StringField(Field):
def __init__(self, fname):
super(StringField, self).__init__(fname, 'varchar(100)')
class IntegerField(Field):
def __init__(self, fname):
super(IntegerField, self).__init__(fname, 'bigint')   
class ModelMetaclass(type):
def __new__(cls, name, bases, attrs):
if name == "Model":
return super(ModelMetaclass, cls).__new__(cls, name, bases, attrs)
else:
mapping = {}
print "Create Model for:", name
for k, v in attrs.items():
if isinstance(v, Field):
print "mapping %s with %s" %(k, v)
mapping[k] = v
attrs['_table'] = name
attrs['_mapping'] = mapping
return type.__new__(cls, name, bases, attrs)
class Model(dict):
__metaclass__ = ModelMetaclass
def __init__(self, **kwargs):
for key in kwargs.keys():
if key not in self.__class__._mapping.keys():
print "Key '%s' is not defined for %s" %(key, self.__class__.__name__)
return
super(Model, self).__init__(**kwargs)
def save(self):
fields = []
params = []
args = []
for k, v in self.__class__._mapping.items():
fields.append(k)
params.append("'{0}'".format(self[k]))
sql = 'insert into %s (%s) values (%s)' % (self.__class__._table, ','.join(fields), ','.join(params))
print 'SQL: %s' %sql
class Student(Model):
id = IntegerField('id_c')
name = StringField('username_c')
email = StringField('email_c')
print "-------------------------------------------------"
print Student._table
print Student._mapping
print "-------------------------------------------------"
s1 = Student(id = 1, name = "Wilber", email = "wilber@sh.com")   
s1.save()
print "-------------------------------------------------"
s2 = Student(id = 1, name = "Wilber", gender = "male")   
  代码中通过元类创建Student类,并将类的属性与数据表关联起来:
DSC0005.png

总结
  本文介绍了Python中元类的概念,通过元类可以在运行时创建类。
  当用户定义一个类class的时候,Python解释器就会在当前类中查找"__metaclass__"属性,如果找到,就通过该属性对应的代码创建类;如果没有找到,就继续以相同的规则查找父类。如果在任何父类中都找不到"__metaclass__",就会用内置的type来创建类对象。
  

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-144820-1-1.html 上篇帖子: TF-IDF算法(2)—python实现 下篇帖子: python 的异常及其处理
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表