当前位置: 首页 > news >正文

【Python】面向对象(三)

目录

  • 面向对象
    • 抽象
    • 接口
    • 封装
    • 内部类
    • 匿名类和匿名对象

面向对象

抽象

什么是抽象(Abstraction)?

抽象是面向对象编程(OOP)的核心原则之一
简单来说:只暴露必要的信息,隐藏实现细节

举个现实例子:

  • 当你开车时,只需要知道方向盘、油门、刹车的使用方法。
  • 你不需要知道发动机内部是如何点火、活塞如何运转的。

这就是抽象:对外提供接口(操作方法),内部细节封装起来

Python 中的抽象类型

  1. 数据抽象(Data Abstraction)

    • 数据的内部表示对用户是隐藏的。
    • 用户通过数据结构(类、对象、方法)访问数据,但无需关心底层实现。

    例如:

    class BankAccount:def __init__(self, balance):self.__balance = balance  # 私有变量(外部无法直接访问)def deposit(self, amount):self.__balance += amountdef withdraw(self, amount):if amount <= self.__balance:self.__balance -= amountelse:print("余额不足")def get_balance(self):return self.__balanceaccount = BankAccount(1000)
    account.deposit(500)
    print(account.get_balance())  # ✅ 输出 1500
    print(account.__balance)      # ❌ AttributeError
    

    用户只知道有 depositwithdraw,但不知道余额是如何存储的。

  2. 过程抽象(Process Abstraction)

    • 隐藏具体的实现过程,只提供统一的接口。
    • 用户关心“做什么”,而不是“怎么做”。

    例如:

    def sort_numbers(nums):return sorted(nums)  # 内部用 Timsort 算法,但用户不需要知道print(sort_numbers([5, 2, 9, 1]))
    

    用户只知道 sort_numbers 会排序,但不用关心它内部用的是快速排序还是归并排序。

抽象类(Abstract Class)

在 Python 里:

  • 普通类可以直接实例化。
  • 抽象类不能直接实例化,只能作为基类(父类)存在。
  • 抽象类的目的是:强制子类实现某些方法

创建抽象类

Python 使用 abc 模块(Abstract Base Class):

from abc import ABC, abstractmethodclass Demo(ABC):   # 继承 ABC@abstractmethoddef method1(self):  # 抽象方法(必须在子类里实现)passdef method2(self):  # 普通方法(子类可直接用)print("concrete method")# obj = Demo()  # ❌ 报错:Can't instantiate abstract class

@abstractmethod:标记方法为抽象方法。

抽象方法的覆盖(Overriding)

子类必须实现父类的抽象方法,否则也不能实例化。

class DemoClass(ABC):@abstractmethoddef method1(self):passdef method2(self):print("concrete method")class ConcreteClass(DemoClass):def method1(self):   # ✅ 必须实现print("override method1")obj = ConcreteClass()
obj.method1()   # override method1
obj.method2()   # concrete method

抽象的意义

  1. 代码复用:抽象类可以有具体方法,子类可直接使用。
  2. 统一接口:强制子类实现某些方法,保证风格一致。
  3. 解耦:调用方只关心抽象接口,而不用关心实现细节。

抽象 vs 封装 vs 多态

  • 抽象(Abstraction):隐藏实现,只暴露接口(“做什么”)。
  • 封装(Encapsulation):把数据和方法打包在一起,并控制访问权限(“保护细节”)。
  • 多态(Polymorphism):不同对象对同一方法调用,表现出不同的行为(“不同实现”)。

区别总结:

  • 抽象:告诉你要做什么。
  • 封装:保护细节,防止被乱改。
  • 多态:不同对象的不同实现。

接口 vs 抽象类

在 Python 里没有“接口”关键字,但可以用抽象类来模拟接口。

  • 如果一个抽象类里全是抽象方法,它就相当于接口。
  • 如果一个抽象类里既有抽象方法又有具体方法,它就是抽象基类。

例如:

from abc import ABC, abstractmethodclass Drawable(ABC):  # 相当于接口@abstractmethoddef draw(self):passclass Circle(Drawable):def draw(self):print("画一个圆")class Rectangle(Drawable):def draw(self):print("画一个矩形")shapes = [Circle(), Rectangle()]
for s in shapes:s.draw()

这样保证所有图形类都有 draw() 方法。

在 Python 中,抽象通过 抽象类 + 抽象方法 实现,本质是“只告诉你能做什么,而不告诉你怎么做”,让代码更清晰、可维护、可扩展。

接口

什么是接口 (Interface)?

