元类有什么用
元类可以动态地创建类,它是制造类的工厂
类(class)实例化 一个 instance对象
type 的实例化就是 类class对象
type 是 Python 自建的元类
type () 函数能返回一个对象的类型 (类型本身也是对象),也就是返回对象的__class__属性值。
如何创建 class 对象呢?
最简单的办法
ini
class MyClass: #python3默认是创建新式类,隐式继承了object
a = 1
type函数
go
type(name, bases, namespace)
MyClass = type('MyClass', (object,), dict(a=1))
- name 对应于类的`__name__`属性
- bases 对应于类的`__bases__`属性
- namespace 对应于类的`__dict__`属性
动态类
type() 函数可以动态创建类,即在运行期动态创建类
如何控制类的创建行为?
使用 metaclass
比如我们想让自定义的类所创建的的对象自动包含 add
方法,即使类本身没有定义这个方法
python
# metaclass是类的模板,所以必须从`type`类型派生:
class ListMetaclass(type):
# __new__ 是在__init__之前被调用的特殊方法
# __new__是用来创建对象并返回这个对象
# 而__init__只是将传入的参数初始化给对象
# 实际中,你很少会用到__new__,除非你希望能够控制对象的创建
# 在这里,类是我们要创建的对象,我们希望能够自定义它,所以我们改写了__new__
# 如果你希望的话,你也可以在__init__中做些事情
# 还有一些高级的用法会涉及到改写__call__,但这里我们就先不这样.
def __new__(cls, name, bases, attrs):
# cls:当前创建的类对象
# name:类的名字
# bases:类继承的父类集合
# attrs:类的方法集合。
print('name: ', name) # 这些打印会在创建MyList类对象的时候打印,而非在创建MyList实例对象的时候
print('bases: ',bases)
print('attrs: ', attrs)
attrs['add'] = lambda self, value: self.append(value)
return type.__new__(cls, name, bases, attrs)
class MyList(list, metaclass=ListMetaclass):
pass
输出
python
name: MyList
bases: (<class 'list'>,)
attrs: {'__module__': '__main__', '__qualname__': 'MyList'}
>>> l=MyList() # 在创建MyList类的时候自动获得了add属性,即一个函数对象
>>> l.add('test1')
>>> l.add('test2')
>>> l
['test1', 'test2']
接口类
一种规范 规定子类应该具备的功能
定义了一些接口(就是函数,但这些函数都没有具体的实现),必须由引用类继承接口类
- 接口的设计目的是 "协作" 与 "解耦合"
- 接口类中没有实现所有的方法
- 鸭子类型算是一种接口类的实现风格
ruby
class Payment: # 接口类
def pay(self, money):
raise NotImplementedError
class AliPay(Payment): # 引用类具体实现
def pay(self, money):
print('AliPay')
class AppPay(Payment):
def pay(self, money):
print('AppPay')
class WeichatPay(Payment):
def zhifu(self, money):
print('WeichatPay')
def pay(payment, money):
# 如果实现类没有提供pay方法,在运行的过程就会报异常,
# 这也是我们接下来要介绍的抽象类存在的目的
payment.pay(money)
>>> p = WeichatPay()
>>> pay(p, 40) # 抛出NotImplementedError
抽象类
一种规范 规定子类应该具备的功能
该类不能被实例化,只能被继承,且子类必须实现抽象方法
- 抽象类的设计目的更多的是 "复用"
- 抽象类实现部分方法
- abc 模块就是用来实现抽象类的
- 抽象基类的目的就是让别的类继承它以及实现特定的抽象方法
- 可以去使用抽象基类做类型检查,这样就确保了子类实现了某些特定的方法
实现抽象基类的方法就是通过使用 abc
这个内建模块
python
import abc
class Payment(abc.ABC): # 使用抽象基类实现接口类
# Payment是一个抽象基类,因为它继承了abc.ABC。
# 另外,你也可以让它从元类ABCMeta直接创建
# abc.abstractmethod这个装饰器去标记抽象方法
# 实现细节由子类去完成
@abc.abstractmethod
def pay(self):
pass
class AliPay(Payment): # 引用类具体实现
def pay(self, money):
print('AliPay')
class AppPay(Payment):
def pay(self, money):
print('AppPay')
class WeichatPay(Payment):
def zhifu(self, money):
print('WeichatPay')
>>> p = AliPay()
>>> isinstance(p, Payment)
True
>>> wp = WeichatPay() # 这里就直接报错了
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: Cant instantiate abstract class WeichatPay with abstract methods
新式类和旧式类
Python 2.x 中默认都是经典类,只有显式继承了 object 才是新式类。
Python 3.x 中默认都是新式类,不必显式的继承 object
新式类对象可以直接通过__class__
属性获取自身类型
新式类增加了__slots__
内置属性,可以把实例属性的种类锁定到__slots__
规定的范围之中
新式类采用广度优先搜索(bfs),而旧式类是采用深度优先搜索(dfs)
新式类增加了__getattribute__
方法
继承搜索的顺序发生了改变
经典类多继承属性搜索顺序:
先深入继承树左侧,再返回,开始找右侧; 深度优先搜索(dfs)
新式类多继承属性搜索顺序
先水平搜索,然后再向上移动 广度优先搜索(bfs)