在Python中,封装是一种面向对象编程(OOP)的特性,它允许我们将数据(属性)和操作这些数据的方法(函数)捆绑在一起,形成一个独立的对象。封装的主要目的是隐藏对象的内部状态,并只通过对象提供的方法来访问和操作这些状态,用于保护对象的数据完整性,并防止外部代码直接访问或修改对象的内部状态。
Python中的封装可以通过定义类来实现,在类中,我们可以将属性和方法定义为私有(只能在类内部访问)或公有(可以在类外部访问)。
以下是一个案例,在自创建的Person类,使用双下划线前缀进行标识两个私有属性__name
和__age
,以及四个公有方法get_name()
、get_age()
、set_name()
和set_age()
,采用公共方法获取其私有属性,同时对修改私有属性进行了合法性检查,在公共方法修改了属性值后如果某个属性值错误,会进行报错并取消该属性的修改
进行封装了的类:
class Person:
def __init__(self, name, age, height, width):
# 私有属性,使用双下划线前缀
self.__name = name
self.__age = age
self.__height = height
self.__width = width
# 公有方法,用于获取私有属性的值
def get_name(self):
return self.__name
def get_age(self):
return self.__age
def get_height(self):
return self.__height
def get_width(self):
return self.__width
# 公有方法,用于设置私有属性的值
def set_name(self, name):
self.__name = name
def set_age(self, age):
if age >= 0 and age <= 120: # 对年龄进行合法性检查
self.__age = age
else:
print("Invalid age!")
def set_height(self, height):
if height >= 0 and height <= 250: # 对身高进行合法性检查
self.__height = height
else:
print("Invalid height!")
def set_width(self, width):
if width >= 50 and width <= 500: # 对体重进行合法性检查
self.__width = width
else:
print("Invalid width!")
# 创建Person对象
person = Person("Alice", 25, 140, 100)
# 通过公有方法访问和修改私有属性
print(f"name:{person.get_name()}, age:{person.get_age()}, height:{person.get_height()},width:{person.get_width()}")
person.set_name("Bob")
person.set_age(-5)
person.set_height(300)
person.set_width(550)
print(f"name:{person.get_name()}, age:{person.get_age()}, height:{person.get_height()},width:{person.get_width()}")
这里可以看到age(-5)、width(550)、height(300)这三个错误的属性值进行了报错并取消了修改
name:Alice, age:25, height:140,width:100
Invalid age!
Invalid height!
Invalid width!
name:Bob, age:25, height:140,width:100
未进行封装的类:
直接使用公共类并访问和修改属性
class Person:
def __init__(self, name, age, height, width):
self.name = name
self.age = age
self.height = height
self.width = width
# 创建Person对象
person = Person("Alice", 25, 140, 100)
# 直接访问和修改属性
print(f"name:{person.name}, age:{person.age}, height:{person.height}, width:{person.width}")
person.name = "Bob"
person.age = 30
person.height = 160
person.width = 200
print(f"name:{person.name}, age:{person.age}, height:{person.height}, width:{person.width}")
# 创建一个新的Person对象并尝试设置无效的属性值
invalid_person = Person("Charlie", -5, 300, 600)
# 输出这个对象属性
print(f"name:{invalid_person.name}, age:{invalid_person.age}, height:{invalid_person.height}, width:{invalid_person.width}")
这里可以看到终端输出的信息中,Charlie的年龄和身高体重存在明显问题,是无效属性值,而且也展示了未进行封装的坏处:
-
数据完整性受损:没有对数据进行合法性检查,无效或不合理的数据可以被接受并存储在对象中。
-
代码可维护性降低:如果后续需要添加额外的逻辑来处理属性,需要找到并修改所有直接修改属性的代码,而不是只修改一个setter方法。
-
安全性问题:对象的内部状态可以被外部代码随意修改,可能导致对象处于不一致或不稳定的状态。
-
封装性丧失:封装是面向对象编程的四大基本原则之一,它允许我们隐藏对象的内部实现细节,只通过明确定义的接口与外界交互。未封装的类违反了这一原则。
name:Alice, age:25, height:140, width:100
name:Bob, age:30, height:160, width:200
name:Charlie, age:-5, height:300, width:600
这里新建一个用于绘制年龄、身高、体重条形图的Python文件,继承上述代码中的Person类,并创建实例进行图形绘制
import matplotlib.pyplot as plt
from Encapsulation import Person
class StatisticsPerson(Person):
instances = [] # 类变量,用于存储实例
def __init__(self, name, age, height, width):
super().__init__(name, age, height, width)
self.instances.append(self) # 将新创建的实例添加到instances列表中
# 使用静态方法统计所有人的年龄、身高和宽度,并绘制条状图
@staticmethod
def plot_statistics():
ages = [person.get_age() for person in StatisticsPerson.instances]
heights = [person.get_height() for person in StatisticsPerson.instances]
widths = [person.get_width() for person in StatisticsPerson.instances]
x = range(len(StatisticsPerson.instances))
plt.figure(figsize=(10, 6))
plt.bar(x, ages, label='Age', width=0.25)
plt.bar([i + 0.25 for i in x], heights, label='Height', width=0.25)
plt.bar([i + 0.5 for i in x], widths, label='Width', width=0.25)
plt.xlabel('Name')
plt.ylabel('Values')
plt.title('Comparison of Age, Height, and Width')
plt.xticks([i + 0.25 for i in x], [person.get_name() for person in StatisticsPerson.instances], rotation=45)
plt.legend()
plt.savefig("GGboy.png")
plt.show()
# 创建StatisticsPerson对象
person1 = StatisticsPerson("Alice", 25, 140, 100)
person2 = StatisticsPerson("Bob", 30, 160, 200)
person3 = StatisticsPerson("Charlie", 40, 175, 150)
# 绘制并保存统计图
StatisticsPerson.plot_statistics()
生成的图片: