首页 > 其他分享 >多态

多态

时间:2024-01-05 19:34:37浏览次数:19  
标签:__ name self 多态 print age def

(一)多态

(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")

标签:__,name,self,多态,print,age,def
From: https://www.cnblogs.com/suyihang/p/17947914

相关文章

  • 封装、继承、多态
    封装、继承、多态1.封装封装就是将属性隐藏,不让外界发现或使用将可以允许外界使用的内容通过接口开发,让用户通过接口使用隐藏属性的方法是通过__变量名1.1封装之隐藏属性隐藏数据属性classTeacher:def__init__(self,name,age):#将名字和年纪都隐藏......
  • 多态在不同语言间是如何实现的?
    先说结论:多态是一种思路概念。不同的语言实现方式不太一样。大致分为两种。1.接口实现 接口实现,是用于静态语言中。特点是只声明。2.继承实现。继承实现,是动态语言中。特点是,父类写个基本的,子类重写父类。就是覆盖着写,来实现。举例:java中的接口示例:interfaceA......
  • Unity引擎2D游戏开发,有限状态机&抽象类多态
    状态机与抽象类观察如下代码:publicclassAttackFinish:StateMachineBehaviour{//OnStateEnteriscalledwhenatransitionstartsandthestatemachinestartstoevaluatethisstateoverridepublicvoidOnStateEnter(Animatoranimator,AnimatorStateIn......
  • C++零基础教程(什么是多态)
    (文章目录)前言本篇文章来给大家讲解一下C++中的多态,学习多态是了解C++特性必不可少的。一、多态的概念多态(Polymorphism)是面向对象编程中一个重要的概念,它允许基于对象的实际类型来调用相应的方法,以实现更灵活和可扩展的代码。在C++中,多态通常通过虚函数(VirtualFunction)和......
  • Java第十课_抽象和多态
    4.面向对象的编程_抽象和多态抽象publicclassPractice{publicstaticvoidmain(String[]args){Student.say();}}publicabstractclassStudent{/*abstract:表示抽象的,可以用来修饰类和函数抽象的本质,是从一......
  • 父类引用对象引用子类对象--多态
    在Java中,父类引用对象调用子类对象的原理涉及到两个关键概念:编译时类型和运行时类型。编译时类型(Compile-timeType):编译时类型是指在编写代码时,你所声明的引用的类型。例如,如果你有一个声明为Animalanimal的引用,编译时类型就是Animal。运行时类型(RuntimeType):运行时类......
  • go的封装、继承与多态的使用
    目录一、封装1.1公有封装1.2私有封装1.2.1工厂函数解析1.2.2&与*指针使用描述1.3深度封装二、继承与多态2.1继承与多态案例2.1.1继承代码分析2.1.2结构体实例化2.1.3多态代码分析一、封装​ 在Go语言中,封装是一种将数据和操作数据的方法组织在一起的概念。封装的目的......
  • 【愚公系列】2023年12月 通用职责分配原则(六)-多态原则(Polymorphism Principle)
    ......
  • 秦疆的Java课程笔记:71 面向对象 什么是多态
    多态即同一方法可以根据发送对象的不同而采用多种不同的行为方式。一个对象的实际类型是确定的,但可以指向对象的引用的类型有很多。(指向父类或者有关系的类。)//父类=======================================publicclassPerson{}//子类=================================......
  • 关于java的多态方法调用顺序的问题
    使用父类类型的引用指向子类的对象,该引用调用的师父类中定义的方法和变量,变量不能被重写(覆盖);如果子类中重写了父类中的一个方法,那么在调用这个方法的时候,将会调用子类中的这个方法;注意特殊情况,如果该父类引用所调用的方法参数列表未定义,就调用该父类的父类中查找,如果还没找到就强......