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

Python的那些事第十篇:隐藏细节与提供接口的艺术Python中的封装

Python中的封装:让代码穿上“隐身衣”

 


 

目录

一、封装的概念:给代码穿上“隐身衣”

二、隐藏内部实现细节:让代码“隐身”

三、私有属性和私有方法:代码的“小秘密”

四、使用双下划线__定义私有属性和方法:隐藏的魔法

五、提供公共接口:代码的“门面”

六、使用公共方法访问私有属性:getter和setter方法

七、完整案例:封装的“魔法世界”

八、封装的好处:代码的“保护罩”

九、封装的“魔法咒语”:如何实现封装

十、封装的“魔法世界”:一个完整的封装案例

十一、封装的“魔法咒语”:总结

十二、总结


一、封装的概念:给代码穿上“隐身衣”

在编程的世界里,代码就像是一群调皮的小精灵,它们有时候会乱跑乱跳,导致程序变得一团糟。而封装(Encapsulation)就像是给这些小精灵穿上了一件神奇的“隐身衣”,让它们在需要的时候才露面,其他时候都乖乖地躲在幕后。封装是面向对象编程(OOP)的核心理念之一,它把数据(属性)和操作数据的代码(方法)绑在一起,同时限制对某些细节的访问。

封装的主要特点

特点描述
数据隐藏就像是把代码的小秘密藏起来,不让别人轻易看到。这样可以保护数据的安全性和一致性,防止被乱七八糟的外部操作搞砸。
提供接口就像是给代码设置了一个“门面”,外部只需要通过这个门面来和代码互动,而不需要知道里面到底发生了什么。这样简化了外部对代码的使用,也让代码更加模块化。
保护对象完整性就像是给代码穿上了一层“保护罩”,防止外部的非法操作。这样可以确保对象的状态始终是有效的,不会因为外部的胡乱操作而变得混乱。

想象一下,你有一个超级宝贝的玩具,你不想让别人随便碰它,因为你担心他们会弄坏它。封装就像是你给这个玩具上了一把锁,只有你知道怎么打开它,别人只能通过你提供的“钥匙”(接口)来和它互动。这样,你的玩具就安全了,而别人也能在不弄坏它的情况下享受它的乐趣。


二、隐藏内部实现细节:让代码“隐身”

封装的核心在于隐藏内部实现细节,这就好像是给代码施了一个“隐身咒”,让外部看不到代码的内部结构,只能通过公开的接口和它互动。这种做法的好处是,外部代码不需要了解对象的内部实现,只需要知道怎么通过接口和它交流。

示例:银行账户的“隐身术”

假设我们有一个BankAccount类,它管理银行账户的余额和交易。我们希望隐藏内部的余额计算逻辑,只提供存款和取款的接口。这就像是你有一个神秘的银行账户,别人只能通过你允许的方式(比如存钱和取钱)来操作,而不能直接看到你的余额是多少。

class BankAccount:
    def __init__(self, balance=0):
        self.__balance = balance  # 内部隐藏的余额属性

    def deposit(self, amount):
        if amount > 0:
            self.__balance += amount
            print(f"Deposited {amount}. New balance: {self.__balance}")
        else:
            print("Deposit amount must be positive")

    def withdraw(self, amount):
        if 0 < amount <= self.__balance:
            self.__balance -= amount
            print(f"Withdrew {amount}. New balance: {self.__balance}")
        else:
            print("Invalid withdraw amount")

在这个例子中,__balance是私有属性,外部无法直接访问,只能通过depositwithdraw方法操作。这就像是银行账户的余额被藏起来了,只有通过银行允许的操作(存钱和取钱)才能改变余额。


三、私有属性和私有方法:代码的“小秘密”

私有属性和私有方法是封装的重要组成部分,它们就像是代码的“小秘密”,只有类的内部知道。私有属性和私有方法以双下划线__开头,表示这些成员只能在类的内部访问。

私有属性:隐藏的宝藏

私有属性就像是藏在宝箱里的宝藏,只有拥有宝箱钥匙的人(也就是类的内部)才能看到。这样可以保护数据的完整性和安全性,防止外部的非法操作。

私有方法:隐藏的魔法

