python中的面向对象特性

面向对象编程(Object-Oriented Programming,简称OOP)是一种编程范式,它使用“对象”来设计软件。面向对象编程的主要特性包括封装、继承、多态性和抽象。这些特性使得OOP特别适合处理大型、复杂的软件系统。

特性

1. 封装(Encapsulation)

封装是OOP的核心特性之一,它指的是将对象的数据(属性)和操作这些数据的方法绑定在一起,形成一个紧密的单元。封装的目的是隐藏对象的内部细节,只暴露必要的操作接口给外界。这样做的好处是减少了外部对对象内部的直接访问,降低了代码的耦合度,提高了数据的安全性和代码的可维护性。

class Person:def __init__(self, name, age):self._name = name  # 使用下划线(_)标识属性为受保护的self._age = agedef get_name(self):return self._namedef set_name(self, name):self._name = namedef get_age(self):return self._agedef set_age(self, age):if age >= 0:self._age = ageelse:print("Age cannot be negative.")# 创建一个Person对象
person1 = Person("Alice", 30)# 通过公共方法访问和修改属性
print(person1.get_name())  # 输出: Alice
print(person1.get_age())  # 输出: 30person1.set_name("Bob")
person1.set_age(25)print(person1.get_name())  # 输出: Bob
print(person1.get_age())  # 输出: 25# 尝试修改年龄为负数,将触发封装的保护机制
person1.set_age(-10)  # 输出: Age cannot be negative.
print(person1.get_age())  # 输出: 25# 尝试直接访问和修改属性,还是可以直接访问和修改,只是不建议这样做,pycharm也会提示
person1._name = 123
print(person1._name)  # 输出: 123

在Python中,尽管使用下划线(_)标识属性为受保护的(protected),但这只是一种命名约定,实际上Python并没有严格的访问控制机制。因此,在上面的例子中仍然可以通过person1._name = 123这样的方式来直接修改对象的属性。

尽管Python中的属性命名约定可以提示其他程序员某些属性是受保护的,但它并不会阻止直接访问和修改这些属性。如果希望实现真正的封装和访问控制,可以考虑使用属性装饰器(property decorator)来定义属性的getter和setter方法,从而实现更严格的属性访问控制。

class Person:def __init__(self, name, age):self._name = nameself._age = age@propertydef name(self):return self._name@name.setterdef name(self, value):self._name = value@propertydef age(self):return self._age@age.setterdef age(self, value):if value >= 0:self._age = valueelse:print("Age cannot be negative.")# 创建一个Person对象
person1 = Person("Alice", 30)# 通过属性访问器访问和修改属性
print(person1.name)  # 输出: Alice
print(person1.age)   # 输出: 30person1.name = "Bob"
person1.age = 25print(person1.name)  # 输出: Bob
print(person1.age)   # 输出: 25person1.age = -10  # 输出: Age cannot be negative.
print(person1.age)  # 输出: 25

2. 继承(Inheritance)

继承是一种创建新类的方式,新创建的类称为子类,它继承了另一个类(称为父类)的属性和方法。继承支持代码复用,使得子类可以扩展或修改父类的行为。

在Python中,继承可以是单继承也可以是多继承。

2.1 单继承

# 定义一个父类
class Animal:def __init__(self, breed):self.breed = breeddef speak(self):print(f"{self.breed} 发出声音。。。")# 定义一个子类,继承自Animal
class Dog(Animal):def __init__(self, breed, name):super().__init__(breed)  # 调用父类的初始化方法self.name = namedef speak(self):super().speak()print(f"{self.name}在叫。。。")# 创建一个Animal对象
animal = Animal("普通动物")
animal.speak()# 创建一个Dog对象
dog = Dog("泰迪", "黄毛")
dog.speak()

2.2 多继承

