多态

(一)多态

(1)什么是多态

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

  • 比如动物---猪狗牛羊

(2)示例

  • 比如动物有多种形态:猫、狗、猪
import abc


# 同一类事物:动物
class Animal(metaclass=abc.ABCMeta):
    @abc.abstractmethod
    def talk(self):
        pass


# 动物的形态之一:人
class People(Animal):
    def talk(self):
        print('你真帅')


# 动物的形态之二:狗
class Dog(Animal):
    def talk(self):
        print('汪汪汪')


# 动物的形态之三:猪
class Pig(Animal):
    def talk(self):
        print('哼唧哼唧')
  • 文件有多种形态:文本文件,可执行文件
import abc


# 同一类事物:文件
class File(metaclass=abc.ABCMeta):
    @abc.abstractmethod
    def click(self):
        pass

# 文件的形态之一:文本文件
class Text(File):  
    def click(self):
        print('open file')

# 文件的形态之二:可执行文件
class ExeFile(File):  
    def click(self):
        print('execute file')

(二)多态性

(1)什么是多态动态绑定(多态性)

  • 多态动态绑定是在继承的背景下使用的,有时也称为多态性
  • 多态性是指在不考虑实例类型的情况下使用实例
  • 在面向对象方法中一般是这样表述多态性:
    • 向不同的对象发送同一条消息(!!!obj.func():是调用了obj的方法func,又称为向obj发送了一条消息func)
    • 不同的对象在接收时会产生不同的行为(即方法)。
    • 也就是说,每个对象可以用自己的方式去响应共同的消息。
    • 所谓消息,就是调用函数,不同的行为就是指不同的实现,即执行不同的函数。
  • 比如:老师.下课铃响了(),学生.下课铃响了()
    • 老师执行的是下班操作
    • 学生执行的是放学操作
    • 虽然二者消息一样,但是执行的效果不同

(2)多态性的分类

  • 多态性分为静态多态性和动态多态性

(1)静态多态性

  • 如任何类型都可以用运算符 + 进行运算

(2)动态多态性

import abc

# 同一类事物:动物
class Animal(metaclass=abc.ABCMeta):
    @abc.abstractmethod
    def talk(self):
        pass
# 动物形态之一:猫
class Cat(Animal):
    def talk(self):
        print(f"喵喵叫")
# 动物形态之二:狗
class Dog(Animal):
    def talk(self):
        print(f"汪汪叫")
# 动物形态之三:猪
class Pig(Animal):
    def talk(self):
        print(f"哼哼哼")

cat=Cat()
dog=Dog()
pig=Pig()
# cat、dog、pig都是动物,只要是动物就肯定有talk的方法
# 不考虑三者具体是什么类型,可以直接使用
cat.talk()# 喵喵叫
dog.talk()# 汪汪叫
pig.talk()# 哼哼哼

# 更进一步可以定义一个接口来使用
def func(obj):
    obj.talk()
func(cat)#喵喵叫
func(dog)#汪汪叫
func(pig)#哼哼哼

(3)为什么要用多态性(多态性的好处)

  • 增加了程序的灵活性
    • 以不变应万变,不论对象千变万化,使用者都是同一种形式去调用,如func(obj)
  • 增加了程序额可扩展性
    • 通过继承animal类创建了一个新的类,使用者无需更改自己的代码,还是用func(obj)去调用

(三)鸭子类型duck_typing

(1)什么是鸭子类型duck_typing

  • 鸭子类型是一种编程风格,决定一个对象是否有正确的接口
    • 关注点在于它的方法或属性
    • 而不是它的类型(如果它看起来像鸭子,像鸭子一样嘎嘎叫,那么它一定是鸭子。)。
  • 通过强调接口而不是特定类型,设计良好的代码通过多态提高了灵活性。
    • 鸭子类型无需使用 type()isinstance() 进行检查(注意,鸭子类型可以用抽象基类来补充)
    • 相反,它通常使用 hasattr() 来检查,或是 EAFP 编程。
  • 但其实我们完全可以不依赖于继承,只需要制造出外观和行为相同对象,同样可以实现不考虑对象类型而使用对象,这正是Python崇尚的“鸭子类型”(duck typing):
    • “如果看起来像、叫声像而且走起路来像鸭子,那么它就是鸭子”。
    • 比起继承的方式,鸭子类型在某种程度上实现了程序的松耦合度

