研究Python中的元类

动态的创建类
1. 通过函数 return class 动态的构建需要的类
def choose_class(name):
	if name == 'foo':
		class Foo:
			pass
		return Foo
	else:
		class Bar:
			pass
		return Bar
MyClass = choose_class('foo') 
print(MyClass)  # 返回的是类
# <class '__main__.choose_class.<locals>.Foo'>
print(MyClass())  # 返回的是类的实例
# <__main__.choose_class.<locals>.Foo object at 0x000002716F328860>
2. 通过type函数构造类

上面的方法显然不够动态, 因为仍然需要自己编写整个类的代码, 由于类也是对象, 所以它们必须是通过什么生成的才对, 当你是用 class 关键字的时候, Python解释器自动创建这个对象. 但就和Python中的大多数事情一样, Python仍然提供给你手动处理的方法—内置函数type()

type有一种完全不同的能力, 它也能动态的创建类. type可以接收一个类的描述作为参数, 然后返回一个类

type的语法: 
type(类名, 父类的元祖 (针对继承的情况, 可以为空), 包含属性的字典 (名称和值) )
class SingleClass:
	pass
# 上面的代码可以通过type手动的创建
SingleClass = type('SingleClass', (), {})
print(SingleClass)

# 下面具体通过几个例子详细的了解type的使用
# 通过class关键字创建类
class Foo:
	name = True
# 使用type函数创建类
Foo = type('Foo', (), {'name': True})	
# 再来用class创建一个类继承自Foo
class FooChild(Foo):
	pass
# 看看type函数怎么创建类的子类吧
FooChild = type('FooChild', (Foo,), {})
# 为FooChild类增加方法
def new_method(self):
	print(self.name)
# 使用type给FooChild增加方法
FooChild = type('FooChild', (Foo,), {'new_method': new_method})
print(hasattr(Foo, 'new_method'))
print(hasattr(FooChild, 'new_method'))

可见Python使用关键字class幕后所作的事就是通过元类来实现的

什么是元类?
通俗的理解: 元类就是创建类的类

实际上type就是一个元类

>>> age = 18
>>> age.__class__
<class 'int'>
>>> name = 'wang'
>>> name.__class__
<class 'str'>
>>> def foo(): pass
...
>>> foo.__class__
<class 'function'>
>>> class Bar: pass
...
>>> b = Bar()
>>> b.__class__
<class '__main__.Bar'>
# 那么__class__的__class__属性又是什么呢?
>>> age.__class__.__class__
<class 'type'>
>>> name.__class__.__class__
<class 'type'>
>>> foo.__class__.__class__
<class 'type'>
>>> b.__class__.__class__
<class 'type'>
>>> Bar.__class__.__class__
<class 'type'>

type就是Python内建的元类, 也可以自定义元类

class Foo(type):
	pass
class FooChild(metaclass=Foo):
	pass

print(Foo.__class__)
# <class 'type'>
def create_class(*args, **kwargs):
    return type(*args, **kwargs)

class FooChild(metaclass=create_class):
    pass

print(FooChild.__class__)
# <class 'type'>
  1. 由上可见, 类对象的metaclass关键字指向的是元类对象或者返回元类对象的函数
  2. 元类的主要目的是什么?
  3. 元类的主要目的就是创建类时能自动的改变类
  4. 元类也是一个类, 也有__new__方法, 不过返回的是类对象, 不是类的实例对象, 要想控制元类返回的对象, 还是要操作元类的__new__方法
def upper_attr(future_class_name, future_class_parents, future_class_attr):
    '''返回一个类对象,将属性都转为大写形式'''
    #选择所有不以'__'开头的属性
    attrs = ((name, value) for name, value in future_class_attr.items() if not name.startswith('__'))
    # 将它们转为大写形式
    uppercase_attr = dict((name.upper(), value) for name, value in attrs)
    #通过'type'来做类对象的创建
    return type(future_class_name, future_class_parents, uppercase_attr)#返回一个类

class Foo(object):
    __metaclass__ = upper_attr
    bar = 'bip' 

print hasattr(Foo, 'bar')
# 输出: False
print hasattr(Foo, 'BAR')
# 输出:True
 
f = Foo()
print f.BAR
# 输出:'bip'