在面向对象编程(OOP)中,接口(Interface)是一种约束,它定义了一组方法,但不提供具体实现。
接口的核心思想:

  • “只规定要做什么,不规定怎么做”
  • 谁实现了接口,就必须提供接口里声明的方法的具体实现。

比如:

  • USB 接口规定了插口的形状、传输协议,但不关心具体是鼠标、键盘还是硬盘。
  • 在代码里,接口就是一种“契约”,确保类中必须实现某些方法。

在 Java、Go 等语言里有关键字 interface,但 Python 没有内置关键字 interface
Python 里通过 抽象基类(Abstract Base Class, 简称 ABC) 来实现接口。

接口与抽象类的区别

特点抽象类 (Abstract Class)接口 (Interface)
方法可以有抽象方法 + 普通方法只能有抽象方法
属性可以有属性、构造方法一般只定义方法,不定义属性
目的提供部分实现,供子类继承和扩展提供“契约”,强制实现方法
Python 实现使用 ABC + @abstractmethod同样使用 ABC,但接口里只写抽象方法

总结:

  • 抽象类是“半成品”(有一些实现)。
  • 接口是“纯规范”(只有约定,不提供实现)。

Python 接口的实现方式

  1. 正式接口(Formal Interface)

    使用 abc 模块中的 ABC@abstractmethod

    步骤:

    1. 定义一个继承自 ABC 的类,作为接口。
    2. 在其中定义抽象方法(使用 @abstractmethod 标记)。
    3. 任何实现接口的类,必须重写接口里的所有方法,否则无法实例化。

    示例:

    from abc import ABC, abstractmethod# 定义接口
    class DemoInterface(ABC):@abstractmethoddef method1(self):pass@abstractmethoddef method2(self):pass# 实现接口的类
    class ConcreteClass(DemoInterface):def method1(self):print("实现了 method1")def method2(self):print("实现了 method2")# 创建对象
    obj = ConcreteClass()
    obj.method1()
    obj.method2()
    

    输出:

    实现了 method1
    实现了 method2
    

    特点:如果 ConcreteClass 没有实现 method1method2,会报错:

    TypeError: Can't instantiate abstract class ConcreteClass with abstract method methodX
    
  2. 非正式接口(Informal Interface)

    Python 是动态语言,支持 鸭子类型(Duck Typing)
    “如果它走路像鸭子,叫声像鸭子,那它就是鸭子”。

    非正式接口就是:

    • 定义一个普通类,里面写一些需要子类实现的方法(可以是空的 pass)。
    • 子类只要写了相应的方法,就相当于“实现”了接口。
    • Python 不会强制检查,只要方法存在就能调用。

    示例:

    # 非正式接口
    class DemoInterface:def displayMsg(self):pass# 实现接口
    class NewClass(DemoInterface):def displayMsg(self):print("这是我的消息")obj = NewClass()
    obj.displayMsg()
    

    输出:

    这是我的消息
    

    特点:

    • 灵活但不严谨:Python 不会强制检查子类是否实现了接口里的方法。
    • 优点:简单、快速,符合 Python “鸭子类型”哲学。
    • 缺点:容易遗漏实现,运行时才会报错。

Python 接口的规则

  1. 接口类不能被实例化

    obj = DemoInterface()  # 会报错
    
  2. 实现接口的类必须实现接口的所有方法(Formal Interface)。
    否则该类本身也必须是抽象类,不能实例化。

  3. 接口类中的方法必须是抽象方法

    • 抽象类允许有普通方法,但接口一般不允许。
  4. 非正式接口 不会强制检查实现,依赖程序员自觉。

接口的应用场景

  1. 插件系统
    例如:音频播放器插件接口,所有插件必须实现 play()stop() 方法。
  2. 统一规范
    比如写一个支付系统,定义一个 PaymentInterface
    • pay(amount)
    • refund(transaction_id)
      微信支付、支付宝支付、银行卡支付都必须实现这两个方法。
  3. 解耦合
    使用接口编程,可以替换底层实现,而不影响调用代码。

多接口实现(多继承)

Python 支持 多继承,所以一个类可以实现多个接口。

from abc import ABC, abstractmethodclass Interface1(ABC):@abstractmethoddef methodA(self):passclass Interface2(ABC):@abstractmethoddef methodB(self):passclass ConcreteClass(Interface1, Interface2):def methodA(self):print("实现了方法 A")def methodB(self):print("实现了方法 B")obj = ConcreteClass()
obj.methodA()
obj.methodB()

输出:

实现了方法 A
实现了方法 B

封装

什么是封装(Encapsulation)?