(2)鸭子类型

# 鸭子类型是一种编程风格,决定一个对象是否有正确的接口
# 如果它看起来像鸭子,像鸭子一样嘎嘎叫,那么它一定是鸭子。

# 二者都像鸭子,因而就可以当鸭子一样去用
class NormalDuck():
    def eat(self):
        print(f"正常鸭子可以吃饭")
    def walk(self):
        print(f"正常鸭子可以走路")


class RockDuck():
    def eat(self):
        print(f"肉鸭子可以吃饭")

    def walk(self):
        print(f"肉鸭子可以走路")

class Chicken()
    def eat(self):
        print(f"鸡可以吃饭")

    def walk(self):
        print(f"鸡可以走路")

(四)反射

(1)什么是反射

  • 通过字符串的形式操作对象的属性
  • 反射是一种程序可以访问、检测和修改其本身状态或行为的能力。
  • 在 Python 中,反射主要指通过字符串的形式操作对象的属性。

(2)Python中的反射

  • 通过字符串的的形式操作对象相关的属性。
  • python中一切皆是对象(都可以使用反射)

(五)反射的方法

(1)反射方法介绍

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

(2)引入

class Student():
    def __init__(self,name,age):
        self.name=name
        self.age=age
    def run(self):
        print(f"{self.name}正在跑步")
# 实例化类得到对象
s=Student(name='syh',age=23)
# 直接操作对象得到属性
print(s.name)#syh
# 查看对象的属性
print(s.__dict__)
#{'name': 'syh', 'age': 23}
# 根据字典取值:拿到对应的值
print(s.__dict__['name'])#syh
# 查看对象的方法
print(s.__dir__())
# ['name', 'age', '__module__', '__init__', 'run', '__dict__', '__weakref__', '__doc__', '__new__', '__repr__', '__hash__', '__str__', '__getattribute__', '__setattr__', '__delattr__', '__lt__', '__le__', '__eq__', '__ne__', '__gt__', '__ge__', '__reduce_ex__', '__reduce__', '__subclasshook__', '__init_subclass__', '__format__', '__sizeof__', '__dir__', '__class__']
# 查看对象中的所有方法
print(dir(s))
#['__class__', '__delattr__', '

(3)getattr方法:获取对象的属性值

  • 语法 : getattr(object, name[, default])
    • 获取对象的属性值,如果对象不存在时,可以知道默认值
class Student():
    def __init__(self,name,age):
        self.name=name
        self.age=age
    def run(self):
        print(f"{self.name}正在跑步")

s1=Student(name='syh',age=23)
# getattr方法:如果存在这个方法或属性,就返回属性值或者方法的内存地址,如果不存在就会报错
res_name=getattr(s1,'name')
print(res_name)#syh
res_age=getattr(s1,'age')
print(res_age)#23
# res_sex=getattr(s1,'sex')
# Traceback (most recent call last):
#   File "D:\old boy\python\python28基础\day31\反射.py", line 38, in <module>
#     res_sex=getattr(s1,'sex')
# AttributeError: 'Student' object has no attribute 'sex'
# getattr方法:获取的对象不存在时,可以知道不存在的时候返回的默认值
res_sex=getattr(s1,'sex','female')
print(res_sex)#female

(4)hasattr方法:判断对象是否具有指定的属性

  • 语法:hasattr(object, name)
    • 判断对象是否具有指定属性
# hasattr判断对象是否存在某属性或者方法
class Student():
    def __init__(self,name,age):
        self.name=name
        self.age=age
    def run(self):
        print(f"{self.name}正在跑步")

s1=Student(name='syh',age=23)
print(hasattr(s1,'name'))#True
print(hasattr(s1,'sex'))#False
print(hasattr(s1,'run'))#True

(5)setattr方法:设置对象的属性值

  • setattr(object, name, value)
    • 设置对象的属性值
# setattr()方法:设置对象的属性值
class Student():
    def __init__(self,name,age):
        self.name=name
        self.age=age
    def run(self):
        print(f"{self.name}正在跑步")
