前言
学一门语言贵在坚持用它,不用就淡忘了,而记录下一篇文章也有助于日后快速回忆。全文分为两大部分,分别是Python基础语法和面向对象。
入门Python其实很容易,但是我们要去坚持学习,每一天坚持很困难,我相信很多人学了一个星期就放弃了,为什么呢?其实没有好的学习资料给你去学习,你们是很难坚持的,在学习Python的过程中有什么不懂得可以加我,从零基础开始到Python各领域的项目实战教程、开发工具与电子书籍,都有整理。与你分享企业当下对于python人才需求及学好python的高效技巧,每晚趣味讲解Python实战操作。V新:itz992 希望对你们有帮助
第一部分 Python基础语法
第一部分 Python基础语法
1. 认识Python
1.1 Python 简介
Python 的创始人为吉多·范罗苏姆(Guido van Rossum)。
Python 的设计目标:
一门简单直观的语言并与主要竞争者一样强大 开源,以便任何人都可以为它做贡献 代码像纯英语那样容易理解 适用于短期开发的日常任务
Python 的设计哲学:
优雅、明确、简单
Python 开发者的哲学是:用一种方法,最好是只有一种方法来做一件事
Python 是完全面向对象的语言,在 Python 中一切皆对象。
可扩展性:如果需要一段关键代码运行得更快或者希望某些算法不公开,可以把这部分程序用 C 或 C++ 编写,然后在 Python 程序中使用它们。
1.2. 第一个Python程序
执行 Python 程序的三种方式:
解释器、交互式运行、IDE运行
Python 是一个格式非常严格的程序设计语言。
python 2.x 默认不支持中文。
ASCII 字符只包含 256 个字符,不支持中文
- Python 2.x 的解释器名称是 python
- Python 3.x 的解释器名称是 python3
为了照顾现有的程序,官方提供了一个过渡版本 —— Python 2.6。
提示:如果开发时,无法立即使用 Python 3.0(还有极少的第三方库不支持 3.0 的语法),建议
先使用 Python 3.0 版本进行开发 然后使用 Python 2.6、Python 2.7 来执行,并且做一些兼容性的处理
IPython 是一个 python 的 交互式 shell,比默认的 python shell 好用得多,它支持 bash shell 命令,适合于学习/验证 Python 语法或者局部代码。
集成开发环境(IDE,Integrated Development Environment)—— 集成了开发软件需要的所有工具,一般包括以下工具:
- 图形用户界面
- 代码编辑器(支持 代码补全/自动缩进)
- 编译器/解释器
- 调试器(断点/单步执行)
- ……
PyCharm 是 Python 的一款非常优秀的集成开发环境
PyCharm运行工具栏
1.3. PyCharm 的设置
PyCharm 的 配置信息 是保存在 用户家目录下 的 .PyCharmxxxx.x 目录下的,xxxx.x 表示当前使用的 PyCharm 的版本号
1.3.1 恢复 PyCharm 的初始设置:
- 关闭正在运行的 PyCharm
- 在终端中执行以下终端命令,删除 PyCharm 的配置信息目录:
$ rm -r ~/.PyCharm2016.3
- 重新启动 PyCharm
1.3.2 PyCharm 安装和启动步骤:
- 执行以下终端命令,解压缩下载后的安装包
$ tar -zxvf pycharm-professional-2017.1.3.tar.gz
- 将解压缩后的目录移动到 /opt 目录下,可以方便其他用户使用
/opt 目录用户存放给主机额外安装的软件
$ sudo mv pycharm-2017.1.3/ /opt/
- 切换工作目录
$ cd /opt/pycharm-2017.1.3/bin
- 启动 PyCharm
$ ./pycharm.sh
1.3.3 设置启动图标
- 在专业版中,选择菜单 Tools / Create Desktop Entry… 可以设置任务栏启动图标
- 注意:设置图标时,需要勾选 Create the entry for all users
快捷方式文件
/usr/share/applications/jetbrains-pycharm.desktop
在 ubuntu 中,应用程序启动的快捷方式通常都保存在 /usr/share/applications 目录下
1.3.4 卸载之前版本的 PyCharm
要卸载 PyCharm 只需要做以下两步工作:
- 删除解压缩目录
$ sudo rm -r /opt/pycharm-2016.3.1/
- 删除家目录下用于保存配置信息的隐藏目录
$ rm -r ~/.PyCharm2016.3/
如果不再使用 PyCharm 还需要将 /usr/share/applications/ 下的 jetbrains-pycharm.desktop 删掉
1.4. 多文件项目的演练
- 开发 项目 就是开发一个 专门解决一个复杂业务功能的软件
- 通常每 一个项目 就具有一个 独立专属的目录,用于保存 所有和项目相关的文件
- 在 PyCharm 中,要想让哪一个 Python 程序能够执行,必须首先通过 鼠标右键的方式执行 一下
- 对于初学者而言,在一个项目中设置多个程序可以执行,是非常方便的,可以方便对不同知识点的练习和测试
- 对于商业项目而言,通常在一个项目中,只有一个 可以直接执行的 Python 源程序
让选中的程序可以执行
2. 注释
- 注释的作用
使用用自己熟悉的语言,在程序中对某些代码进行标注说明,增强程序的可读性
2.1 单行注释(行注释)
- 以 # 开头,# 右边的所有东西都被当做说明文字,而不是真正要执行的程序,只起到辅助说明作用
print("hello python") # 输出 `hello python`
为了保证代码的可读性,# 后面建议先添加一个空格,然后再编写相应的说明文字;为了保证代码的可读性,注释和代码之间 至少要有 两个空格。
2.2 多行注释(块注释)
- 要在 Python 程序中使用多行注释,可以用 一对 连续的 三个 引号(单引号和双引号都可以)
"""这是一个多行注释在多行注释之间,可以写很多很多的内容……""" print("hello python")
提示:
- 注释不是越多越好,对于一目了然的代码,不需要添加注释
- 对于 复杂的操作,应该在操作开始前写上若干行注释
- 对于 不是一目了然的代码,应在其行尾添加注释(为了提高可读性,注释应该至少离开代码 2 个空格)
- 绝不要描述代码,假设阅读代码的人比你更懂 Python,他只是不知道你的代码要做什么
2.3 代码规范:
- Python 官方提供有一系列 PEP(Python Enhancement Proposals) 文档,其中第 8 篇文档专门针对 Python 的代码格式 给出了建议,也就是俗称的 PEP 8: 文档地址:https://www.python.org/dev/peps/pep-0008/ 谷歌有对应的中文文档:http://zh-google-styleguide.readthedocs.io/en/latest/google-python-styleguide/python_style_rules/
3. 运算符
3.1 算数运算符
是完成基本的算术运算使用的符号,用来处理四则运算,而“+”和“*”还可以用来处理字符串。
运算符 描述 实例 + 加 10 + 20 = 30 - 减 10 - 20 = -10 * 乘 10 * 20 = 200 / 除 10 / 20 = 0.5 // 取整除 返回除法的整数部分(商) 9 // 2 输出结果 4 % 取余数 返回除法的余数 9 % 2 = 1 ** 幂 又称次方、乘方,2 ** 3 = 8
3.2 比较(关系)运算符
运算符 描述 == 检查两个操作数的值是否 相等,如果是,则条件成立,返回 True != 检查两个操作数的值是否 不相等,如果是,则条件成立,返回 True > 检查左操作数的值是否 大于右操作数的值,如果是,则条件成立,返回 True < 检查左操作数的值是否 小于 右操作数的值,如果是,则条件成立,返回 True >= 检查左操作数的值是否 大于或等于 右操作数的值,如果是,则条件成立,返回 True <= 检查左操作数的值是否 小于或等于 右操作数的值,如果是,则条件成立,返回 True
Python 2.x 中判断 不等于 还可以使用 <> 运算符 != 在 Python 2.x 中同样可以用来判断 不等于
3.3 赋值运算符
- 在 Python 中,使用 = 可以给变量赋值。在算术运算时,为了简化代码的编写,Python 还提供了一系列的 与 算术运算符 对应的 赋值运算符,注意:赋值运算符中间不能使用空格。
运算符 描述 实例 = 简单的赋值运算符 c = a + b 将 a + b 的运算结果赋值为 c += 加法赋值运算符 c += a 等效于 c = c + a -= 减法赋值运算符 c -= a 等效于 c = c - a *= 乘法赋值运算符 c *= a 等效于 c = c * a /= 除法赋值运算符 c /= a 等效于 c = c / a //= 取整除赋值运算符 c //= a 等效于 c = c // a %= 取 模 (余数)赋值运算符 c %= a 等效于 c = c % a **= 幂赋值运算符 c **= a 等效于 c = c ** a
3.4 身份运算符
身份运算符比较两个对象的内存位置。常用的有两个身份运算符,如下所述:
运算符 描述 示例 is 判断两个标识符是不是引用同一个对象 x is y,类似 id(x) == id(y) is not 判断两个标识符是不是引用不同对象 x is not y,类似 id(a) != id(b)
辨析
- is 用于判断 两个变量引用的对象是否为同一个
- == 用于判断 引用变量的值 是否相等
3.5 成员运算符
Python成员运算符测试给定值是否为序列中的成员。 有两个成员运算符,如下所述:
运算符 描述 in 如果在指定的序列中找到一个变量的值,则返回true,否则返回false。 not in 如果在指定序列中找不到变量的值,则返回true,否则返回false。
3.6 逻辑运算符
运算符 逻辑表达式 描述 and x and y 只有 x 和 y 的值都为 True,才会返回 True
否则只要 x 或者 y 有一个值为 False,就返回 False or x or y 只要 x 或者 y 有一个值为 True,就返回 True
只有 x 和 y 的值都为 False,才会返回 False not not x 如果 x 为 True,返回 False
如果 x 为 False,返回 True
3.7 运算符优先级
- 以下表格的算数优先级由高到最低顺序排列:
运算符 描述 ** 幂 (最高优先级) * / % // 乘、除、取余数、取整除 + - 加法、减法 <= < > >= 比较运算符 == != 等于运算符 = %= /= //= -= += *= **= 赋值运算符 is is not 身份运算符 in not in 成员运算符 not or and 逻辑运算符
<补>程序执行原理
Python程序执行示意图
- 操作系统会首先让 CPU 把 Python 解释器 的程序复制到 内存 中
- Python 解释器 根据语法规则,从上向下 让 CPU 翻译 Python 程序中的代码
- CPU 负责执行翻译完成的代码
Python 的解释器有多大?
- 执行以下终端命令可以查看 Python 解释器的大小
# 1\. 确认解释器所在位置$ which python# 2\. 查看 python 文件大小(只是一个软链接)$ ls -lh /usr/bin/python# 3\. 查看具体文件大小$ ls -lh /usr/bin/python2.7
4. 变量
4.1 变量定义
- 在 Python 中,每个变量 在使用前都必须赋值,变量 赋值以后 该变量 才会被创建
- 可以用 其他变量的计算结果 来定义变量
- 变量名 只有在 第一次出现 才是 定义变量
变量名 = 值
使用交互式方式,如果要查看变量内容,直接输入变量名即可,不需要使用 print 函数
使用解释器执行,如果要输出变量的内容,必须要要使用 print 函数
4.2 变量的类型
- 在 Python 中定义变量是 不需要指定类型(在其他很多高级语言中都需要),Python 可以根据 = 等号右侧的值,自动推导出变量中存储数据的类型
- 数据类型可以分为 数字型 和 非数字型 数字型 整型 (int):Python3中的所有整数都表示为长整数。 因此,长整数没有单独的数字类型。 浮点型(float) 布尔型(bool) :真 True 非 0 数 —— 非零即真,假 False 0。 复数型 (complex):复数是由x + yj表示的有序对的实数浮点数组成,其中x和y是实数,j是虚数单位。 非数字型:有些运算符还支持这些数据类型,详见4.4.5.3 运算符。 字符串(str):加号(+)是字符串连接运算符,星号(*)是重复运算符。 列表(list) 元组(tuple) 字典(dict)
提示:在 Python 2.x 中,整数 根据保存数值的长度还分为:
int(整数) long(长整数)
- 使用 type 函数可以查看一个变量的类型
In [1]: type(name)
<补>不同类型变量之间的计算
- 数字型变量 之间可以直接计算
- 在 Python 中,两个数字型变量是可以直接进行 算数运算的
- 如果变量是 bool 型,在计算时 True 对应的数字是 1 False 对应的数字是 0
- 字符串变量 之间使用 + 拼接字符串
- 字符串变量 可以和 整数 使用 * 重复拼接相同的字符串
- 数字型变量 和 字符串 之间 不能进行其他计算
<补>从键盘获取输入信息:input
- 在 Python 中可以使用 input 函数从键盘等待用户的输入
- 用户输入的 任何内容 Python 都认为是一个 字符串
字符串变量 = input("提示信息:")
<补>类型转换函数
函数 说明 int(x) 将 x 转换为一个整数 float(x) 将 x 转换到一个浮点数 str(x) 将对象x转换为字符串表示形式 tuple(s) 将s转换为元组 list(s) 将s转换为列表
price = float(input("请输入价格:"))
<补>格式化输出:print
- 如果希望输出文字信息的同时,一起输出 数据,就需要使用到 格式化操作符
- % 被称为 格式化操作符,专门用于处理字符串中的格式 包含 % 的字符串,被称为 格式化字符串 % 和不同的 字符 连用,不同类型的数据 需要使用 不同的格式化字符
格式化字符 含义 %s 字符串 %d 有符号十进制整数,%06d 表示输出的整数显示位数,不足的地方使用 0 补全 %f 浮点数,%.2f 表示小数点后只显示两位 %% 输出 %
- 语法格式如下:
print("格式化字符串" % 变量1)print("格式化字符串" % (变量1, 变量2...))
4.4.5 公共方法和变量的高级应用
4.4.5.1 内置函数
Python 包含了以下内置函数:
函数 描述 备注 len(item) 计算容器中元素个数 del(item) 删除变量 del 有两种方式 max(item) 返回容器中元素最大值 如果是字典,只针对 key 比较 min(item) 返回容器中元素最小值 如果是字典,只针对 key 比较 cmp(item1, item2) 比较两个值,-1 小于 / 0 相等 / 1 大于 Python 3.x 取消了 cmp 函数
注意:字符串 比较符合以下规则: “0” < “A” < “a”。
4.4.5.2 切片
描述 Python 表达式 结果 支持的数据类型 切片 “0123456789”[::-2] “97531” 字符串、列表、元组
- 切片 使用 索引值 来限定范围,从一个大的 字符串 中 切出 小的 字符串
- 列表 和 元组 都是 有序 的集合,都能够 通过索引值 获取到对应的数据
- 字典 是一个 无序 的集合,是使用 键值对 保存数据
面向对象编程 —— Object Oriented Programming 简写 OOP
- 面向过程 —— 怎么做?
把完成某一个需求的 所有步骤 从头到尾 逐步实现
根据开发需求,将某些 功能独立 的代码 封装 成一个又一个 函数
最后完成的代码,就是顺序地调用 不同的函数
特点:
注重 步骤与过程,不注重职责分工
如果需求复杂,代码会变得很复杂
开发复杂项目,没有固定的套路,开发难度很大! - 面向对象 —— 谁来做? 相比较函数,面向对象 是更大的封装,根据职责在 一个对象中封装多个方法 在完成某一个需求前,首先确定 职责 —— 要做的事情(方法)
根据 职责 确定不同的 对象,在 对象 内部封装不同的 方法(多个)
最后完成的代码,就是顺序地让 不同的对象 调用 不同的方法
特点:
注重 对象和职责,不同的对象承担不同的职责
更加适合应对复杂的需求变化,是专门应对复杂项目开发,提供的固定套路
需要在面向过程基础上,再学习一些面向对象的语法 - 类和对象
类 是对一群具有 相同 特征 或者 行为 的事物的一个统称,是抽象的,特征 被称为 属性,行为 被称为 方法。
对象 是 由类创建出来的一个具体存在,是类的实例化。
在程序开发中,要设计一个类,通常需要满足一下三个要素: 类名 这类事物的名字,满足大驼峰命名法 属性 这类事物具有什么样的特征 方法 这类事物具有什么样的行为
2. 面向对象基础语法
2.1 dir 内置函数和内置方法
在 Python 中 对象几乎是无所不在的,我们之前学习的 变量、数据、函数 都是对象。
在 Python 中可以使用以下两个方法验证:
- 在 标识符 / 数据 后输入一个点 .,然后按下 TAB 键,iPython 会提示该对象能够调用的方法列表。
- 使用内置函数 dir 传入 标识符 / 数据,可以查看对象内的 所有属性及方法。
提示__方法名__格式的方法是 Python 提供的 内置方法 / 属性。
序号 方法名 类型 作用 01 new 方法 创建对象时,会被 自动 调用 02 init 方法 对象被初始化时,会被 自动 调用 03 del 方法 对象被从内存中销毁前,会被 自动 调用 04 str 方法 返回对象的描述信息,print 函数输出使用
提示 利用好 dir() 函数,在学习时很多内容就不需要死记硬背了。
2.2 定义简单的类(只包含方法)
面向对象是更大的封装,在 一个类中封装多个方法,这样通过这个类创建出来的对象,就可以直接调用这些方法了!
定义一个只包含方法的类:
class 类名: def 方法1(self, 参数列表): pass def 方法2(self, 参数列表): pass
方法 的定义格式和之前学习过的函数几乎一样,区别在于第一个参数必须是 self。
注意:类名 的 命名规则 要符合 大驼峰命名法。
当一个类定义完成之后,要使用这个类来创建对象,语法格式如下:
对象变量 = 类名()
在面向对象开发中,引用的概念是同样适用的!
使用 print输出 对象变量,默认情况下,是能够输出这个变量 引用的对象 是 由哪一个类创建的对象,以及 在内存中的地址(十六进制表示)。
提示:在计算机中,通常使用 十六进制 表示 内存地址。
如果在开发中,希望使用 print输出 对象变量 时,能够打印 自定义的内容,就可以利用 __str__这个内置方法了:
class Cat: def __init__(self, new_name): self.name = new_name print("%s 来了" % self.name) def __del__(self): print("%s 去了" % self.name) def __str__(self): return "我是小猫:%s" % self.nametom = Cat("Tom")print(tom)
注意:__str__方法必须返回一个字符串。
2.3 方法中的 self 参数
在 Python 中,要 给对象设置属性,非常的容易,只需要在 类的外部的代码 中直接通过 对象.设置一个属性即可,但是不推荐使用:
class Cat: """这是一个猫类""" def eat(self): print("小猫爱吃鱼") def drink(self): print("小猫在喝水")tom = Cat()# 给对象设置属性tom.name = "Tom"
因为:对象属性的封装应该封装在类的内部
由哪一个对象调用的方法,方法内的 self就是哪一个对象的引用
- 在类封装的方法内部,self 就表示当前调用方法的对象自己,在方法内部:
可以通过 self.访问对象的属性,也可以通过 self.调用对象的其他方法。 - 调用方法时,程序员不需要传递 self 参数。
- 在 类的外部,通过变量名.访问对象的 属性和方法
在 类封装的方法中,通过 self.访问对象的 属性和方法
2.4 初始化方法:init
- 当使用 类名() 创建对象时,会 自动 执行以下操作:
为对象在内存中分配空间 —— 创建对象
为对象的属性设置初始值 —— 初始化方法(init)
init 方法是 专门 用来定义一个类具有哪些属性的方法!
- 在 init 方法内部使用 self.属性名 = 属性的初始值 就可以定义属性,定义属性之后,再使用 类创建的对象,都会拥有该属性。
- 在开发中,如果希望在 创建对象的同时,就设置对象的属性,可以对 init 方法进行 改造: 把希望设置的属性值,定义成 __init__方法的参数 在方法内部使用 self.属性 = 形参 接收外部传递的参数 在创建对象时,使用 类名(属性1, 属性2…) 调用
class Cat: def __init__(self, name): print("初始化方法 %s" % name) self.name = name ... tom = Cat("Tom")...lazy_cat = Cat("大懒猫")...
2.5 私有属性和私有方法
应用场景
- 在实际开发中,对象 的 某些属性或方法 可能只希望 在对象的内部被使用,而 不希望在外部被访问到
- 私有属性 就是 对象 不希望公开的 属性
- 私有方法 就是 对象 不希望公开的 方法
定义方式
- 在 定义属性或方法时,在 属性名或者方法名前 增加 两个下划线,定义的就是 私有 属性或方法:
私有属性和私有方法
伪私有属性和私有方法
Python 中,并没有 真正意义 的 私有
在给 属性、方法 命名时,实际是对名称做了一些特殊处理,使得外界无法访问到
处理方式:在 名称 前面加上_类名 => _类名__名称
# 私有属性,外部不能直接访问到print(xiaofang._Women__age)# 私有方法,外部不能直接调用xiaofang._Women__secret()
提示:在日常开发中,不要使用这种方式,访问对象的 私有属性 或 私有方法。
3. 封装、继承和多态
面向对象三大特性:
- 封装 根据 职责 将 属性 和 方法 封装 到一个抽象的 类 中
- 继承 实现代码的重用,相同的代码不需要重复的编写
- 多态 不同的对象调用相同的方法,产生不同的执行结果,增加代码的灵活度
3.1 继承
3.1.1 单继承
继承的概念:子类 拥有 父类 以及 父类的父类 中封装的所有 属性 和 方法。
class 类名(父类名): pass
当 父类 的方法实现不能满足子类需求时,可以对方法进行重写(override)
重写 父类方法有两种情况:
- 覆盖 父类的方法:父类的方法实现 和 子类的方法实现完全不同
具体的实现方式,就相当于在 子类中 定义了一个 和父类同名的方法并且实现。 - 对父类方法进行 扩展:子类的方法实现 中 包含 父类的方法实现
在子类中 重写 父类的方法;在需要的位置使用 super().父类方法 来调用父类方法的执行代码;其他的位置针对子类的需求,编写 子类特有的代码实现。
关于 super
- 在 Python 中 super 是一个 特殊的类
- super()就是使用 super 类创建出来的对象
- 最常 使用的场景就是在 重写父类方法时,调用 在父类中封装的方法实现
调用父类方法的另外一种方式:在 Python 2.x 时,如果需要调用父类的方法,还可以使用以下方式:父类名.方法(self)。目前在 Python 3.x 还支持这种方式,但不推荐使用,因为一旦 父类发生变化,方法调用位置的 类名 同样需要修改。
父类的 私有属性 和 私有方法
子类对象 不能 在自己的方法内部,直接 访问 父类的 私有属性 或 私有方法
子类对象 可以通过 父类 的 公有方法 间接 访问到 私有属性 或 私有方法
私有属性、方法 是对象的隐私,不对外公开,外界 以及 子类 都不能直接访问 私有属性、方法 通常用于做一些内部的事情
3.1.2 多继承
子类 可以拥有 多个父类,并且具有 所有父类 的 属性 和 方法,例如:孩子 会继承自己 父亲 和 母亲 的 特性。
class 子类名(父类名1, 父类名2...): pass
Python 中的 MRO算法(Method Resolution Order)
- 如果 不同的父类 中存在 同名的方法,子类对象 在调用方法时,会调用 哪一个父类中的方法呢?
提示:开发时,应该尽量避免这种容易产生混淆的情况! —— 如果 父类之间 存在 同名的属性或者方法,应该 尽量避免使用多继承。 - Python 中针对 类 提供了一个 内置属性__mro__ 可以查看 方法 搜索顺序 在搜索方法时,是按照 mro 的输出结果 从左至右 的顺序查找的 如果在当前类中 找到方法,就直接执行,不再搜索 如果 没有找到,就查找下一个类 中是否有对应的方法,如果找到,就直接执行,不再搜索 如果找到最后一个类,还没有找到方法,程序报错
MRO 是 method resolution order —— 方法搜索顺序,主要用于 在多继承时判断 方法、属性 的调用 路径
新式类与旧式(经典)类
- 新式类:以 object 为基类的类,推荐使用
- 经典类:不以 object为基类的类,不推荐使用
在 Python 3.x 中定义类时,如果没有指定父类,会 默认使用 object作为该类的 基类 —— Python 3.x 中定义的类都是 新式类,在 Python 2.x 中定义类时,如果没有指定父类,则不会以 object 作为 基类。
- 为了保证编写的代码能够同时在 Python 2.x 和 Python 3.x 运行!今后在定义类时,如果没有父类,建议统一继承自 object:
class 类名(object): pass
object 是 Python 为所有对象提供的 基类,提供有一些内置的属性和方法,可以使用 dir(object) 函数查看。
3.2 多态
面向对象三大特性:
- 封装 根据 职责 将 属性 和 方法 封装 到一个抽象的 类 中 定义类的准则
- 继承 实现代码的重用,相同的代码不需要重复的编写 设计类的技巧 子类针对自己特有的需求,编写特定的代码
- 多态 不同的 子类对象 调用相同的 父类方法,产生不同的执行结果 增加代码的灵活度 以 继承 和 重写父类方法 为前提 调用方法的技巧,不会影响到类的内部设计
多态 更容易编写出出通用的代码,做出通用的编程,以适应需求的不断变化!
案例:
在 Dog 类中封装方法 game:普通狗只是简单的玩耍
定义 XiaoTianDog 继承自 Dog,并且重写 game 方法:哮天犬需要在天上玩耍
定义 Person 类,并且封装一个 和狗玩 的方法:在方法内部,直接让 狗对象 调用 game 方法
多态示例
Person 类中只需要让 狗对象 调用 game 方法,而不关心具体是 什么狗。
4. 类属性和类方法
4.1 类的结构
通常会把:
创建出来的 对象 叫做 类的实例
创建对象的 动作 叫做 实例化
对象的属性 叫做 实例属性
对象调用的方法 叫做 实例方法
每一个对象 都有自己独立的内存空间,保存各自不同的属性
多个对象的方法,在内存中只有一份,在调用方法时,需要把对象的引用传递到方法内部
各个不同的属性,独一份的方法
在 Python 中,类是一个特殊的对象。
Python 中 一切皆对象:
class AAA: 定义的类属于 类对象 obj1 = AAA() 属于 实例对象
在程序运行时,类同样会被加载到内存
在程序运行时,类对象在内存中只有一份,使用 一个类可以创建出很多个对象实例
除了封装实例的属性和方法外,类对象还可以拥有自己的属性和方法——类属性、类方法,通过 类名. 的方式可以 访问类的属性 或者 调用类的方法
类的结构
4.2 类属性和实例属性
类属性 就是 类对象中定义的属性
通常用来记录与这个类相关的特征
类属性不会用于记录具体对象的特征
示例:
定义一个 工具类,每件工具都有自己的 name:
需求 —— 知道使用这个类,创建了多少个工具对象?
属性的获取机制
在 Python 中 属性的获取 存在一个 向上查找机制
因此,要访问类属性有两种方式:
- 类名.类属性
- 对象.类属性 (不推荐,因为如果使用 对象.类属性 = 值 赋值语句,只会给对象添加一个属性,而不会影响到类属性的值)
4.3 类方法和静态方法
4.3.1 类方法
- 类属性 就是针对 类对象 定义的属性 使用 赋值语句 在 class 关键字下方可以定义 类属性 类属性 用于记录 与这个类相关 的特征
- 类方法 就是针对 类对象 定义的方法 在 类方法 内部可以直接访问 类属性 或者调用其他的 类方法
语法如下
@classmethoddef 类方法名(cls): pass
- 类方法需要用 修饰器 @classmethod 来标识,告诉解释器这是一个类方法
- 类方法的 第一个参数 应该是 cls 由 哪一个类 调用的方法,方法内的 cls 就是 哪一个类的引用 这个参数和 实例方法 的第一个参数是 self 类似 提示 使用其他名称也可以,不过习惯使用 cls
- 通过 类名. 调用 类方法,调用方法时,不需要传递 cls 参数
- 在方法内部 可以通过 cls. 访问类的属性 也可以通过 cls. 调用其他的类方法
示例
- 定义一个 工具类,每件工具都有自己的 name
- 需求 —— 在 类 封装一个 show_tool_count 的类方法,输出使用当前这个类,创建的对象个数
@classmethoddef show_tool_count(cls): """显示工具对象的总数""" print("工具对象的总数 %d" % cls.count)
4.3.2 静态方法
- 在开发时,如果需要在 类 中封装一个方法,这个方法: 既 不需要 访问 实例属性或者调用 实例方法 也 不需要 访问 类属性 或者调用 类方法
- 这个时候,可以把这个方法封装成一个 静态方法
语法如下
@staticmethoddef 静态方法名(): pass
- 静态方法 需要用 修饰器 @staticmethod 来标识,告诉解释器这是一个静态方法
- 通过 类名. 调用 静态方法
示例:
- 静态方法 show_help 显示游戏帮助信息
- 类方法 show_top_score 显示历史最高分
- 实例方法 start_game 开始当前玩家的游戏
探索:
- 实例方法 —— 方法内部需要访问 实例属性 实例方法 内部可以使用 类名. 访问类属性
- 类方法 —— 方法内部 只需要访问 类属性
- 静态方法 —— 方法内部,不需要访问 实例属性 和 类属性
5. 单例
5.1 单例设计模式
-
设计模式 设计模式 是 前人工作的总结和提炼,通常,被人们广泛流传的设计模式都是针对 某一特定问题 的成熟的解决方案 使用 设计模式 是为了可重用代码、让代码更容易被他人理解、保证代码可靠性
-
单例设计模式 目的 —— 让 类 创建的对象,在系统中 只有 唯一的一个实例 每一次执行 类名() 返回的对象,内存地址是相同的
-
单例设计模式的应用场景 音乐播放 对象 回收站 对象 打印机 对象 ……
5.2 静态方法: new
- 使用 类名() 创建对象时,Python 的解释器 首先 会 调用 new 方法为对象 分配空间
- new 是一个 由 object 基类提供的 内置的静态方法,主要作用有两个: 在内存中为对象 分配空间 返回 对象的引用
- Python 的解释器获得对象的 引用 后,将引用作为 第一个参数,传递给 init 方法
重写 new 方法 的代码非常固定!
- 重写 new 方法 一定要 return super().new(cls),否则 Python 的解释器 得不到 分配了空间的 对象引用,就不会调用对象的初始化方法
- 注意:new 是一个静态方法,在调用时需要 主动传递 cls 参数
5.3 Python 中的单例
- 单例 —— 让 类 创建的对象,在系统中 只有 唯一的一个实例 定义一个 类属性,初始值是 None,用于记录 单例对象的引用 重写 new 方法 如果 类属性 is None,调用父类方法分配空间,并在类属性中记录结果 返回 类属性 中记录的 对象引用
只执行一次初始化工作
- 在每次使用 类名() 创建对象时,Python 的解释器都会自动调用两个方法: new 分配空间 init 对象初始化
- 在对 new 方法改造之后,每次都会得到 第一次被创建对象的引用
- 但是:初始化方法还会被再次调用
需求
- 让 初始化动作 只被 执行一次
解决办法
- 定义一个类属性 init_flag 标记是否 执行过初始化动作,初始值为 False
- 在 init 方法中,判断 init_flag,如果为 False 就执行初始化动作
- 然后将 init_flag 设置为 True
- 这样,再次 自动 调用 init 方法时,初始化动作就不会被再次执行 了
Tips
1、Python 能够自动的将一对括号内部的代码连接在一起:
'''**需求*** 定义 `input_password` 函数,提示用户输入密码* 如果用户输入长度 < 8,抛出异常* 如果用户输入长度 >=8,返回输入的密码'''def input_password(): # 1\. 提示用户输入密码 pwd = input("请输入密码:") # 2\. 判断密码长度,如果长度 >= 8,返回用户输入的密码 if len(pwd) >= 8: return pwd # 3\. 密码长度不够,需要抛出异常 # 1> 创建异常对象 - 使用异常的错误信息字符串作为参数 ex = Exception("密码长度不够") # 2> 抛出异常对象 raise extry: user_pwd = input_password() print(user_pwd)except Exception as result: print("发现错误:%s" % result)
2、一个对象的 属性 可以是 另外一个类创建的对象。
3、在__init__方法中定义类的属性时,如果 不知道设置什么初始值,可以设置为 None):None 关键字 表示 什么都没有,表示一个 空对象,没有方法和属性,是一个特殊的常量。可以将 None 赋值给任何一个变量。
在 Python 中针对 None 比较时,建议使用is 判断
4、eval() 函数十分强大 —— 将字符串 当成 有效的表达式 来求值 并 返回计算结果
在开发时千万不要使用 eval 直接转换 input 的结果,举个例子:
__import__('os').system('ls')# 等价代码import osos.system("终端命令")
。
收藏
举报
*10 *条评论