封装是面向对象编程(OOP)的三大支柱之一(封装、继承、多态)。
它的核心思想是:

  • 把属性(数据)和方法(操作数据的逻辑)绑定在一个单元(类)中
  • 同时 限制外部直接访问对象的内部数据,而是通过类的方法来操作数据。

类比:
你去银行存钱时,并不会直接操作数据库(余额表),而是通过“柜员/ATM”的接口(方法)来完成存取。
这样做的好处:安全、可控,避免外部随意改数据。

Python 封装 vs C++/Java 封装

C++/Java 中,类成员可以通过 访问修饰符 来控制访问权限:

  • public:公有,任何地方都能访问。
  • protected:受保护,只能在类和子类中访问。
  • private:私有,只能在类内部访问。

但是 Python 没有真正的访问修饰符关键字
在 Python 里:

  • 默认所有变量和方法都是 公有的(public)。
  • 通过 命名约定(前缀 ___)来模拟访问控制。

Python 的封装实现

  1. 公有成员(Public Members)

    默认情况下,类里的变量和方法都是公有的,可以直接访问:

    class Student:def __init__(self, name="Rajaram", marks=50):self.name = name      # 公有属性self.marks = marks    # 公有属性s1 = Student()
    s2 = Student("Bharat", 25)print(s1.name, s1.marks)  # ✅ 可直接访问
    print(s2.name, s2.marks)
    

    输出:

    Rajaram 50
    Bharat 25
    

    缺点:外部可以随便修改 marks,破坏了封装性。

  2. 受保护成员(Protected Members)

    在 Python 中,约定俗成

    • 属性或方法名前加一个下划线 _,表示“受保护成员”。
    • 外部仍然可以访问,但程序员约定上应该把它当作“半私有”,不要随意访问。
    class Student:def __init__(self, name="Rajaram", marks=50):self._name = name       # 受保护属性self._marks = marks     # 受保护属性s = Student()
    print(s._name)   # ✅ 能访问,但不推荐
    
  3. 私有成员(Private Members)

    在 Python 中:

    • 属性或方法名前加两个下划线 __,表示“私有成员”。
    • Python 会进行 名称重整(Name Mangling),使得外部不能直接访问。
    class Student:def __init__(self, name="Rajaram", marks=50):self.__name = name      # 私有属性self.__marks = marks    # 私有属性def studentdata(self):print(f"Name: {self.__name}, Marks: {self.__marks}")s1 = Student()
    s1.studentdata()   # ✅ 内部方法可以访问print(s1.__name)   # ❌ AttributeError: 'Student' object has no attribute '__name'
    

    外部无法直接访问 __name__marks,实现了数据隐藏。

Name Mangling(名字改编)

虽然 Python 把 __变量名 处理成私有的,但其实它只是改了名字。

  • 内部会变成 _类名__变量名
  • 所以理论上你依然可以访问,但不推荐这么做。
print(s1._Student__name)   # ✅ Rajaram
print(s1._Student__marks)  # ✅ 50

Python 的理念:信任程序员,不做强制限制,只靠约定。
不像 Java 那样“硬性禁止”,Python 更灵活,但也要求程序员自律。

方法的封装

不仅属性可以封装,方法也可以封装。

  • 公有方法(默认):对外暴露接口。
  • 受保护方法:约定只在类和子类中使用。
  • 私有方法:只能在类内部调用。
class Student:def __init__(self, name):self.__name = namedef display(self):       # 公有方法self.__show_name()   # 内部调用私有方法def _protected_method(self):print("受保护的方法")def __show_name(self):   # 私有方法print(f"Name: {self.__name}")s = Student("Alice")
s.display()                # ✅ Name: Alice
s._protected_method()      # ⚠️ 可访问,但不推荐
s.__show_name()            # ❌ AttributeError

封装的意义

  1. 数据安全:防止外部随意篡改对象的属性。
  2. 抽象接口:对外只暴露必要的方法(API),内部逻辑隐藏。
  3. 易维护:修改内部实现时,外部调用方式不受影响。
  4. 模块化:把数据和操作封装在一起,更符合 OOP 思想。

封装 + Getter/Setter

在 Python 中,如果你想控制属性的读取/修改,可以使用 属性方法(property)

class Student:def __init__(self, name, marks):self.__name = nameself.__marks = marks@propertydef name(self):  # getterreturn self.__name@name.setterdef name(self, value):  # setterif len(value) > 0:self.__name = valueelse:print("名字不能为空")s = Student("Tom", 90)
print(s.name)     # ✅ Tom
s.name = "Jerry"  # ✅ 修改
print(s.name)     # ✅ Jerry
s.name = ""       # ❌ 名字不能为空

