学习笔记
开发工具:Spyder


文章目录

  • 封装
    • 定义
    • 私有成员
      • 举个例子1
      • 举个例子2
      • 举个例子3
    • `__slots__`属性
    • 属性`@property`
      • 案例(可读、可写)
        • 发现问题

封装

定义

  • 从数据角度来说

封装是将一些基本数据类型复合成一个自定义类型,即将数据与对数据的操作封装起来。

  • 从行为角度讲

封装是向类外提供功能,隐藏实现的细节;将复杂的东西藏起来,只给别人提供一种调用。

  • 从设计的角度讲

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

②封装变化
对变化的地方(行为上的变化点)独立封装(独立的放到一个类中),避免影响其他类。

③高内聚
类中各个方法都在完成一项任务(单一职责的类),即一个类可以有多个方法,每个方法都在做一个小功能,但是这些小功能都是在做同一项任务

④低耦合
类与类的关联性与依赖性要低(每个类独立),即当一个类改变时,对其他类的影响要尽量低。
(最好的低耦合是:一个类被抛弃,其他的类不受其影响)

备注1:最高的内聚,莫过于类中仅包含1个方法,但是将会导致高内聚高耦合。最低的耦合,莫过于类中包含所有方法,但是将会导致低内聚低耦合。
备注2:举个例子,电脑硬件(鼠标、键盘、内存条…)就具有高度集成化(高内聚),又可插拔(低耦合)的特点。

私有成员

  • 作用

私有成员为无需向类外提供访问的成员,可以通过私有化对成员进行屏蔽。但是在python中,并不是真正的实现了屏蔽,这只是一种障眼法(解释器会改变双下划线开头的变量名),我们可以通过【_类名__成员名】对私有成员进行访问。

  • 做法

在想要被私有的成员开头,加上双下划线【__】。

举个例子1

代码:

class Bunny:def __init__(self, name = "", age = 0):self.name = nameself.__age = agew01 = Bunny()
print(w01.__age)

结果:

报错!由报错信息可知,并不存在变量【__age】,这是因为解释器改变了【__age】的变量名。

举个例子2

代码:

class Bunny:def __init__(self, name = "", age = 0):self.name = nameself.__age = agew01 = Bunny()print(w01._Bunny__age)
print(w01.__dict__)

结果:

举个例子3

我们再举一个例子,不仅实现成员的私有,而且在创建实例对象时,对参数进行判断。

代码:

class Bunny:def __init__(self, name, age):self.set_name(name)self.set_age(age)def set_name(self, value):self.__name = valuedef get_name(self):return self.__namedef set_age(self, value):if 0<= value < 7:self.__age = valueelse:self.__age = 0print("输入错误")def get_age(self):return self.__ageb01 = Bunny("大白", 5)
b01.set_name("小黄")
b01.set_age(10)
b01.set_age(4)
print(b01.get_name())
print(b01.get_age())

结果:

__slots__属性

  • 作用

限定一个类创建的实例,只能有固定的实例变量,不能再额外添加。

  • 语法
__slots__ = ("变量名1","变量名2"…..)
  • 说明

含有__slots__属性的类所创建的对象没有__dict__属性, 即此实例不用字典来存储对象的实例属性。

  • 优缺点

优点:访止用户因错写属性的名称而发生程序错误。
缺点:丧失了动态语言可以在运行时为对象添加变量的灵活性。

  • 例子(不使用__slots__)

代码:

class Bunny:def __init__(self, name, age):self.name = nameself.age = agew01 = Bunny("小黄", 6)
w01.sex = "公"
print(w01.__dict__)

结果:

  • 例子(使用__slots__将实例变量数固定)

代码:

class Bunny:__slots__ = ("name", "age")def __init__(self, name, age):self.name = nameself.age = agew01 = Bunny("小黄", 6)
w01.sex = "公"

结果:

由结果可知可知,当我们自己多增加一个实例变量时,报错了,python提示我们,Bunny类中没有属性sex.

属性@property

  • 定义及调用

  • 说明

①通过两个公开的属性,保护一个私有的变量。
@property负责读取,@属性名.setter负责写入

案例(可读、可写)

在做这个案例之前我们先回顾一个知识点:

代码第9行:若直接将list01赋值给list02,则list02得到的是list01所指的对象的地址。
代码第10行:若将list01[:]赋值给list03,则list03得到的是list01新创建的对象的地址。
若每次通过切片返回新对象(即第10行的操作),都会另外开辟空间,创建新对象,占用过多内存。

我们看以下代码:

class Bunny:def __init__(self, name):self.name = nameself.__foods = []self.__weight = 7@propertydef name(self):return self.__name@name.setterdef name(self, value):self.__name = value@propertydef weight(self):return self.__weightb01 = Bunny("小白")print(b01.name, b01.weight)

