Python小白学习教程从入门到入坑------第二十三课 封装(语法进阶)

时间:2024-11-03 17:33:05

面向对象的三大特征:封装、继承、多态

一、封装

1.1 何为封装

封装:在Python中指的是隐藏对象中一些不希望被外部所访问到的属性或者方法。将复杂的信息、流程给包起来,内部处理,让使用者只需要通过简单的操作步骤,就能实现。

在Python中,封装(Encapsulation)是面向对象编程(OOP)的一个核心概念。

规范的说法:封装指的是将对象的属性和方法结合在一起,并隐藏对象的内部实现细节,只通过公开的接口(通常是方法)与外部进行交互。封装的目的在于保护对象的内部状态,防止外部代码直接访问和修改,从而确保对象的完整性和安全性。

当不使用封装时,类里面的属性可以被看见甚至可以被修改

eg:

class Person:
    name = 'junjun'    # 类属性
pe = Person()
print(pe.name)      # 输出内容:junjun
Person.name = 'yuyu'
print(Person.name)  # 输出内容:yuyu
print(pe.name)      # 输出内容:yuyu

这种不封装的代码很容易被篡改,会带来一系列的安全问题,为了解决安全性这一问题,我们要对类进行封装操作

1.2 隐藏属性(私有权限)

隐藏属性:只允许在类的内部使用,无法通过对象访问

在属性名或者方法名前面加上两个下划线__

class Person:
    name = "junjun"   # 类属性
    __age = 18        # 隐藏属性
pe = Person()
print(pe.name)
print(pe.__age)    #报错: __age被隐藏,不可以在类的外部访问类的隐藏属性

但如果类的隐藏属性已经被定义好了,我们又需要从外边访问它的值该怎么做呢?

这里有两种方法:

第一种: (了解即可,不太正规)

注意:隐藏属性实际上是将名字修改为:_类名__属性名,上面例子中的__age 就被修改为_Person__age

eg:

class Person:
    name = "junjun"   # 类属性
    __age = 28        # 隐藏属性
pe = Person()
print(pe.name)
# print(pe.__age)    #报错: __age被隐藏,不可以在类的外部访问类的隐藏属性
print(pe._Person__age)
# 输出结果:
# junjun
# 28

此种方法也可以对隐藏属性进行修改

class Person:
    name = "junjun"   # 类属性
    __age = 28       # 隐藏属性
pe = Person()
print(pe.name)
# print(pe.__age)    #报错: __age被隐藏,不可以在类的外部访问类的隐藏属性
print(pe._Person__age)
pe._Person__age = 18
print(pe._Person__age)
# 输出结果:
# junjun
# 28
# 18

 第二种:在类的内部访问——推荐使用,正规手段

eg:

class Person:
    name = "junjun"   # 类属性
    __age = 28       # 隐藏属性
    def introduce(self):   # 实例方法
        Person.__age = 16    # 可以对隐藏属性进行更改
        print(f"{Person.name}的年龄是{Person.__age}")  # 在实例方法中访问类属性和隐藏属性
pe = Person()
pe.introduce()
# 输出结果:junjun的年龄是16

1.3 私有属性/方法

1. xxx:普通属性/方法,如果是类中定义的,则类可以在任何地方使用

2. _xxx: 单下划线开头,声明私有属性/方法,如果定义在类中,外部可以使用,子类也可以继承,但是在另一个py文件中通过 from  xxx   import  *  导入时,无法导入。此种命名方法一般是为了避免与Python关键字冲突而采用的命名方法

3. __xxx: 双下划线开头,隐藏属性,如果定义在类中,无法在外部直接访问,子类不会继承,要访问只能通过间接的方式,另一个py文件中通过 from  xxx  import  *导入时,也无法导入 。这种命名方法一般是Python中的魔法方法或属性,都是有特殊含义或者功能的,自己不要轻易定义

eg:

class Person:
    name = "junjun"   # 类属性
    __age = 28       # 隐藏属性(双下划线)
    _sex = "女"      # 私有属性(单下划线)
pe = Person()
# print(pe.sex)  报错
# 使用对象名._属性名调用
print(pe._sex)
# 使用对象._类名__属性名访问隐藏属性
print(pe._Person__age)
# 输出结果:
# 女
# 28

1.4 隐藏方法 

在Python中,没有严格意义上的“隐藏方法”的概念,因为Python是一种动态类型语言,并且其访问控制机制相对宽松

然而,有一些约定俗成的命名和编码规范,可以让开发者通过命名来暗示方法的可见性和使用意图 

class Man:
    def __play(self):     # 隐藏方法
        print("玩手机")
    def funa(self):       # 平平无奇的实例方法
        print("平平无奇的实例方法")
        Man.__play(self)   # 在实例方法中调用私有方法   ————不推荐
        self.__play()     # 推荐使用,更简便
ma = Man()
ma.funa()

1.5 私有方法

在Python中,尽管没有像其他某些编程语言(例如Java)那样的严格访问修饰符来定义私有方法,但Python有一种约定俗成的方式来暗示某个方法是私有的

这种方式主要是通过在方法名前添加一个下划线前缀(_)来实现的。需要注意的是,这仅仅是一种约定,而不是Python语言本身的强制规则

私有方法通常被设计为仅在类的内部使用,而不应被类的外部代码直接调用

尽管从技术上讲,外部代码仍然可以访问这些以单下划线开头的方法,但按照Python的社区规范,这样的访问是不被推荐的,因为它违背了封装的原则

eg:

class MyClass:  
    def __init__(self, value):  
        self._private_value = value  # 私有属性  
  
    def _private_method(self):  
        # 这是一个私有方法,它应该只在类的内部被调用  
        print(f"Private value is: {self._private_value}")  
  
    def public_method(self):  
        # 这是一个公共方法,它可以被类的外部代码调用  
        self._private_method()  # 在类的内部调用私有方法  
  
# 创建类的实例  
obj = MyClass(42)  
  
# 调用公共方法,它会间接调用私有方法  
obj.public_method()  # 输出: Private value is: 42  
  
# 直接调用私有方法(虽然技术上可行,但不符合Python的封装原则)  
# obj._private_method()  # 这行代码在技术上是可行的,但通常不建议这样做

在上面的示例中,_private_method 是一个私有方法,它应该只在 MyClass 类的内部被调用

public_method 是一个公共方法,它可以被类的外部代码调用,并且它内部调用了私有方法 _private_method

尽管可以通过 obj._private_method() 直接调用私有方法,但这样做违背了封装的原则,并可能导致代码的可维护性和可读性降低

因此,建议遵循Python的社区规范,不要直接访问以单下划线开头的私有方法和属性

需要注意的是,双下划线前缀(__)会导致Python进行名称改写(name mangling),但这通常用于避免子类意外覆盖父类的方法或属性,而不是用于定义私有方法

使用双下划线前缀的方法名会在类定义时被改写为 _ClassName__methodName 的形式,但仍然可以通过这个改写后的名称在外部访问

因此,双下划线前缀也不是严格意义上的私有方法定义

今天的分享就到这里了,希望能帮助到大家~