# 定义一个父类
class Animal:def __init__(self, name):self.name = nameprint("【初始化动物类】")def speak(self):print(f"{self.name} 发出声音。。。")# 定义另一个父类
class Flyable:def __init__(self):print("【初始化飞行类】")def fly(self):print("飞在高空中。。。")# 定义一个子类,继承自Animal和Flyable
class Bird(Animal, Flyable):def __init__(self, name, species):super().__init__(name)  # 调用Animal类的初始化方法self.species = speciesprint("【初始化鸟类】")def speak(self):print(f"{self.species} {self.name} 啾啾叫。。。")# 创建一个Bird对象
bird = Bird("小七", "麻雀")
bird.speak()
bird.fly()print(Bird.__mro__)

2.3 MRO

在以上多继承的例子中,Bird类的构造器中super调用,怎么知道是Animal类而不是Flyable类的???

答:在Python中,当使用super()函数时,它遵循方法解析顺序(Method Resolution Order, MRO)来决定如何从多个基类中继承属性和方法。

MRO(Method Resolution Order)是一种规则,用于确定Python中类的方法调用顺序。当在面向对象编程中使用继承时,尤其是在多重继承的情况下,可能会出现一个方法在多个父类中都存在的情况。为了解决这种潜在的歧义,Python使用MRO来明确地确定应该首先从哪个父类中寻找该方法。

MRO背后的算法最初基于C3线性化算法。这个算法有几个关键目标:

  • 子类总是优先于父类
  • 如果一个类继承自多个父类,则根据它们在定义中出现的顺序排列
  • 每个父类保证按照其声明时相同的顺序被检查

从Python 2.3开始,默认情况下所有新式类(即显式或隐式地继承自object)都使用C3线性化来计算MRO。可以通过两种方式查看任何给定类的MRO:

  1. 使用内置函数help()
  2. 查看该类的__mro__属性。

