Python的元类的用法

元类介绍

type可以直接生成类(class),但也可以先生成元类(metaclass),再使用元类批量定制类(class)

使用 class 创建类 ​

  class Hello():
      def say(self, name='world'):
          print('Hello, %s' % name) 
        
  h = Hello()
  h.say()

使用 type 直接创建

  def say(self, name='world'):
      print("Hello, %s" % name)
     
  Hello = type('Hello', (object, ), dict(say_hello=say))
  h = Hello()
  h.say()

先生成元类 metaclass,再批量创建

  # 传入type
  class SayMetaClass(type):
  ​
      # 传入      类名称、父类、属性
      def __new__(cls, name, bases, attrs):
          # 添加属性 
          attrs['say_'+name] = lambda self,value,saying=name: print(saying+','+value+'!')
          # 传承三大:类名称、父类、属性
          return type.__new__(cls, name, bases, attrs)
  ​
  # 创建类
  class Hello(object, metaclass=SayMetaClass):
      pass
  ​
  # 创建实列
  hello = Hello()
  ​
  # 调用实例方法
  hello.say_Hello('world!')  # Hello, world!   类名 + 传入的参数
  ​
  class NiHao(object, metaclass=SayMetacalss):
      pass
  ​
  n = NiHao()
  n.say_NiHao("中国")  # NiHao, 中国!

元类的应用

参考文章 快速掌握元类

ORM 对象关系映射

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)
  ​
  ​
  class StringField(Field):
  ​
      def __init__(self, name):
          super().__init__(name, 'varchar(100)')
  ​
  ​
  class IntegerField(Field):
  ​
      def __init__(self, name):
          super().__init__(name, 'bigint')
  ​
  ​
  class ModelMetaClass(type):
      """元类"""
      """
      创建一个新的字典mapping
      将每一个类的属性,通过.items()遍历其键值对。
      如果值是Field类,则打印键值,并将这一对键值绑定到mapping字典上。
      将刚刚传入值为Field类的属性删除。
      创建一个专门的__mappings__属性,保存字典mapping。
      创建一个专门的__table__属性,保存传入的类的名称。
      """
      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)
  ​
  ​
  class Model(dict, metaclass=ModelMetaClass):
      """创建 Modle 来自于元类"""
  ​
      def __init__(self, **kwargs):
          super().__init__(**kwargs)
  ​
      def __getattr__(self, key):
          try:
              return self[key]
          except KeyError:
              raise AttributeError("'Model' object has no attribute '%s'" % key)
  ​
      def __setattr__(self, key, value):
          self[key] = value
  ​
      def save(self):
          """模拟 sql 语句"""
          fields = []
          args = []
          for k, v in self.__mappings__.items():
              fields.append(v.name)
              args.append(getattr(self, k, None))
          sql = 'insert into %s (%s) values (%s)' % (self.__table__, ','.join(fields), ','.join(str(i) for i in args))
          print("SQL: %s" % sql)
          print("Args: %s" % str(args))
  ​
  ​
  class User(Model):
      """创建子类"""
      """
          id= IntegerField(‘id’)就会自动解析为:
              Model.__setattr__(self, ‘id’, IntegerField(‘id’))
              因为IntergerField(‘id’)是Field的子类的实例,自动触发元类的__new__,
              所以将IntergerField(‘id’)存入__mappings__并删除这个键值对
      """
      id = IntegerField('id')
      name = StringField('username')
      email = StringField('email')
      password = StringField('password')
  ​
  """
  实例化过程中
  先调用Model.__setattr__,将键值载入私有对象
  然后调用元类的“天赋”,ModelMetaclass._new_,将Model中的私有对象,只要是Field的实例,都自动存入u.__mappings__
  """
  # 实例化 User
  u = User(id=12345, name='Batman', email='12343@qq.com', password='qwerqwer')
  u.save()

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值