@property 可以把方法当成属性调用,实现优雅的封装。

Python 的封装并不像 Java/C++ 那样“硬性约束”,而是通过 命名约定 + name mangling 来实现,更灵活、更“成人化”,它相信程序员不会乱来。

内部类

什么是 Inner Class(内部类)

在 Python 中,一个类定义在另一个类里面,就叫 内部类(Inner Class),也叫 嵌套类(Nested Class)

特点:

  • 内部类的对象通常作为外部类的属性存在。
  • 内部类拥有外部类的作用域,可以更方便地表示“某些类与外部类紧密相关”。
  • 从组织结构上,内部类让代码更清晰(属于某个整体的类被放在一起)。

基本语法

class Outer:def __init__(self):passclass Inner:def __init__(self):pass

这里:

  • Outer 是外部类(Outer Class)。
  • Inner 是定义在外部类里的内部类(Inner Class)。

示例

class Student:def __init__(self):self.name = "Ashish"self.subs = self.Subjects()  # 内部类的实例,作为外部类的属性def show(self):print("Name:", self.name)self.subs.display()# 定义内部类class Subjects:def __init__(self):self.sub1 = "Physics"self.sub2 = "Chemistry"def display(self):print("Subjects:", self.sub1, self.sub2)s1 = Student()
s1.show()

输出:

Name: Ashish
Subjects: Physics Chemistry

这里 Subjects 内部类 变成了 Student 的一个属性,所以 s1.show() 调用时,也能顺便访问 Subjects 的内容。

除了通过外部类属性访问,你也可以 独立创建内部类对象

Student().Subjects().display()

输出:

Subjects: Physics Chemistry

注意:这种方式等价于 Outer.Inner(),语法是 外部类名.内部类名()

为什么要用内部类?

  1. 逻辑分组
    内部类表示“从属于外部类的结构”,例如:学生(外部类)和科目(内部类)。
  2. 作用域隔离
    内部类的作用范围被限制在外部类中,不会轻易被外部其他代码引用。
  3. 结构清晰
    比如在组织(Organization)里定义部门(Department),部门里再定义小组(Team),结构一目了然。

内部类的类型

  1. Multiple Inner Class(多个内部类)

    一个外部类里可以有多个互不干扰的内部类。

    class Organization:def __init__(self):self.dep1 = self.Department1()self.dep2 = self.Department2()def showName(self):print("Organization Name: Tutorials Point")class Department1:def display(self):print("In Department 1")class Department2:def display(self):print("In Department 2")org = Organization()
    org.showName()
    org.dep1.display()
    org.dep2.display()
    

    输出:

    Organization Name: Tutorials Point
    In Department 1
    In Department 2
    

    外部类 Organization 相当于“容器”,内部类 Department1Department2 表示不同的部门。

  2. Multilevel Inner Class(多级嵌套类)

    内部类里还可以再定义内部类 → 多层嵌套

    class Organization:def __init__(self):self.dep = self.Department()def showName(self):print("Organization Name: Tutorials Point")class Department:def __init__(self):self.team = self.Team1()def displayDep(self):print("In Department")class Team1:def displayTeam(self):print("Team 1 of the department")org = Organization()
    org.showName()
    org.dep.displayDep()
    org.dep.team.displayTeam()
    

    输出:

    Organization Name: Tutorials Point
    In Department
    Team 1 of the department
    

    层级结构:

    • Organization(组织)
      • Department(部门)
        • Team1(小组)

    非常适合描述 树形/层级关系 的场景。

内部类 vs 普通类

特性内部类普通类
定义位置外部类内部顶层
作用域外部类作用域内全局
访问方式Outer.Inner()直接用类名
用途表示逻辑上隶属于外部类的结构独立存在

注意事项

  1. 内部类并不会自动继承外部类,只是写在里面,属于“包含”关系,而不是“继承”。
  2. 如果要访问外部类的属性,需要通过外部类的实例引用。
  3. 内部类也可以单独实例化,不一定非要依附外部类。

匿名类和匿名对象

type() 函数

在 Python 里,一切皆对象。类本身也是对象,类的“类”就是 type

class MyClass:def __init__(self):self.myvar = 10obj = MyClass()print("class of int:", type(int))
print("class of list:", type(list))
print("class of dict:", type(dict))
print("class of MyClass:", type(MyClass))
print("class of obj:", type(obj))

输出:

class of int <class 'type'>
class of list <class 'type'>
class of dict <class 'type'>
class of MyClass <class 'type'>
class of obj <class '__main__.MyClass'>