Bird 类继承自 AnimalFlyable 类。根据定义的继承顺序(即首先是 Animal, 然后是 Flyable

print(Bird.__mro__)

输出:

(<class '__main__.Bird'>, <class '__main__.Animal'>, <class '__main__.Flyable'>, <class 'object'>)

这意味着当调用 super().__init__(name) 时,Python会按照MRO列表查找下一个具有 __init__ 方法的类。因此,在这种情况下,它首先找到并调用了 Animal.__init__ 方法而不是 Flyable.__init__

3. 多态性(Polymorphism)

多态性是指不同类的对象对同一消息作出响应的能力,即同一个接口可以被不同的对象以不同的方式实现。这意味着编写的代码可以对不同类型的对象进行操作,而不需要知道具体的类是什么,只需要知道对象支持的接口即可。多态性提高了程序的灵活性和可扩展性。

# 定义一个父类
class Animal:def speak(self):pass# 定义两个子类,分别实现了speak方法
class Dog(Animal):def speak(self):return "Woof!"class Cat(Animal):def speak(self):return "Meow!"# 定义一个函数,接受Animal对象作为参数,并调用其speak方法
def make_sound(animal):return animal.speak()# 创建一个Dog对象和一个Cat对象
dog = Dog()
cat = Cat()# 调用make_sound函数,分别传入Dog对象和Cat对象
print("Dog对象的调用", make_sound(dog))  # 输出: Woof!
print("Cat对象的调用", make_sound(cat))  # 输出: Meow!

4. 抽象(Abstraction)

抽象是将复杂的现实问题简化的过程,它通过创建简化的模型来表示复杂的实体,这个模型只包含对于当前问题重要的信息。在面向对象编程中,抽象通常是通过定义类来实现的,类提供了一种方式来定义抽象数据类型,通过隐藏所有的实现细节,只暴露出有限的接口与外界交互。

抽象类是一种不能被实例化的类,它通常用作其他类的基类,定义了一些抽象方法,子类需要实现这些抽象方法。Python通过抽象基类(Abstract Base Classes, ABCs)提供了对抽象的支持。使用abc模块可以创建抽象基类。

from abc import ABC, abstractmethod# 定义一个抽象基类 形状,如果一个类继承自ABC类,那么这个类就是一个抽象基类。
class Shape(ABC):# 通过注解将area和perimeter方法标记为抽象方法,子类必须实现这些方法。@abstractmethoddef area(self):pass@abstractmethoddef perimeter(self):pass# 实现具体子类 圆形
class Circle(Shape):def __init__(self, radius):self.radius = radius# 实现area方法def area(self):return 3.14 * self.radius ** 2# 实现perimeter方法def perimeter(self):return 2 * 3.14 * self.radius# 实现具体子类 矩形
class Rectangle(Shape):def __init__(self, length, width):self.length = lengthself.width = width# 实现area方法def area(self):return self.length * self.width# 实现perimeter方法def perimeter(self):return 2 * (self.length + self.width)# 尝试创建Shape实例将引发错误,因为它是一个抽象基类。TypeError: Can't instantiate abstract class Shape with abstract methods area, perimeter
# shape = Shape()# 创建Circle和Rectangle对象并调用它们的方法。计算圆形和矩形的面积和周长。
circle = Circle(radius=5)
print(f"Circle Area: {circle.area()}")
print(f"Circle Perimeter: {circle.perimeter()}")rectangle = Rectangle(length=4, width=6)
print(f"Rectangle Area: {rectangle.area()}")
print(f"Rectangle Perimeter: {rectangle.perimeter()}")

在这个示例中:

  • Shape 类通过继承自 ABC 并标记 area()perimeter() 方法为抽象方法(使用装饰器 @abstractmethod),成为了一个抽象基类。这意味着你不能直接实例化 Shape, 而必须先定义其子类,并且在子类中实现所有标记为抽象的方法。
  • 我们定义了两个具体的形状:圆形 (Circle) 和矩形 (Rectangle) ,作为 Shape 的子类型,并分别实现了计算面积和周长的逻辑。
  • 最后部分展示了如何创建具体形状对象并调用它们各自计算面积和周长的功能。

通过这种方式,我们利用了面向对象编程中“接口”或“契约”的概念:即一组预期行为(在本例中即计算面积和周长),而不需要关心这些行为背后具体如何实施数学逻辑。

对象的关系

在面向对象编程中,对象之间的关系主要可以分为以下几种类型:

  1. 继承(Inheritance):表示一个类(子类)继承另一个类(父类)的属性和方法。这是一种“是一个”(is-a)关系,例如,狗是一种动物,因此狗类可以继承自动物类。
  2. 组合(Composition):表示一个对象包含另一个对象或多个对象的实例作为其成员变量。这表明了一种“有一个”(has-a)关系。例如,汽车有引擎,所以汽车类可以包含引擎类的实例作为其成员。
  3. 聚合(Aggregation):也是一种特殊形式的组合,但它允许被包含对象与容器对象之间保持相对独立;即容器对象被销毁时,并不意味着被包含的对象也会被销毁。例如,部门和员工之间就是聚合关系;部门解散并不意味着里面的员工也消失了。
  4. 依赖(Dependency):指一个类使用到另外一个类。这通常体现在某个类的方法通过参数、返回值或局部变量使用到另外一个类型。这是一种较弱的关联方式,“使用了”(uses-a) 关系。
  5. 关联(Association):表示两个或多个对象之间有联系但彼此相对独立。它们可能会互相交流信息而无需彼此了解对方更多信息。“知道”(knows-a) 关系描述了两个及以上完全独立构造体之间通过消息传递进行通信而建立起来的链接。
  6. 实现(Implementation):特指接口与实现该接口的具体类之间的关系,在某些语言中如Java中非常明显。“能做”(can-do) 关系描述了类型如何通过接口声明自己能够执行哪些操作。

通俗点讲:

人类继承(Inheritance)自动物类,人的各种器官类组合(Composition)成了人类,很多人聚合(Aggregation)成一个部门,工具依赖(Dependency)人类才能使用,人之间的朋友关系就是关联(Association)。

示例说明

  • 继承示例:

    2.1 单继承

  • 组合示例:

    class Engine:def __init__(self, horsepower):self.horsepower = horsepowerdef start(self):print("引擎启动。。。")def stop(self):print("引擎停止。。。")class Car:def __init__(self, make, model, engine):self.make = makeself.model = modelself.engine = enginedef start(self):print(f"{self.make} {self.model} 汽车启动。。。")self.engine.start()def stop(self):print(f"{self.make} {self.model} 汽车停止。。。")self.engine.stop()# 创建一个Engine对象
    engine = Engine(200)# 创建一个Car对象,将Engine对象作为参数传入
    car = Car("丰田", "凯美瑞", engine)# 启动汽车
    car.start()# 停止汽车
    car.stop()
    
  • 聚合示例:

    当创建两个Employee对象时,它们共享同一个Department对象。两个员工对象独立存在,但它们都包含对同一个部门对象的引用。这就是聚合关系的特点。

    聚合关系常用于描述整体与部分之间的关系,但两者之间可以独立存在,且部分对象可以被多个整体对象共享。

    # 部门类
    class Department:def __init__(self, name):self.name = name# 员工类,多个员工聚合成一个部门
    class Employee:def __init__(self, name, department):self.name = nameself.department = department# 创建一个Department对象
    engineering_department = Department("技术开发部")# 创建两个Employee对象,共享同一个Department对象
    employee1 = Employee("张三", engineering_department)
    employee2 = Employee("李四", engineering_department)# 输出每个员工所在的部门名称
    print(f"{employee1.name}{employee1.department.name} 工作。")
    print(f"{employee2.name}{employee2.department.name} 工作。")
  • 关联示例:

    描述了两个类之间的对象相互连接。关联可以是双向的,也可以是单向的。在这种关系中,一个类的对象知道另一个类的对象,并通过这个知识进行交互。但是,彼此之间不强制性地拥有或控制对方。

    简单例子:每位作家(Writer)可能会写多本书(Book),而每本书都只能有一位作者。这里就形成了Writer和Book之间的关联关系。

    class Writer:def __init__(self, name):self.name = nameself.books = []  # Writer knows about Bookdef write_book(self, book_title):book = Book(book_title, self)self.books.append(book)return bookdef get_books(self):return [book.title for book in self.books]class Book:def __init__(self, title, author):self.title = titleself.author = author  # Book knows about Writer# 使用示例
    author1 = Writer('George Orwell')
    book1 = author1.write_book('1984')
    book2 = author1.write_book('Animal Farm')print(f"{author1.name} has written: {author1.get_books()}")
    
  • 依赖示例:

    依赖关系(Dependency)是面向对象编程中的一种使用关系,其中一个类的实现依赖于另一个类的定义。这种关系通常表现为局部变量、方法参数或对静态方法的调用。依赖关系意味着如果一个类改变了,它可能会影响到依赖它的其他类。

    # 报告的格式化类
    class ReportFormatter:def format(self, report_data):# 假设这里有复杂的格式化逻辑return f"格式化报告: {report_data}"# 报告的生成类
    class ReportGenerator:def generate(self, data):formatter = ReportFormatter()  # ReportGenerator 依赖于 ReportFormatterformatted_data = formatter.format(data)print(formatted_data)# 使用示例
    generator = ReportGenerator()
    generator.generate("周报")

    由于ReportGenerator直接创建并使用了ReportFormatter, 如果后者发生改变(例如构造函数参数发生变化),那么前者也可能需要相应地进行修改以适应这些改动。因此可以说,ReportGenerator对于ReportFormatter存在着依赖关系。

  • 实现示例:

    1. 抽象(Abstraction)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/760943.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

kail linux破解密码--- 详细过程(配合图文让你看了就会)

1.准备工作 1.vmware虚拟机 2.kali的系统 3.无线网卡一张(这个是必须的我买的是30多块) 4.这里为了实验&#xff0c;和直观的看到效果&#xff0c;用手机开了一个wifi然后使用kali进行破解 2.下载kali然后安装到虚拟机vmware 直接在官网下载 Get Kali | Kali Linux 我选…

WebXR实践——利用aframe框架浏览器展示全景图片

一、效果 话不多说&#xff0c;先上效果 二、代码 index.html <!DOCTYPE html> <html><head><meta charset"utf-8"><title>360&deg; Image</title><meta name"description" content"360&deg; Imag…

面试算法-64-零钱兑换

题目 给你一个整数数组 coins &#xff0c;表示不同面额的硬币&#xff1b;以及一个整数 amount &#xff0c;表示总金额。 计算并返回可以凑成总金额所需的 最少的硬币个数 。如果没有任何一种硬币组合能组成总金额&#xff0c;返回 -1 。 你可以认为每种硬币的数量是无限的…

【机器学习】深入解析线性回归模型

&#x1f388;个人主页&#xff1a;豌豆射手^ &#x1f389;欢迎 &#x1f44d;点赞✍评论⭐收藏 &#x1f917;收录专栏&#xff1a;机器学习 &#x1f91d;希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出指正&#xff0c;让我们共同学习、交流进…

高精度铸铁平台制造工艺有多精细——河北北重机械

高精度铸铁平台制造工艺通常包括以下几个步骤&#xff1a; 材料准备&#xff1a;选择合适的铸铁材料&#xff0c;并确保其质量符合要求。常用的铸铁材料包括灰铸铁、球墨铸铁等。 模具制造&#xff1a;根据平台的设计要求&#xff0c;制造适用的模具。模具一般由砂型、金属模具…

【python】flask基于cookie和session来实现会话控制

✨✨ 欢迎大家来到景天科技苑✨✨ &#x1f388;&#x1f388; 养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; &#x1f3c6; 作者简介&#xff1a;景天科技苑 &#x1f3c6;《头衔》&#xff1a;大厂架构师&#xff0c;华为云开发者社区专家博主&#xff0c;…

js数组去重常见方法

简单数组 1、使用filter()方法&#xff1a;通过filter()方法遍历数组&#xff0c;返回仅包含首次出现的元素的新数组。 const arr [1, 2, 3, 4, 2, 3, 5]; const list arr.filter((item, index) > arr.indexOf(item) index); console.log(list); // [1, 2, 3, 4, 5]2、…

【开源-土拨鼠充电系统】鸿蒙 HarmonyOS 4.0 App+微信小程序+云平台

✨本人自己开发的开源项目&#xff1a;土拨鼠充电系统 ✨踩坑不易&#xff0c;还希望各位大佬支持一下&#xff0c;在Gitee或GitHub给我点个 Start ⭐⭐&#x1f44d;&#x1f44d; ✍Gitee开源项目地址&#x1f449;&#xff1a;https://gitee.com/cheinlu/groundhog-charging…

力扣Lc19--- 268. 丢失的数字(java版)-2024年3月20日

1.题目描述 2.知识点 &#xff08;1&#xff09;比如数组里面有n个数&#xff0c;然后计算这n个数的总和(用等差求和数列计算&#xff09;,然后减去数组的和&#xff0c;用总和减去数组和即为所得 &#xff08;2&#xff09;加强型 for 循环&#xff08;也称为 for-each 循环&…

spring boot切面execution表达式添加多个包路径

问题描述 在Spring Boot中&#xff0c;如果你想为多个包中的方法创建一个切面&#xff0c;你可以在Pointcut注解中使用||操作符来指定多个包。 解决方案&#xff1a; // 定义切入点为两个包中的任意方法 Pointcut("execution(* com.example.package1..*.*(..)) || execu…

Leetcode 459:重复的子字符串

给定一个非空的字符串 s &#xff0c;检查是否可以通过由它的一个子串重复多次构成。 示例 1: 输入: s "abab" 输出: true 解释: 可由子串 "ab" 重复两次构成。示例 2: 输入: s "aba" 输出: false示例 3: 输入: s "abcabcabcabc&quo…

单片机--数电(4)

触发器 数字电路中&#xff1a;分组合逻辑电路与时序逻辑电路两大类 组合逻辑电路的基本单元是门电路&#xff08;与或非等一些门电路&#xff09; 时序逻辑电路的基本单元是触发器 触发器与门电路的区别 门电路某一时刻的输出信号完全取决于该时刻的输入信号&#xff0c;…

销售数据分析怎么做?用好这5个数据分析方法与模型就足够了。

企业经营其实简单来说就是做买卖&#xff0c;有了买卖自然就产生了销售数据&#xff0c;那怎么能让这些销售数据产生价值呢&#xff1f;答案就是数据分析。通过对销售数据的分析&#xff0c;可以帮助企业及时洞察市场动向&#xff0c;发现企业销售过程中的问题&#xff0c;调整…

使用C/C++举例说明使用宏定义时需要注意的各个点

好的&#xff0c;下面我将分别举例说明使用宏定义时需要注意的各个点&#xff1a; &#xff08;a&#xff09;宏名和替换文本之间用空格分开&#xff1a; #define MAX_VALUE 100 // 正确的宏定义&#xff0c;MAX_VALUE是宏名&#xff0c;100是替换文本 不要写成等号连接的形…

ResNet《Deep Residual Learning for Image Recognition》

ResNet论文学习 引言Deep Residual Learning 深度残差学习网络结构 总结代码复现 引言 深度网络自然地以端到端的多层方式集成低/中/高级特征和分类器&#xff0c;特征的“级别”可通过堆叠层的数量来丰富 随着网络层数加深&#xff0c;提取的特征越强&#xff0c;但是 网络…

2024.3.21 ARM

串口控制三盏灯亮灭 main.c #include "uart4.h"int main() {//串口初始化uart4_init();//led初始化led_init();char i;//char s[128];int count 0;int count2 0;int count3 0;while (1){i getchar();putchar(i);//最终的现象&#xff1a;键盘输入a&#xff0c;…

表情识别数据集

表情视频数据集 在许多的研究中,研究者通常会把人脸表情识别区分为静态的人脸表情识别(static facial emotion recognition)和动态的人脸表情识别(dynamic facial emotion recognition)。前者希望通过单张图片辨别人的表情从而达到识别人情绪的目的,而后者希望感知视频/…

进程的概念 | PCB | Linux下的task_struct | 父子进程和子进程

在讲进程之前首先就是需要去回顾一下我们之前学的操作系统是干嘛的&#xff0c;首先操作系统是一个软件&#xff0c;它是对上提供一个良好高效&#xff0c;稳定的环境的&#xff0c;这是相对于用户来说的&#xff0c;对下是为了进行更好的软硬件管理的&#xff0c;所以操作系统…

多数据源 - dynamic-datasource | 进阶 - 数据库加密

文章目录 实现原理EncDataSourceInitEvent 类与 DataSourceInitEvent 接口配置 DataSourceInitEvent 为 Bean使用案例第 1 步:使用私钥加密第 2 步:在 YAML 中使用 ENC(xxx) 配置相关文章🗯️ 上节回顾:前节中,了解了 dynamic-datasource 的懒加载/初始化 SQL/无数据源启…

TinyEMU源码分析之虚拟机初始化

TinyEMU源码分析之虚拟机初始化 1 初始化结构参数2 配置RAM地址空间3 初始化设备4 拷贝BIOS和Kernel5 手动写入5条指令6 体验第一条指令的执行 本文属于《 TinyEMU模拟器基础系列教程》之一&#xff0c;欢迎查看其它文章。 本文中使用的代码&#xff0c;均为伪代码&#xff0c…