22. 面向对象之多态

1. 多态

1.1 概念

多态指的是一类事物有多种形态

比如动物有多种形态:人、猴、鸭

1.2 代码示例

from abc import ABC, abstractmethod

# 对于程序来说,定义一个基类可以有多个子类
class Animal(ABC):
    @abstractmethod
    def run(self):
        pass

    @abstractmethod
    def speak(self, name):
        print(f'{name}正在说话')

# 第一种形态:人
class Person(Animal):
    def run(self):
        print('可以润')

    def speak(self, name):
        print(f'{name}正在说话')

# 第二种形态:猴
class Monkey(Animal):
    def run(self):
        print('猴哥也可以润')

# 第三种形态:鸭子
class Duck(Animal):
    def run(self):
        print('鸭子也可以润')

1.3 多态性

动态绑定:在程序运行的过程中,根据对象的类型,动态的将方法进行绑定

动态多样性:动态绑定在继承背景下的特性就叫动态多态性

静态多样性:如任何类型都可以用运算符加号+  进行运算

from abc import ABC, abstractmethod

# 同一类事物:动物
class Animal(ABC):
    @abstractmethod
    def run(self):
        pass

# 第一种形态:人
class Person(Animal):
    def run(self):
        print('可以润')

# 第二种形态:猴
class Monkey(Animal):
    def run(self):
        print('猴哥也可以润')

# 第三种形态:鸭子
class Duck(Animal):
    def run(self):
        print('鸭子也可以润')

person1 = Person()
monkey1 = Monkey()
duck1 = Duck()

# person1、monkey1、duck1都是动物,只要是动物肯定有run方法
# 于是可以不用考虑三个对象的具体是声明类型,而直接使用
person1.run()
monkey1.run()
duck1.run()

# 更进一步,可以定义一个统一的接口来使用
def func(obj):
    obj.run()

python中一切皆对象,本身就支持多态性

# 在不考虑对象类型的情况下统计对象的长度
a.__len__()
b.__len__()
c.__len__()

# python内置了一个统一的接口
len(a)
len(b)
len(c)

2. 鸭子类型

鸭子类型(duck typing)是一种编程风格,不是一个真实存在的约束关系,是一种普遍的规范

看起来像、声音像、走起路来像鸭子,那么它就是鸭子

# 二者看起来都像文件,因而就可以当文件一样去用,然而它们并没有直接的关系
class Txt:  # Txt类有两个与文件类型同名的方法,即read和write
    def read(self):
        pass

    def write(self):
        pass


class Video:  # Video类也有两个与文件类型同名的方法,即read和write
    def read(self):
        pass

    def write(self):
        pass

3. 绑定方法和非绑定方法

3.1 概念

绑定方法(动态方法):

绑定给谁,谁来调用就自动将它本身当作第一个参数传入

绑定给类的方法、绑定给对象的方法

 

非绑定方法(静态方法):

既不绑定给类也不绑定给对象

 

一句话概括:方法即函数,可以理解为绑定函数、非绑定函数

3.2 绑定给对象的方法

概述:

  绑定给对象的方法就是在类内部直接定义的方法------即在类内部写的函数,写的时候自动带上self参数

  特点就是会自动补全self参数

  对象调用绑定给对象的方法的时候不需要传入self参数,类调用对象的绑定方法的时候需要传入self参数(对象名)

class Student:

    def __init__(self, name):
        self.name = name

    def run(self):  # 绑定给对象的方法就是在类内部定义的方法,自动补全self对象
        print(f'这是run方法,{self.name}可以润')


stu1 = Student(name='lavigne')  # 实例化得到的对象

# (1)对象调用对象的绑定方法
#    不需要传递self参数,在对象调用对象的绑定方法的时候会自动将对象作为self参数传进去
stu1.run()  # 这是run方法,lavigne可以润

# (2)类调用对象的绑定方法
#    必须主动给self传入一个对象
stu2 = Student(name='avril')
# Student.run()  # 报错  TypeError: Student.run() missing 1 required positional argument: 'self'
Student.run(stu1)  # 这是run方法,lavigne可以润
Student.run(stu2)  # 这是run方法,avril可以润

3.3 绑定给类的方法  classmethod

类在使用时会将类本身当作参数传给类方法的第一个参数(即便是对象来调用也会将类当作第一个参数传入)

classmethod把类中的函数定义成类方法

class Student:
    def __init__(self, name):
        self.name = name

    @classmethod
    def read(cls):  # 绑定给类的方法,必须用classmethod装饰器,并且在定义函数的时候默认补全参数cls
        print(f'当前是绑定给类的read方法:{cls}')


# (1)对象调用绑定给类的方法
#    不需要传入cls参数
#    会自动识别实例化当前对象的类,直接将类传入
stu1 = Student(name='lavigne')
stu1.read()  # 当前是绑定给类的read方法:<class '__main__.Student'>

# (2)类调用绑定给类的方法
#    不需要传入cls参数
#    默认将当前类作为cls的默认参数传入
Student.read()  # 当前是绑定给类的read方法:<class '__main__.Student'>