私有方法就像是隐藏的魔法,只有在类的内部才能施展。它们通常用于实现内部逻辑,外部不需要知道这些魔法的存在。

示例:神秘的“Person”类

假设我们有一个Person类,它管理一个人的名字和年龄。我们希望隐藏内部的名字和年龄信息,只通过公开的方法来访问和修改。

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

    def __greet(self):  # 私有方法
        return f"Hello, my name is {self.__name} and I am {self.__age} years old."

    def introduce(self):
        print(self.__greet())  # 内部调用私有方法

在这个例子中,__name__age是私有属性,__greet是私有方法。它们只能在类内部使用,外部无法直接访问。这就像是一个人的内心世界,只有他自己知道,别人只能通过他的言行来了解他。


四、使用双下划线__定义私有属性和方法:隐藏的魔法

在Python中,双下划线__前缀用于定义私有属性和方法。这种命名方式会触发名称重整(name mangling),使得外部无法直接访问这些成员。这就像是给代码施了一个“隐身咒”,让外部看不到它们的存在。

名称重整:魔法的名字变形

Python会将私有属性和方法的名称重整为_ClassName__attribute的形式。虽然可以通过这种方式访问私有成员,但这违背了封装的设计初衷。这就像是你发现了一个隐藏的宝藏,但宝藏的主人并不希望你找到它,而你强行找到了它,这可能会带来一些不必要的麻烦。

示例:神秘的“隐身术”

假设我们有一个MyClass类,它有一个私有属性和一个私有方法。我们来看看外部如何尝试访问它们,以及Python如何阻止这种行为。

class MyClass:
    def __init__(self, value):
        self.__private_value = value  # 私有属性

    def __private_method(self):  # 私有方法
        return f"This is a private method with value {self.__private_value}"

    def public_method(self):
        return self.__private_method()  # 内部调用私有方法

obj = MyClass(42)
print(obj.public_method())  # 正常调用
# print(obj.__private_value)  # 报错,无法直接访问
# print(obj.__private_method())  # 报错,无法直接访问

在这个例子中,__private_value__private_method是私有的,外部无法直接访问。这就像是你有一个秘密花园,只有你知道怎么进入,别人无法找到它的入口。


五、提供公共接口:代码的“门面”

封装的另一个重要方面是提供公共接口。这就像是给代码设置了一个“门面”,外部可以通过这个门面和代码互动,而不需要知道里面到底发生了什么。通过定义公有方法,外部可以在不直接操作内部数据的情况下,安全地对对象进行操作。

公有方法:代码的“门面”

公有方法是类的外部可以访问的方法。它们通常用于实现对私有属性的访问和修改。这就像是你家的大门,别人可以通过大门进入你的家,但不能直接进入你的卧室。

示例:汽车的“控制面板”

假设我们有一个Car类,它管理汽车的品牌和型号。我们希望隐藏内部的品牌和型号信息,只通过公开的方法来访问和修改。

class Car:
    def __init__(self, make, model):
        self.__make = make  # 私有属性
        self.__model = model  # 私有属性

    def get_make(self):  # 公有方法
        return self.__make

    def set_make(self, make):  # 公有方法
        self.__make = make

    def get_model(self):  # 公有方法
        return self.__model

    def set_model(self, model):  # 公有方法
        self.__model = model

    def display_info(self):  # 公有方法
        print(f"Car: {self.__make} {self.__model}")

# 创建一个汽车对象
my_car = Car("Toyota", "Corolla")
my_car.display_info()  # 输出:Car: Toyota Corolla

# 修改品牌
my_car.set_make("Honda")
my_car.display_info()  # 输出:Car: Honda Corolla

在这个例子中,__make__model是私有属性,外部无法直接访问,只能通过get_makeset_makeget_modelset_model等公有方法来操作。这就像是汽车的控制面板,你可以通过控制面板来操作汽车的各种功能,但不需要知道汽车内部的机械结构。


六、使用公共方法访问私有属性:getter和setter方法

在封装中,公共方法通常用于访问和修改私有属性。这些方法被称为getter和setter方法。getter方法用于获取私有属性的值,而setter方法用于设置私有属性的值。