结果:

在代码中,我们设置变量name为可读可写,而变量weight设置为只读(即只可读取不可以更改),如果我们试图更改weight则会报错。


发现问题

备注:我发现在可读可写的情况下(如变量name),在__init__()方法中,变量(name)前可以不加双下划线,即可以写成【self.name = name】,而只读情况下(如变量weight),在__init__()方法中,变量(weight)前要加双下划线,即可以写成【self.__weight = 7】,若不加双下划线则会报错【AttributeError: can’t set attribute】。这是为啥呢?

比如:

代码1:

class Bunny:#__slots__ = ("__name", "__weight")def __init__(self, name):self.name = nameself.weight = 7@propertydef name(self):return self.__name@name.setterdef name(self, value):self.__name = value@propertydef weight(self):return self.__weightb01 = Bunny("小白")
print(b01.name, b01.weight)

结果1:

代码2:

class Bunny:#__slots__ = ("__name", "__weight")def __init__(self, name):self.name = nameself.__weight = 7@propertydef name(self):return self.__name@name.setterdef name(self, value):self.__name = value@propertydef weight(self):return self.__weightb01 = Bunny("小白")
print(b01.name, b01.weight)

结果2:

代码3:

class Bunny:#__slots__ = ("__name", "__weight")def __init__(self, name):self.name = nameself.weight = 7@propertydef name(self):return self.__name@name.setterdef name(self, value):self.__name = value@propertydef weight(self):return self.__weight@weight.setterdef weight(self, value):self.__weight = valueb01 = Bunny("小白")
print(b01.name, b01.weight)

结果3:


我们改一下weight试试:

代码:

class Bunny:def __init__(self, name):self.name = nameself.__foods = []self.__weight = 7@propertydef name(self):return self.__name@name.setterdef name(self, value):self.__name = value@propertydef weight(self):return self.__weightb01 = Bunny("小白")
b01.weight = 10

结果:

报错了!

接下来,我们把foods设置为只读,并试着更改一下foods:

class Bunny:def __init__(self, name):self.name = nameself.__foods = ["提草", "兔粮"]self.__weight = 7@propertydef name(self):return self.__name@name.setterdef name(self, value):self.__name = value@propertydef weight(self):return self.__weight@propertydef foods(self):return self.__foodsb01 = Bunny("小白")
b01.foods = ["提草", "兔粮", "白菜"]

结果:

嗯!报错了。

但在只读情况下真的不可以更改么?我们看一下以下这段代码:

class Bunny:def __init__(self, name):self.name = nameself.__foods = ["提草", "兔粮"]self.__weight = 7@propertydef name(self):return self.__name@name.setterdef name(self, value):self.__name = value@propertydef weight(self):return self.__weight@propertydef foods(self):return self.__foodsb01 = Bunny("小白")
b01.foods.append("白菜")
print(b01.name, b01.weight, b01.foods)

结果:

惊!不仅没有报错,而且真的修改了b01.foods所关联的列表。

我们画一个简易内存图来解释一下:

①当执行【b01 = Bunny("小白")】时:

②当执行【b01.foods.append("白菜")】时,【b01.foods】返回了【self.__foods】,【self.__foods】提供了可变对象的地址(foods关联的是列表对象,列表是可变类型),通过地址,我们找到了列表对象,并对列表对象进行修改。但是在Bunny实例对象内部,存放的foods所关联对象的地址没有变,改变的只是列表对象本身,这种情况满足只读属性的性质:

但是如果直接用【b01.foods = ["提草", "兔粮", "白菜"]】的方式对变量foods进行更改,则在内存中会创建新的列表对象,Bunny实例对象内部所存储的foods所关联对象的地址则会改变,则不满足只读属性的性质。

但如果我们再更改一下代码,返回foods列表的切片:

class Bunny:def __init__(self, name):self.name = nameself.__foods = ["提草", "兔粮"]self.__weight = 7@propertydef name(self):return self.__name@name.setterdef name(self, value):self.__name = value@propertydef weight(self):return self.__weight@propertydef foods(self):return self.__foods[:]b01 = Bunny("小白")
b01.foods.append("白菜")
print(b01.name, b01.weight, b01.foods)

结果:

由结果可知,b01.foods并没有被更改。这是为啥呢?
这是因为我们在执行【b01.foods.append("白菜")】时,【b01.foods】返回的是【self.__foods[:]】,也就是说,返回了【self.__foods】的切片,即返回了一个新创建的列表对象的地址。则,我们通过这个新地址找到的新列表对象,并不是【self.__foods】所关联的列表对象,所以就无法对b01.foods所关联的原列表对象进行修改。

