Python
1 Python
1.1 解释器
计算机不能直接理解任何除机器语言以外的语言,所以必须要把
程序员所写的程序语言翻译成机器语言,计算机才能执行程序。将
其他语言翻译成机器语言的工具,被称为编译器
编译器翻译的方式有两种:一个是编译,另外一个是解释。两
种方式之间的区别在于翻译时间点的不同。当编译器以解释
方式运行的时候,也称之为解释器
编译型语言:程序在执行之前需要一个专门的编译过程,把程
序编译成为机器语言的文件,运行时不需要重新翻译,直接使
用编译的结果就行了。程序执行效率高,依赖编译器,跨平台性差些。
如 C、C++
解释型语言:解释型语言编写的程序不进行预先编译,以文本方式
存储程序代码,会将代码一句一句直接运行。在发布程序时,看
起来省了道编译工序,但是在运行程序的时候,必须先解释再运行
1.1.1 编译型语言和解释型语言对比
速度 —— 编译型语言比解释型语言执行速度快
跨平台性 —— 解释型语言比编译型语言跨平台性好
1.2 Python 特点
Python 是完全面向对象的语言
函数、模块、数字、字符串都是对象,在 Python 中一切皆对象
完全支持继承、重载、多重继承
支持重载运算符,也支持泛型设计
Python 拥有一个强大的标准库,Python 语言的核心只包含 数字、
字符串、列表、字典、文件 等常见类型和函数,而由 Python 标
准库提供了 系统管理、网络通信、文本处理、数据库接口、图
形系统、XML 处理 等额外的功能
Python 社区提供大量的第三方模块,使用方式与标准库类似。
它们的功能覆盖 科学计算、人工智能、机器学习、Web 开发、
数据库接口、图形系统 多个领域
1.3 第一个 HelloPython程序
1.3.1 Python 源程序的基本概念
1. Python 源程序就是一个特殊格式的文本文件,可以使用任意文
本编辑软件做 Python的开发
2. Python 程序的文件扩展名通常都是 py
1.3.2 演练步骤
print ( "hello python" )
print ( "hello world" )
在终端中输入以下命令执行 01-HelloPython.py
$ python 01-HelloPython.py
print是 python中我们学习的第一个函数
print函数的作用,可以把 ""内部的内容,输出到屏幕上
1.4 执行 Python 程序的三种方式
1. Python 的解释器
# 使用 python 2.x 解释器
$ python xxx.py
# 使用 python 3.x 解释器
$ python3 xxx.py
2. 交互式运行 Python 程序
直接在终端中运行解释器,而不输入要执行的文件名
在 Python 的 Shell中直接输入 Python 的代码,会立即看
到程序执行结果
3. Python 的 IDE —— PyCharm
2 注释
2.1 注释的作用
使用用自己熟悉的语言,在程序中对某些代码进行标注说
明,增强程序的可读性
2.2 单行注释(行注释)
以 #开头,#右边的所有东西都被当做说明文字,而不
是真正要执行的程序,只起到辅助说明作用
在代码上方增加的单行注释
为了保证代码的可读性,#后面建议先添加一个空格,然后
再编写相应的说明文字
在代码后面增加的单行注释
但是,需要注意的是,为了保证代码的可读性,注释和代
码之间至少要有两个空格
2.3 多行注释(块注释)
如果希望编写的 注释信息很多,一行无法显示,就可以使用多行注释
要在 Python 程序中使用多行注释,可以用 一对 连续的三
个引号(单引号和双引号都可以)
print ( "hello hello" )
"""
这是一个多行注释
。。。。
。。。。
。。。。
注释结束了
"""
print ( "hello world" )
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
在 Python 中 *运算符还可以用于字符串,计算结果就是
字符串重复指定次数的结果
In [ 1 ] : "-" * 50
Out[ 1 ] : '----------------------------------------'
3.2 算数运算符的优先级
和数学中的运算符的优先级一致,在 Python 中进行数学计算时,
同样也是:
先乘除后加减
同级运算符是从左至右计算
可以使用 ()调整计算的优先级
以下表格的算数优先级由高到最低顺序排列
运算符 描述 ** 幂 (最高优先级) * / % // 乘、除、取余数、取整除 + - 加法、减法
例如:
2 + 3 * 5 = 17
2 + 3) * 5 = 25
2 * 3 + 5 = 11
2 * (3 + 5) = 16
4 变量的基本使用
4.1 变量定义
在 Python 中,每个变量在使用前都必须赋值,变量赋值
以后该变量才会被创建
等号(=)用来给变量赋值
=左边是一个变量名
=右边是存储在变量中的值
变量名 = 值
4.1.1 变量演练1 —— iPython
In [ 1 ] : qq_number = "1234567"
In [ 2 ] : qq_number
Out[ 2 ] : '1234567'
In [ 3 ] : qq_password = "123"
In [ 4 ] : qq_password
Out[ 4 ] : '123'
使用交互式方式,如果要查看变量内容,直接输入变量名即可,
不需要使用 print`函数
4.1.2 变量演练 2 —— PyCharm
qq_number = "1234567"
qq_password = "123"
print ( qq_number)
print ( qq_password)
使用解释器执行,如果要输出变量的内容,必须要要使用 print函数
4.1.3 变量演练 3 —— 超市买苹果
需求
苹果的价格是 8.5 元/斤
买了 7.5 斤苹果
计算付款金额
price = 8.5
weight = 7.5
money = weight * price
money = money - 5
print ( money)
4.2 变量的类型
在内存中创建一个变量,会包括:
1. 变量的名称
2. 变量保存的数据
3. 变量存储数据的类型
4. 变量的地址(标示)
4.2.1 变量类型的演练 —— 个人信息
需求
定义变量保存小明的个人信息
姓名:小明
年龄:18岁
性别:是男生
身高:1.75米
体重:75.0公斤
"""
姓名:小明
年龄:18 岁
性别:是男生
身高:1.75 米
体重:75.0 公斤
"""
name = "小明"
age = 18
gender = False
height = 1.75
weight = 75
print ( name)
4.2.2 变量的类型
在 Python中定义变量是 不需要指定类型(在其他很
多高级语言中都需要)
数据类型可以分为 数字型 和 非数字型
数字型
整型 (int)
浮点型(float)
布尔型(bool)
真 True 非 0 数 —— 非零即真
假 False 0
复数型 (complex)
主要用于科学计算,例如:平面场问题、波动问题、电感电容等问题
非数字型
字符串
列表
元组
字典
提示:在 Python 2.x 中,整数根据保存数值的长度还分为:
int(整数)
long(长整数)
使用 type函数可以查看一个变量的类型
In [ 1 ] : type ( name)
4.2.3 不同类型变量之间的计算
数字型变量之间可以直接计算
在 Python 中,两个数字型变量是可以直接进行 算数运算的
如果变量是 bool型,在计算时
True对应的数字是1
False对应的数字是0
1. 定义整数 i = 10
2. 定义浮点数 f = 10.5
3. 定义布尔型 b = True
4.2.4 字符串变量 之间使用+拼接字符串
在 Python 中,字符串之间可以使用+拼接生成新的字符串
In [ 1 ] : first_name = "三"
In [ 2 ] : last_name = "张"
In [ 3 ] : first_name + last_name
Out[ 3 ] : '三张'
4.2.5 字符串变量可以和整数使用*重复拼接相同的字符串
In [ 1 ] : "-" * 50
Out[ 1 ] : '--------------------------------------------------'
4.2.6 数字型变量和字符串之间不能进行其他计算
In [ 1 ] : first_name = "zhang"
In [ 2 ] : x = 10
In [ 3 ] : x + first_name
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
TypeError: unsupported operand type ( s) for + : 'int' and 'str'
类型错误:+ 不支持的操作类型:int 和str
4.3 变量的输入
所谓输入,就是用代码获取用户通过键盘输入的信息
在 Python 中,如果要获取用户在键盘上的输入信息,
需要使用到 input函数
4.3.1 关于函数
4.3.1.1 input 函数实现键盘输入
在 Python 中可以使用input函数从键盘等待用户的输入
用户输入的任何内容 Python 都认为是一个字符串
字符串变量 = input ( "提示信息:" )
4.3.1.2 类型转换函数
函数 说明 int(x) 将 x 转换为一个整数 float(x) 将 x 转换到一个浮点数
4.3.1.3 变量输入演练 —— 超市买苹果增强版
需求
收银员输入苹果的价格,单位:元/斤
收银员输入用户购买苹果的重量,单位:斤
计算并且输出付款金额
4.3.1.3.1 演练方式 1
price_str = input ( "苹果的单价:" )
weight_str = input ( "苹果的重量:" )
price = float ( price_str)
weight = float ( weight_str)
money = price * weight
print ( money)
4.3.1.3.2 演练方式 2
定义一个浮点变量接收用户输入的同时,就使用 floa函数进行转换
price = float ( input ( "苹果的单价:" ) )
weight = float ( input ( "苹果的重量:" ) )
money = price * weight
print ( money)
4.4 变量的格式化输出
苹果单价 9.00元/斤,购买了 5.00斤,需要支付45.00元
在 Python 中可以使用 print 函数将信息输出到控制台
如果希望输出文字信息的同时一起输出数据,就需要使用到
格式化操作符
%被称为格式化操作符,专门用于处理字符串中的格式
包含%的字符串,被称为格式化字符串
%和不同的字符连用不同类型的数据需要使用不同的格式化字符
格式化字符 含义 %s 字符串 %d 有符号十进制整数,%06d
表示输出的整数显示位数,不足的地方使用 0
补全 %f 浮点数,%.2f
表示小数点后只显示两位 %% 输出 %
语法格式如下:
print ( "格式化字符串" % 变量1 )
print ( "格式化字符串" % ( 变量1 , 变量2 . . . ) )
4.4.1 格式化输出演练 —— 基本练习
需求
1. 定义字符串变量name,输出我的名字叫 小明,请多多关照!
2. 定义整数变量student_no,输出我的学号是 000001
3. 定义小数 price、weight、money,输出苹果单
价 9.00 元/斤,购买了 5.00 斤,需要支付 45.00 元
4. 定义一个小数 scale,输出数据比例是 10.00%
name = "大小明"
print ( "我的名字叫 %s,请多多关照!" % name)
student_no = 100123456
print ( "我的学号是 %06d" % student_no)
price = 8.5
weight = 7.5
money = price * weight
print ( "苹果单价 %.2f 元/斤,购买了 %.3f 斤,需要支付 %.4f 元" % ( price, weight, money) )
scale = 0.8
print ( "数据比例是 %.2f%%" % ( scale * 100 ) )
4.5 变量的命名
4.5.1 标识符
标识符可以由字母、下划线和数字组成
不能以数字开头
不能与关键字重名
4.5.2 关键字
关键字就是在 Python内部已经使用的标识符
关键字具有特殊的功能和含义
开发者不允许定义和关键字相同的名字的标示符
通过以下命令可以查看 Python中的关键字
In [ 1 ] : import keyword
In [ 2 ] : print ( keyword. kwlist)
4.5.3 变量的命名规则
命名规则可以被视为一种惯例,并无绝对与强制
目的是为了增加代码的识别和可读性
注意Python中的标识符是 区分大小写的
1. 在定义变量时,为了保证代码格式,= 的左右应该各保留一个空格
2. 在 Python中,如果变量名需要由二个或多个单词组成时,可以
按照以下方式命名
3. 每个单词都使用小写字母
4. 单词与单词之间使用_下划线连接
例如:first_name、last_name、qq_number、qq_password
4.5.4 驼峰命名法
当变量名是由二个或多个单词组成时,还可以利用驼峰命名法来命名
小驼峰式命名法
第一个单词以小写字母开始,后续单词的首字母大写
例如:`firstName`、`lastName`
大驼峰式命名法
每一个单词的首字母都采用大写字母
例如:`FirstName`、`LastName`、`CamelCase`
5 判断(if)语句
5.1 if 判断语句基本语法
if 要判断的条件:
条件成立时,要做的事情
……
5.2 判断语句演练 —— 判断年龄
需求
1. 定义一个整数变量记录年龄
2. 判断是否满 18 岁 (>=)
3. 如果满 18 岁,允许进网吧嗨皮
age = 15
if age >= 18 :
print ( "你已经成年,欢迎进网吧嗨皮" )
print ( "欢迎欢迎,热泪欢迎!" )
print ( "看看什么时候会执行" )
5.3 else 处理条件不满足的情况
if 要判断的条件:
条件成立时,要做的事情
……
else :
条件不成立时,要做的事情
……
5.4 判断语句演练 —— 判断年龄改进
需求
1. 输入用户年龄
2. 判断是否满 18 岁 (>=)
3. 如果满 18 岁,允许进网吧嗨皮
4. 如果未满 18 岁,提示回家写作业
age = int ( input ( "请输入年龄:" ) )
if age >= 18 :
print ( "你已经成年,欢迎来网吧嗨皮" )
else :
print ( "你还没有成年,请回家写作业吧" )
print ( "这句代码什么时候执行?" )
6 逻辑运算
Python 中的逻辑运算符包括:与 and/or/not三种
6.1 and
条件1 and 条件2
两个条件同时满足,返回 True
只要有一个不满足,就返回 False
条件 1 条件 2 结果 成立 成立 成立 成立 不成立 不成立 不成立 成立 不成立 不成立 不成立 不成立
6.2 or
条件1 or 条件2
两个条件只要有一个满足,返回 True
两个条件都不满足,返回 False
条件 1 条件 2 结果 成立 成立 成立 成立 不成立 成立 不成立 成立 成立 不成立 不成立 不成立
6.3 not
not 条件
6.4 逻辑运算演练
1. 练习1: 定义一个整数变量 age,编写代码判断年龄是否正确
要求人的年龄在 0-120 之间
2. 练习2: 定义两个整数变量 python_score、c_score,编写
代码判断成绩
要求只要有一门成绩 > 60 分就算合格
3. 练习3: 定义一个布尔型变量 is_employee,编写代码判断是
否是本公司员工
如果不是提示不允许入内
答案 1:
age = 12
"""
10000
age >= 0 or age <= 120
age >= 0 and age <= 120
"""
if age >= 0 and age <= 120 :
print ( "年龄正确" )
else :
print ( "年龄不正确" )
答案 2:
python_score = 50
c_score = 50
if python_score > 60 or c_score > 60 :
print ( "考试通过" )
else :
print ( "考试失败,继续努力" )
)
答案 3:
is_employee = False
if not is_employee:
print ( "非本公司人员,请勿入内" )
7 if 语句进阶
7.1 elif
在开发中,使用 if可以判断条件
使用 else可以处理条件不成立的情况
但是,如果希望再增加一些条件,条件不同,需要执行的
代码也不同时,就可以使用 elif`
语法格式如下:
if 条件1 :
条件1 满足执行的代码
……
elif 条件2 :
条件2 满足时,执行的代码
……
elif 条件3 :
条件3 满足时,执行的代码
……
else :
以上条件都不满足时,执行的代码
……
7.2 elif 演练 —— 女友的节日
需求
1. 定义holiday_name字符串变量记录节日名称
2. 如果是情人节应该 买玫瑰看电影
3. 如果是平安夜应该买苹吃大餐
4. 如果是生日应该买蛋糕
5. 其他的日子每天都是节日啊……
holiday_name = "生日"
if holiday_name == "情人节" :
print ( "买玫瑰" )
print ( "看电影" )
elif holiday_name == "平安夜" :
print ( "买苹果" )
print ( "吃大餐" )
elif holiday_name == "生日" :
print ( "买蛋糕" )
else :
print ( "每天都是节日啊" )
7.3 if的嵌套
在开发中,使用if进行条件判断,如果希望在条件成立的执行语
句中再增加条件判断,就可以使用if 的嵌套
if 的嵌套的应用场景就是:在之前条件满足的前提下,再增加
额外的判断
if 的嵌套的语法格式,除了缩进之外和之前的没有区别
语法格式如下:
if 条件 1 :
条件 1 满足执行的代码
……
if 条件 1 基础上的条件 2 :
条件 2 满足时,执行的代码
……
else :
条件 2 不满足时,执行的代码
else :
条件1 不满足时,执行的代码
……
7.3.1 if 的嵌套演练 —— 火车站安检
需求
1. 定义布尔型变量has_ticke表示是否有车票
2. 定义整型变量knife_length表示刀的长度,单位:厘米
3. 首先检查是否有车票,如果有,才允许进行安检
4. 安检时,需要检查刀的长度,判断是否超过 20 厘米
如果超过 20 厘米,提示刀的长度,不允许上车
如果不超过 20 厘米,安检通过
5. 如果没有车票,不允许进门
has_ticket = True
knife_length = 10
if has_ticket:
print ( "车票检查通过,准备开始安检" )
if knife_length > 20 :
print ( "您携带的刀太长了,有 %d 公分长!" % knife_length)
print ( "不允许上车" )
else :
print ( "安检已经通过,祝您旅途愉快!" )
else :
print ( "大哥,请先买票" )
8 综合应用 —— 石头剪刀布
需求
1. 从控制台输入要出的拳 —— 石头(1)/剪刀(2)/布(3)
2. 电脑随机出拳 —— 先假定电脑只会出石头,完成整体代码功能
3. 比较胜负
序号 规则 1 石头 胜 剪刀 2 剪刀 胜 布 3 布 胜 石头
8.1 基础代码实现
import random
player = int ( input ( "请输入您要出的拳 石头(1)/剪刀(2)/布(3):" ) )
computer = random. randint( 1 , 3 )
print ( "玩家选择的拳头是 %d - 电脑出的拳是 %d" % ( player, computer) )
if ( ( player == 1 and computer == 2 )
or ( player == 2 and computer == 3 )
or ( player == 3 and computer == 1 ) ) :
print ( "欧耶,电脑弱爆了!" )
elif player == computer:
print ( "真是心有灵犀啊,再来一盘" )
else :
print ( "不服气,我们决战到天明!" )
9 运算符
9.1 算数运算符
运算符 描述 实例 + 加 10 + 20 = 30 - 减 10 - 20 = -10 * 乘 10 * 20 = 200 / 除 10 / 20 = 0.5 // 取整除 返回除法的整数部分(商) 9 // 2 输出结果 4 % 取余数 返回除法的余数 9 % 2 = 1 ** 幂 又称次方、乘方,2 ** 3 = 8
9.2 比较(关系)运算符
运算符 描述 == 检查两个操作数的值是否 相等 ,如果是,则条件成立,返回 True != 检查两个操作数的值是否 不相等 ,如果是,则条件成立,返回 True > 检查左操作数的值是否 大于 右操作数的值,如果是,则条件成立,返回 True < 检查左操作数的值是否 小于 右操作数的值,如果是,则条件成立,返回 True >= 检查左操作数的值是否 大于或等于 右操作数的值,如果是,则条件成立,返回 True <= 检查左操作数的值是否 小于或等于 右操作数的值,如果是,则条件成立,返回 True
Python 2.x 中判断不等于还可以使用 <>运算符
!=在 Python 2.x 中同样可以用来判断 不等于
9.3 逻辑运算符
运算符 逻辑表达式 描述 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
9.4 赋值运算符
在 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
9.5 运算符的优先级
以下表格的算数优先级由高到最低顺序排列
运算符 描述 ** 幂 (最高优先级) * / % // 乘、除、取余数、取整除 + - 加法、减法 <= < > >= 比较运算符 == != 等于运算符 = %= /= //= -= += *= **= 赋值运算符 not or and 逻辑运算符
10 循环
10.1 while语句基本语法
初始条件设置 —— 通常是重复执行的 计数器
while 条件( 判断 计数器 是否达到 目标次数) :
条件满足时,做的事情1
条件满足时,做的事情2
条件满足时,做的事情3
. . . ( 省略) . . .
处理条件( 计数器 + 1 )
10.1.1 第一个 while 循环
需求
打印 5 遍 Hello Python
i = 1
while i <= 5 :
print ( "Hello Python" )
i += 1
print ( "循环结束后,i = %d" % i)
10.1.2 死循环
由于程序员的原因,忘记在循环内部修改循环的判断条件,
导致循环持续执行,程序无法终止!
10.1.3 Python 中的计数方法
常见的计数方法有两种,可以分别称为:
自然计数法(从1开始)—— 更符合人类的习惯
程序计数法(从0开始)—— 几乎所有的程序语言都选择从0开始计数
因此,大家在编写程序时,应该尽量养成习惯:除非需求的特殊要求,
否则 循环 的计数都从 0 开始
10.1.4 循环计算
在程序开发中,通常会遇到利用循环重复计算的需求
遇到这种需求,可以:
1. 在while上方定义一个变量,用于存放最终计算结果
2. 在循环体内部,每次循环都用最新的计算结果,更新之
3. 前定义的变量
需求
计算 0 ~ 100 之间所有数字的累计求和结果
result = 0
i = 0
while i <= 100 :
print ( i)
result += i
i += 1
print ( "0~100之间的数字求和结果 = %d" % result)
计算 0 ~ 100 之间 所有 偶数的累计求和结果
result = 0
i = 0
while i <= 100 :
if i % 2 == 0 :
print ( i)
result += i
i += 1
print ( "0~100之间的偶数累加结果 = %d" % result)
10.2 break 和 continue
break和continue是专门在循环中使用的关键字
break某一条件满足时,退出循环,不再执行后续重复的代码
continue某一条件满足时,不执行后续重复的代码
break和continue只针对当前所在循环有效
10.2.1 break
在循环过程中,如果 某一个条件满足后,不再希望循环继续执行,可
以使用 break退出循环
i = 0
while i < 10 :
if i == 3 :
break
print ( i)
i += 1
print ( "over" )
break只针对当前所在循环有效
10.2.2 continue
在循环过程中,如果某一个条件满足后,不希望执行循环代码,但
是又不希望退出循环,可以使用 continue
也就是:在整个循环中,只有某些条件,不需要执行循环代码,而
其他条件都需要执行
i = 0
while i < 10 :
if i == 3 :
i += 1
continue
print ( i)
i += 1
continue只针对当前所在循环有效
10.3 while循环嵌套
while嵌套就是:while 里面还有 while
while 条件 1 :
条件满足时,做的事情1
条件满足时,做的事情2
条件满足时,做的事情3
. . . ( 省略) . . .
while 条件 2 :
条件满足时,做的事情1
条件满足时,做的事情2
条件满足时,做的事情3
. . . ( 省略) . . .
处理条件 2
处理条件 1
10.3.1 循环嵌套演练 —— 九九乘法表
第 1 步:用嵌套打印小星星
需求
在控制台连续输出五行 *,每一行星号的数量依次递增
*
**
***
****
*****
row = 1
while row <= 5 :
print ( "*" * row)
row += 1
第 2 步:使用循环嵌套打印小星星
对 print函数的使用做一个增强
在默认情况下,print函数输出内容之后,会自动在内容末尾增加换行
如果不希望末尾增加换行,可以在 print 函数输出内容的后面增
加 end=""`
其中 "" 中间可以指定 print函数输出内容之后,继续希望显示
的内容
print ( "*" , end= "---" )
print ( "*" )
end=""表示向控制台输出内容结束之后,不会换行
假设Python没有提供字符串的 * 操作拼接字符串
需求
在控制台连续输出五行 *,每一行星号的数量依次递增
开发步骤
1> 完成 5 行内容的简单输出
2> 分析每行内部的*应该如何处理?
每行显示的星星和当前所在的行数是一致的
嵌套一个小的循环,专门处理每一行中列的星星显示
row = 1
while row <= 5 :
col = 1
"""
1 1
2 2
3 3
4 4
5 5
"""
while col <= row:
print ( "*" , end= "" )
col += 1
print ( "" )
row += 1
第 3 步: 九九乘法表
1 * 1 = 1
1 * 2 = 2 2 * 2 = 4
1 * 3 = 3 2 * 3 = 6 3 * 3 = 9
1 * 4 = 4 2 * 4 = 8 3 * 4 = 12 4 * 4 = 16
1 * 5 = 5 2 * 5 = 10 3 * 5 = 15 4 * 5 = 20 5 * 5 = 25
1 * 6 = 6 2 * 6 = 12 3 * 6 = 18 4 * 6 = 24 5 * 6 = 30 6 * 6 = 36
1 * 7 = 7 2 * 7 = 14 3 * 7 = 21 4 * 7 = 28 5 * 7 = 35 6 * 7 = 42 7 * 7 = 49
1 * 8 = 8 2 * 8 = 16 3 * 8 = 24 4 * 8 = 32 5 * 8 = 40 6 * 8 = 48 7 * 8 = 56 8 * 8 = 64
1 * 9 = 9 2 * 9 = 18 3 * 9 = 27 4 * 9 = 36 5 * 9 = 45 6 * 9 = 54 7 * 9 = 63 8 * 9 = 72 9 * 9 = 81
开发步骤
1. 打印 9 行小星星
*
**
***
****
*****
******
*******
********
*********
2. 将每一个 * 替换成对应的行与列相乘
row = 1
while row <= 9 :
col = 1
while col <= row:
print ( "%d * %d = %d" % ( col, row, row * col) , end= "\t" )
col += 1
print ( "" )
row += 1
10.3.2 字符串中的转义字符
\t在控制台输出一个制表符,协助在输出文本时垂直方向保持对齐
\n在控制台输出一个 换行符
制表符的功能是在不使用表格的情况下在垂直方向按列对齐文本
转义字符 描述 \\ 反斜杠符号 \’ 单引号 \" 双引号 \n 换行 \t 横向制表符 \r 回车
11 函数
11.1 函数的定义
定义函数的格式如下:
def 函数名( ) :
函数封装的代码
……
11.2 第一个函数演练
需求
1. 编写一个打招呼 say_hello的函数,封装三行打招呼的代码
2. 在函数下方调用打招呼的代码
name = "小明"
def say_hello ( ) :
"""打招呼"""
print ( "hello 1" )
print ( "hello 2" )
print ( "hello 3" )
print ( name)
say_hello( )
print ( name)
11.3 函数的文档注释
在开发中,如果希望给函数添加注释,应该在定义函数的下方,使用
连续的三对引号
在连续的三对引号之间编写对函数的说明文字
在函数调用位置,使用快捷键 CTRL + Q可以查看函数的说明信息
注意:因为函数体相对比较独立,函数定义的上方,应该和其他
代码(包括注释)保留 两个空行
11.4 函数的参数
演练需求
1. 开发一个sum_2_num的函数
2. 函数能够实现两个数字的求和功能
演练代码如下:
def sum_2_num ( num1, num2) :
"""对两个数字的求和"""
result = num1 + num2
print ( "%d + %d = %d" % ( num1, num2, result) )
sum_2_num( 1 , 2 )
11.4.1 形参和实参
形参:定义函数时,小括号中的参数,是用来接收参数用的,在函数
内部 作为变量使用
实参:调用函数时,小括号中的参数,是用来把数据传递到函数内部用的
11.5 函数的返回值
在程序开发中,有时候,会希望一个函数执行结束后,告诉调用者一个结果,以便调用者针对具体的结果做后续的处理
返回值是函数完成工作后,最后 给调用者的一个结果
在函数中使用return关键字可以返回结果
调用函数一方,可以使用变量来接收函数的返回结果
注意:return表示返回,后续的代码都不会被执行
def sum_2_num ( num1, num2) :
"""对两个数字的求和"""
result = num1 + num2
return result
sum_result = sum_2_num( 10 , 20 )
print ( "计算结果:%d" % sum_result)
11.6 函数的嵌套调用
一个函数里面又调用了另外一个函数,这就是函数嵌套调用
def test1 ( ) :
print ( "*" * 50 )
def test2 ( ) :
print ( "-" * 50 )
test1( )
print ( "+" * 50 )
test2( )
12 模块
12.1 使用模块中的函数
模块就好比是工具包,要想使用这个工具包中的工具,就
需要导入 import这个模块
每一个以扩展名py结尾的Python源代码文件都是一个模块
在模块中定义的全局变量、函数都是模块能够提供给外界直
接使用的工具
可以在一个 Python 文件中定义 变量 或者 函数
然后在另外一个文件中使用import导入这个模块
导入之后,就可以使用模块名.变量/模块名.函数 的方式,使用
这个模块中定义的变量或者函数
模块可以让曾经编写过的代码方便的被复用!
import hm_10_分隔线模块
hm_10_分隔线模块. print_line( "-" , 50 )
print ( hm_10_分隔线模块. name)
12.2 模块名也是一个标识符
标示符可以由字母、下划线和数字组成
不能以数字开头
不能与关键字重名
13 列表(数组)
13.1 列表的定义
List(列表) 是 Python中使用 最频繁 的数据类型,在其他语言中
通常叫做数组
13.2 列表常用操作
序号 分类 关键字 / 函数 / 方法 说明 1 增加 列表.insert(索引, 数据) 在指定位置插入数据 列表.append(数据) 在末尾追加数据 列表.extend(列表2) 将列表2 的数据追加到列表 2 修改 列表[索引] = 数据 修改指定索引的数据 3 删除 del 列表[索引] 删除指定索引的数据 列表.remove[数据] 删除第一个出现的指定数据 列表.pop 删除末尾数据 列表.pop(索引) 删除指定索引数据 列表.clear 清空列表 4 统计 len(列表) 列表长度 列表.count(数据) 数据在列表中出现的次数 5 排序 列表.sort() 升序排序 列表.sort(reverse=True) 降序排序 列表.reverse() 逆序、反转
name_list = [ "zhangsan" , "lisi" , "wangwu" ]
print ( name_list[ 2 ] )
print ( name_list. index( "wangwu" ) )
name_list[ 1 ] = "李四"
name_list. append( "王小二" )
name_list. insert( 1 , "小美眉" )
temp_list = [ "孙悟空" , "猪二哥" , "沙师弟" ]
name_list. extend( temp_list)
name_list. remove( "wangwu" )
name_list. pop( )
name_list. pop( 3 )
name_list. clear( )
print ( name_list)
name_list = [ "张三" , "李四" , "王五" , "王小二" , "张三" ]
list_len = len ( name_list)
print ( "列表中包含 %d 个元素" % list_len)
count = name_list. count( "张三" )
print ( "张三出现了 %d 次" % count)
name_list. remove( "张三" )
print ( name_list)
name_list = [ "zhangsan" , "lisi" , "wangwu" , "wangxiaoer" ]
num_list = [ 6 , 8 , 4 , 1 , 10 ]
name_list. reverse( )
num_list. reverse( )
print ( name_list)
print ( num_list)
13.3 del 关键字
使用 del关键字(delete) 同样可以删除列表中元素
del关键字本质上是用来将一个变量从内存中删除的
如果使用del关键字将变量从内存中删除,后续的代码就不
能再使用这个变量了
name_list = [ "张三" , "李四" , "王五" ]
del name_list[ 1 ]
name = "小明"
del name
print ( name)
print ( name_list)
13.4 循环遍历
使用 for就能够实现迭代遍历
name_list = [ "张三" , "李四" , "王五" , "王小二" ]
"""
顺序的从列表中依次获取数据,每一次循环过程中,数据都会保存在
my_name 这个变量中,在循环体内部可以访问到当前这一次获取到的数据
for my_name in 列表变量:
print("我的名字叫 %s" % my_name)
"""
for my_name in name_list:
print ( "我的名字叫 %s" % my_name)
14 元组
14.1 元组的定义
Tuple(元组)与列表类似,不同之处在于元组的元素不能修改
元组表示多个元素组成的序列
14.1.1 创建空元组
info_tuple = ( )
14.1.2 元组中只包含一个元素时,需要在元素后面添加逗号
info_tuple = ( 50 , )
14.2 元组常用操作
info_tuple = ( "zhangsan" , 18 , 1.75 , "zhangsan" )
print ( info_tuple[ 0 ] )
print ( info_tuple. index( "zhangsan" ) )
print ( info_tuple. count( "zhangsan" ) )
print ( len ( info_tuple) )
14.3 循环遍历
info_tuple = ( "zhangsan" , 18 , 1.75 )
for my_info in info_tuple:
print ( my_info)
在 Python中,可以使用 for 循环遍历所有非数字型类型的变量:
列表、元组、字典 以及字符串
提示:在实际开发中,除非能够确认元组中的数据类型,否则针对
元组的循环遍历需求并不是很多
14.4 格式化字符串
格式字符串,格式化字符串后面的 ()本质上就是一个元组
info_tuple = ( "小明" , 21 , 1.85 )
print ( "%s 年龄是 %d 身高是 %.2f" % info_tuple)
info_str = "%s 年龄是 %d 身高是 %.2f" % info_tuple
print ( info_str)
14.5 元组和列表之间的转换
使用 list函数可以把元组转换成列表
list ( 元组)
使用tuple函数可以把列表转换成元组
tuple ( 列表)
15 字典
15.1 字典的定义
字典用 {}定义
xiaoming = { "name" : "小明" ,
"age" : 18 ,
"gender" : True ,
"height" : 1.75 ,
"weight" : 75.5 }
print ( xiaoming)
15.2 字典常用操作
xiaoming_dict = { "name" : "小明" }
print ( xiaoming_dict[ "name" ] )
xiaoming_dict[ "age" ] = 18
xiaoming_dict[ "name" ] = "小小明"
xiaoming_dict. pop( "name" )
print ( xiaoming_dict)
xiaoming_dict = { "name" : "小明" ,
"age" : 18 }
print ( len ( xiaoming_dict) )
temp_dict = { "height" : 1.75 ,
"age" : 20 }
xiaoming_dict. update( temp_dict)
xiaoming_dict. clear( )
print ( xiaoming_dict)
15.3 循环遍历
xiaoming_dict = { "name" : "小明" ,
"qq" : "123456" ,
"phone" : "10086" }
for k in xiaoming_dict:
print ( "%s - %s" % ( k, xiaoming_dict[ k] ) )
提示:在实际开发中,由于字典中每一个键值对保存数据的类型是不同的,所以针对字典的循环遍历需求并不是很多
15.4 应用场景
尽管可以使用for in遍历字典
但是在开发中,更多的应用场景是:
使用 多个键值对,存储描述一个物体的相关信息—— 描述更复
杂的数据信息
将多个字典放在一个列表中,再进行遍历,在循环体内部针对每一
个字典进行相同的处理
card_list = [
{ "name" : "张三" ,
"qq" : "12345" ,
"phone" : "110" } ,
{ "name" : "李四" ,
"qq" : "54321" ,
"phone" : "10086" }
]
for card_info in card_list:
print ( card_info)
16 字符串
16.1 字符串的常用操作
str1 = "hello python"
str2 = '我的外号是"大西瓜"'
print ( str2)
print ( str1[ 6 ] )
for char in str2:
print ( char)
hello_str = "hello hello"
print ( len ( hello_str) )
print ( hello_str. count( "llo" ) )
print ( hello_str. count( "abc" ) )
print ( hello_str. index( "llo" ) )
print ( hello_str. index( "abc" ) )
16.2 字符串判断方法
方法 说明 string.isspace() 如果 string 中只包含空格,则返回 True string.isalnum() 如果 string 至少有一个字符并且所有字符都是字母或数字则返回 True string.isalpha() 如果 string 至少有一个字符并且所有字符都是字母则返回 True string.isdecimal() 如果 string 只包含数字则返回 True,全角数字
string.isdigit() 如果 string 只包含数字则返回 True,全角数字
、⑴
、\u00b2
string.isnumeric() 如果 string 只包含数字则返回 True,全角数字
,汉字数字
string.istitle() 如果 string 是标题化的(每个单词的首字母大写)则返回 True string.islower() 如果 string 中包含至少一个区分大小写的字符,并且所有这些(区分大小写的)字符都是小写,则返回 True string.isupper() 如果 string 中包含至少一个区分大小写的字符,并且所有这些(区分大小写的)字符都是大写,则返回 True
space_str = " \t\n\r"
print ( space_str. isspace( ) )
num_str = "一千零一"
print ( num_str)
print ( num_str. isdecimal( ) )
print ( num_str. isdigit( ) )
print ( num_str. isnumeric( ) )
16.3 字符串的查找和替换
方法 说明 string.startswith(str) 检查字符串是否是以 str 开头,是则返回 True string.endswith(str) 检查字符串是否是以 str 结束,是则返回 True string.find(str, start=0, end=len(string)) 检测 str 是否包含在 string 中,如果 start 和 end 指定范围,则检查是否包含在指定范围内,如果是返回开始的索引值,否则返回 -1
string.rfind(str, start=0, end=len(string)) 类似于 find(),不过是从右边开始查找 string.index(str, start=0, end=len(string)) 跟 find() 方法类似,不过如果 str 不在 string 会报错 string.rindex(str, start=0, end=len(string)) 类似于 index(),不过是从右边开始 string.replace(old_str, new_str, num=string.count(old)) 把 string 中的 old_str 替换成 new_str,如果 num 指定,则替换不超过 num 次
hello_str = "hello world"
print ( hello_str. startswith( "Hello" ) )
print ( hello_str. endswith( "world" ) )
print ( hello_str. find( "llo" ) )
print ( hello_str. find( "abc" ) )
print ( hello_str. replace( "world" , "python" ) )
print ( hello_str)
16.4 大小写转换
方法 说明 string.capitalize() 把字符串的第一个字符大写 string.title() 把字符串的每个单词首字母大写 string.lower() 转换 string 中所有大写字符为小写 string.upper() 转换 string 中的小写字母为大写 string.swapcase() 翻转 string 中的大小写
16.5 文本对齐
方法 说明 string.ljust(width) 返回一个原字符串左对齐,并使用空格填充至长度 width 的新字符串 string.rjust(width) 返回一个原字符串右对齐,并使用空格填充至长度 width 的新字符串 string.center(width) 返回一个原字符串居中,并使用空格填充至长度 width 的新字符串
poem = [ "\t\n登鹳雀楼" ,
"王之涣" ,
"白日依山尽\t\n" ,
"黄河入海流" ,
"欲穷千里目" ,
"更上一层楼" ]
for poem_str in poem:
print ( "|%s|" % poem_str. strip( ) . center( 10 , " " ) )
16.6 去除空白字符
方法 说明 string.lstrip() 截掉 string 左边(开始)的空白字符 string.rstrip() 截掉 string 右边(末尾)的空白字符 string.strip() 截掉 string 左右两边的空白字符
16.7 字符串拆分和拼接
方法 说明 string.partition(str) 把字符串 string 分成一个 3 元素的元组 (str前面, str, str后面) string.rpartition(str) 类似于 partition() 方法,不过是从右边开始查找 string.split(str="", num) 以 str 为分隔符拆分 string,如果 num 有指定值,则仅分隔 num + 1 个子字符串,str 默认包含 ‘\r’, ‘\t’, ‘\n’ 和空格 string.splitlines() 按照行(’\r’, ‘\n’, ‘\r\n’)分隔,返回一个包含各行作为元素的列表 string.join(seq) 以 string 作为分隔符,将 seq 中所有的元素(的字符串表示)合并为一个新的字符串
poem_str = "登鹳雀楼\t 王之涣 \t 白日依山尽 \t \n 黄河入海流 \t\t 欲穷千里目 \t\t\n更上一层楼"
print ( poem_str)
poem_list = poem_str. split( )
print ( poem_list)
result = " " . join( poem_list)
print ( result)
16.8 字符串的切片
切片方法适用于字符串、列表、元组
切片使用索引值来限定范围,从一个大的字符串中切出小的字符串
列表和元组都是有序的集合,都能够通过索引值获取到对应的数据
字典是一个无序的集合,是使用键值对保存数据
字符串[开始索引:结束索引:步长]
注意:
1. 指定的区间属于 左闭右开型 [开始索引, 结束索引)=> 开始索引 >= 范围 < 结束索引
从 起始位开始,到结束位的前一位结束(不包含结束位本身)
2. 从头开始,开始索引数字可以省略,冒号不能省略
3. 到末尾结束,结束索引数字可以省略,冒号不能省略
4. 步长默认为 1,如果连续切片,数字和冒号都可以省略
16.9 索引的顺序和倒序
在 Python中不仅支持顺序索引,同时还支持倒序索引
所谓倒序索引就是从右向左计算索引
最右边的索引值- 1 ,依次递减
num_str = "0123456789"
print ( num_str[ 2 : 6 ] )
print ( num_str[ 2 : ] )
print ( num_str[ : 6 ] )
print ( num_str[ : ] )
print ( num_str[ : : 2 ] )
print ( num_str[ 1 : : 2 ] )
print ( num_str[ - 1 ] )
print ( num_str[ 2 : - 1 ] )
print ( num_str[ - 2 : ] )
print ( num_str[ : : - 1 ] )
17 公共方法(字典、元组、列表、字符串)
17.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"
17.2 切片
描述 Python 表达式 结果 支持的数据类型 切片 “0123456789”[::-2] “97531” 字符串、列表、元组
切片使用索引值来限定范围,从一个大的字符串中切出小的字符串
列表和元组都是有序的集合,都能够通过索引值获取到对应的数据
字典是一个无序的集合,是使用键值对保存数据,所以不可以使用切片
18 高级运算符
运算符 Python 表达式 结果 描述 支持的数据类型 + [1, 2] + [3, 4] [1, 2, 3, 4] 合并 字符串、列表、元组 * [“Hi!”] * 4 [‘Hi!’, ‘Hi!’, ‘Hi!’, ‘Hi!’] 重复 字符串、列表、元组 in 3 in (1, 2, 3) True 元素是否存在 字符串、列表、元组、字典 not in 4 not in (1, 2, 3) True 元素是否不存在 字符串、列表、元组、字典 > >= == < <= (1, 2, 3) < (2, 2, 3) True 元素比较 字符串、列表、元组
注意
in在对字典操作时,判断的是字典的键
in和 not in被称为成员运算符
19 成员运算符
成员运算符用于测试序列中是否包含指定的成员
运算符 描述 实例 in 如果在指定的序列中找到值返回 True,否则返回 False 3 in (1, 2, 3)
返回 True
not in 如果在指定的序列中没有找到值返回 True,否则返回 False 3 not in (1, 2, 3)
返回 False
20 完整的 for 循环语法
for 变量 in 集合:
循环体代码
else
代码
如果集合的元素全部遍历完,则会执行else 的代码,否则不会执行
for num in [ 1 , 2 , 3 ] :
print ( num)
if num == 2 :
break
else :
print ( "会执行吗?" )
print ( "循环结束" )
20.1 应用场景
在迭代遍历嵌套的数据类型时,例如一个列表包含了多个字典
需求:要判断 某一个字典中 是否存在 指定的 值
如果存在,提示并且退出循环
如果不存在,在循环整体结束后,希望得到一个统一的提示
students = [
{ "name" : "阿土" } ,
{ "name" : "小美" }
]
find_name = "张三"
for stu_dict in students:
print ( stu_dict)
if stu_dict[ "name" ] == find_name:
print ( "找到了 %s" % find_name)
break
else :
print ( "抱歉没有找到 %s" % find_name)
print ( "循环结束" )
21 变量进阶
21.1 变量的引用
变量 和 数据 都是保存在 内存中的
在Python中 函数的参数传递以及返回值都是靠引用 传递的
21.2 引用的概念
在 Python中
变量和数据是分开存储的
数据保存在内存中的一个位置
变量中保存着数据在内存中的地址
变量中记录数据的地址,就叫做引用
使用id()函数可以查看变量中保存数据所在的内存地址
21.3 可变和不可变类型
不可变类型,内存中的数据不允许被修改:
数字类型 `int`, `bool`, `float`, `complex`, `long(2.x)`
字符串 `str`
元组 `tuple`
可变类型,内存中的数据可以被修改:
列表 `list`
字典 `dict`
注意:字典的 `key` 只能使用不可变类型的数据
22 局部变量和全局变量
22.1 局部变量
局部变量是在函数内部定义的变量,只能在函数内部使用
函数执行结束后,函数内部的局部变量,会被系统回收
不同的函数,可以定义相同的名字的局部变量,但是彼此
之间不会产生影响
22.2 局部变量的作用
在函数内部使用,临时保存函数内部需要使用的数据
def demo1 ( ) :
num = 10
print ( num)
num = 20
print ( "修改后 %d" % num)
def demo2 ( ) :
num = 100
print ( num)
demo1( )
demo2( )
print ( "over" )
22.3 局部变量的生命周期
所谓生命周期就是变量从被创建到被系统回收的过程
局部变量在函数执行时才会被创建
函数执行结束后局部变量被系统回收
局部变量在生命周期 内,可以用来存储函数内部临时使用到的数据
22.4 全局变量
全局变量是在函数外部定义的变量,所有函数内部都可以使用这个变量
num = 10
def demo1 ( ) :
print ( num)
def demo2 ( ) :
print ( num)
demo1( )
demo2( )
print ( "over" )
注意:函数执行时,需要处理变量时会:
1. 首先查找函数内部是否存在 指定名称 的局部变量,
如果有,直接使用
2. 如果没有,查找函数外部是否存在指定名称的全局变量,
如果有,直接使用
3. 如果还没有,程序报错!
22.4.1 函数不能直接修改 全局变量的引用
全局变量是在函数外部定义的变量(没有定义在某一个函数内),
所有函数内部 都可以使用这个变量
num = 10
def demo1 ( ) :
print ( "demo1" + "-" * 50 )
num = 100
print ( num)
def demo2 ( ) :
print ( "demo2" + "-" * 50 )
print ( num)
demo1( )
demo2( )
print ( "over" )
注意:只是在函数内部定义了一个局部变量而已,只是变量
名相同 —— 在函数内部不能直接修改全局变量的值
22.4.2 在函数内部修改全局变量的值
如果在函数中需要修改全局变量,需要使用 `global` 进行声明
num = 10
def demo1 ( ) :
print ( "demo1" + "-" * 50 )
global num
num = 100
print ( num)
def demo2 ( ) :
print ( "demo2" + "-" * 50 )
print ( num)
demo1( )
demo2( )
print ( "over" )
22.4.3 全局变量定义的位置
为了保证所有的函数都能够正确使用到全局变量,应该将全
局变量定义在其他函数的上方
a = 10
def demo ( ) :
print ( "%d" % a)
print ( "%d" % b)
print ( "%d" % c)
b = 20
demo( )
c = 30
注意
由于全局变量 c,是在调用函数之后,才定义的,在执行函数时,
变量还没有定义,所以程序会报错!
22.4.4 全局变量命名的建议
为了避免局部变量和全局变量出现混淆,在定义全局变量时,
有些公司会有一些开发要求,例如:
全局变量名前应该增加 `g_` 或者 `gl_` 的前缀
提示:具体的要求格式,各公司要求可能会有些差异
23 缺省参数
定义函数时,可以给 某个参数指定一个默认值,具有默认值的
参数就叫做缺省参数
调用函数时,如果没有传入缺省参数的值,则在函数内部使用定
义函数时指定的 参数默认值
函数的缺省参数,将常见的值设置为参数的缺省值,从而简化
函数的调用
23.1 指定函数的缺省参数
在参数后使用赋值语句,可以指定参数的缺省值
def print_info ( name, gender= True ) :
"""
:param name: 班上同学的姓名
:param gender: True 男生 False 女生
"""
gender_text = "男生"
if not gender:
gender_text = "女生"
print ( "%s 是 %s" % ( name, gender_text) )
print_info( "小明" )
print_info( "老王" )
print_info( "小美" , False )
提示
1. 缺省参数,需要使用最常见的值作为默认值!
2. 如果一个参数的值不能确定,则不应该设置默认值,具体
的数值在调用函数时,由外界传递!
23.2 缺省参数的注意事项
23.2.1 缺省参数的定义位置
必须保证带有默认值的缺省参数在参数列表末尾
所以,以下定义是错误的!
def print_info ( name, gender= True , title) :
23.2.2 调用带有多个缺省参数的函数
在调用函数时,如果有多个缺省参数,需要指定参数名,这
样解释器才能够知道参数的对应关系!
def print_info ( name, title= "" , gender= True ) :
"""
:param title: 职位
:param name: 班上同学的姓名
:param gender: True 男生 False 女生
"""
gender_text = "男生"
if not gender:
gender_text = "女生"
print ( "%s%s 是 %s" % ( title, name, gender_text) )
print_info( "小明" )
print_info( "老王" , title= "班长" )
print_info( "小美" , gender= False )
24 多值参数
24.1 定义支持多值参数的函数
有时可能需要一个函数能够处理的参数个数是不确定的,这个时
候,就可以使用
多值参数
python中有两种多值参数:
参数名前增加 一个 `*` 可以接收 元组
参数名前增加两个`*` 可以接收字典
一般在给多值参数命名时,习惯使用以下两个名字
`*args` —— 存放元组参数,前面有一个 `*`
`**kwargs` —— 存放字典参数,前面有两个 `*`
`args` 是 `arguments` 的缩写,有变量的含义
`kw` 是 `keyword` 的缩写,`kwargs` 可以记忆键值对参数
def demo ( num, * args, ** kwargs) :
print ( num)
print ( args)
print ( kwargs)
demo( 1 , 2 , 3 , 4 , 5 , name= "小明" , age= 18 , gender= True )
提示:多值参数的应用会经常出现在网络上一些大牛开发的框
架中,知道多值参数,有利于我们能够读懂大牛的代码**
24.2 多值参数案例 —— 计算任意多个数字的和
需求
1. 定义一个函数 `sum_numbers`,可以接收的任意多个整数
2. 功能要求:将传递的 **所有数字累加** 并且返回累加结果
def sum_numbers ( * args) :
num = 0
for n in args:
num += n
return num
print ( sum_numbers( 1 , 2 , 3 ) )
24.3 元组和字典的拆包
在调用带有多值参数的函数时,如果希望:
将一个元组变量,直接传递给args
将一个字典变量,直接传递给kwargs
就可以使用 拆包,简化参数的传递,拆包** 的方式是:
在元组变量前,增加一个`*`
在字典变量前,增加 两个`*`
def demo ( * args, ** kwargs) :
print ( args)
print ( kwargs)
gl_nums = ( 1 , 2 , 3 )
gl_xiaoming = { "name" : "小明" , "age" : 18 }
demo( * gl_nums, ** gl_xiaoming)
25 函数的递归
函数调用自身的编程技巧称为递归
25.1 递归函数的特点
一个函数内部调用自己
函数内部可以调用其他函数,当然在函数内部也可以调用自己
代码特点
1. 函数内部的代码是相同的,只是针对参数不同,处理的结果不同
2. 当 参数满足一个条件时,函数不再执行
这个非常重要,通常被称为递归的出口,否则会出现死循环!
def sum_number ( num) :
print ( num)
if num == 1 :
return
sum_number( num - 1 )
sum_number( 3 )
25.2 递归案例 —— 计算数字累加
1. 定义一个函数 sum_numbers
2. 能够接收一个 num的整数参数
3. 计算 1 + 2 + ... num 的结果
def sum_numbers ( num) :
if num == 1 :
return 1
temp = sum_numbers( num - 1 )
return num + temp
result = sum_numbers( 100 )
print ( result)
26 面向对象(OOP)
面向对象编程 —— Object Oriented Programming 简写 OOP
26.1 类和对象
26.1.1 类
类是对一群具有相同特征或者行为的事物的一个统称,是抽象
的不能直接使用
特征被称为 属性
行为被称为 方法
类就相当于制造飞机时的图纸,是一个模板,是负责创建对象的
26.1.2 对象
对象是由类创建出来的一个具体存在,可以直接使用
由哪一个类创建出来的,对象就拥有在哪一个类中定义的:
属性
方法
对象就相当用图纸制造的飞机
在程序开发中,应该 先有类,再有对象
26.1.3 类和对象的关系
类是模板,对象是根据类这个模板创建出来的,应该先有类,再有对象
类只有一个,而 对象可以有很多个
不同的对象之间属性可能会各不相同
类中定义了什么属性和方法,对象中就有什么属性和方法,不
可能多,也不可能少
26.1.4 类的设计
在使用面相对象开发前,应该首先分析需求,确定一下,程
序中需要包含哪些类!
在程序开发中,要设计一个类,通常需要满足一下三个要素:
类名 这类事物的名字,满足大驼峰命名法
属性 这类事物具有什么样的特征
方法 这类事物具有什么样的行为
大驼峰命名法
每一个单词的首字母大写
单词与单词之间没有下划线
类名的确定
名词提炼法 分析 整个业务流程,出现的 名词,通常就是找到的类
属性和方法的确定
对 对象的特征描述,通常可以定义成属性
对象具有的行为(动词),通常可以定义成方法
提示:需求中没有涉及的属性或者方法在设计类时,不需要考虑
26.1.5 面相对象基础语法
26.1.5.1 dir 内置函数
在 Python 中 对象几乎是无所不在的,我们之前学习的 变量、
数据、函数 都是对象
使用内置函数 dir 传入 标识符 / 数据,可以查看对象内的所
有属性及方法
提示 __方法名__ 格式的方法是 Python 提供的 内置方法 / 属性
26.1.6 定义简单的类(只包含方法)
面向对象是更大的封装在 一个类中封装多个方法,这样 通过这
个类创建出来的对象,就可以直接调用这些方法了!
class 类名:
def 方法1 ( self, 参数列表) :
pass
def 方法2 ( self, 参数列表) :
pass
方法 的定义格式和之前学习过的函数 几乎一样
区别在于第一个参数必须是 self
注意:类名的命名规则要符合大驼峰命名法
26.1.7 创建对象
当一个类定义完成之后,要使用这个类来创建对象,语法格式如下:
对象变量 = 类名()
26.1.7.1 第一个面向对象程序
小猫爱吃鱼,小猫要喝 水
分析
定义一个猫类 Cat
定义两个方法 eat 和 drink
按照需求 —— 不需要定义属性
class Cat :
"""这是一个猫类"""
def eat ( self) :
print ( "小猫爱吃鱼" )
def drink ( self) :
print ( "小猫在喝水" )
tom = Cat( )
tom. drink( )
tom. eat( )
引用概念的强调
在面向对象开发中,引用的概念是同样适用的!
在Python中使用类创建对象之后,tom 变量中 仍然记录的是
对象在内存中的地址
也就是 tom 变量 引用 了 新建的猫对象
使用 print 输出 对象变量,默认情况下,是能够输出这个变量
引用的对象 是 由哪一个类创建的对象,以及 在内存中的地
址(十六进制表示)
提示:在计算机中,通常使用 十六进制 表示 内存地址
十进制 和 十六进制 都是用来表达数字的,只是表示的方式不一样
十进制 和 十六进制 的数字之间可以来回转换
%d 可以以 10 进制 输出数字
%x 可以以 16 进制 输出数字
26.1.8 方法中的 self 参数
26.1.8.1 给对象增加属性
在Python 中,要给对象设置属性,非常的容易,但是不推荐使用
因为:对象属性的封装应该封装在类的内部
只需要在类的外部的代 中直接通过 . 设置一个属性即可
注意:这种方式虽然简单,但是不推荐使用!
class Cat :
def eat ( self) :
print ( "%s 爱吃鱼" % self. name)
def drink ( self) :
print ( "%s 要喝水" % self. name)
tom = Cat( )
tom. eat( )
tom. drink( )
tom. name = "Tom"
26.1.8.2 使用 self 在方法内部输出每一只猫的名字
由 哪一个对象 调用的方法,方法内的 self 就是 哪一个对象的引用
在类封装的方法内部,self 就表示 当前调用方法的对象自己
调用方法时,程序员不需要传递 self 参数
在方法内部,可以通过 self. 访问对象的属性,也可以通过 self. 调用其他的对象方法
改造代码如下:
class Cat :
def eat ( self) :
print ( "%s 爱吃鱼" % self. name)
tom = Cat( )
tom. name = "Tom"
tom. eat( )
lazy_cat = Cat( )
lazy_cat. name = "大懒猫"
lazy_cat. eat( )
在类的外部,通过变量名. 访问对象的属性和方法
在类封装的方法中,通过 self. 访问对象的属性和方法
26.1.9 初始化方法
当使用 类名() 创建对象时,会 自动 执行以下操作:
为对象在内存中 分配空间 —— 创建对象
为对象的属性 设置初始值 —— 初始化方法(init)
这个 初始化方法 就是 __init__ 方法,__init__ 是对象的内
置方法
__init__ 方法是 专门 用来定义一个类 具有哪些属性的方法!
26.1.9.1 在初始化方法内部定义属性
在 __init__ 方法内部使用 self.属性名 = 属性的初始值
就可以 定义属性
定义属性之后,再使用 Cat 类创建的对象,都会拥有该属性
class Cat :
def __init__ ( self) :
print ( "这是一个初始化方法" )
self. name = "Tom"
def eat ( self) :
print ( "%s 爱吃鱼" % self. name)
tom = Cat( )
tom. eat( )
26.1.9.2 改造初始化方法 —— 初始化的同时设置初始值
在开发中,如果希望在创建对象的同时,就设置对象的属性,
可以对 __init__ 方法进行改造
把希望设置的属性值,定义成 __init__ 方法的参数
在方法内部使用 self.属性 = 形参 接收外部传递的参数
在创建对象时,使用 类名(属性1, 属性2...) 调用
class Cat :
def __init__ ( self, new_name) :
print ( "这是一个初始化方法" )
self. name = new_name
def eat ( self) :
print ( "%s 爱吃鱼" % self. name)
tom = Cat( "Tom" )
print ( tom. name)
lazy_cat = Cat( "大懒猫" )
lazy_cat. eat( )
26.1.10 内置方法和属性
26.1.10.1 del 方法
在 Python 中
当使用 类名() 创建对象时,为对象分配完空间后,
自动调用 __init__ 方法
当一个对象被从内存中销毁 前,会 自动调用 __del__ 方法
应用场景
__init__ 改造初始化方法,可以让创建对象更加灵活
__del__ 如果希望在对象被销毁前,再做一些事情,
可以考虑一下 __del__ 方法
生命周期
一个对象从调用类名()创建,生命周期开始
一个对象的 __del__ 方法一旦被调用,生命周期结束
在对象的生命周期内,可以访问对象属性,或者让对象调用方法
class Cat :
def __init__ ( self, new_name) :
self. name = new_name
print ( "%s 来了" % self. name)
def __del__ ( self) :
print ( "%s 去了" % self. name)
tom = Cat( "Tom" )
print ( tom. name)
del tom
print ( "-" * 50 )
26.1.10.2 str 方法
在 Python 中,使用 print 输出 对象变量,默认情况下,会输出
这个变量 引用的对象 是 由哪一个类创建的对象,以及 在内存中
的地址(十六进制表示)
如果在开发中,希望使用 print 输出 对象变量 时,能够打印 自定
义的内容,就可以利用 __str__ 这个内置方法了
注意:__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. name
tom = Cat( "Tom" )
print ( tom)
26.1.11 身份运算符
身份运算符用于比较 两个对象的内存地址是否一致 —— 是否
是对同一个对象的引用
在 Python 中针对 None 比较时,建议使用 is 判断
26.1.11.1 is 与 == 区别:
is 用于判断两个变量引用对象是否为同一个
== 用于判断 引用变量的值 是否相等
>>> a = [1, 2, 3]
>>> b = [1, 2, 3]
>>> b is a
False
>>> b == a
True
26.2 私有属性和私有方法
在实际开发中,对象 的 某些属性或方法 可能只希望 在对象的
内部被使用,而 不希望在外部被访问到
私有属性 就是 对象 不希望公开的 属性
私有方法 就是 对象 不希望公开的 方法
定义方式
在定义属性或方法时,在属性名或者方法名前增加两个下划线,
定义的就是私有属性或方法
class Women :
def __init__ ( self, name) :
self. name = name
self. __age = 18
def __secret ( self) :
print ( "我的年龄是 %d" % self. __age)
xiaofang = Women( "小芳" )
26.3 伪私有属性和私有方法(科普)
提示:在日常开发中,不要使用这种方式,访问对象的 私有属性
或 私有方法
Python 中,并没有 真正意义 的 私有
在给属性、方法 命名时,实际是对名称做了一些特殊处理,使
得外界无法访问到
处理方式:在名称前面加上 _类名 => _类名__名称
class Women :
def __init__ ( self, name) :
self. name = name
self. __age = 18
def __secret ( self) :
print ( "%s 的年龄是 %d" % ( self. name, self. __age) )
xiaofang = Women( "小芳" )
print ( xiaofang. _Women__age)
xiaofang. _Women__secret( )
26.4 继承
面向对象三大特性
封装 根据 职责 将 属性 和 方法 封装 到一个抽象的 类 中
继承 实现代码的重用,相同的代码不需要重复的编写
多态 不同的对象调用相同的方法,产生不同的执行结果,增
加代码的灵活度
26.4.1 单继承
26.4.1.1 继承的概念、语法和特点
继承的概念:子类拥有父类的所有方法 属性
继承的语法
class 类名( 父类名) :
pass
子类继承自父类,可以直接享受父类中已经封装好的方法,
不需要再次开发
子类中应该根据 职责,封装子类特有的属性和方法
专业术语
Dog 类是 Animal 类的子类,Animal 类是 Dog 类的父类,
Dog 类从 Animal 类继承
Dog 类是 Animal 类的派生类,Animal 类是 Dog 类的基类,
Dog 类从 Animal 类派生
继承的传递性
C类从B类继承,B类又从A类继承
那么C类就具有B类和A类的所有属性和方法
子类拥有父类以及父类的父类中封装的所有 属性 和 方法
26.4.2 方法的重写
子类 拥有父类 的所有方法和属性
子类 继承自父类,可以直接享受父类中已经封装好的方法,
不需要再次开发
当父类的方法实现不能满足子类需求时,可以对方法进行
重写(override)
重写 父类方法有两种情况:
覆盖父类的方法
对父类方法进行 扩展
1) 覆盖父类的方法
如果在开发中,父类的方法实现和子类的方法实现,完全不同
就可以使用覆盖的方式,在子类中重新编写父类的方法实现
具体的实现方式,就相当于在子类中定义了一个和父类同名的
方法并且实现
重写之后,在运行时,只会调用子类中重写的方法,而不再会调
用父类封装的方法
2) 对父类方法进行扩展
如果在开发中,子类的方法实现中包含父类的方法实现
父类原本封装的方法实现是子类方法的一部分
就可以使用扩展的方式
在子类中重写父类的方法
在需要的位置使用 super().父类方法 来调用父类方法的执行
代码其他的位置针对子类的需求,编写 子类特有的代码实现
26.4.3 关于 super
在Python中super是一个特殊的类
super() 就是使用super类创建出来的对象
最常使用的场景就是在重写父类方法时,调用 在父类中封
装的方法实现
class Animal :
def eat ( self) :
print ( "吃---" )
def drink ( self) :
print ( "喝---" )
def run ( self) :
print ( "跑---" )
def sleep ( self) :
print ( "睡---" )
class Dog ( Animal) :
def bark ( self) :
print ( "汪汪叫" )
class XiaoTianQuan ( Dog) :
def fly ( self) :
print ( "我会飞" )
def bark ( self) :
print ( "神一样的叫唤..." )
Dog. bark( self)
print ( "$%^*%^$%^#%$%" )
xtq = XiaoTianQuan( )
xtq. bark( )
26.4.4 父类的私有属性和私有方法
子类对象不能在自己的方法内部,直接访问父类的私有属性或私有方法
子类对象可以通过父类的公有方法间接访问到私有属性或私有方法
私有属性、方法是对象的隐私,不对外公开,外界以及子类都不能直
接访问
私有属性、方法通常用于做一些内部的事情
B的对象不能直接访问 __num2 属性
B的对象不能在demo方法内访问 __num2 属性
B的对象可以在demo方法内,调用父类的 test 方法
父类的test方法内部,能够访问 __num2 属性和 __test 方法
class A :
def __init__ ( self) :
self. num1 = 100
self. __num2 = 200
def __test ( self) :
print ( "私有方法 %d %d" % ( self. num1, self. __num2) )
def test ( self) :
print ( "父类的公有方法 %d" % self. __num2)
self. __test( )
class B ( A) :
def demo ( self) :
print ( "子类方法 %d" % self. num1)
self. test( )
pass
b = B( )
print ( b)
b. demo( )
26.4.5 多继承
子类可以拥有多个父类,并且具有所有父类的属性和方法
例如:孩子会继承自己父亲和母亲的特性
语法
class 子类名(父类名1, 父类名2...)
pass
class A :
def test ( self) :
print ( "test 方法" )
class B :
def demo ( self) :
print ( "demo 方法" )
class C ( A, B) :
"""多继承可以让子类对象,同时具有多个父类的属性和方法"""
pass
c = C( )
c. test( )
c. demo( )
26.4.5.1 多继承的使用注意事项
如果不同的父类中存在同名的方法,子类对象在调用方法时,
会调用哪一个父类中的方法呢?
提示:开发时,应该尽量避免这种容易产生混淆的情况! —— 如
果父类之间存在同名的属性或者方法,应该尽量避免使用多继承
Python 中的 MRO —— 方法搜索顺序(知道)
Python 中针对 类 提供了一个 内置属性 __mro__ 可以查看方法
搜索顺序
MRO 是 method resolution order,主要用于在多继承时判断方
法、属性的调用路径
print(C.__mro__)
输出结果
(<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>)
在搜索方法时,是按照 __mro__ 的输出结果从左至右的顺序查找的
如果在当前类中找到方法,就直接执行,不再搜索
如果没有找到,就查找下一个类中是否有对应的方法,如果找到,
就直接执行,不再搜索
如果找到最后一个类,还没有找到方法,程序报错
class A :
def test ( self) :
print ( "A --- test 方法" )
def demo ( self) :
print ( "A --- demo 方法" )
class B :
def test ( self) :
print ( "B --- test 方法" )
def demo ( self) :
print ( "B --- demo 方法" )
class C ( B, A) :
"""多继承可以让子类对象,同时具有多个父类的属性和方法"""
pass
c = C( )
c. test( )
c. demo( )
print ( C. __mro__)
26.5 多态
面向对象三大特性
封装根据职责将属性和方法封装 到一个抽象的类中
定义类的准则
继承实现代码的重用,相同的代码不需要重复的编写
设计类的技巧
子类针对自己特有的需求,编写特定的代码
多态不同的子类对象调用相同的 父类方法,产生不同的执行结果
多态可以增加代码的灵活度
以继承和重写父类方法为前提
是调用方法的技巧,不会影响到类的内部设计
多态案例演练
需求
在Dog类中封装方法game
普通狗只是简单的玩耍
定义XiaoTianDog继承自Dog,并且重写game方法
哮天犬需要在天上玩耍
定义Person类,并且封装一个和狗玩的方法
在方法内部,直接让狗对象调用game方法
Person 类中只需要让 狗对象 调用 game 方法,而不关心具体是
什么狗
game 方法是在 Dog 父类中定义的
在程序执行时,传入不同的 狗对象 实参,就会产生不同的执行效果
多态 更容易编写出出通用的代码,做出通用的编程,以适应
需求的不断变化!
class Dog ( object ) :
def __init__ ( self, name) :
self. name = name
def game ( self) :
print ( "%s 蹦蹦跳跳的玩耍..." % self. name)
class XiaoTianDog ( Dog) :
def game ( self) :
print ( "%s 飞到天上去玩耍..." % self. name)
class Person ( object ) :
def __init__ ( self, name) :
self. name = name
def game_with_dog ( self, dog) :
print ( "%s 和 %s 快乐的玩耍..." % ( self. name, dog. name) )
dog. game( )
wangcai = XiaoTianDog( "飞天旺财" )
xiaoming = Person( "小明" )
xiaoming. game_with_dog( wangcai)
26.6 类属性和类方法
26.6.1 类属性
类属性就是给类对象中定义的属性
通常用来记录与这个类相关的特征
类属性不会用于记录具体对象的特征
示例需求
定义一个 工具类
每件工具都有自己的 name
需求 —— 知道使用这个类,创建了多少个工具对象?
class Tool ( object ) :
count = 0
def __init__ ( self, name) :
self. name = name
Tool. count += 1
tool1 = Tool( "斧头" )
tool2 = Tool( "榔头" )
tool3 = Tool( "水桶" )
print ( Tool. count)
26.6.2 属性的获取机制(科普)
在 Python 中 属性的获取 存在一个 向上查找机制
因此,要访问类属性有两种方式:
类名.类属性
对象.类属性 (不推荐)
class Tool ( object ) :
count = 0
def __init__ ( self, name) :
self. name = name
Tool. count += 1
tool1 = Tool( "斧头" )
tool2 = Tool( "榔头" )
tool3 = Tool( "水桶" )
print ( "工具对象总数 %d" % tool3. count)
注意
如果使用 对象.类属性 = 值 赋值语句,只会 给对象添加一个属性,
而不会影响到 类属性的值
class Tool ( object ) :
count = 0
def __init__ ( self, name) :
self. name = name
Tool. count += 1
tool1 = Tool( "斧头" )
tool2 = Tool( "榔头" )
tool3 = Tool( "水桶" )
tool3. count = 99
print ( "工具对象总数 %d" % tool3. count)
print ( "===> %d" % Tool. count)
26.6.3 类方法和静态方法
26.6.3.1 类方法
类属性就是针对类对象定义的属性
使用赋值语句在class关键字下方可以定义类属性
类属性用于记录与这个类相关的特征
类方法就是针对类对象定义的方法
在类方法内部可以直接访问类属性或者调用其他的类方法
语法如下
@classmethod
def 类方法名( cls) :
pass
类方法需要用 修饰器 @classmethod 来标识,告诉解释器这是一
个类方法
类方法的第一个参数应该是cls
由哪一个类调用的方法,方法内的cls就是 哪一个类的引用
这个参数和实例方法的第一个参数是 self 类似
提示 使用其他名称也可以,不过习惯使用 cls
通过 类名. 调用 类方法,调用方法时,不需要传递 cls 参数
在方法内部
可以通过 cls. 访问类的属性
也可以通过 cls. 调用其他的类方法
示例需求
定义一个 工具类
每件工具都有自己的 name
需求 —— 在 类 封装一个 show_tool_count 的类方法,
输出使用当前这个类,创建的对象个数
class Tool ( object ) :
count = 0
@classmethod
def show_tool_count ( cls) :
print ( "工具对象的数量 %d" % cls. count)
def __init__ ( self, name) :
self. name = name
Tool. count += 1
tool1 = Tool( "斧头" )
tool2 = Tool( "榔头" )
Tool. show_tool_count( )
26.6.3.2 静态方法
在开发时,如果需要在类中封装一个方法,这个方法:
既不需要访问 实例属性 或者调用实例方法
也不需要访问类属性或者调用类方法
这个时候,可以把这个方法封装成一个静态方法
@staticmethod
def 静态方法名():
pass
静态方法 需要用 修饰器 @staticmethod 来标识,告诉解释器这是一
个静态方法
通过 类名. 调用 静态方法
class Dog ( object ) :
@staticmethod
def run ( ) :
print ( "小狗要跑..." )
Dog. run( )
26.6.3.3 方法综合案例
需求
设计一个 Game 类
属性:
定义一个 类属性 top_score 记录游戏的 历史最高分
定义一个 实例属性 player_name 记录 当前游戏的玩家姓名
方法:
静态方法 show_help 显示游戏帮助信息
类方法 show_top_score 显示历史最高分
实例方法 start_game 开始当前玩家的游戏
主程序步骤
1) 查看帮助信息
2) 查看历史最高分
3) 创建游戏对象,开始游戏
案例小结
实例方法 —— 方法内部需要访问 实例属性
实例方法 内部可以使用 类名. 访问类属性
类方法 —— 方法内部 只 需要访问 类属性
静态方法 —— 方法内部,不需要访问 实例属性 和 类属性
class Game ( object ) :
top_score = 0
def __init__ ( self, player_name) :
self. player_name = player_name
@staticmethod
def show_help ( ) :
print ( "帮助信息:让僵尸进入大门" )
@classmethod
def show_top_score ( cls) :
print ( "历史记录 %d" % cls. top_score)
def start_game ( self) :
print ( "%s 开始游戏啦..." % self. player_name)
Game. show_help( )
Game. show_top_score( )
game = Game( "小明" )
game. start_game( )
26.7 单例
26.7.1 单例设计模式
设计模式
设计模式是前人工作的总结和提炼,通常,被人们广泛流传的设计模式都
是针对某一特定问题的成熟的解决方案
使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代
码可靠性
单例设计模式
目的 —— 让类创建的对象,在系统中只有唯一的一个实例
每一次执行类名()返回的对象,内存地址是相同的
单例设计模式的应用场景
音乐播放 对象
回收站 对象
打印机 对象
……
26.7.2 __new__方法
使用 类名() 创建对象时,Python的解释器首先会调用 __new__ 方
法为对象分配空间
__new__ 是一个 由 object 基类提供的内置的静态方法,主要作
用有两个:
1) 在内存中为对象分配空间
2) 返回对象的引用
Python的解释器获得对象的引用后,将引用作为第一个参数,传
递给 __init__ 方法
重写 __new__ 方法的代码非常固定!
重写 __new__ 方法 一定要 return super().__new__(cls)
否则 Python 的解释器 得不到 分配了空间的 对象引用,就不
会调用对象的初始化方法
注意:__new__ 是一个静态方法,在调用时需要 主动传递
cls 参数
class MusicPlayer ( object ) :
def __new__ ( cls, * args, ** kwargs) :
print ( "创建对象,分配空间" )
instance = super ( ) . __new__( cls)
return instance
def __init__ ( self) :
print ( "播放器初始化" )
player = MusicPlayer( )
print ( player)
26.7.3 Python 中的单例
单例 —— 让类创建的对象,在系统中只有唯一的一个实例
定义一个类属性,初始值是None,用于记录单例对象的引用
重写 __new__ 方法
如果类属性 is None,调用父类方法分配空间,并在类属性中
记录结果
返回类属性中记录的对象引用
class MusicPlayer ( object ) :
instance = None
def __new__ ( cls, * args, ** kwargs) :
if cls. instance is None :
cls. instance = super ( ) . __new__( cls)
return cls. instance
只执行一次初始化工作
在每次使用类名() 创建对象时,Python 的解释器都会自动调用
两个方法:
__new__ 分配空间
__init__ 对象初始化
需求
让 初始化动作 只被 执行一次
解决办法
定义一个类属性 init_flag 标记是否 执行过初始化动作,初
始值为False
在 __init__ 方法中,判断 init_flag,如果为False就执行初
始化动作
然后将 init_flag 设置为 True
这样,再次自动调用 __init__ 方法时,初始化动作就不会被
再次执行了
class MusicPlayer ( object ) :
instance = None
init_flag = False
def __new__ ( cls, * args, ** kwargs) :
if cls. instance is None :
cls. instance = super ( ) . __new__( cls)
return cls. instance
def __init__ ( self) :
if not MusicPlayer. init_flag:
print ( "初始化音乐播放器" )
MusicPlayer. init_flag = True
player1 = MusicPlayer( )
print ( player1)
player2 = MusicPlayer( )
print ( player2)
26.8 异常
26.8.1 异常的概念
程序在运行时,如果 Python解释器遇到一个错误,会停止程序
的执行,并且提示一些错误信息,这就是异常
程序停止执行并且提示错误信息这个动作,我们通常称之为:抛
出(raise)异常
26.8.2 捕获异常
在程序开发中,如果 对某些代码的执行不能确定是否正确,可
以增加 try(尝试) 来捕获异常
捕获异常最简单的语法格式:
try :
尝试执行的代码
except :
出现错误的处理
try 尝试,下方编写要尝试代码,不确定是否能够正常执行的代码
except 如果不是,下方编写尝试失败的代码
简单异常捕获演练 —— 要求用户输入整数
try :
num = int ( input ( "请输入数字:" ) )
except :
print ( "请输入正确的数字" )
26.8.3 错误类型捕获
在程序执行时,可能会遇到不同类型的异常,并且需要针
对不同类型的异常,做出不同的响应,这个时候,就需
要捕获错误类型了
语法如下:
try :
pass
except 错误类型1 :
pass
except ( 错误类型2 , 错误类型3 ) :
pass
except Exception as result:
print ( "未知错误 %s" % result)
异常类型捕获演练 —— 要求用户输入整数
提示用户输入一个整数
使用 8 除以用户输入的整数并且输出
try :
num = int ( input ( "请输入整数:" ) )
result = 8 / num
print ( result)
except ValueError:
print ( "请输入正确的整数" )
except ZeroDivisionError:
print ( "除 0 错误" )
捕获未知错误
在开发时,要预判到所有可能出现的错误,还是有一定难度的
如果希望程序无论出现任何错误,都不会因为Python 解释
器抛出异常而被终止,可以再增加一个 except
语法如下:
except Exception as result:
print ( "未知错误 %s" % result)
try :
num = int ( input ( "输入一个整数:" ) )
result = 8 / num
print ( result)
except ValueError:
print ( "请输入正确的整数" )
except Exception as result:
print ( "未知错误 %s" % result)
26.8.4 异常捕获完整语法
在实际开发中,为了能够处理复杂的异常情况,完整的异常语法如下:
提示:
try :
pass
except 错误类型1 :
pass
except 错误类型2 :
pass
except ( 错误类型3 , 错误类型4 ) :
pass
except Exception as result:
print ( result)
else :
pass
finally :
print ( "无论是否有异常,都会执行的代码" )
else 只有在没有异常时才会执行的代码
finally 无论是否有异常,都会执行的代码
之前一个演练的 完整捕获异常 的代码如下:
try :
num = int ( input ( "请输入整数:" ) )
result = 8 / num
print ( result)
except ValueError:
print ( "请输入正确的整数" )
except ZeroDivisionError:
print ( "除 0 错误" )
except Exception as result:
print ( "未知错误 %s" % result)
else :
print ( "正常执行" )
finally :
print ( "执行完成,但是不保证正确" )
26.9 异常的传递
异常的传递 —— 当函数/方法执行出现异常,会将异常传递
给函数/方法的调用一方
如果传递到主程序,仍然没有异常处理,程序才会被终止
提示
在开发中,可以在主函数中增加异常捕获
而在主函数中调用的其他函数,只要出现异常,都会传递到主
函数的异常捕获 中
这样就不需要在代码中,增加大量的异常捕获,能够保证代码的整洁
需求
定义函数 demo1() 提示用户输入一个整数并且返回
定义函数 demo2() 调用 demo1()
在主程序中调用 demo2()
def demo1 ( ) :
return int ( input ( "请输入一个整数:" ) )
def demo2 ( ) :
return demo1( )
try :
print ( demo2( ) )
except ValueError:
print ( "请输入正确的整数" )
except Exception as result:
print ( "未知错误 %s" % result)
26.10 抛出 raise 异常
在开发中,除了代码执行出错 Python解释器会抛出异常之外
还可以根据应用程序特有的业务需求 主动抛出异常
示例
提示用户 输入密码,如果 长度少于 8,抛出 异常
注意
当前函数只负责提示用户输入密码,如果密码长度不正确,需
要其他的函数进行额外处理
因此可以抛出异常由其他需要处理的函数 捕获异常
Python 中提供了一个Exception异常类
在开发时,如果满足特定业务需求时,希望抛出异常,可以:
创建 一个 Exception 的 对象
使用 raise 关键字 抛出 异常对象
定义 input_password 函数,提示用户输入密码
如果用户输入长度 < 8,抛出异常
如果用户输入长度 >=8,返回输入的密码
def input_password ( ) :
pwd = input ( "请输入密码:" )
if len ( pwd) >= 8 :
return pwd
print ( "主动抛出异常" )
ex = Exception( "密码长度不够" )
raise ex
try :
print ( input_password( ) )
except Exception as result:
print ( result)
27 模块和包
27.1 模块
模块是 Python 程序架构的一个核心概念
每一个以扩展名py结尾的 Python源代码文件都是一个 模块
模块名 同样也是一个标识符,需要符合标识符的命名规则
在模块中定义的全局变量 、函数、类 都是提供给外界直接
使用的 工具
模块就好比是 工具包,要想使用这个工具包中的工具,就需
要先导入这个模块
27.2 模块的两种导入方式
1. import 导入
import 模块名1, 模块名2
建议使用
import 模块名1
import 模块名2
导入之后
通过 模块名.使用模块提供的工具 —— 全局变量、函数、类
使用 as 指定模块的别名
如果模块的名字太长,可以使用 as 指定模块的名称,以方便在
代码中的使用
import 模块名1 as 模块别名
注意:模块别名 应该符合 大驼峰命名法
import hm_01_测试模块1 as DogModule
import hm_02_测试模块2 as CatModule
DogModule. say_hello( )
CatModule. say_hello( )
dog = DogModule. Dog( )
print ( dog)
cat = CatModule. Cat( )
print ( cat)
2. from...import 导入
如果希望从某一个模块中,导入部分工具,就可以使
用 from ... import 的方式
import模块名是一次性把模块中所有工具全部导入,并且通
过 模块名/别名 访问
# 从 模块 导入 某一个工具
from 模块名1 import 工具名
导入之后
不需要 通过 模块名.
可以直接使用 模块提供的工具 —— 全局变量、函数、类
from hm_01_测试模块1 import Dog
from hm_02_测试模块2 import say_hello
say_hello( )
wangcai = Dog( )
print ( wangcai)
注意
如果两个模块,存在同名的函数,那么后导入模块的函数,会
覆盖掉先导入的函数
开发时import代码应该统一写在代码的顶部,更容易及时发现冲突
一旦发现冲突,可以使用 as 关键字 给其中一个工具起一个别名
from hm_02_测试模块2 import say_hello as module2_say_hello
from hm_01_测试模块1 import say_hello
say_hello( )
module2_say_hello( )
27.3 模块的搜索顺序[扩展]
Python 的解释器在导入模块时,会:
搜索当前目录指定模块名的文件,如果有就直接导入
如果没有,再搜索系统目录
在开发时,给文件起名,不要和系统的模块文件重名
Python中每一个模块都有一个内置属性 __file__ 可以查看
模块的完整路径
import random
print ( random. __file__)
rand = random. randint( 0 , 10 )
print ( rand)
注意:如果当前目录下,存在一个random.py 的文件,程
序就无法正常执行了!
这个时候,Python的解释器会加载当前目录下的 random.py 而
不会加载系统的 random 模块
27.4 原则 — 每一个文件都应该是可以被导入的
一个 独立的 Python 文件 就是一个 模块
在导入文件时,文件中 所有没有任何缩进的代码 都会被执行一遍!
在实际开发中,每一个模块都是独立开发的,大多都有专人负责
开发人员通常会在模块下方增加一些测试代码
仅在模块内使用,而被导入到其他文件中不需要执行
__name__ 属性
__name__ 属性可以做到,测试模块的代码 只在测试情况下被运行,
而在被导入时不会被执行!
__name__ 是 Python 的一个内置属性,记录着一个 字符串
如果是被其他文件导入的,__name__ 就是 模块名
如果 是当前执行的程序 __name__ 是 __main__
在很多 Python 文件中都会看到以下格式的代码:
def main ( ) :
pass
if __name__ == "__main__" :
main( )
27.5 包(Package)
包是一个包含多个模块的特殊目录
目录下有一个特殊的文件 __init__.py
好处
使用 import 包名可以一次性导入包中所有的模块
案例演练
新建一个 hm_message 的 包
在目录下,新建两个文件 send_message 和 receive_message
在 send_message 文件中定义一个 send 函数
在 receive_message 文件中定义一个 receive 函数
在外部直接导入 hm_message 的包
__init__.py
要在外界使用包中的模块,需要在 __init__.py 中指定 对
外界提供的模块列表
# 从 当前目录导入模块列表
from . import send_message
from . import receive_message
__init__. py
from . import send_message
from . import receive_message
receive_message. py
def receive ( ) :
return "这是来自 100xx 的短信"
send_message. py
def send ( text) :
print ( "正在发送 %s..." % text)
hm_导入包. py
import hm_message
hm_message. send_message. send( "hello" )
txt = hm_message. receive_message. receive( )
print ( txt)
28 文件
28.1 文件的概念和作用
计算机的文件,就是存储在某种长期储存设备上的一段数据
长期存储设备包括:硬盘、U 盘、移动硬盘、光盘...
文件的作用
将数据长期保存下来,在需要的时候使用
28.2 文件的存储方式
在计算机中,文件是以二进制的方式保存在磁盘上的
文本文件和二进制文件
文本文件
可以使用文本编辑软件查看
本质上还是二进制文件
例如:python 的源程序
二进制文件
保存的内容不是给人直接阅读的,而是提供给其他软件使用的
例如:图片文件、音频文件、视频文件等等
二进制文件不能使用 文本编辑软件 查看
28.3 文件的基本操作
在计算机中要操作文件的套路非常固定,一共包含三个步骤:
打开文件
读、写文件
读将文件内容读入内存
写将内存内容写入文件
关闭文件
28.4 操作文件的函数/方法
在Python中要操作文件需要记住 1 个函数和 3 个方法
open 函数负责打开文件,并且返回文件对象
read/write/close 三个方法都需要通过 文件对象 来调用
28.5 read 方法 —— 读取文件
open 函数的第一个参数是要打开的文件名(文件名区分大小写)
如果文件存在,返回文件操作对象
如果文件不存在,会抛出异常
read 方法可以一次性 读入并返回文件的所有内容
close方法负责关闭文件
如果忘记关闭文件,会造成系统资源消耗,而且会影响到后续
对文件的访问
注意:read 方法执行后,会把 文件指针 移动到 文件的末尾
file = open ( "README" )
text = file . read( )
print ( text)
file . close( )
文件指针
文件指针标记从哪个位置开始读取数据
第一次打开文件时,通常文件指针会指向文件的开始位置
当执行了read方法后,文件指针会移动到读取内容的末尾
默认情况下会移动到 文件末尾
如果执行了一次 read 方法,读取了所有内容,那么再次调用
read 方法,还能够获得到内容吗?
不能
第一次读取之后,文件指针移动到了文件末尾,再次调用不
会读取到任何的内容
28.6 打开文件的方式
open 函数默认以 只读方式 打开文件,并且返回文件对象
语法如下:
f = open("文件名", "访问方式")
提示
频繁的移动文件指针,会影响文件的读写效率,开发中更多的时候会以
只读、只写 的方式来操作文件
写入文件示例
# 打开文件
f = open("README", "w")
f.write("hello python!\n")
f.write("今天天气真好")
# 关闭文件
f.close()
28.7 按行读取文件内容
read 方法默认会把文件的 所有内容 一次性读取到内存
如果文件太大,对内存的占用会非常严重
readline 方法
readline 方法可以一次读取一行内容
方法执行后,会把文件指针移动到下一行,准备再次读取
读取大文件的正确姿势
# 打开文件
file = open("README")
while True:
# 读取一行内容
text = file.readline()
# 判断是否读到内容
if not text:
break
# 每读取一行的末尾已经有了一个 `\n`
print(text, end="")
# 关闭文件
file.close()
28.8 文件读写案例 —— 复制文件
目标
用代码的方式,来实现文件复制过程
小文件复制
打开一个已有文件,读取完整内容,并写入到另外一个文件
file_read = open ( "README" )
file_write = open ( "README[复件]" , "w" )
text = file_read. read( )
file_write. write( text)
file_read. close( )
file_write. close( )
大文件复制
打开一个已有文件,逐行读取内容,并顺序写入到另外一个文件
# 1. 打开文件
file_read = open("README")
file_write = open("README[复件]", "w")
# 2. 读取并写入文件
while True:
# 每次读取一行
text = file_read.readline()
# 判断是否读取到内容
if not text:
break
file_write.write(text)
# 3. 关闭文件
file_read.close()
file_write.close()
28.9 文件/目录的常用管理操作
在终端 / 文件浏览器、 中可以执行常规的 文件 / 目录 管
理操作,例如:
创建、重命名、删除、改变路径、查看目录内容、……
在 Python 中,如果希望通过程序实现上述功能,需要
导入 os 模块
文件操作
目录操作
提示:文件或者目录操作都支持 相对路径 和 绝对路径
29 操作SQLite数据库
29.1 导入数据库
python2.5以后的安装包已经自带SQLite3的软件包了,所以直接导入使用即可。
import sqlite3
29.2 连接数据库
con = sqlite3. connect( "DEMO.db" )
29.3 创建游标
cur = con. cursor( )
29.4 CURD操作
29.4.1 创建表
sql = "CREATE TABLE IF NOT EXISTS test(id INTEGER PRIMARY KEY,name TEXT,age INTEGER)"
cur. execute( sql)
29.4.2 添加数据(需要提交)
data = "1,'Desire',5"
cur. execute( 'INSERT INTO test VALUES (%s)' % data)
cur. execute( "INSERT INTO test values(?,?,?)" , ( 6 , "zgq" , 20 ) )
cur. executemany( 'INSERT INTO test VALUES (?,?,?)' , [ ( 3 , 'name3' , 19 ) , ( 4 , 'name4' , 26 ) ] )
29.4.3 更新数据(需要提交)
cur. execute( "UPDATE test SET name=? WHERE id=?" , ( "nihao" , 1 ) )
cur. execute( "UPDATE test SET name='haha' WHERE id=3" )
29.4.4 删除数据(需要提交)
cur. execute( "DELETE FROM test WHERE id=?" , ( 1 , ) )
cur. execute( "DELETE FROM test WHERE id=3" )
29.4.5 查询数据
29.4.5.1 查询所有数据
cur. execute( "select * from Test" )
print ( cur. fetchall( ) )
29.4.5.2 查询第一条数据
cur. execute( "select * from Test" )
print ( cur. fetchone( ) )
29.4.5.3 查询多条数据
print ( cur. fetchmany( 3 ) )
29.4.5.4 事务的提交和回滚
con. commit( )
con. rollback( )
29.4.5.5 断开会话连接,释放资源
cur. close( )
con. close( )
29.4.6 关于SQLite数据类型的Typelessness(无类型)
sql = "CREATE TABLE IF NOT EXISTS user(clo_1,clo_2,clo_3)"
cur. execute( sql)
添加数据的时候就可以添加任意类型的数据(没有限制)
sql = 'INSERT INTO user VALUES (?,?,?)'
data = [ ( 9 , 'name3' , 19 ) , ( 'name4' , 26 , "nihao" ) , ( 'nihao' , 3 , 2.89 ) ]
cur. executemany( sql, data)
con. commit( )
想要获取该该课程markdown笔记(脑图+笔记)。可以扫描以下
微信公众号二维码。或者搜索微信公众号-Java大世界。回复phthon
即可获取笔记获取方式。