类和对象(二)
5. 高级用法
5.1 type和object
在python
中一切皆对象,一个python
对象可能拥有两个属性,__class__
和__bases__
,前者表示这个对象是谁创建的,后者表示一个类的父类是谁。
- type为对象的顶点,所有对象都创建自type。
- object为类继承的顶点,所有类都继承自object。
参考自链接
5.2 使用type()函数定义类
type
是创建类对象的类。也就是说,type
本身就是一个类,其它所有类都是type
类的一个实例化对象。
在我们自定义一个类的时候,实际上就是对type
类中__call__
方法的重载。
那么我们就可以使用type
来定义一个类了。
使用type()
定义类时, 可指定三个参数:
- 要创建的类名
- 该类继承的父类,使用元组表示
- 该类绑定的类变量和方法,用字典表示
def eat(self):
print("Man is eating!")
Person = type('Person', (object, ), dict(eat=eat, gender='male'))
p = Person()
p.eat()
print(p.gender) # eat方法和gender属性均可以被访问到。
5.3 metaclass的使用
metaclass
是type
的子类,通过替换type
的__call__
运算符重载机制。也就是说我们可以通过metaclass
来控制类的创建过程。
只有继承了type
类的类才能被称为一个元类。
一个类没有声明自己的元类,那么它的元类默认就是type
。
我们可以通过继承type
类来自定义元类,然后在定义类的时候指定关键字metaclass
的值就可以使用自定义的元类来定义类了。
例如我们使用自定义元类来检查类中是否有指定的方法:
from inspect import isfunction
class MyMeta(type): # 自定义元类需继承type
def __new__(cls, *args, **kwargs):
_class = super().__new__(cls, *args, **kwargs)
if _class.__name__ == "Animal":
if not hasattr(_class, 'run') or not isfunction(getattr(_class, 'run')): # 检查Animal类中是否有run方法。
raise Exception("类{}没有实现run方法".format(_class.__name__))
return _class
class Animal(metaclass=MyMeta):
pass
dog = Animal() # 会抛出异常
元类的__new__
方法负责构建普通类,__init__
方法负责对这个普通类进行初始化。
使用元类实现单例模式:
class Singleton(type):
def __init__(self, *args, **kwargs):
self.__instance = None
super().__init__(*args, **kwargs)
def __call__(self, *args, **kwargs):
if self.__instance is None:
self.__instance = super().__call__(*args, **kwargs)
return self.__instance
class TestSingle(metaclass=Singleton):
pass
t = TestSingle()
t1 = TestSingle()
print(t, t1, t == t1)
#======output========
<__main__.TestSingle object at 0x0000018B6FD975F8> <__main__.TestSingle object at 0x0000018B6FD975F8> True
5.4 类作为装饰器
利用类中的__call__
方法将类实例变成一个可调用对象。
import time
class TimeDecorator:
def __init__(self, func):
self.__func = func # 私有属性__func指向了被装饰函数的地址
def __call__(self, *args, **kwargs):
start_time = time.time()
self.__func(*args, **kwargs)
end_time = time.time()
print("函数运行时间为:", end_time - start_time)
@TimeDecorator # 相当于test = TimeDecorator(test), 在后面调用test的时候,将会调用装饰器中的__call__方法,参数也将传给__call__方法。
def test(a, b):
print("a+b=", a + b)
time.sleep(1)
test(2, 4)
若装饰器带参数,则将会改用如下方式:
import time
class TimeDecorator:
def __init__(self, s_t): # 这里接收装饰器的参数
self.s_t = s_t
def __call__(self, func): # 这里接收被装饰函数
def wrapper(*args, **kwargs): # 这里接收被装饰函数的参数
start_time = time.time()
func(*args, **kwargs)
end_time = time.time()
if self.s_t == "start_time":
print("函数运行开始时间为:", start_time)
else:
print("函数运行结束时间为:", end_time)
print("函数运行时间为:", end_time - start_time)
return wrapper # 这里返回装饰后函数地址
@TimeDecorator("start_time") # 相当于test = TimeDecorator("start_time")(test),也就是__call__中的wrapper函数的地址
def test(a, b):
print("a+b=", a + b)
time.sleep(1)
test(2, 4)
5.5 property装饰器
它可以将一个带有返回值的方法作为一个属性来进行调用。
class Student:
def __init__(self, age):
self.__age = age
@property # 将age方法变成属性调用
def age(self):
return self.__age
@age.setter # 将age属性变成一个可修改可指定的属性
def age(self, value):
assert isinstance(value, int), "请设置int类型的整数"
if value <= 0:
raise ValueError("您输入的年龄不合法")
else:
self.__age = value
@age.deleter # 删除属性
def age(self):
print("删除了__age属性")
del self.__age
s = Student(10)
print(s.age)
s.age = 23
print(s.age)
del s.age
@property提供了可读可写可删除的操作,如果只想读取,那么另外两个就不需要定义。也就相当于提供了一个私有属性的访问接口。
5.6 super的使用
super()
是python
中调用父类(超类)的一种方法,在子类中可以通过super()
方法来调用父类的方法。
以调用__init__()
方法为例,在子类中调用父类的相应方法如下:
super().__init__(*args, **kwargs)
,会按照类的继承顺序去自动查找父类super(类名, self).__init__(*args, **kwargs)
,这将会调用类名对应的类的父类的方法
class A:
def __init__(self):
print("调用了A的初始化方法")
class B(A):
def __init__(self):
print("调用了B的初始化方法")
class C(A):
def __init__(self):
print("调用了C的初始化方法")
class D(B, C):
def __init__(self):
super().__init__()
print(D.mro()) # 将会按照这个打印的顺序来查找父类(超类)中的__init__()方法
d = D()
可以尝试将类B
中的__init__
方法注释掉,然后再看一下输出就可以明白了。