31天Python入门——第18天:面向对象三大特性·封装继承多态

|----------------|
| 你好,我是安然无虞。 |

文章目录

    • 面向对象三大特性
      • [1. 封装](#1. 封装)
      • [2. 继承](#2. 继承)
      • [3. 多态](#3. 多态)
      • [4. 抽象基类](#4. 抽象基类)
      • [5. 补充练习](#5. 补充练习)

面向对象三大特性

面向对象编程(Object-Oriented Programming, 简称OOP)有三大特性, 分别是封装继承多态.这些特性是面向对象编程的基础, 它们使得程序的设计更加灵活、易于维护和扩展.

1. 封装

封装是指将数据和操作数据的方法封装在一个单独的单元中, 对外部隐藏数据的具体实现细节, 只暴露必要的接口供外部访问.

通过封装, 可以实现数据的隐藏和保护, 防止外部直接访问对象的内部数据, 同时还可以控制对数据的修改和操作, 确保数据的有效性和安全性.

封装的优势:

  • 提高代码的安全性和可靠性, 外部无法直接修改对象的内部数据, 只能通过定义的接口进行访问和操作.使得对象的数据对外部是不可见的, 提高了数据的安全性和私密性.
  • 封装可以将一组数据和操作封装成一个对象, 使得代码更加模块化和可复用.其他部分的代码可以通过调用对象的接口来使用其功能, 无需关心内部的具体实现.
  • 封装可以降低代码之间的耦合性. 对象之间通过接口进行通信, 而不是直接访问和修改其内部状态, 这使得代码更加灵活和易于维护.
python 复制代码
class Student:

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

    def introduce(self):
        print(f"大家好, 我的名字叫{self.name}, 今年{self.age}岁")


# 创建实例对象
stu = Student('张三', 18)
stu.name = '李四'
stu.age = 20


print(stu.name)
print(stu.age)

# 李四
# 20

# 这里的实例变量name和age都是公有属性, 类外可以直接访问和修改.

封装的实现

在 Python 中, 封装通过类来实现.将数据和操作封装在一个类中.类定义了对象的属性和方法, 属性表示对象的状态, 方法表示对象的行为.

在 Python 中, 封装并不是强制性的, 所有的属性和方法默认都是公开的, 外部可以直接访问和修改.但是, 可以通过命名约定来约束对属性和方法的访问.通常, 以下命名约定用于指定属性和方法的访问控制:

公有属性和方法: 默认情况下, 类中的属性和方法都是公有的, 它们的名字前都没有下划线, 这类型的属性和方法在类的外部, 内部, 子类中, 都是可以正常访问的.

私有属性和方法 : 以双下划线(__)开头的属性和方法表示为私有的, 外部不能直接访问.如果外部需要访问或修改这些属性, 应该通过公开的接口方法来实现.

受保护的属性和方法 : 以单下划线(_)开头的属性和方法表示为受保护的, 外部可以访问, 但是应该将其视为内部实现, 不建议直接访问.

python 复制代码
class Student:

    def __init__(self, name, age):
        self.__name = name # 私有属性
        self.age = age

    def introduce(self):
        print(f"大家好, 我的名字叫{self.__name}, 今年{self.age}岁")


# 创建实例对象
stu = Student('张三', 18)
stu.__name = '李四' # 理论上来说类外不能访问和设置私有属性
stu.age = 20


print(stu.__name)
print(stu.age)

# 李四
# 20

# 为什么这里在类外也可以访问和设置私有属性呢?

# 如果我们执行实例方法.introduce():
stu.introduce()
# 大家好, 我的名字叫张三, 今年20岁

# 我们发现类内的数据并没有改变
# 所以上面在类外设置私有变量值, 对类的内部并没有影响.

我们在类外使用和设置类内的私有属性需要通过类内的公有方法:

python 复制代码
class Student:

    def __init__(self, name, age):
        self.__name = name
        self.__age = age

    def introduce(self):
        print(f"大家好, 我的名字叫{self.__name}, 今年{self.__age}岁")

    def get_name(self):
        return self.__name

    def get_age(self):
        return self.__age

    def set_name(self, name):
        self.__name = name

    def set_age(self, age):
        self.__age = age


# 创建实例对象
stu = Student('张三', 18)

stu.set_name('李四')
stu.set_age(20)

name = stu.get_name()
age = stu.get_age()

print(name)
print(age)

私有属性的原理:

python 复制代码
class A:
  def __init__(self):
    self.__attr = 1
    self._attr = 2
    self.attr = 3
    
    
a = A()
print(a.__attr) # 报错
print(a._attr) # 2
print(a.attr) # 3

# Python当中私有的实现, 实际上就是变量名改了一个名字, 仅此而已

我们能够看到保护属性和公有属性在存储的时候都是原来的名字, 但是对于私有属性__attr在存储的时候名字变了, 在私有属性名字前使用了单下划线+类名作为前缀.

所以说如果我们在类中使用修改后的私有变量名, 是可以正常访问到私有变量的, 没错是这样的:

python 复制代码
class A:
    def __init__(self):
        self.__attr = 1
        self._attr = 2
        self.attr = 3


a = A()
print(a._A__attr) # 1
print(a._attr) # 2
print(a.attr) # 3

所以之前在类外设置私有属性的值, 其实访问的不是类内的私有属性, 而是类外自己设置的一个新的属性, 因为类内的私有属性名字已经变了.

2. 继承

继承是面向对象编程中的一种重要特性, 它允许一个类(子类)从另一个类(父类)继承属性和方法, 并且可以在此基础上进行扩展和修改.

通过继承, 子类可以复用父类的代码, 避免重复编写相同的功能, 同时可以在子类中添加新的属性和方法, 或者重写父类的方法, 从而实现代码的灵活和易于扩展.

优势

  1. 代码重用: 继承允许子类从父类继承属性和方法, 避免了重复编写相同的代码, 提高了代码的重用性.
  2. 代码组织和扩展: 继承可以使代码的组织结构更加清晰, 将通用的功能封装在父类中, 特定的功能在子类中实现, 便于代码的维护和扩展.
  3. 代码复杂性降低: 继承使得代码的层次结构更加清晰, 提高了代码的可读性和可维护性, 降低了代码的复杂性.

语法

在Python中, 继承使用class 子类名(父类名)的语法来实现, 子类拥有父类的所有属性和方法.子类可以直接使用父类的方法, 也可以在子类中添加新的方法或者重写父类的方法.(私有属性是不能继承过来的)

python 复制代码
class Person:

    # 类属性
    id_number = 123123

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

    def greet(self):
        print(f"你好, 我叫 {self.name}")

class Student(Person):
    """单继承"""
    def __init__(self, name, stu_id):
        super().__init__(name)
        self.name = name
        self.stu_id = stu_id

    def show_stu_id(self):
        print(f"姓名: {self.name}, 学号: {self.stu_id}")


stu = Student('张三', 123)
stu.show_stu_id()
stu.greet()
print(stu.id_number)

# 姓名: 张三, 学号: 123
# 你好, 我叫 张三
# 123123

多继承

注意: Python虽然支持多继承, 但是不建议使用, 只使用单继承即可.

在Python中, 一个子类可以继承多个父类, 这种继承方式称为多继承.多继承允许子类同时拥有多个父类的属性和方法.

方法重写

子类可以重写父类的方法, 即在子类中重新实现与父类同名的方法.这样做可以根据子类的需要修改或扩展方法的行为.

调用父类的成员

super: super() 用于调用父类的成员, 它可以在子类中调用父类的成员.

python 复制代码
class Singer:

    def sing(self):
        print("正在唱歌")

class Dancer:

    def dance(self):
        print("正在跳舞")

class SingerDancer(Singer, Dancer):
    """多继承"""
    pass

singer_dancer = SingerDancer()
singer_dancer.sing()
singer_dancer.dance()

print(SingerDancer.mro())
# [<class '__main__.SingerDancer'>, <class '__main__.Singer'>, <class '__main__.Dancer'>, <class 'object'>]

# MRO 确保了在多继承的情况下,方法的调用顺序是明确且一致的. 它避免了方法调用的歧义,并确保了 Python 的多继承机制能够正确工作.

3. 多态

多态就是多种状态, 即做某一个行为(同样的行为)的时候, 使用不同的对象会得到不同的状态.

在面向对象编程中, 多态是一种重要的概念, 它允许不同的对象对相同的方法做出不同的响应.简而言之, 多态使得可以使用统一的接口来调用不同类的对象, 而无需关心对象的具体类型, 从而实现更灵活和可扩展的代码设计.

在Python中, 多态性是由动态类型和动态绑定实现的.具体来说, Python是一种动态类型语言, 这意味着变量的类型在运行时根据赋值而确定, 而不是在编译时确定.因此, 同一个方法名可以在不同的类中实现, 并且可以根据对象的类型调用不同类的方法

python 复制代码
class Cat:

     def say(self):
         print("喵喵")


class Dog:

    def say(self):
        print("汪汪")

animal = Cat()
# animal = Dog()
animal.say()

4. 抽象基类

抽象基类(Abstract Base Classes, 简称ABC)是Python中的一个重要概念, 它允许我们定义抽象类和抽象方法, 从而可以规范子类的行为.

抽象基类并不能被实例化, 只能用于继承.子类必须实现抽象基类中定义的所有抽象方法.

Python中的抽象基类通过abc模块提供, 使用抽象基类可以达到以下几个目的:

  1. 规范子类行为: 抽象基类允许我们定义一组接口或方法, 子类必须实现这些方法, 从而规范了子类的行为.
  2. 约束方法命名: 通过抽象基类可以强制子类必须实现指定的方法, 这样可以避免方法名拼写错误或者忘记实现方法的问题.
  3. 多态性支持: 抽象基类实现了多态性, 可以在使用抽象基类的地方接受多种不同的子类对象, 从而实现更灵活的代码设计.
  4. 文档化接口: 抽象基类可以帮助文档化接口, 让开发者了解哪些方法必须实现, 哪些是可选的.
python 复制代码
from abc import ABCMeta, abstractmethod

class Animal(metaclass=ABCMeta): # ABCMeta 是一个元类,用于创建抽象基类
    """抽象基类"""
    """元类就是创建类的类"""
	# abstractmethod 是一个装饰器,用于定义抽象方法
	# 子类中必须实现这些抽象方法
    @abstractmethod
    def say(self):
    	# 在抽象基类中,当一个方法被定义为抽象方法时
    	# 通常会使用raise NotImplementedError来明确表示这个方法需要在子类中被具体实现
        raise NotImplementedError

class Cat(Animal):

     def say(self):
         print("喵喵")


class Dog(Animal):

    def say(self):
        print("汪汪")

animal = Cat()
animal.say()
python 复制代码
class Phone:# 抽象基类
  
  def call(self): # 抽象方法
    raise NotImplementedError
   
 	def music(self):
    raise NotImplementedError
    
  def wechat(self):
    raise NotImplementedError
    

class Apple(Phone):
  pass

class HuaWei(Phone):
  pass

5. 补充练习

python 复制代码
"""
设计一个图形类(Shape),包含计算面积的方法(calculate_area)。然后设计三个子类:圆类(Circle)、
矩形类(Rectangle)和三角形类(Triangle),分别实现计算面积的方法。要求实现以下功能:

分别创建圆、矩形和三角形的对象,并设置它们的相关属性(例如半径、长和宽、底和高等)。
分别调用这些对象的计算面积方法,并打印出计算结果。
"""
from math import pi


class Shape:

    def calculate_area(self):
        raise NotImplementedError


class Circle(Shape):

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

    def calculate_area(self):
        area = pi * self.radius ** 2
        return area


class Rectangle(Shape):

    def __init__(self, length, width):
        self.length = length
        self.width = width

    def calculate_area(self):
        area = self.length * self.width
        return area


class Triangle(Shape):

    def __init__(self, base, height):
        self.base = base
        self.height = height

    def calculate_area(self):
        area = self.base * self.height * 0.5
        return area


# shape = Circle(5)
# shape = Rectangle(5, 6)
shape = Triangle(5, 6)
_area = shape.calculate_area()
print(_area)

|----------------------|
| 遇见安然遇见你,不负代码不负卿。 |
| 谢谢老铁的时间,咱们下篇再见~ |

相关推荐
Asthenia04128 分钟前
链路追踪视角:MyBatis-Plus 如何基于 MyBatis 封装 BaseMapper
后端
Ai 编码助手15 分钟前
基于 Swoole 的高性能 RPC 解决方案
后端·rpc·swoole
翻滚吧键盘16 分钟前
spring打包,打包错误
java·后端·spring
夕颜11144 分钟前
记录一下关于 Cursor 设置的问题
后端
凉白开3381 小时前
Scala基础知识
开发语言·后端·scala
不要不开心了1 小时前
Scala内容
开发语言·pytorch·flask·scala·dash
2401_824256861 小时前
Scala的函数式编程
开发语言·后端·scala
RadNIkMan1 小时前
Python学习(二)操作列表
网络·python·学习
幻想趾于现实1 小时前
C# Winform 入门(2)之发送邮件
开发语言·c#
半盏茶香1 小时前
启幕数据结构算法雅航新章,穿梭C++梦幻领域的探索之旅——堆的应用之堆排、Top-K问题
java·开发语言·数据结构·c++·python·算法·链表