Getter方法:获取私有属性的值

Getter方法就像是一个“窥视孔”,让你可以从外部看到私有属性的值,但不能直接修改它。

Setter方法:设置私有属性的值

Setter方法就像是一个“小窗口”,让你可以从外部设置私有属性的值,但需要通过这个窗口来进行操作。

示例:银行账户的“窥视孔”和“小窗口”

假设我们有一个BankAccount类,它管理银行账户的余额。我们希望隐藏内部的余额信息,只通过getter和setter方法来访问和修改。

class BankAccount:
    def __init__(self, balance=0):
        self.__balance = balance  # 私有属性

    def get_balance(self):  # Getter方法
        return self.__balance

    def set_balance(self, balance):  # Setter方法
        if balance >= 0:
            self.__balance = balance
        else:
            print("Balance cannot be negative")

    def deposit(self, amount):
        if amount > 0:
            self.__balance += amount
            print(f"Deposited {amount}. New balance: {self.__balance}")
        else:
            print("Deposit amount must be positive")

    def withdraw(self, amount):
        if 0 < amount <= self.__balance:
            self.__balance -= amount
            print(f"Withdrew {amount}. New balance: {self.__balance}")
        else:
            print("Invalid withdraw amount")

# 创建一个银行账户对象
my_account = BankAccount(100)
print(my_account.get_balance())  # 输出:100

# 存款
my_account.deposit(50)
print(my_account.get_balance())  # 输出:150

# 取款
my_account.withdraw(30)
print(my_account.get_balance())  # 输出:120

# 直接设置余额
my_account.set_balance(200)
print(my_account.get_balance())  # 输出:200

在这个例子中,__balance是私有属性,外部无法直接访问,只能通过get_balanceset_balance方法来操作。这就像是银行账户的余额被藏起来了,只有通过银行允许的操作(存钱、取钱和设置余额)才能改变余额。


七、完整案例:封装的“魔法世界”

现在,让我们用一个完整的案例来展示封装的“魔法世界”。假设我们有一个MagicBox类,它管理一个神秘的魔法盒子。这个盒子有一个隐藏的宝藏(私有属性),只有通过特定的魔法咒语(公有方法)才能打开和查看。

class MagicBox:
    def __init__(self, treasure):
        self.__treasure = treasure  # 私有属性,隐藏的宝藏

    def open_box(self):  # 公有方法,打开盒子
        print("The magic box is opening...")
        return self.__treasure

    def put_treasure(self, treasure):  # 公有方法,放入宝藏
        if treasure:
            print("Putting treasure into the magic box...")
            self.__treasure = treasure
        else:
            print("You must put something into the box!")

    def take_treasure(self):  # 公有方法,取出宝藏
        if self.__treasure:
            print("Taking treasure out of the magic box...")
            taken_treasure = self.__treasure
            self.__treasure = None  # 清空盒子
            return taken_treasure
        else:
            print("The magic box is empty!")

# 创建一个魔法盒子
magic_box = MagicBox("Golden Coin")

# 打开盒子查看宝藏
print(magic_box.open_box())  # 输出:The magic box is opening... Golden Coin

# 放入新的宝藏
magic_box.put_treasure("Magic Wand")
print(magic_box.open_box())  # 输出:The magic box is opening... Magic Wand

# 取出宝藏
print(magic_box.take_treasure())  # 输出:Taking treasure out of the magic box... Magic Wand
print(magic_box.open_box())  # 输出:The magic box is opening... None

在这个例子中,__treasure是私有属性,外部无法直接访问,只能通过open_boxput_treasuretake_treasure等公有方法来操作。这就像是一个神秘的魔法盒子,只有通过特定的魔法咒语才能打开和查看里面的宝藏。


八、封装的好处:代码的“保护罩”

封装不仅仅是隐藏代码的内部实现,它还带来了许多好处。这些好处就像是给代码穿上了一层“保护罩”,让代码更加安全、可靠和易于维护。

1. 数据安全和一致性

通过封装,我们可以隐藏内部数据,防止外部的非法操作。这就像是给代码的宝藏上了一把锁,只有通过允许的方式才能访问和修改数据,从而确保数据的安全性和一致性。