说明:

  • 内置类(intlistdict)和用户自定义类(MyClass)的类型都是 type
  • objMyClass 的实例,所以 type(obj) 显示 <class '__main__.MyClass'>

type() 的三参数形式

type() 除了能查看对象类型,还能 动态创建类

语法:

newclass = type(name, bases, dict)

参数解释:

  1. name:新类的名字(字符串),会成为类的 __name__ 属性。
  2. bases:父类的元组(可空,默认继承 object)。
  3. dict:类的命名空间字典(属性、方法)。

示例:动态创建类

MyDynamicClass = type("MyDynamicClass",   # 类名(object,),          # 父类{"x": 10, "show": lambda self: print("x =", self.x)}  # 属性 & 方法
)obj = MyDynamicClass()
obj.show()

输出:

x = 10

这就相当于写了:

class MyDynamicClass(object):x = 10def show(self):print("x =", self.x)

匿名类(Anonymous Class)

所谓“匿名类”,就是用 type() 创建时不给类起名字。

anon = type('', (object,), {})
  • '' → 类名为空字符串(匿名)。
  • (object,) → 继承 object
  • {} → 目前没有属性或方法。

匿名对象(Anonymous Object)

创建匿名类的实例:

anon = type('', (object,), {})
obj = anon()
print("type of obj:", type(obj))

输出:

type of obj: <class '__main__.'>

可以看到类名是空的 → 匿名类,obj 就是匿名对象。

匿名类 + 动态属性 & 方法

匿名类强大之处在于 可以直接定义属性和方法,不用写 class 关键字。

def getA(self):return self.aobj = type('',                # 匿名(object,),         # 继承 object{'a': 5, 'b': 6, 'c': 7,'getA': getA,                          # 普通函数'getB': lambda self: self.b            # lambda 匿名函数}
)()print(obj.getA(), obj.getB())

输出:

5 6

等价于:

class _:a = 5b = 6c = 7def getA(self): return self.adef getB(self): return self.bobj = _()

为什么用匿名类?

  1. 快速定义小类,避免写一大堆 class 模板。
  2. 动态生成类,适合框架/库开发(如 ORM、元类编程、动态代理)。
  3. 数据临时封装,像结构体一样快速组合数据和方法。

匿名类 vs 普通类

特性匿名类普通类
定义方式type('', bases, dict)class MyClass: ...
是否有名字没有(__name__=''有名字
适用场景临时快速用长期维护、清晰结构
可读性差(别人不容易理解)好(常规方式)
http://www.dtcms.com/a/395314.html

相关文章:

  • 05-django项目的跨域处理
  • go语言并发
  • Qt QSS 美化完整教程文档
  • jwt与token+redis,哪种方案更好用?
  • 基于react的前端项目开发和实战总结(umi框架)
  • 【iOS】YYModel
  • 修改docker配置使其支持本机tcp连接
  • ReportFragment:Android 生命周期的桥梁与兼容性解决方案
  • 力扣Hot100--234.回文链表
  • 视觉语言大模型(VLM)的产业落地:从Qwen-VL技术解析到医疗、车险行业革新
  • 零基础新手小白快速了解掌握服务集群与自动化运维(七)Nginx模块--Nginx Web服务
  • 一个硬盘选MBR 还是GPT
  • 【含文档+PPT+源码】基于GPT+SpringBoot的个人健康管理与咨询系统设计与实现
  • 【项目实战 Day5】springboot + vue 苍穹外卖系统(Redis + 店铺经营状态模块 完结)
  • 旧衣回收小程序:非技术视角下的价值重构与发展前景
  • 使用vue-i18n实现语言切换
  • 做小程序找哪家公司,解析小程序开发定制公司哪家适合你
  • 【python】python进阶——math模块
  • NHD-6108 全自动远、近光检测仪:智能高效的汽车灯光检测方案
  • 《 Linux 点滴漫谈: 一 》开源之路:Linux 的历史、演进与未来趋势
  • C#和微软System.Speech.Synthesis库实现语音合成
  • C++概述 (一)
  • 【开题答辩全过程】以 基于springboot的高校仪器共享管理系统设计和实现为例,包含答辩的问题和答案
  • 【python】FastAPI简介
  • IDEA lombok注解无效的问题,运行时提示java: 找不到符号或者方法
  • Windows 系统部署 Kronos 金融 K 线基础模型——基于 EPGF 架构
  • 010 Rust流程控制
  • MyBatisPlus快速入门:简化CRUD操作
  • 网络编程套接字(三)---简单的TCP网络程序
  • 背景建模(基于视频,超炫)项目实战!