总结:

  定义绑定给类的方法的时候,必须用装饰器@classmethod;类似于绑定给对象的方法函数,会自动补全参数cls

  对象调用绑定给类的方法,不需要传入cls参数

  类调用绑定给类的方法,不需要传入cls参数

3.4 非绑定方法  staticmethod

在类内部用staticmethod装饰的函数即非绑定方法,就是普通函数

staticmethod既不绑定给类,也不绑定给对象,在定义函数的时候和普通函数一样不会补全任何默认参数

class Student:
    def __init__(self, grade):
        self.grade = grade

    @staticmethod
    def run(name):  # 非绑定方法,必须用装饰器staticmethod,在定义函数时和普通函数一样不会补全默认参数
        print(f'{name}可以润')

# (1)对象调用非绑定方法
stu1 = Student(grade=2)
stu1.run(name='lavigne')

# (2)类调用非绑定方法
Student.run(name='avril')

总结:

  在定义非绑定方法(静态方法、非绑定函数)必须要用装饰器@staticmethod,并且装饰后的函数和普通函数一样

  类和对象调用非绑定方法时,函数定义哪些参数就传入哪些参数,和普通函数一样

4. 反射

4.1 概念

反射是一种特性,允许程序在运行时检查、访问和修改对象的属性和方法

 4.2 反射方法介绍

hasattr(obj, key)  # 判断对象是否具有指定属性
getattr(obj, key[, default])  # 获取对象的属性值,如果属性不存在,可设置默认值
setattr(obj, key, value)  # 设置对象的属性值
delattr(obj, key)  # 删除对象的属性

4.3 hasattr

class Student:
    def __init__(self, name, grade):
        self.name = name
        self.grade = grade

    def run(self):
        print('这是绑定给对象的方法')

    @classmethod
    def sing(cls):
        print('这是绑定给类的方法')

    @staticmethod
    def rap():
        print('这是非绑定/静态方法')

stu1 = Student(name='lavigne', grade=2)

# hasattr(obj, key)
# 从对象中判断属性是否存在,有则返回True,无则返回False
print(hasattr(stu1, 'name'))  # True
print(hasattr(stu1, 'hobby'))  # False

# 除了可以判断数据属性,也可以判断方法属性(函数)
print(hasattr(stu1, 'rap'))  # True

4.4 getattr

class Student:
    def __init__(self, name, grade):
        self.name = name
        self.grade = grade

    def run(self):
        print('这是绑定给对象的方法')

    @classmethod
    def sing(cls):
        print('这是绑定给类的方法')

    @staticmethod
    def rap():
        print('这是非绑定/静态方法')


stu1 = Student(name='lavigne', grade=2)

# getattr(obj, key[, default])
# 从对象中获取键对应的值,键值对存在则将值返回;不存在则抛出异常
# 可以指定键不存在的时候的默认值
print(getattr(stu1, 'name'))
# print(getattr(stu1, 'hobby'))  # AttributeError: 'Student' object has no attribute 'hobby'
print(getattr(stu1, 'hobby', None))  # None

# 除了可以从对象获取数据属性,也可以获取方法属性(函数),返回函数的内存地址
print(getattr(stu1, 'rap'))  # <function Student.rap at 0x000001E00E4A17E0>
# 根据获取到的内存地址调用函数
getattr(stu1, 'rap')()  # 这是非绑定/静态方法

4.5 setattr

class Student:
    def __init__(self, name, grade):
        self.name = name
        self.grade = grade

    def run(self):
        print('这是绑定给对象的方法')

    @classmethod
    def sing(cls):
        print('这是绑定给类的方法')

    @staticmethod
    def rap():
        print('这是非绑定/静态方法')

stu1 = Student(name='lavigne', grade=2)
print(stu1.__dict__)  # {'name': 'lavigne', 'grade': 2}

# setattr(obj, key, value)
# 向对象中设置数据属性
setattr(stu1, 'hobby', 'music')  # 键不存在则新增
print(stu1.__dict__)  # {'name': 'lavigne', 'grade': 2, 'hobby': 'music'}
setattr(stu1, 'hobby', 'surf')  # 键存在则修改
print(stu1.__dict__)  # {'name': 'lavigne', 'grade': 2, 'hobby': 'surf'}

# 向类中添加方法属性(函数)
def holiday():
    print('这是在类外部定义的函数')
setattr(Student, 'vocation', holiday)  # 往类的属性字典中增加了一个键值对,键为vocation,值为holiday
# print(Student.__dict__)  # 'vocation': <function holiday at 0x000001BC11303370>
print(Student.vocation())  # 这是在类外部定义的函数

# 向对象中添加方法属性
print(stu1.__dict__)  # {'name': 'lavigne', 'grade': 2, 'hobby': 'surf'}
setattr(stu1, 'rest', holiday)  # 往对象的属性字典中新增了一个键值对,值是函数名
print(stu1.__dict__)  # {'name': 'lavigne', 'grade': 2, 'hobby': 'surf', 'rest': <function holiday at 0x000001E629AE3E20>}

4.6 delattr