python面向对象(part2)--封装相关推荐

  1. Python面向对象二 封装与修饰器

    Python面向对象二 封装与修饰器 1. 特殊方法 2. 封装 2.1 封装的引入 2.2 封装实例一 2.3 封装实例二 3. property装饰器 1. 特殊方法 • 在类中可以定义一些特殊方 ...

  2. python面向对象编程 -- 封装、继承

    面向对象编程 -- 封装.继承 面向对象编程三要素:封装.继承和多态.本文主要看和封装.继承相关的概念:在python中多态的概念比较模糊,本文不做讨论. 1 封装 封装:将数据和操作组装到一起,对外 ...

  3. python面向对象编程(封装与继承)

    一. 面向过程编程语言 "面向过程"(Procedure Oriented)是一种以过程为中心的编程思想.分析出解决问题所需要的步 骤,然后用函数把这些步骤一步一步实现,使用的时候 ...

  4. python 面向对象的封装_Python面向对象封装操作案例详解

    本文实例讲述了Python面向对象封装操作.分享给大家供大家参考,具体如下: 目标 封装 小明爱跑步 存放家具 01. 封装 封装 是面向对象编程的一大特点 面向对象编程的 第一步 ―― 将 属性 和 ...

  5. python 面向对象之封装与类与对象

    封装 一,引子 从封装本身的意思去理解,封装就好像是拿来一个麻袋,把小猫,小狗,小王八,小老虎一起装进麻袋,然后把麻袋封上口子.照这种逻辑看,封装='隐藏',这种理解是相当片面的 二,先看如何隐藏 在 ...

  6. python面向对象之封装、继承与多态

    目录 面向对象程序设计 类和对象 属性查找 数据属性 实例属性 类的封装 封装数据 封装⽅法(隔离复杂度) 类的⽅法 类的继承 继承概述 单继承 多继承 ⽅法重写 继承原理 深度优先&⼴度优先 ...

  7. python面向对象(1) —— 封装

    面向对象由封装.继承和多态组成 封装:属性和方法写到类里 继承:子类继承父类方法与属性 多态:子类重写父类后再实例化为不同对象产生不同结果 类与对象 class Gun():def __init__( ...

  8. 8.python之面相对象part.6(python面向对象之封装的概念)

    一.什么是封装? 对于什么是封装,我找到了一个很好的例子...就不在这里做解释了,以下内容来源于峰氏理论...与博主无关.... (下面内容转载自egon的博客) 你钱包的有多少钱(数据的封装) 你的 ...

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

    本章节的内容常常在面试题中出现,大家根据本文的思路可以很好的掌握. 文章目录 封装浅层次 一.使用方法,封装变量 二.使用property (读取方法,写入方法),封装变量 三.使用property ...

最新文章

  1. mpvue 转uniapp 导航栏样式错乱问题修复 tabbar 样式修复
  2. MS SQL SERVER2005 分页存储过程
  3. mysql 常用命令 | 表间 弱关联 join
  4. Java程序员从笨鸟到菜鸟之(一百零六)java操作office和pdf文件(四)页面列表导出cvs,excel、pdf报表.
  5. python+opencv 给女朋友照片加上个性相框,学会等着她夸你。
  6. Boost::context模块callcc的stack测试程序
  7. Git之回退已经提交到远程仓库的代码(已经push的代码)
  8. 创建型模式——抽象工厂模式
  9. Oracle 11g 间隔分区(INTERVAL)批量规范命名
  10. 入驻商户卖隐形眼镜护理液 饿了么遭监管警告
  11. FoneDog Data Recovery数据恢复教程
  12. 中文CTEX加下划线无法换行问题
  13. R中两种常用并行方法——2. snowfall
  14. 语义网技术综述(web3.0)
  15. 百度地图之添加覆盖物并响应事件
  16. linux下ss工具简介
  17. 【CVPR 2020】蒸馏篇(四):Online Knowledge Distillation via Collaborative Learning
  18. JDK8新特性:Lambda表达式、Stream流、日期时间工具类
  19. 人工智能合一算法java语言实现_人工智能合一算法UNify.doc
  20. 关于聚类问题的算法python代码实现-K-均值聚类方法

热门文章

  1. pytorch 构建神经网络模型总结
  2. oracle asm 异机挂载,oracle 异机恢复 从asm到文件系统成功实例
  3. xampp php连接mysql数据库_关于PHP xampp连接数据库之后查询失败,数据库连接成功,表中也有数据...
  4. 【机器学习】集成学习ensemble之随机森林
  5. ascii码01100001_ASCII编码和Unicode编码
  6. python函数返回值_Python中函数的返回值示例浅析
  7. mysql中的extract()函数
  8. web项目通过ajax提交数据太大报错
  9. QT-Qt获取当前时间并格式化输出及将积秒转换成时间
  10. jqgrid 启用键盘操作bindKeys