05 元类
5.1 类与对象
-
直接使用
type
type是python中的内置类,该类是其他类对象的默认类,它是创建其他类的默认类–或者说是默认元类。type构造函数接受3个位置参数:
name
类的名称 字符串类型,bases
该类的基类 支持继承 元组,attrs
类中所有属性 字典 )def init(self, name): self.name = name def eat(self): pass def go_to_vet(self): pass Animal = type("Animal", (object, ), { "__doc__": 'A class representing an arbitrary animal.', "__init__": init, "eat": eat, "go_to_vet": go_to_vet, }) a = Animal("a") print(a.name) # 子类 def meow(self): pass def purr(self): pass Cat = type("Cat", (Animal, ), { 'meow': meow, 'purr': purr })
-
type链
object
是类继承链中的最高级,type
是元类层级的最高级
5.2 编写元类
-
类只是对象,元类只是类。 元类的行为继承自type,任何type的子类都可以作为元类
-
自定义元类最重要的方法
__new__
方法;处理类的创建,返回一个新类。含4个位置参数- 元类自身,伪装成一个参数的方式绑定方法类似;习惯命名
cls
string
类型的类名称(name)- 类继承的基类元组(bases)
- 类包含的属性字典(attrs)
- 元类自身,伪装成一个参数的方式绑定方法类似;习惯命名
-
自定义元类不会实现
__init__
-
# type 的子类 class Meta(type): def __(cls, name, bases, attrs): return super(Meta, cls).__new__(cls, name, bases, attrs) # C 是 Meta对象实例的子类 C = Meta("C", (object, ), {}) print(type(C)) # <class '__main__.Meta'> D = Meta("D", (C, ), {}) print(type(D)) # <class '__main__.Meta'> # N 是type对象实例的子类 class N(): pass print(type(N)) n = N() # <class 'type'> print(type(n)) # <class '__main__.N'>
-
元类与基类(如果存在)在一起声明
class C(metaclass=Meta): pass
-
跨版本兼容性
import six class C(six.with_metaclass(Meta)): pass @six.add_metaclass(Meta) class C(object): pass
-
5.4 何时使用元类
-
说明性类声明
-
类验证 : 一个类必须遵循特定的接口
class FooOrBar(type): def __new__(cls, name, bases, attrs): if 'foo' in attrs and 'bar' in attrs: raise TypeError("class {} cannot contain both 'foo' and 'bar' attribute.".format(name)) if 'foo' not in attrs and 'bar' not in attrs: raise TypeError("class {} must provide either a 'foo' or a 'bar' attribute.".format(name)) return super(FooOrBar, cls).__new__(cls, name, bases, attrs) class Vaild(metaclass=FooOrBar): foo = 42
# 实例化类返回之前检查它的属性,而不是查看`attrs`字典 class FooOrBar(type): def __new__(cls, name, bases, attrs): # 调用type的构造函数从基类获得所有属性 answer = super(FooOrBar, cls).__new__(cls, name, bases, attrs) if hasattr(answer, 'foo') and hasattr(answer, "bar"): raise TypeError("class {} cannot contain both 'foo' and 'bar' attribute.".format(name)) if not hasattr(answer, 'foo') and not hasattr(answer, "bar"): raise TypeError("class {} must provide either a 'foo' or a 'bar' attribute.".format(name)) return answer class Vaild(metaclass=FooOrBar): foo = 42 # pass class AlsoValid(Vaild): pass
-
非继承属性: 元类用作一种工具,是类中特定的属性不会自动继承
class Meta(type): def __new__(cls, name, bases, attrs): if attrs.get('abstract', False): return super(Meta, cls).__new__(cls, name, bases, attrs) # AbstractClass 不会获取元类的功能,子类也不会继承 class AbstractClass(metaclass=Meta): abstract = True class RegularClass(AbstractClass): abstract = False
5. 6 meta-coding(元编码)
-
用于查看应用程序中其他代码的代码
class Logged(type): def __new__(cls, name, bases, attrs): for key, value in attrs.items(): if callable(value): attrs[key] = cls.log_call(value) return super(Logged, cls).__new__(cls, name, bases, attrs) @staticmethod def log_call(fxn): def inner(*args, **kwargs): print("The function %s was called with arguments %r and keyword arguments %r."%(fxn.__name__, args, kwargs)) try: response = fxn(*args, **kwargs) print("The function call to %s was successful."%fxn.__name__) return response except Exception as exc: print("The function call to %s raised an exception: %r"%(fxn.__name__, exc)) raise return inner class MyClass(metaclass=Logged): def foo(self): print("123") def bar(self): raise TypeError("oh noes")