s1=Student(name='syh',age=23)
# 先判断s1对象是否具有该属性
print(hasattr(s1,'sex'))#False
# 如果不存在,setattr添加
setattr(s1,'sex','female')
# 如果存在,就获取属性值
print(hasattr(s1,'sex'))#True

(6)delattr方法:删除对象的属性

  • delattr(object, name)
    • 删除对象的属性
class Student():
    def __init__(self,name,age):
        self.name=name
        self.age=age
    def run(self):
        print(f"{self.name}正在跑步")
s1=Student(name='syh',age=23)

# 先判断对象是否具有该属性
print(hasattr(s1,'name'))#True
# 如果存在就直接delattr删除该属性
delattr(s1,'name')
print(hasattr(s1,'name'))#False

(拓展)

(1)类也是对象

  • 在Python中一切皆对象,所以我们也可以将类作为反射方法的第一个参数,反射其属性
class Student():
    def __init__(self,name,age):
        self.name=name
        self.age=age
    def run(self):
        print(f"{self.name}正在跑步")
# 判断类是否具有该属性
res_name=hasattr(Student,'name')
print(res_name)# False
res_age=hasattr(Student,'age')
print(res_age)# False
res_run=hasattr(Student,'run')
print(res_run)# True
# 获取类中的属性值
print(getattr(Student,'run'))
# <function Student.run at 0x0000017219C62950>
# 设置类的属性
setattr(Student,'name','su')
print(hasattr(Student,'name'))#True
# 删除类中的属性
delattr(Student,'name')
print(hasattr(Student,'name'))#False

(2)反射当前模块成员

# 当前模块
#<module '__main__' from 'D:\\old boy\\python\\python28基础\\day31\\反射.py'>
thismodule=sys.modules[__name__]
import sys
thismodule=sys.modules[__name__]
#<module '__main__' from 'D:\\old boy\\python\\python28基础\\day31\\反射.py'>
def s1():
    pass
def s2():
    pass
res_s1=hasattr(thismodule,'s1')
print(res_s1)#True
print(getattr(thismodule,'s1'))
#<function s1 at 0x00000207BBA52290>

(7)反射的好处

# 反射的好处
# 反射的好处就是,可以事先定义好接口,接口只有在被完成后才会真正执行,这实现了即插即用,这其实是一种‘后期绑定’,意味着可以在程序运行过程中动态地绑定接口的实现。
# 这种灵活性使得程序更容易扩展和维护

(1)实现可插拔机制

  • 反射的好处就是,可以事先定义好接口,接口只有在被完成后才会真正执行,这实现了即插即用,这其实是一种‘后期绑定’,意味着可以在程序运行过程中动态地绑定接口的实现。
  • 这种灵活性使得程序更容易扩展和维护。
class PluginInterface:
    def execute(self):
        pass


class PluginA(PluginInterface):
    def execute(self):
        print("插件A被启动")


class PluginB(PluginInterface):
    def execute(self):
        print("插件B被启动")


def run_plugin(plugin):
    plugin.execute()


# 使用反射调用插件
plugin_name = input("请输入插件名字(PluginA or PluginB)  :>>>> ")
# 从全局名称空间中获取到 插件名字对应的类
plugin_class = globals().get(plugin_name)

# 判断一下当前类是否存在 并且 判断当前类是否 有 PluginInterface 接口
if plugin_class and issubclass(plugin_class, PluginInterface):
    # 如果都成立会触发相应的方法
    run_plugin(plugin_class())
else:
    # 不存在则抛出异常
    print("Invalid plugin name")

(2)动态导入模块(基于反射当前模块成员)

  • 动态导入模块是指在程序运行时根据字符串的形式导入模块。
  • 通过反射,可以动态导入模块的成员,实现更灵活的代码组织和管理。
import importlib

module_name = input("请输入模块名 :>>>> ")
method_name = input("请输入方法名 :>>>> ")

try:
    # 动态导入模块
    module = importlib.import_module(module_name)
    # 反射是否存在当前方法
    method = getattr(module, method_name)
    # 如果存在则执行当前方法
    method()
except ImportError:
    print("Module not found")
except AttributeError:
    print("Method not found")
posted @   苏苏!!  阅读(19)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)
点击右上角即可分享
微信分享提示