真实的生产环境下的代码应该是这样的(python3大版本下)

class UpperAttrMetaclass(type):
    def __new__(cls, name, bases, dct):
        attrs = ((name, value) for name, value in dct.items() if not name.startswith('__')
        uppercase_attr  = dict((name.upper(), value) for name, value in attrs)
        return type.__new__(cls, name, bases, uppercase_attr)class UpperAttrMetaclass(type):
    def __new__(cls, name, bases, dct):
        attrs = ((name, value) for name, value in dct.items() if not name.startswith('__'))
        uppercase_attr = dict((name.upper(), value) for name, value in attrs)
        return super(UpperAttrMetaclass, cls).__new__(cls, name, bases, uppercase_attr)
使用元类来实现Django中的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)

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


#二、定义元类,控制Model对象的创建
class ModelMetaclass(type):
    '''定义元类'''
    def __new__(cls, name, bases, attrs):
        if name=='Model':
            return super(ModelMetaclass,cls).__new__(cls, name, bases, attrs)
            # return type.__new__(cls, name, bases, attrs)
        mappings = dict()
        for k, v in attrs.items():
            # 保存类属性和列的映射关系到mappings字典
            if isinstance(v, Field):
                print('Found mapping: %s==>%s' % (k, v))
                mappings[k] = v
        for k in mappings.keys():
            #将类属性移除,使定义的类字段不污染User类属性,只在实例中可以访问这些key
            attrs.pop(k)
        attrs['__table__'] = name.lower() # 假设表名和为类名的小写,创建类时添加一个__table__类属性
        attrs['__mappings__'] = mappings # 保存属性和列的映射关系,创建类时添加一个__mappings__类属性
        return super(ModelMetaclass,cls).__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))


#最后,我们使用定义好的ORM接口,使用起来非常的简单。
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()

# Found mapping: id==><IntegerField:id>
# Found mapping: name==><StringField:username>
# Found mapping: email==><StringField:email>
# Found mapping: password==><StringField:password>
# SQL: insert into user (id,username,email,password) values (?,?,?,?)
# ARGS: [12345, 'Michael', 'test@orm.org', 'my-pwd']
元类的使用场景之一: 实现单例模式
class Singleton(type):
    def __init__(self, *args, **kwargs):
        print("__init__")
        self.__instance = None
        super(Singleton, self).__init__(*args, **kwargs)

    def __call__(self, *args, **kwargs):
        print("__call__")
        if self.__instance is None:
            self.__instance = super(Singleton, self).__call__(*args, **kwargs)
        return self.__instance


class Foo(metaclass=Singleton):
    # 在代码执行到这里的时候,元类中的__new__方法和__init__方法其实已经被执行了,而不是在Foo实例化的时候执行。且仅会执行一次。
    pass


foo1 = Foo()
foo2 = Foo()
# print(Foo.__dict__)  # _Singleton__instance': <__main__.Foo object at 0x100c52f10> 存在一个私有属性来保存属性,而不会污染Foo类(其实还是会污染,只是无法直接通过__instance属性访问)

print(foo1 is foo2)  # True

基于上面的例子

  • 元类生成的一个类对象是Foo, 我们只需要在Foo实例中添加一个私有属性(__instance)来保存生成的单例即可, 所以这里重写的是__init__方法, 而不是__new__方法
  • 关于__call__方法为什么会调用呢? 调用的是Singleton这个元类的__call__方法, 而Foo类只是Singleton类的一个实例罢了, 所以Foo()就会调用)

上面说到了也可以重写__new__方法来实现单例模式, 但是不推荐, 这里把代码给上, 有兴趣可以研究

class Singleton(type):
    def __new__(cls, name, bases, attrs):
        print("__new__")
        attrs["_instance"] = None
        return super(Singleton, cls).__new__(cls, name, bases, attrs)

    def __call__(self, *args, **kwargs):
        print("__call__")
        if self._instance is None:
            self._instance = super(Singleton, self).__call__(*args, **kwargs)
        return self._instance


class Foo(metaclass=Singleton):
    pass


foo1 = Foo()
foo2 = Foo()
print(Foo.__dict__)
print(foo1 is foo2)  # True
总结: 玩转以上代码, 对学习Python有很大帮助, 对面向对象也是很大的帮助
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值