class Student:
    def __init__(self, name, grade):
        self.name = name
        self.grade = grade

    def run(self):
        print('这是绑定给对象的方法')

    @classmethod
    def sing(cls):
        print('这是绑定给类的方法')

    @staticmethod
    def rap():
        print('这是非绑定/静态方法')

stu1 = Student(name='lavigne', grade=2)

# delattr(obj ,key)  删除对象中的键值对
print(stu1.__dict__)  # {'name': 'lavigne', 'grade': 2}
delattr(stu1, 'grade')
print(stu1.__dict__)  # {'name': 'lavigne'}

# 对象不允许删除类中的任何方法属性(函数)
delattr(stu1, 'run')  # AttributeError: run
delattr(stu1, 'sing')  # AttributeError: sing
delattr(stu1, 'rap')  # AttributeError: rap

# 但是类允许删除类中的任何方法属性(函数)
print(Student.__dict__)
delattr(Student, 'run')
delattr(Student, 'sing')
delattr(Student, 'rap')
print(Student.__dict__)

5. Class机制内置方法

# __init__ :初始化类时触发 **
# __del__ :删除类时触发
# __iter__ :迭代器
# __str__ :str函数或者print函数触发 **
# __repr__ :repr或者交互式解释器触发
# __doc__ :打印类内的注释内容 **
# __enter__ :打开文档触发
# __exit__ :关闭文档触发
# __getattr__ : 访问不存在的属性时调用
# __setattr__ :设置实例对象的一个新的属性时调用
# __delattr__ :删除一个实例对象的属性时调用
# __setitem__ :列表添加值
# __getitem__ :将对象当作list使用
# __delitem__ :列表删除值

# __call__ :对象后面加括号,触发执行
# __new__ :构造类时触发

# __init__          实例化类得到对象,对象的数据属性初始化时触发
class Student:
    def __init__(self, name):
        self.name = name
        print('触发__init__')


stu1 = Student(name='lavigne')  # 触发__init__
# __del__       用于在对象销毁之前执行一些清理操作。它通常用于关闭文件、释放资源等。这个方法不是析构方法。
class Student:
    def __init__(self, name):
        self.name = name

    def __del__(self):  # 对象被销毁之之前触发
        print("object is being released")


stu1 = Student(name='lavigne')  # 有实例化对象这个语句则会触发上面__del__,没有这个语句则不会
# __str__
# 该方法在对象被打印时自动触发
# print功能打印的就是它的返回值
# 该方法必须返回字符串类型
class Student:
    def __init__(self, name):
        self.name = name

    def __str__(self):
        # 返回类型必须是字符
        return f'{self.name}正在度假'


stu1 = Student(name='lavigne')
# 打印对象,触发__str__,拿到返回值然后展示
print(stu1)  # lavigne正在度假
# __doc__
# 用来打印类的注释内容
class Student:
    """
    这是类的注释信息
    """

    def __init__(self, name):
        self.name = name

print(Student.__doc__)  # 这是类的注释信息
# 反射    对象.键  触发

# (1) __getattr__(self, key)    键不存在才触发
class Student:
    def __init__(self, name):
        self.name = name

    def __getattr__(self, key):
        return f'属性{key}不存在'

stu1 = Student(name='lavigne')
print(stu1.name)  # lavigne
print(stu1.hobby)  # 属性hobby不存在

# (2)__setattr__ 对象.属性名 = 属性值 设置或修改键值对时触发 class Student: def __init__(self, name): self.name = name def __setattr__(self, key, value): print(f'键值对{key}:{value}新增成功') stu2 = Student(name='lavigne') # 键值对name:lavigne新增成功
# (3)__delattr__ del 对象.属性名 删除键触发 class Student: def __init__(self, name): self.name = name def __delattr__(self, key): print(f'数据属性{key}已被删除') stu3 = Student(name= 'lavigne') del stu3.name # 数据属性name已被删除
# 反射    对象[键]  触发

# (1) __getitem__(self, key)  对象[键]获取对象后面中括号输入的值
class Student:
    def __init__(self, name):
        self.name = name

    def __getitem__(self, key):
        return f'{self.name} is {key}'


stu1 = Student(name='lavigne')
print(stu1['name'])  # lavigne is name
print(stu1.name)  # 不会触发getitem


# # (2)__setitem__  对象[键] = 值  设置或修改键值对时触发
class Student:
    def __init__(self, name):
        self.name = name

    def __setitem__(self, key, value):
        print(f'键值对{key}:{value}新增成功')


stu2 = Student(name='lavigne')
stu2['age'] = 20  # 键值对age:20新增成功
stu2.__dict__.update({'num': '001'})  # 不会触发__setitem__
print(stu2.__dict__)  # {'name': 'lavigne', 'num': '001'}


# # (3)__delitem__    del 对象[键]  删除键触发
class Student:
    def __init__(self, name, id):
        self.name = name
        self.id = id

    def __delitem__(self, key):
        print(f'数据属性{key}已被删除')


stu3 = Student(name='lavigne', id='001')
del stu3['id']  # 数据属性id已被删除
del stu3.name  # 不会触发__delitem__

 

 

 

 

 

posted @ 2024-08-16 21:37  hbutmeng  阅读(3)  评论(0编辑  收藏  举报