Python小白学习教程从入门到入坑------第二十三课 封装(语法进阶)
面向对象的三大特征:封装、继承、多态
一、封装
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 的形式,但仍然可以通过这个改写后的名称在外部访问
因此,双下划线前缀也不是严格意义上的私有方法定义
今天的分享就到这里了,希望能帮助到大家~