1. type
通过type(对象实例的引用)
我们能拿到对象实际的类型,而我们的的class
可以为type
类型的实例,如果调用type(class)
我们回去的是一个type
类型。 比如我们下面的例子:
class A(object):
pass
a = A()
print(type(a)) # 输出: <class '__main__.A'> , 是我们自定义的类A, 这里的模块名称是__main__因为我们使用当前文件启动,否则的话模块名应该是文件名
print(type(A)) # 输出: <class 'type'> ,说明我们的class本身是```type```类型的实例
1.1 手工创建class类型
type
构造函数接收3个参数: 类名、父类的元组、属性和方法的dict,通过type
构造函数和我们通过class定义出来的类完全一样。 但是能运行我们动态创建。
def get_age(self, age):
return 3 * age;
Student = type('Student', (object,), dict(get_age=get_age))
s = Student()
print(type(s)) # 输出: <class '__main__.Student'>
print(s.get_age(10)) # 输出: 30
2. metaclass
类似创建实例的模板,你可以将metaclass理解为创建类的模板,metaclass能过动态的修改引用元类(metaclass)的类绑定的属性和方法。
Python中class的默认metaclass是type
,定义metaclass
也是通过继承type
类实现的。
让我们先可以一个入门的测试: 给引用metaclass的类自动添加一个hello()方法:
#!/usr/bin/env python
# -*-coding: UTF-8 -*-
class HelloMeta(type):
def hello(cls):
print("%s, %s, a HelloMeta type class" % (type(cls()), str(cls))) # 这里拿到的是一个class对象引用cls,如果想要访问实例变量怎么做?
def __call__(self, *args, **kwargs):
cls = type.__call__(self, *args, **kwargs) # 这里的self是Metaclass实例,这个例子中就是HelloMeta的实例,创建的cls是指定了Metaclass的类的实例
setattr(cls, "hello", self.hello) # 然后我们把metaclass的方法赋值给新加的class,后面class的实例就能调用这个方法了
print(type(self))
return cls
class TryHello(object, metaclass=HelloMeta):
def greate(self):
self.hello()
h = TryHello()
h.greate()
业务开发中metaclass
可能没多大用处,但是创建各种框架都大量使用了metaclass,比如我们场景的Django、SQLAlchemy、Flask等等。
2.1 实现原理
之前我们已经了解到实例的type是class,class的type是type,甚至type本身的type都是type自己。
>>> class Student(object):
... pass
...
>>> s = Student()
>>> type(s)
<class '__main__.Student'>
>>> type(Student)
<class 'type'>
>>> type(type(Student))
<class 'type'>
在Python3中定义新的class时, 可以指定一个metaclass, 这个metaclass的值默认是type
。 我们可以复写type
里的特殊方法来自定义类的生成规则:
方法 | 说明 | 参数 |
---|---|---|
__prepare__ |
_new__ 的attr字段的默认初始化值 |
接口定义: @classmethod def prepare(mcls, name, bases): return odict() |
__new__ |
用于创建新类的实例 |
def __new__(mcls, name:str, bases:tuple, attr, **kwargs) mcls: 元类的实例引用 name:新类的类名 bases:新类的父类,tuple里每个元素都是class实例的引用 attr:类的名称空间,包括类的所有属性,由 __prepare__ 返回通常我们只修改参数,继续传递给父类 __new__ 创建类实例:return super(Final, cls).__new__(mcls, name, bases, attr)
|
__init__ |
通过__new__ 创建新类的实例后,传递给该方法做初始化 |
def __init__(cls, name, bases, attrs, **kwargs) cls: 新类的class对象的引用 name:新类名称 bases:新类父类tuple attrs:新类名称空间,包括各种属性和方法 通常我们只需要修改name、bases、attr,调用父类方法: def __init__(self, name, bases, attrs, **kwargs): super(Final, self).__init__(name, bases, attrs) init方法没有返回值 |
__call__ |
新类构造函数调用之前调用 | 接口定义: def call(cls, *args, **kw) 修改参数、缓存逻辑,在必要的情况下调用父类的构造器完成初始化。 |
2.2 decorators实现AOP
在metaclass出现之前,我们都是通过decorators修改函数的行为的。 下面是一个decorators的使用实例,使用类似于Java里的AOP,但是会比AOP要强大、简单的多 :
from functools import wraps
def logs(fn):
@wraps(fn)
# 为了记住fn的名字,如果没有这一行新的函数通过__name__放函数名返回的是wrapper
def wrapper(*args, **kwargs):
print("log: %s, %s" % (str(args), str(kwargs)))
return fn(*args, **kwargs)
return wrapper
@logs
def test(a, b, c):
return a + b + c
class A(object):
@logs
def sayHello(self, name):
return "hello %s" % (name,)
print(test(1, 2, 3))
print(test.__name__)
a = A()
print(a.sayHello("zhangsan"))
2.3 metaclass实现decorators
decorators从某种程度上说已经足够好用了,但是假设我们有一个类,类下有多个方法,我想对所有方法都做函数转换,这个时候在每个方法上去加@log
就略显麻烦了,这个时候metaclass的优势就体现出来了。
我们可以拷到decorators的log方法可以不加修改的继续使用,通过复写__new__
我们筛选出所有的types.FunctionType
和types.MethodType
,将对应方法封装为新方法:
from functools import wraps
import types
def logs(fn):
@wraps(fn)
def wrapper(*args, **kwargs):
print("log: %s, %s" % (str(args), str(kwargs)))
return fn(*args, **kwargs)
return wrapper
class LogMetaclass(type):
# def __new__(cls, *args, **kwargs):
def __new__(cls, name, parents, attrs, *args, **kwargs):
# cls: Metaclass的类, name: 新类名称, parents: 新类的父类tuple, attrs: 新类的属性和方法dict
for k, v in attrs.items():
if type(v) is types.FunctionType or type(v) is types.MethodType:
attrs[k] = logs(v)
return super(LogMetaclass, cls).__new__(cls, name, parents, attrs) # 所有不影响的属性继续传递个父类(type类本身)的__new__方法创建
class TryHello(object, metaclass=LogMetaclass):
def say_hello(self, name):
print("hello %s" % (name,))
h = TryHello()
h.say_hello("zhangsan")
2.4 metaclass禁止继承
通过type(class)
返回的是metaclass信息,因为多数类不指定metaclass,默认值是```type``。
我们通过定义元类Final
,在Dog
类初始化时,通过bases
我们会拿到它的父类Animal
,而Animal
标记了使用metaclass=Final
,通type(Animal)
我们获取到Final
,这个时候raise RuntimeException
,做到了任何父类以Final
做为metaclass的情况下都不创建class
#!/usr/bin/env python
# -*- coding:utf-8 -*-
class Final(type):
def __new__(cls, name, bases, attr):
# Final cannot be subclassed
# check that a Final class has not been passed as a base
# if so, raise error, else, create the new class with Final attributes
type_arr = [type(x) for x in bases]
for i in type_arr:
if i is Final:
raise RuntimeError("You cannot subclass a Final class")
return super(Final, cls).__new__(cls, name, bases, attr)
class Animal(metaclass=Final):
pass
class Dog(Animal):
pass
a = Animal()
d = Dog()
2.5 实现class单例模式
#!/usr/bin/env python
# -*- coding:utf-8 -*-
class Singleton(type):
instance = None
def __call__(cls, *args, **kw):
if not cls.instance: # 拦截构造器调用,如果实例存在的话直接返回
cls.instance = super(Singleton, cls).__call__(*args, **kw)
return cls.instance
class ASingleton(object, metaclass=Singleton):
# __metaclass__ = Singleton
pass
a = ASingleton()
b = ASingleton()
print(a is b) # 这里输出是True