> 文档中心 > 【python面向对象】封装(面试题常考)

【python面向对象】封装(面试题常考)


本章节的内容常常在面试题中出现,大家根据本文的思路可以很好的掌握。

文章目录

  • 封装浅层次
    • 一、使用方法,封装变量
    • 二、使用property (读取方法,写入方法),封装变量
    • 三、使用property ,封装变量
    • 四、@property讲解
  • 封装深层次
    • 一、封装设计思想
      • 课后练习1
      • 课后练习2
    • 二、__slots__讲解
  • 总结

封装浅层次

数据角度讲,将一些基本数据类型复合成一个自定义类型。

封装数据:
敌人(姓名,血量,攻击力,防御力)
二维向量(x,y)

优势:
1、更符合人类的思考方式。
2、将数据与对数据的操作整合在一起。

行为角度讲,向类外提供必要的功能,隐藏实现的细节。

封装行为:
二维列表助手类DoublelListHelper(获取多个元素get_elements
向量(向左╱向右,求模长,求方向等)

优势:以”模块化的方式进行编程,更可以集中精力设计、组织、指挥多个类协同工作。

一、使用方法,封装变量

#使用方法,封装变量class   Wife:    def __init__(self,name,age, weight): self.name = name # 本质:障眼法 #实质:实际将变量名改为︰_类名__age # self.__age = age self.set_age(age) # self.__weight = weight self.set_weight(weight)    # 提供公开的读写方法    def get_age(self): return self.__age    def set_age(self,value): if 21<= value <= 31:     self.__age =value else:     raise ValueError("我不要")    # 提供公开的读写方法    def get_weight(self): return self.__weight    def set_weight(self,value): if 40<= value <= 80:     self.__weight =value else:     raise ValueError("我不要")"""#不建议下面的做法w01 = Wife("铁锤公主",87,87)#重新创建了新实例变量(没有改变类中定义的_age)# w01.__age = 107#修改了类中定义的私有变量w01._Wife__age = 107#__dict__:python内置变量,存储对象的实例变量print(w01.__dict__)"""w01 = Wife("铁锤公主",30,57)w01.set_age(25)w01.set_weight(45)print(w01.get_age())print(w01.get_weight())#25#45

练习:
定义敌人类(姓名,攻击力10–50,血量100–200)
创建一个敌人对象,可以修改数据,读取数据。

class  Enemy:    def __init__(self,name,attack,hp): self.name = name self.set_attack(attack) self.set_hp(hp)    def set_attack(self,value): if  10 <=  value <= 50:     self.__attack = value else:     raise ValueError("不行")    def get_attack(self): return self.__attack    def set_hp(self,value): if 100 <= value <= 200:     self.__hp = value else:     raise ValueError("不行")    def get_hp(self): return self.__hpenemy = Enemy("灭霸",20,100)enemy.set_attack(30)print(enemy.get_attack())#30

进阶:

二、使用property (读取方法,写入方法),封装变量

"""    使用property (读取方法,写入方法),封装变量。"""class   Wife:    def __init__(self,name,age, weight): self.name = name # self.set_age(age) self.age = age # self.set_weight(weight) self.weight = weight    # 提供公开的读写方法    def get_age(self): return self.__age    def set_age(self,value): if 21<= value <= 31:     self.__age =value else:     raise ValueError("我不要")    #属性 property对象拦截对age类变量的读写操作    age = property(get_age,set_age)    # 提供公开的读写方法    def get_weight(self): return self.__weight    def set_weight(self,value): if 40<= value <= 80:     self.__weight =value else:     raise ValueError("我不要")    #属性 property对象拦截对weight类变量的读写操作    weight = property(get_weight,set_weight)w01 = Wife("铁锤公主",30,57)# w01.set_age(25)w01.age = 25print(w01.age)w01.weight = 45print(w01.weight)# 25# 45

练习:改写上面的敌人类

class  Enemy:    def __init__(self,name,attack,hp): self.name = name self.attack = attack self.hp = hp    def set_attack(self,value): if  10 <=  value <= 50:     self.__attack = value else:     raise ValueError("不行")    def get_attack(self): return self.__attack    attack = property(get_attack,set_attack)    def set_hp(self,value): if 100 <= value <= 200:     self.__hp = value else:     raise ValueError("不行")    def get_hp(self): return self.__hp    hp = property(get_hp,set_hp)    #hp = property(None,set_hp)#只读属性enemy = Enemy("灭霸",20,100)enemy.attack= 30enemy.hp = 120print(enemy.attack)print(enemy.hp)# 30# 120

内存图:
在这里插入图片描述

三、使用property ,封装变量

"""    使用property ,封装变量。"""class   Wife:    def __init__(self,name,age, weight): self.name = name # self.set_age(age) self.age = age # self.set_weight(weight) self.weight = weight    # 提供公开的读写方法    #创建property对象,只负责拦截读取操作    @property    def age(self): return self.__age    #只负责拦截写入操作    @age.setter    def age(self,value): if 21<= value <= 31:     self.__age =value else:     raise ValueError("我不要")    #属性 property对象拦截对age类变量的读写操作    @property    # 提供公开的读写方法    def weight(self): return self.__weight    @weight.setter    def weight(self,value): if 40<= value <= 80:     self.__weight =value else:     raise ValueError("我不要")w01 = Wife("铁锤公主",30,57)# w01.set_age(25)w01.age = 25print(w01.age)w01.weight = 45print(w01.weight)# 25# 45

练习:改写敌人类

class  Enemy:    def __init__(self,name,attack,hp): self.name = name self.attack = attack self.hp = hp    @property    def attack(self): return self.__attack    @attack.setter    def attack(self,value): if  10 <=  value <= 50:     self.__attack = value else:     raise ValueError("不行")    @property    def hp(self): return self.__hp    @hp.setter    def hp(self,value): if 100 <= value <= 200:     self.__hp = value else:     raise ValueError("不行")enemy = Enemy("灭霸",20,100)enemy.attack= 30enemy.hp = 120print(enemy.attack)print(enemy.hp)# 30# 120

四、@property讲解

公开的实例变量,缺少逻辑验证。私有的实例变量与两个公开的方法相结合,又使调用者的操作略显复杂。而属性可以将两个方法的使用方式像操作变量一样方便。

  • 定义:
   @property    def name(self): return self.__name    @name.setter    def name(self,name): self.__name = name
  • 调用:

对象.属性名 = 数据
变量 = 对象.属性名

  • 说明:
    1、通常两个公开的属性,保护一个私有的变量。
    2、 @property负责读取,@属性名.setter负责写入。
    3、只写:属性名= property(None,写入方法名)。

封装深层次

设计角度讲

(1)分而治之

  • 将一个大的需求分解为许多类,每个类处理一个独立的功能。
  • 拆分好处:便于分工,便于复用,可扩展性强。

(2)变则疏之

  • 变化的地方独立封装,避免影响其他类。

(3)高内聚(目标一致)

  • 类中各个方法都在完成一项任务(单一职责的类)。

(4)低耦合

  • 类与类的关联性与依赖度要低(每个类独立)。
    例如:【硬件高度集成化,又要可插拔】

一、封装设计思想

"""    封装设计思想 需求︰老张开车去东北"""class Person:    def __init__(self,name): self.name = name    @property    def name(self): return self.__name    @name.setter    def name(self,value): self.__name = value    def go_to(self,str_position,type): """     去 :param str_position:位置 :param type:方式 """ print(self.name,"去",str_position) type.run(str_position)class Car:    def run(self,str_position): """     行驶 :param str_position: 位置 """ print("汽车行驶到:"+ str_position)lz = Person("老张")car = Car()lz.go_to("东北",car)# 老张 去 东北# 汽车行驶到:东北

练习:请以面向对象的思想,描述下列场景:小明在招商银行取钱。(面试题)

class Person:    def __init__(self,name,money): self.name = name self.money = money    @property    def name(self): return  self.__name    @name.setter    def name(self,value): self.__name = value    @property    def money(self): return self.__money    @money.setter    def money(self, value): self.__money = valueclass Bank:    def __init__(self,name,money): self.name = name self.money = money    @property    def name(self): return  self.__name    @name.setter    def name(self,value): self.__name = value    @property    def money(self): return self.__money    @money.setter    def money(self, value): self.__money = value    def withdraw_money(self,person,value): """     取钱 """ self.money -= value person.money += value print(person.name,"取了%d钱"%value)person = Person("小明",0)bank = Bank("招商银行",100000)bank.withdraw_money(person,1000)#小明 取了1000

方法在哪个类写,由谁承担便有谁来做

课后练习1

请用面向对象思想,描述以下场景∶
张无忌 教 赵敏 九阳神功
赵敏 教 张无忌 化妆
张无忌 上班 挣了 10000
赵敏 上班 挣了 6000
思考∶变化点是数据的不同还是行为的不同。

class Person:    def __init__(self,name): self.name = name    @property    def name(self): return self.__name    @name.setter    def name(self,value): self.__name = value    def teach(self,other,skill): print(self.name,"教",other.name,skill)    def work(self, money): print(self.name,"上班挣了%d钱"%money)person01 = Person("张无忌")person02 = Person(" 赵敏 ")person01.teach(person02,"九阳神功")person02.teach(person01,"化妆")person01.work(10000)person02.work(6000)# 张无忌 教  赵敏  九阳神功#  赵敏  教 张无忌 化妆# 张无忌 上班挣了10000钱#  赵敏  上班挣了6000

上面的例子体会∶对象区分数据的不同

课后练习2

请用面向对象思想,描述以下场景∶
玩家(攻击力)攻击敌人(血量),敌人受伤(掉血),还可能死亡((掉装备,加分)。
敌人(攻击力)攻击玩家,玩家(血量)受伤(掉血/碎屏),还可能死亡(游戏结束)。

class Player:    def __init__(self,atk,hp): self.atk = atk self.hp = hp    def attack01(self,other): #打的逻辑 print("玩家攻击敌人") # 通过敌人对象地址﹐调用实例方法。 other.damage02(self.atk)    def damage01(self,value): print("玩家受伤了") self.hp -= value if self.hp <= 0:     self.__death()     # 私有的死亡方法    def __death(self): # 死亡的逻辑 print("玩家死喽") print("游戏结束")class Enemy:    def __init__(self,atk,hp): self.atk= atk self.hp = hp    def damage02(self,value): print("敌人受伤了") #受伤的逻辑 self.hp -= value if self.hp <= 0:     self.__death()    # 私有的死亡方法    def __death(self): # 死亡的逻辑 print("死亡") print("掉装备") print("加分")    def attack02(self,other): print("敌人攻击玩家") other.damage01(self.atk)p01 = Player(100,1000)e01 = Enemy(10,200)#第一回合p01.attack01(e01)e01.attack02(p01)#第二回合p01.attack01(e01)# 玩家攻击敌人# 敌人受伤了# 敌人攻击玩家# 玩家受伤了# 玩家攻击敌人# 敌人受伤了# 死亡# 掉装备# 加分

此题心得:当这两个名称重复时,程序会默认调用Int型对象,但Int对象没有什么调用可言,就爆出了这个错误(TypeError: ‘int’ object is not callable解决办法),解决方法也很简单,要么更改变量名,要么更改方法名。

上面的例子体会∶类区别行为的不同

二、__slots__讲解

  • 作用:限定一个类创建的实例只能有固定的实例变量。
  • 语法:
在类中定义__slots__=(“变量名1”﹐”变量名2”...)__slots__=("__age")
  • 优点:防止用户因错写属性的名称而发生程序错误。
  • 缺点:丧失了动态语言可以在运行时为对象添加变量的灵活性。

总结

封装

  • 数据角度: 将多个变量封装到一个自定义类中。(优势:符合人类的思考方式;可以将数据与对数据的操作封装到一起)
  • 功能角度: 对外提供必要的功能,隐藏实现的细节。
    ----私有化∶将名称命名为以双下划线开头,内部修改成员名称
    ---- 属性:对实例变量的保护(拦截读/写操作)
    ----记住:通过对象地址调用实例成员
    ----slots:限定类创建的对象只能有固定的实例变量。
  • 设计角度:
    ----分而治之:将大的需求分解为多个类,每个类负责一个职责。
    ----变则疏之:遇到变化点单独封装为一个类。
    ----高内聚∶一个类有且只有一个发生变化的原因。
    ----低耦合:类与类的关系松散。