动态的创建类
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'>
- 由上可见, 类对象的metaclass关键字指向的是元类对象或者返回元类对象的函数
- 元类的主要目的是什么?
- 元类的主要目的就是创建类时能自动的改变类
- 元类也是一个类, 也有__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