2. 代码的可维护性

封装让代码更加模块化,每个类都封装了自己的功能,外部只需要通过接口和它互动。这就像是把代码分成了一个个小盒子,每个盒子都有自己的功能,当需要修改或扩展功能时,只需要打开对应的盒子,而不会影响到其他盒子。

3. 代码的可扩展性

封装让代码更加灵活,可以轻松地扩展功能。这就像是给代码的盒子留了一个“扩展接口”,当需要添加新的功能时,只需要在接口上进行扩展,而不需要重新设计整个盒子。

4. 代码的可读性

封装让代码更加清晰,每个类都有明确的职责,通过接口和外部互动。这就像是给代码写了一本“说明书”,让其他开发者更容易理解和使用代码。


九、封装的“魔法咒语”:如何实现封装

实现封装其实并不难,只需要记住几个简单的“魔法咒语”:

  1. 使用双下划线__定义私有属性和方法:这是封装的核心,通过双下划线__,我们可以隐藏代码的内部实现,让外部无法直接访问。

  2. 提供公共接口(公有方法):通过定义公有方法,我们可以让外部通过接口和代码互动,而不需要知道内部的实现细节。

  3. 使用getter和setter方法访问私有属性:通过getter和setter方法,我们可以安全地访问和修改私有属性,同时可以添加一些额外的逻辑来保护数据的完整性。

示例:封装的“魔法咒语”

假设我们有一个Student类,它管理学生的姓名和成绩。我们希望隐藏内部的姓名和成绩信息,只通过公开的方法来访问和修改。

class Student:
    def __init__(self, name, score):
        self.__name = name  # 私有属性
        self.__score = score  # 私有属性

    def get_name(self):  # Getter方法
        return self.__name

    def set_name(self, name):  # Setter方法
        self.__name = name

    def get_score(self):  # Getter方法
        return self.__score

    def set_score(self, score):  # Setter方法
        if 0 <= score <= 100:
            self.__score = score
        else:
            print("Score must be between 0 and 100")

    def display_info(self):  # 公有方法
        print(f"Student: {self.__name}, Score: {self.__score}")

# 创建一个学生对象
student = Student("Alice", 85)
student.display_info()  # 输出:Student: Alice, Score: 85

# 修改姓名
student.set_name("Bob")
student.display_info()  # 输出:Student: Bob, Score: 85

# 修改成绩
student.set_score(90)
student.display_info()  # 输出:Student: Bob, Score: 90

# 尝试设置非法成绩
student.set_score(105)  # 输出:Score must be between 0 and 100

在这个例子中,__name__score是私有属性,外部无法直接访问,只能通过get_nameset_nameget_scoreset_score等公有方法来操作。这就像是给学生的姓名和成绩穿上了“隐身衣”,只有通过允许的方式才能访问和修改它们。

十、封装的“魔法世界”:一个完整的封装案例

现在,让我们用一个完整的案例来展示封装的“魔法世界”。假设我们有一个Library类,它管理一个图书馆的书籍。这个图书馆有一个隐藏的书籍列表(私有属性),只有通过特定的操作(公有方法)才能查看和借阅书籍。

class Library:
    def __init__(self):
        self.__books = ["Harry Potter", "The Hobbit", "1984"]  # 私有属性,隐藏的书籍列表

    def add_book(self, book):  # 公有方法,添加书籍
        if book:
            print(f"Adding book: {book}")
            self.__books.append(book)
        else:
            print("You must add a valid book!")

    def remove_book(self, book):  # 公有方法,移除书籍
        if book in self.__books:
            print(f"Removing book: {book}")
            self.__books.remove(book)
        else:
            print(f"Book '{book}' not found in the library")

    def borrow_book(self, book):  # 公有方法,借阅书籍
        if book in self.__books:
            print(f"Borrowing book: {book}")
            self.__books.remove(book)
        else:
            print(f"Book '{book}' not available")

    def return_book(self, book):  # 公有方法,归还书籍
        if book:
            print(f"Returning book: {book}")
            self.__books.append(book)
        else:
            print("You must return a valid book")

    def display_books(self):  # 公有方法,显示所有书籍
        print("Available books in the library:")
        for book in self.__books:
            print(book)

