Python基础(8)——类和对象(二)

本文讲解了Python中type和object的基础概念,如何使用type()创建类,metaclass的应用实例,类作为装饰器的技巧,以及super函数的使用。通过实例演示,理解类的深层次操作和设计模式。
摘要由CSDN通过智能技术生成

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的使用

metaclasstype的子类,通过替换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__方法注释掉,然后再看一下输出就可以明白了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值