Python - 封装



封装是将属性和方法捆绑到一个单元中的过程。它是面向对象编程范式所基于的主要支柱之一。

我们知道类是对象的自定义原型。它定义了一组数据成员和方法,能够处理数据。

根据数据封装原则,描述对象的那些数据成员对类外部的环境是隐藏的。它们只能通过同一类中的方法访问。另一方面,方法本身可以从类上下文外部访问。因此,对象数据被方法封装。这样,封装可以防止直接访问对象数据。

在 Python 中实现封装

C++Java这样的语言使用访问修饰符来限制对类成员(即变量和方法)的访问。这些语言有关键字 public、protected 和 private 来指定访问类型。

如果类成员可以在程序的任何地方访问,则称其为public。仅允许在类内部访问私有成员。通常,方法被定义为 public,实例变量被定义为 private。这种 private 实例变量和 public 方法的安排确保了封装的实现。

与这些语言不同,Python没有规定类成员可以具有的访问类型。默认情况下,Python 类中的所有变量和方法都是 public,如下例所示。

示例 1

这里,我们有一个 Employee 类,它包含实例变量 **name** 和 **age**。这个类的对象具有这两个属性。因为它们是公开的,所以可以直接从类外部访问。

class Student:
   def __init__(self, name="Rajaram", marks=50):
      self.name = name
      self.marks = marks

s1 = Student()
s2 = Student("Bharat", 25)

print ("Name: {} marks: {}".format(s1.name, s2.marks))
print ("Name: {} marks: {}".format(s2.name, s2.marks))

它将产生以下输出 -

Name: Rajaram marks: 50
Name: Bharat marks: 25

在上面的例子中,实例变量是在类内部初始化的。但是,没有限制从类外部访问实例变量的值,这违反了封装的原则。

虽然没有关键字来强制可见性,但 Python 有一种用特殊方式命名实例变量的约定。在 Python 中,在变量/方法名称前加一个或两个下划线来模拟受保护和私有访问修饰符的行为。

如果变量名前缀为两个下划线(例如“**__age**”),则该实例变量是私有的,类似地,如果变量名前缀为一个下划线(例如“**_salary**”)。

示例 2

让我们修改 Student 类。添加另一个实例变量 salary。将 name 设置为私有,并将 **marks** 设置为私有,方法是在它们前面加上双下划线。

class Student:

   def __init__(self, name="Rajaram", marks=50):
      self.__name = name
      self.__marks = marks
   def studentdata(self):
      print ("Name: {} marks: {}".format(self.__name, self.__marks))
      
s1 = Student()
s2 = Student("Bharat", 25)

s1.studentdata()
s2.studentdata()
print ("Name: {} marks: {}".format(s1.__name, s2.__marks))
print ("Name: {} marks: {}".format(s2.__name, __s2.marks))

运行此代码时,它将产生以下输出 -

Name: Rajaram marks: 50
Name: Bharat marks: 25
Traceback (most recent call last):
 File "C:\Python311\hello.py", line 14, in <module>
  print ("Name: {} marks: {}".format(s1.__name, s2.__marks))
AttributeError: 'Student' object has no attribute '__name'

上面的输出清楚地表明,实例变量 name 和 age 可以被类内部声明的方法(studentdata() 方法)访问,但是双下划线前缀使变量成为私有的,因此,从类外部访问它们是被限制的,这会引发 AttributeError 错误。

什么是名称改编(Name Mangling)?

Python 并没有完全阻止对私有数据的访问。它只是将其留给程序员的智慧,不要编写任何从类外部访问它的代码。您仍然可以通过 Python 的名称改编技术访问私有成员。

名称改编是将带有双下划线的成员名称更改为 object._class__variable 格式的过程。如果需要,它仍然可以从类外部访问,但应避免这种做法。

在我们的例子中,私有实例变量 "__name" 通过将其更改为以下格式来进行改编

obj._class__privatevar

因此,要访问 "s1" 对象的 "__marks" 实例变量的值,将其更改为 "s1._Student__marks"。

将上面程序中的 print() 语句更改为 -

print (s1._Student__marks)

它现在打印 50,即 s1 的分数。

因此,我们可以得出结论,Python 并没有完全按照面向对象编程的理论实现封装。它采用了一种更成熟的方法来处理它,通过规定一个命名约定,并允许程序员在确实需要在公共范围内访问私有数据时使用名称改编。

广告