面向对象示例 封装
1 面向对象示例
1.1 需求
选课系统项目中涉及到诸多数据与功能,要求引入面向对象的思想对其进行高度整合
- 学校
- 班级
- 课程
1.2 学校
共有属性:学校名
独有属性:校区名,地址,班级
方法:添加班级,打印班级
class School:
# 对象共有的属性
full_name = 'Old Boy' # 学校名
# 定制对象独有的属性
def __init__(self, name, addr):
self.name = name # 校区名
self.addr = addr # 校区地址
self.class_list = [] # 校区的班级列表
def add_class(self, class_obj):
self.class_list.append(class_obj)
def print_all_class_names(self):
print(self.name)
for each_obj in self.class_list:
print(each_obj.name, end='\t')
def print_all_course_names(self):
print(self.name)
for each_obj in self.class_list:
each_obj.print_all_course_names()
def print_all_course_details(self):
print(self.name)
for each_obj in self.course_list:
each_obj.print_all_course_details()
1.3 班级
属性:班级名,课程
方法:添加课程,打印课程
class Class:
def __init__(self, name):
self.name = name
self.course_list = []
def add_course(self, course_obj):
self.course_list.append(course_obj)
def print_all_course_names(self):
print(self.name)
for each_obj in self.course_list:
print(each_obj.name)
def print_all_course_details(self):
print(self.name)
for each_obj in self.course_list:
each_obj.print_course_detail()
1.4 课程
属性:课程名,周期, 价格
方法:打印课程信息
class Course:
def __init__(self, name, circle, price):
self.name = name
self.circle = circle
self.price = price
def print_course_detail(self):
print(self.name, self.circle, self.price)
1.5 造数据
tj_sch_obj = School('老男孩天津校区', '天津市河东区XX路XX号')
sh_sch_obj = School('老男孩上海校区', '上海市嘉定区XX路XX号')
tj1_cla_obj = Class('天津1期')
sh1_cla_obj = Class('上海1期')
tj1_sch_obj.add_class(tj1_cla_obj)
sh1_sch_obj.add_class(sh1_cla_obj)
ui1_course_obj = Course('UI设计1期', '3月', 18000)
cad2_course_obj = Course('CAD设计1期', '3月', 18000)
android1_course_obj = Course('安卓开发1期', '4月', 22000)
ios2_course_obj = Course('IOS开发1期', '4月', 22000)
tj1_cla_obj.add_class(ui1_class_obj)
tj1_cla_obj.add_class(cad1_class_obj)
sh1_cla_obj.add_class(android1_class_obj)
sh1_cla_obj.add_class(ios1_class_obj)
2 封装
2.1 封装的概念
封装是面向对象三大特性中的核心,封装的实质就是整合。
2.2 隐藏属性/方法
只在属性名或方法名前添加__前缀,表示这个属性或者方法是私有的,可以实现对外隐藏的效果。
class Demo:
__x = 1
def print_x(self):
print(self.__x) # 1
print(self.__print_xx.__name__) # __print_xx
def __print_xx(self):
print(self.__x)
demo_obj = Demo()
demo_obj.print_x() # 1
# demo_obj.__print_xx() # AttributeError: 'Demo' object has no attribute '__print_xx'
# demo_obj.__x # AttributeError: 'Demo' object has no attribute '__x'
# Demo.__x # AttributeError: type object 'Demo' has no attribute '__x'
print(Demo.__dict__)
# {'__module__': '__main__', '_Demo__x': 1, 'print_x': <function Demo.print_x at 0x000001B58BA764C0>, '__dict__': <attribute '__dict__' of 'Demo' objects>, '__weakref__': <attribute '__weakref__' of 'Demo' objects>, '__doc__': None}
demo_obj._Demo__x
Demo._Demo__x
demo_obj._Demo__print_xx() # 1
- 在类外部无法直接访问拥有__前缀的属性和方法,但知道了类名和属性/方法名后可以通过名字 _类名__属性/方法 访问。所以这种操作并没有严格意义上地限制外部访问,仅仅只是名字变形。
- 在类内部是可以直接访问拥有__前缀的属性和方法的,因为在类定义阶段类内部拥有__前缀的属性/方法统一发生了名字变形。
- 变形操作只在类定义阶段发生一次,在类定义之后任何赋值操作都不会变形。
class Demo:
__x = 1 # 发生名字变形
Demo.__y = 3 # 不会发生名字变形
demo_obj = Demo()
print(demo_obj._Demo__x) # 1
print(demo_obj.__y) # 3
2.3 使用方法
- 隐藏属性
将数据隐藏起来就限制了类外部对数据的直接操作,然后类内应该提供相应的接口来允许类外部间接地操作数据。
作为类的设计者,可以在接口函数上添加逻辑代码来控制使用者对类属性的访问和修改。
class Person:
def __init__(self, name, age):
self.__name = name
self.__age = age
def get_name(self):
return self.__name
def get_age(self):
return self.__age
def set_age(self, age):
if type(age) is not int:
print('年龄只接受整数。')
else:
self.__age = age
p_obj1 = Person('王长贵', 25)
print(p_obj1.get_name()) # 王长贵
p_obj1.set_age(26)
print(p_obj1.get_age()) # 26
- 隐藏方法
目的的是为了隔离复杂度。
例如ATM程序的取款功能,该功能由很多子功能组成,例如插卡、身份认证、输入金额、打印小票、取钱等,而对使用者来说只需要取款这个功能接口即可,其余功能都可以隐藏起来。
class ATM:
def __card(self):
print('插卡')
def __auth(self):
print('用户认证')
def __input(self):
print('输入取款金额')
def __print_bill(self):
print('打印账单')
def __take_money(self):
print('取款')
def withdraw(self):
self.__card()
self.__auth()
self.__input()
self.__print_bill()
self.__take_money()
obj=ATM()
obj.withdraw()