# 创建一个图书馆对象
library = Library()

# 显示所有书籍
library.display_books()
# 输出:
# Available books in the library:
# Harry Potter
# The Hobbit
# 1984

# 添加书籍
library.add_book("The Great Gatsby")
library.display_books()
# 输出:
# Available books in the library:
# Harry Potter
# The Hobbit
# 1984
# The Great Gatsby

# 借阅书籍
library.borrow_book("The Hobbit")
library.display_books()
# 输出:
# Available books in the library:
# Harry Potter
# 1984
# The Great Gatsby

# 归还书籍
library.return_book("The Hobbit")
library.display_books()
# 输出:
# Available books in the library:
# Harry Potter
# The Hobbit
# 1984
# The Great Gatsby

# 尝试移除不存在的书籍
library.remove_book("The Catcher in the Rye")
# 输出:Book 'The Catcher in the Rye' not found in the library

在这个例子中,__books是私有属性,外部无法直接访问,只能通过add_bookremove_bookborrow_bookreturn_bookdisplay_books等公有方法来操作。这就像是一个神秘的图书馆,只有通过允许的方式才能查看和借阅书籍。


十一、封装的“魔法咒语”:总结

封装是面向对象编程的核心理念之一,它通过隐藏内部实现细节、提供公共接口和使用私有属性和方法,让代码更加安全、可靠和易于维护。通过封装,我们可以把代码的“小秘密”藏起来,只通过允许的方式和外部互动,从而保护代码的完整性和安全性。

封装的“魔法咒语”

  1. 隐藏内部实现细节:通过双下划线__定义私有属性和方法,隐藏代码的内部实现。

  2. 提供公共接口:通过定义公有方法,让外部可以通过接口和代码互动。

  3. 使用getter和setter方法:通过getter和setter方法访问和修改私有属性,保护数据的完整性。

封装的好处

  1. 数据安全和一致性:隐藏内部数据,防止外部的非法操作。

  2. 代码的可维护性:代码更加模块化,易于维护和扩展。

  3. 代码的可扩展性:代码更加灵活,可以轻松地扩展功能。

  4. 代码的可读性:代码更加清晰,易于理解和使用。

通过封装,我们可以让代码变得更加优雅和高效,就像给代码穿上了一层“隐身衣”,让它在编程的世界里更加安全和自由地奔跑。


十二、总结

封装就像是给代码穿上了一件“隐身衣”,让它的“小秘密”藏起来,只在需要的时候才露面。通过封装,我们可以让代码变得更加安全、可靠和易于维护,就像给代码的宝藏上了一把锁,只有通过允许的方式才能打开。



http://www.kler.cn/a/531057.html

相关文章:

  • nodejs:js-mdict 的下载、安装、测试、build
  • 成绩案例demo
  • 【最长上升子序列Ⅱ——树状数组,二分+DP,纯DP】
  • 微信登录模块封装
  • 牛客网 除2!(详解)c++
  • Java篇之继承
  • Leetcode—598. 区间加法 II【简单】
  • golang命令大全7--性能优化与分析
  • Vue - readonly 与 shallowReadonly
  • 模拟实战-用CompletableFuture优化远程RPC调用
  • 【优先算法】专题——位运算
  • 存储器知识点2
  • 基础IO的学习
  • 代码随想录-训练营-day18
  • 【go语言】grpc 快速入门
  • 30分钟入门CompletableFuture并发工具使用
  • 【漫话机器学习系列】077.范数惩罚是如何起作用的(How Norm Penalties Work)
  • mac执行brew services list时,无法连接GitHub
  • 谷歌Titans模型论文解析,Transformer迎来变革拐点——DeepSeek能否“接招”?
  • 七. Redis 当中 Jedis 的详细刨析与使用
  • 【自开发工具介绍】SQLSERVER的ImpDp和ExpDp工具03
  • 【建站】专栏目录
  • 51c视觉~CV~合集10
  • Windows图形界面(GUI)-QT-C/C++ - QT Stacked Widget
  • 运维自动化工具集:构建高效运维体系的密钥
  • 浏览器模块化难题