人生苦短,我用 Python —— Life is short, you need Python
Python 的创始人为吉多·范罗苏姆(Guido van Rossum)
计算机不能直接理解任何除机器语言以外的语言,所以必须要把程序员所写的程序语言翻译成机器语言,计算机才能执行程序。将其他语言翻译成机器语言的工具,被称为编译器
编译器翻译的方式有两种:一个是编译,另外一个是解释。两种方式之间的区别在于翻译时间点的不同。当编译器以解释方式运行的时候,也称之为解释器
1999 年,吉多·范罗苏姆向 DARPA 提交了一条名为 “Computer Programming for Everybody” 的资金申请,并在后来说明了他对 Python 的目标:
这些想法中的基本都已经成为现实,Python 已经成为一门流行的编程语言
在 Python 社区,吉多被称为“仁慈的独裁者”
同一样问题,用不同的语言解决,代码量差距还是很多的,一般情况下
Python
是Java
的 1/5,所以说 人生苦短,我用 Python
C
或 C++
编写,然后在 Python
程序中使用它们HelloPython
程序Python 2.x
与 3.x
版本简介Python
程序的三种方式
python
/ python3
ipython
PyCharm
HelloPython
程序Python
的开发.py
认识Python
目录认识Python
目录下新建 01-HelloPython.py
文件01-HelloPython.py
并且输入以下内容:print("hello python") print("hello world")
01-HelloPython.py
$ python 01-HelloPython.py
python
中我们学习的第一个 函数
BUG
,是程序员在开发时非常常见的,初学者常见错误的原因包括:
每一个程序员都是在不断地修改错误中成长的
pirnt("Hello world")
NameError: name 'pirnt' is not defined 名称错误:'pirnt' 名字没有定义
print
写在一行SyntaxError: invalid syntax 语法错误:语法无效
每行代码负责完成一个动作
IndentationError: unexpected indent 缩进错误:不期望出现的缩进
- Python 是一个格式非常严格的程序设计语言
- 目前而言,大家记住每行代码前面都不要增加空格
目前市场上有两个 Python 的版本并存着,分别是 Python 2.x
和 Python 3.x
SyntaxError: Non-ASCII character '\xe4' in file 01-HelloPython.py on line 3, but no encoding declared; see http://python.org/dev/peps/pep-0263/ for details 语法错误: 在 01-HelloPython.py 中第 3 行出现了非 ASCII 字符 '\xe4',但是没有声明文件编码 请访问 http://python.org/dev/peps/pep-0263/ 了解详细信息
ASCII
字符只包含256
个字符,不支持中文- 有关字符编码的问题,后续会讲
* error 错误 * name 名字 * defined 已经定义 * syntax 语法 * invalid 无效 * Indentation 索引 * unexpected 意外的,不期望的 * character 字符 * line 行 * encoding 编码 * declared 声明 * details 细节,详细信息 * ASCII 一种字符编码
Python 2.x
与 3.x
版本简介目前市场上有两个 Python 的版本并存着,分别是 Python 2.x
和 Python 3.x
新的 Python 程序建议使用
Python 3.0
版本的语法
Python
的早期版本,这是一个 较大的升级Python 3.0
在设计的时候 没有考虑向下兼容
Python
版本设计的程序都无法在 Python 3.0
上正常执行Python 2.x
的语法和库Python 3.0
的迁移,允许使用部分 Python 3.0
的语法与函数Python 2.7
被确定为 最后一个Python 2.x 版本提示:如果开发时,无法立即使用 Python 3.0(还有极少的第三方库不支持 3.0 的语法),建议
- 先使用
Python 3.0
版本进行开发- 然后使用
Python 2.6
、Python 2.7
来执行,并且做一些兼容性的处理
python
/ python3
# 使用 python 2.x 解释器 $ python xxx.py # 使用 python 3.x 解释器 $ python3 xxx.py
Python 的解释器 如今有多个语言的实现,包括:
CPython
—— 官方版本的 C 语言实现Jython
—— 可以运行在 Java 平台IronPython
—— 可以运行在 .NET 和 Mono 平台PyPy
—— Python 实现的,支持 JIT 即时编译Shell
中直接输入 Python 的代码,会立即看到程序执行结果exit()
>>> exit()
在 python 解释器中,按热键 ctrl + d
可以退出解释器
python shell
好用得多
bash shell
命令Python 2.x 使用的解释器是 ipython
Python 3.x 使用的解释器是 ipython3
要退出解释器可以有以下两种方式:
exit
In [1]: exit
在 IPython 解释器中,按热键 ctrl + d
,IPython
会询问是否退出解释器
$ sudo apt install ipython
PyCharm
集成开发环境(IDE
,Integrated Development Environment)—— 集成了开发软件需要的所有工具,一般包括以下工具:
PyCharm
是 Python 的一款非常优秀的集成开发环境PyCharm
除了具有一般 IDE 所必备功能外,还可以在 Windows
、Linux
、macOS
下使用PyCharm
适合开发大型项目
文件导航区域 能够 浏览/定位/打开 项目文件
文件编辑区域 能够 编辑 当前打开的文件
控制台区域 能够:
右上角的 工具栏 能够 执行(SHIFT + F10) / 调试(SHIFT + F9) 代码
通过控制台上方的单步执行按钮(F8),可以单步执行代码
使用用自己熟悉的语言,在程序中对某些代码进行标注说明,增强程序的可读性
以 #
开头,#
右边的所有东西都被当做说明文字,而不是真正要执行的程序,只起到辅助说明作用
示例代码如下:
# 这是第一个单行注释 print("hello python")
为了保证代码的可读性,
#
后面建议先添加一个空格,然后再编写相应的说明文字
在程序开发时,同样可以使用 #
在代码的后面(旁边)增加说明性的文字
但是,需要注意的是,为了保证代码的可读性,注释和代码之间 至少要有 两个空格
示例代码如下:
print("hello python") # 输出 `hello python`
如果希望编写的 注释信息很多,一行无法显示,就可以使用多行注释
要在 Python 程序中使用多行注释,可以用 一对 连续的 三个 引号(单引号和双引号都可以)
示例代码如下:
""" 这是一个多行注释 在多行注释之间,可以写很多很多的内容…… """ print("hello python")
在一些正规的开发团队,通常会有 代码审核 的惯例,就是一个团队中彼此阅读对方的代码
Python
官方提供有一系列 PEP(Python Enhancement Proposals) 文档任何语言的程序员,编写出符合规范的代码,是开始程序生涯的第一步
# 算数运算符 **计算机**,顾名思义就是负责进行 **数学计算** 并且 **存储计算结果** 的电子设备 ## 目标 * 算术运算符的基本使用 ## 01. 算数运算符 * 算数运算符是 **运算符的一种** * 是完成基本的算术运算使用的符号,用来处理四则运算 | 运算符 | 描述 | 实例 | | :----: | :----: | ------------------------------------------ | | + | 加 | 10 + 20 = 30 | | - | 减 | 10 - 20 = -10 | | * | 乘 | 10 * 20 = 200 | | / | 除 | 10 / 20 = 0.5 | | // | 取整除 | 返回除法的整数部分(商) 9 // 2 输出结果 4 | | % | 取余数 | 返回除法的余数 9 % 2 = 1 | | ** | 幂 | 又称次方、乘方,2 ** 3 = 8 | * 在 Python 中 `*` 运算符还可以用于字符串,计算结果就是字符串重复指定次数的结果 ```python In [1]: "-" * 50 Out[1]: '----------------------------------------'
和数学中的运算符的优先级一致,在 Python 中进行数学计算时,同样也是:
()
调整计算的优先级以下表格的算数优先级由高到最低顺序排列
运算符 | 描述 |
---|---|
** | 幂 (最高优先级) |
* / % // | 乘、除、取余数、取整除 |
+ - | 加法、减法 |
2 + 3 * 5 = 17
(2 + 3) * 5 = 25
2 * 3 + 5 = 11
2 * (3 + 5) = 16
计算机中包含有较多的硬件,但是一个程序要运行,有 三个 核心的硬件,分别是:
程序要执行,首先要被加载到内存
# 1. 确认解释器所在位置 $ which python # 2. 查看 python 文件大小(只是一个软链接) $ ls -lh /usr/bin/python # 3. 查看具体文件大小 $ ls -lh /usr/bin/python2.7
提示:建立 软链接 的目的,是为了方便使用者不用记住使用的解释器是 哪一个具体版本
程序就是 用来处理数据 的!
程序就是用来处理数据的,而变量就是用来存储数据的
=
左边是一个变量名=
右边是存储在变量中的值变量名 = 值
变量定义之后,后续就可以直接使用了
# 定义 qq_number 的变量用来保存 qq 号码 In [1]: qq_number = "1234567" # 输出 qq_number 中保存的内容 In [2]: qq_number Out[2]: '1234567' # 定义 qq_password 的变量用来保存 qq 密码 In [3]: qq_password = "123" # 输出 qq_password 中保存的内容 In [4]: qq_password Out[4]: '123'
使用交互式方式,如果要查看变量内容,直接输入变量名即可,不需要使用
# 定义 qq 号码变量 qq_number = "1234567" # 定义 qq 密码变量 qq_password = "123" # 在程序中,如果要输出变量的内容,需要使用 print 函数 print(qq_number) print(qq_password)
使用解释器执行,如果要输出变量的内容,必须要要使用
- 可以用 其他变量的计算结果 来定义变量
- 变量定义之后,后续就可以直接使用了
需求
# 定义苹果价格变量 price = 8.5 # 定义购买重量 weight = 7.5 # 计算金额 money = price * weight print(money)
# 定义苹果价格变量 price = 8.5 # 定义购买重量 weight = 7.5 # 计算金额 money = price * weight # 只要买苹果就返 5 元 money = money - 5 print(money)
提问
price
/weight
/money
money = money - 5
是在定义新的变量还是在使用变量?
需求
利用 单步调试 确认变量中保存数据的类型
提问
str
—— 字符串bool
—— 布尔(真假)int
—— 整数float
—— 浮点数(小数)Python
中定义变量时需要指定类型吗?
Python
可以根据 =
等号右侧的值,自动推导出变量中存储数据的类型Python
中定义变量是 不需要指定类型(在其他很多高级语言中都需要)int
)float
)bool
)
True
非 0 数
—— 非零即真False
0
complex
)
提示:在 Python 2.x 中,整数 根据保存数值的长度还分为:
int
(整数)long
(长整数)
type
函数可以查看一个变量的类型In [1]: type(name)
bool
型,在计算时
True
对应的数字是 1
False
对应的数字是 0
演练步骤
i = 10
f = 10.5
b = True
+
拼接字符串+
拼接生成新的字符串In [1]: first_name = "三" In [2]: last_name = "张" In [3]: first_name + last_name Out[3]: '三张'
*
重复拼接相同的字符串In [1]: "-" * 50 Out[1]: '--------------------------------------------------'
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`
input
函数函数 | 说明 |
---|---|
print(x) | 将 x 输出到控制台 |
type(x) | 查看 x 的变量类型 |
input
函数从键盘等待用户的输入字符串变量 = input("提示信息:")
函数 | 说明 |
---|---|
int(x) | 将 x 转换为一个整数 |
float(x) | 将 x 转换到一个浮点数 |
需求
# 1. 输入苹果单价 price_str = input("请输入苹果价格:") # 2. 要求苹果重量 weight_str = input("请输入苹果重量:") # 3. 计算金额 # 1> 将苹果单价转换成小数 price = float(price_str) # 2> 将苹果重量转换成小数 weight = float(weight_str) # 3> 计算付款金额 money = price * weight print(money)
提问
price_str
记录用户输入的价格字符串price
记录转换后的价格数值float
函数进行转换price = float(input("请输入价格:"))
提示
苹果单价
9.00
元/斤,购买了5.00
斤,需要支付45.00
元
print
函数将信息输出到控制台%
被称为 格式化操作符,专门用于处理字符串中的格式
%
的字符串,被称为 格式化字符串%
和不同的 字符 连用,不同类型的数据 需要使用 不同的格式化字符格式化字符 | 含义 |
---|---|
%s | 字符串 |
%d | 有符号十进制整数,%06d 表示输出的整数显示位数,不足的地方使用 0 补全 |
%f | 浮点数,%.2f 表示小数点后只显示两位 |
%% | 输出 % |
print("格式化字符串" % 变量1) print("格式化字符串" % (变量1, 变量2...))
需求
name
,输出 我的名字叫 小明,请多多关照!student_no
,输出 我的学号是 000001price
、weight
、money
,输出 苹果单价 9.00 元/斤,购买了 5.00 斤,需要支付 45.00 元scale
,输出 数据比例是 10.00%print("我的名字叫 %s,请多多关照!" % name) print("我的学号是 %06d" % student_no) print("苹果单价 %.02f 元/斤,购买 %.02f 斤,需要支付 %.02f 元" % (price, weight, money)) print("数据比例是 %.02f%%" % (scale * 100))
需求
************************************************** 公司名称 姓名 (职位) 电话:电话 邮箱:邮箱 **************************************************
实现代码如下:
""" 在控制台依次提示用户输入:姓名、公司、职位、电话、电子邮箱 """ name = input("请输入姓名:") company = input("请输入公司:") title = input("请输入职位:") phone = input("请输入电话:") email = input("请输入邮箱:") print("*" * 50) print(company) print() print("%s (%s)" % (name, title)) print() print("电话:%s" % phone) print("邮箱:%s" % email) print("*" * 50)
标示符就是程序员定义的 变量名、函数名
名字 需要有 见名知义 的效果
思考:下面的标示符哪些是正确的,哪些不正确为什么?
fromNo12 from#12 my_Boolean my-Boolean Obj2 2ndObj myInt My_tExt _test test!32 haha(da)tt jack_rose jack&rose GUI G.U.I
Python
内部已经使用的标识符通过以下命令可以查看 Python
中的关键字
In [1]: import keyword In [2]: print(keyword.kwlist)
提示:关键字的学习及使用,会在后面的课程中不断介绍
import
关键字 可以导入一个 “工具包”在
Python
中不同的工具包,提供有不同的工具
命名规则 可以被视为一种 惯例,并无绝对与强制
目的是为了 增加代码的识别和可读性
注意 Python
中的 标识符 是 区分大小写的
在定义变量时,为了保证代码格式,=
的左右应该各保留一个空格
在 Python
中,如果 变量名 需要由 二个 或 多个单词 组成时,可以按照以下方式命名
_
下划线 连接first_name
、last_name
、qq_number
、qq_password
firstName
、lastName
FirstName
、LastName
、CamelCase
生活中的判断几乎是无所不在的,我们每天都在做各种各样的选择,如果这样?如果那样?……
if 今天发工资: 先还信用卡的钱 if 有剩余: 又可以happy了,O(∩_∩)O哈哈~ else: 噢,no。。。还的等30天 else: 盼着发工资
正是因为有了判断,才使得程序世界丰富多彩,充满变化!
判断语句 又被称为 “分支语句”,正是因为有了判断,才让程序有了很多的分支
在 Python
中,if 语句 就是用来进行判断的,格式如下:
if 要判断的条件: 条件成立时,要做的事情 ……
注意:代码的缩进为一个
tab
键,或者 4 个空格 —— 建议使用空格
- 在 Python 开发中,Tab 和空格不要混用!
我们可以把整个 if 语句看成一个完整的代码块
需求
# 1. 定义年龄变量 age = 18 # 2. 判断是否满 18 岁 # if 语句以及缩进部分的代码是一个完整的代码块 if age >= 18: print("可以进网吧嗨皮……") # 3. 思考!- 无论条件是否满足都会执行 print("这句代码什么时候执行?")
注意:
if
语句以及缩进部分是一个 完整的代码块思考
在使用 if
判断时,只能做到满足条件时要做的事情。那如果需要在 不满足条件的时候,做某些事情,该如何做呢?
答案
else
,格式如下:
if 要判断的条件: 条件成立时,要做的事情 …… else: 条件不成立时,要做的事情 ……
注意:
if
和 else
语句以及各自的缩进部分共同是一个 完整的代码块需求
# 1. 输入用户年龄 age = int(input("今年多大了?")) # 2. 判断是否满 18 岁 # if 语句以及缩进部分的代码是一个完整的语法块 if age >= 18: print("可以进网吧嗨皮……") else: print("你还没长大,应该回家写作业!") # 3. 思考!- 无论条件是否满足都会执行 print("这句代码什么时候执行?")
and
条件1 and 条件2
True
False
条件 1 | 条件 2 | 结果 |
---|---|---|
成立 | 成立 | 成立 |
成立 | 不成立 | 不成立 |
不成立 | 成立 | 不成立 |
不成立 | 不成立 | 不成立 |
or
条件1 or 条件2
True
False
条件 1 | 条件 2 | 结果 |
---|---|---|
成立 | 成立 | 成立 |
成立 | 不成立 | 成立 |
不成立 | 成立 | 成立 |
不成立 | 不成立 | 不成立 |
not
not 条件
条件 | 结果 |
---|---|
成立 | 不成立 |
不成立 | 成立 |
age
,编写代码判断年龄是否正确
python_score
、c_score
,编写代码判断成绩
is_employee
,编写代码判断是否是本公司员工
答案 1:
# 练习1: 定义一个整数变量 age,编写代码判断年龄是否正确 age = 100 # 要求人的年龄在 0-120 之间 if age >= 0 and age <= 120: print("年龄正确") else: print("年龄不正确")
答案 2:
# 练习2: 定义两个整数变量 python_score、c_score,编写代码判断成绩 python_score = 50 c_score = 50 # 要求只要有一门成绩 > 60 分就算合格 if python_score > 60 or c_score > 60: print("考试通过") else: print("再接再厉!")
答案 3:
# 练习3: 定义一个布尔型变量 `is_employee`,编写代码判断是否是本公司员工 is_employee = True # 如果不是提示不允许入内 if not is_employee: print("非公勿内")
elif
if
可以 判断条件else
可以处理 条件不成立 的情况elif
if 条件1: 条件1满足执行的代码 …… elif 条件2: 条件2满足时,执行的代码 …… elif 条件3: 条件3满足时,执行的代码 …… else: 以上条件都不满足时,执行的代码 ……
if 条件1 and 条件2: 条件1满足 并且 条件2满足 执行的代码 ……
注意
elif
和 else
都必须和 if
联合使用,而不能单独使用if
、elif
和 else
以及各自缩进的代码,看成一个 完整的代码块需求
holiday_name
字符串变量记录节日名称holiday_name = "平安夜" if holiday_name == "情人节": print("买玫瑰") print("看电影") elif holiday_name == "平安夜": print("买苹果") print("吃大餐") elif holiday_name == "生日": print("买蛋糕") else: print("每天都是节日啊……")
if
的嵌套elif 的应用场景是:同时 判断 多个条件,所有的条件是 平级 的
if
进行条件判断,如果希望 在条件成立的执行语句中 再 增加条件判断,就可以使用 if 的嵌套if 条件 1: 条件 1 满足执行的代码 …… if 条件 1 基础上的条件 2: 条件 2 满足时,执行的代码 …… # 条件 2 不满足的处理 else: 条件 2 不满足时,执行的代码 # 条件 1 不满足的处理 else: 条件1 不满足时,执行的代码 ……
需求
has_ticket
表示是否有车票knife_length
表示刀的长度,单位:厘米# 定义布尔型变量 has_ticket 表示是否有车票 has_ticket = True # 定义整数型变量 knife_length 表示刀的长度,单位:厘米 knife_length = 20 # 首先检查是否有车票,如果有,才允许进行 安检 if has_ticket: print("有车票,可以开始安检...") # 安检时,需要检查刀的长度,判断是否超过 20 厘米 # 如果超过 20 厘米,提示刀的长度,不允许上车 if knife_length >= 20: print("不允许携带 %d 厘米长的刀上车" % knife_length) # 如果不超过 20 厘米,安检通过 else: print("安检通过,祝您旅途愉快……") # 如果没有车票,不允许进门 else: print("大哥,您要先买票啊")
目标
import
导入模块(“工具包”)的使用需求
序号 | 规则 |
---|---|
1 | 石头 胜 剪刀 |
2 | 剪刀 胜 布 |
3 | 布 胜 石头 |
# 从控制台输入要出的拳 —— 石头(1)/剪刀(2)/布(3) player = int(input("请出拳 石头(1)/剪刀(2)/布(3):")) # 电脑 随机 出拳 - 假定电脑永远出石头 computer = 1 # 比较胜负 # 如果条件判断的内容太长,可以在最外侧的条件增加一对大括号 # 再在每一个条件之间,使用回车,PyCharm 可以自动增加 8 个空格 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("不行,我要和你决战到天亮!")
Python
中,要使用随机数,首先需要导入 随机数 的 模块 —— “工具包”import random
导入模块后,可以直接在 模块名称 后面敲一个 .
然后按 Tab
键,会提示该模块中包含的所有函数
random.randint(a, b)
,返回 [a, b]
之间的整数,包含 a
和 b
例如:
random.randint(12, 20) # 生成的随机数n: 12 <= n <= 20 random.randint(20, 20) # 结果永远是 20 random.randint(20, 10) # 该语句是错误的,下限必须小于上限
数学符号表链接:https://zh.wikipedia.org/wiki/数学符号表
运算符 | 描述 | 实例 |
---|---|---|
+ | 加 | 10 + 20 = 30 |
- | 减 | 10 - 20 = -10 |
* | 乘 | 10 * 20 = 200 |
/ | 除 | 10 / 20 = 0.5 |
// | 取整除 | 返回除法的整数部分(商) 9 // 2 输出结果 4 |
% | 取余数 | 返回除法的余数 9 % 2 = 1 |
** | 幂 | 又称次方、乘方,2 ** 3 = 8 |
*
运算符还可以用于字符串,计算结果就是字符串重复指定次数的结果In [1]: "-" * 50 Out[1]: '----------------------------------------'
运算符 | 描述 |
---|---|
== | 检查两个操作数的值是否 相等,如果是,则条件成立,返回 True |
!= | 检查两个操作数的值是否 不相等,如果是,则条件成立,返回 True |
> | 检查左操作数的值是否 大于 右操作数的值,如果是,则条件成立,返回 True |
< | 检查左操作数的值是否 小于 右操作数的值,如果是,则条件成立,返回 True |
>= | 检查左操作数的值是否 大于或等于 右操作数的值,如果是,则条件成立,返回 True |
<= | 检查左操作数的值是否 小于或等于 右操作数的值,如果是,则条件成立,返回 True |
Python 2.x 中判断 不等于 还可以使用
<>
运算符
!=
在 Python 2.x 中同样可以用来判断 不等于
运算符 | 逻辑表达式 | 描述 |
---|---|---|
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 |
=
可以给变量赋值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 |
运算符 | 描述 |
---|---|
** | 幂 (最高优先级) |
* / % // | 乘、除、取余数、取整除 |
+ - | 加法、减法 |
<= < > >= | 比较运算符 |
== != | 等于运算符 |
= %= /= //= -= += *= **= | 赋值运算符 |
not or and | 逻辑运算符 |
在程序开发中,一共有三种流程方式:
while
循环基本使用循环的作用就是让 指定的代码 重复的执行
while
循环最常用的应用场景就是 让执行的代码 按照 指定的次数 重复 执行
需求 —— 打印 5 遍 Hello Python
思考 —— 如果要求打印 100 遍怎么办?
while
语句基本语法初始条件设置 —— 通常是重复执行的 计数器 while 条件(判断 计数器 是否达到 目标次数): 条件满足时,做的事情1 条件满足时,做的事情2 条件满足时,做的事情3 ...(省略)... 处理条件(计数器 + 1)
注意:
while
语句以及缩进部分是一个 完整的代码块需求
# 1. 定义重复次数计数器 i = 1 # 2. 使用 while 判断条件 while i <= 5: # 要重复执行的代码 print("Hello Python") # 处理计数器 i i = i + 1 print("循环结束后的 i = %d" % i)
注意:循环结束后,之前定义的计数器条件的数值是依旧存在的
由于程序员的原因,忘记 在循环内部 修改循环的判断条件,导致循环持续执行,程序无法终止!
=
可以给变量赋值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 |
常见的计数方法有两种,可以分别称为:
1
开始)—— 更符合人类的习惯0
开始)—— 几乎所有的程序语言都选择从 0 开始计数因此,大家在编写程序时,应该尽量养成习惯:除非需求的特殊要求,否则 循环 的计数都从 0 开始
在程序开发中,通常会遇到 利用循环 重复计算 的需求
遇到这种需求,可以:
while
上方定义一个变量,用于 存放最终计算结果需求
# 计算 0 ~ 100 之间所有数字的累计求和结果 # 0. 定义最终结果的变量 result = 0 # 1. 定义一个整数的变量记录循环的次数 i = 0 # 2. 开始循环 while i <= 100: print(i) # 每一次循环,都让 result 这个变量和 i 这个计数器相加 result += i # 处理计数器 i += 1 print("0~100之间的数字求和结果 = %d" % result)
开发步骤
# 0. 最终结果 result = 0 # 1. 计数器 i = 0 # 2. 开始循环 while i <= 100: # 判断偶数 if i % 2 == 0: print(i) result += i # 处理计数器 i += 1 print("0~100之间偶数求和结果 = %d" % result)
break
和continue
是专门在循环中使用的关键字
break
某一条件满足时,退出循环,不再执行后续重复的代码continue
某一条件满足时,不执行后续重复的代码
break
和continue
只针对 当前所在循环 有效
break
退出循环i = 0 while i < 10: # break 某一条件满足时,退出循环,不再执行后续重复的代码 # i == 3 if i == 3: break print(i) i += 1 print("over")
break
只针对当前所在循环有效
continue
i = 0 while i < 10: # 当 i == 7 时,不希望执行需要重复执行的代码 if i == 7: # 在使用 continue 之前,同样应该修改计数器 # 否则会出现死循环 i += 1 continue # 重复执行的代码 print(i) i += 1
continue
时,条件处理部分的代码,需要特别注意,不小心会出现 死循环
continue
只针对当前所在循环有效
while
循环嵌套while
嵌套就是:while
里面还有 while
while 条件 1: 条件满足时,做的事情1 条件满足时,做的事情2 条件满足时,做的事情3 ...(省略)... while 条件 2: 条件满足时,做的事情1 条件满足时,做的事情2 条件满足时,做的事情3 ...(省略)... 处理条件 2 处理条件 1
需求
*
,每一行星号的数量依次递增* ** *** **** *****
# 1. 定义一个计数器变量,从数字1开始,循环会比较方便 row = 1 while row <= 5: print("*" * row) row += 1
知识点 对 print
函数的使用做一个增强
在默认情况下,print
函数输出内容之后,会自动在内容末尾增加换行
如果不希望末尾增加换行,可以在 print
函数输出内容的后面增加 , end=""
其中 ""
中间可以指定 print
函数输出内容之后,继续希望显示的内容
语法格式如下:
# 向控制台输出内容结束之后,不会换行 print("*", end="") # 单纯的换行 print("")
end=""
表示向控制台输出内容结束之后,不会换行
假设 Python
没有提供 字符串的 *
操作 拼接字符串
需求
*
,每一行星号的数量依次递增* ** *** **** *****
开发步骤
*
应该如何处理?
列
的星星显示row = 1 while row <= 5: # 假设 python 没有提供字符串 * 操作 # 在循环内部,再增加一个循环,实现每一行的 星星 打印 col = 1 while col <= row: print("*", end="") col += 1 # 每一行星号输出完成后,再增加一个换行 print("") row += 1
需求 输出 九九乘法表,格式如下:
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
开发步骤
* ** *** **** ***** ****** ******* ******** *********
*
替换成对应的行与列相乘# 定义起始行 row = 1 # 最大打印 9 行 while row <= 9: # 定义起始列 col = 1 # 最大打印 row 列 while col <= row: # end = "",表示输出结束后,不换行 # "\t" 可以在控制台输出一个制表符,协助在输出文本时对齐 print("%d * %d = %d" % (col, row, row * col), end="\t") # 列数 + 1 col += 1 # 一行打印完成的换行 print("") # 行数 + 1 row += 1
字符串中的转义字符
\t
在控制台输出一个 制表符,协助在输出文本时 垂直方向 保持对齐\n
在控制台输出一个 换行符制表符 的功能是在不使用表格的情况下在 垂直方向 按列对齐文本
转义字符 | 描述 |
---|---|
\\ | 反斜杠符号 |
\' | 单引号 |
\" | 双引号 |
\n | 换行 |
\t | 横向制表符 |
\r | 回车 |
面向对象编程 —— Object Oriented Programming
简写 OOP
所有步骤
从头到尾
逐步实现特点
相比较函数,面向对象 是 更大 的 封装,根据 职责 在 一个对象中 封装 多个方法
特点
类 和 对象 是 面向对象编程的 两个 核心概念
在程序开发中,应该 先有类,再有对象
在使用面相对象开发前,应该首先分析需求,确定一下,程序中需要包含哪些类!
在程序开发中,要设计一个类,通常需要满足一下三个要素:
CapWords
名词提炼法 分析 整个业务流程,出现的 名词,通常就是找到的类
提示:需求中没有涉及的属性或者方法在设计类时,不需要考虑
需求
需求
dir
内置函数self
参数dir
内置函数(知道)Python
中 对象几乎是无所不在的,我们之前学习的 变量、数据、函数 都是对象在 Python
中可以使用以下两个方法验证:
.
,然后按下 TAB
键,iPython
会提示该对象能够调用的 方法列表dir
传入 标识符 / 数据,可以查看对象内的 所有属性及方法提示 __方法名__
格式的方法是 Python
提供的 内置方法 / 属性,稍后会给大家介绍一些常用的 内置方法 / 属性
序号 | 方法名 | 类型 | 作用 |
---|---|---|---|
01 | __new__ |
方法 | 创建对象时,会被 自动 调用 |
02 | __init__ |
方法 | 对象被初始化时,会被 自动 调用 |
03 | __del__ |
方法 | 对象被从内存中销毁前,会被 自动 调用 |
04 | __str__ |
方法 | 返回对象的描述信息,print 函数输出使用 |
提示 利用好 dir()
函数,在学习时很多内容就不需要死记硬背了
面向对象 是 更大 的 封装,在 一个类中 封装 多个方法,这样 通过这个类创建出来的对象,就可以直接调用这些方法了!
Python
中要定义一个只包含方法的类,语法格式如下:class 类名: def 方法1(self, 参数列表): pass def 方法2(self, 参数列表): pass
self
,大家暂时先记住,稍后介绍 self
注意:类名 的 命名规则 要符合 大驼峰命名法
对象变量 = 类名()
需求
分析
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 进制 输出数字lazy_cat = Cat() lazy_cat.eat() lazy_cat.drink()
提问:
tom
和lazy_cat
是同一个对象吗?
self
参数Python
中,要 给对象设置属性,非常的容易,但是不推荐使用
.
设置一个属性即可注意:这种方式虽然简单,但是不推荐使用!
tom.name = "Tom" ... lazy_cat.name = "大懒猫"
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.
访问对象的 属性和方法tom = Cat() tom.drink() tom.eat() tom.name = "Tom" print(tom)
AttributeError: 'Cat' object has no attribute 'name' 属性错误:'Cat' 对象没有 'name' 属性
提示
类名()
创建对象时,会 自动 执行以下操作:
init
)__init__
方法,__init__
是对象的内置方法
__init__
方法是 专门 用来定义一个类 具有哪些属性的方法!
在 Cat
中增加 __init__
方法,验证该方法在创建对象时会被自动调用
class Cat: """这是一个猫类""" def __init__(self): print("初始化方法")
__init__
方法内部使用 self.属性名 = 属性的初始值
就可以 定义属性Cat
类创建的对象,都会拥有该属性class Cat: def __init__(self): print("这是一个初始化方法") # 定义用 Cat 类创建的猫对象都有一个 name 的属性 self.name = "Tom" def eat(self): print("%s 爱吃鱼" % self.name) # 使用类名()创建对象的时候,会自动调用初始化方法 __init__ tom = Cat() tom.eat()
__init__
方法进行 改造
__init__
方法的参数self.属性 = 形参
接收外部传递的参数类名(属性1, 属性2...)
调用class Cat: def __init__(self, name): print("初始化方法 %s" % name) self.name = name ... tom = Cat("Tom") ... lazy_cat = Cat("大懒猫") ...
序号 | 方法名 | 类型 | 作用 |
---|---|---|---|
01 | __del__ |
方法 | 对象被从内存中销毁前,会被 自动 调用 |
02 | __str__ |
方法 | 返回对象的描述信息,print 函数输出使用 |
__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 是一个全局变量 tom = Cat("Tom") print(tom.name) # del 关键字可以删除一个对象 del tom print("-" * 50)
__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)
需求
75.0
公斤0.5
公斤1
公斤提示:在 对象的方法内部,是可以 直接访问对象的属性 的!
class Person: """人类""" def __init__(self, name, weight): self.name = name self.weight = weight def __str__(self): return "我的名字叫 %s 体重 %.2f 公斤" % (self.name, self.weight) def run(self): """跑步""" print("%s 爱跑步,跑步锻炼身体" % self.name) self.weight -= 0.5 def eat(self): """吃东西""" print("%s 是吃货,吃完这顿再减肥" % self.name) self.weight += 1 xiaoming = Person("小明", 75) xiaoming.run() xiaoming.eat() xiaoming.eat() print(xiaoming)
需求
75.0
公斤45.0
公斤0.5
公斤1
公斤提示
需求
4
平米2
平米1.5
平米剩余面积
add_item
方法,向房间 添加家具 时,让 剩余面积 -= 家具面积思考:应该先开发哪一个类?
答案 —— 家具类
class HouseItem: def __init__(self, name, area): """ :param name: 家具名称 :param area: 占地面积 """ self.name = name self.area = area def __str__(self): return "[%s] 占地面积 %.2f" % (self.name, self.area) # 1. 创建家具 bed = HouseItem("席梦思", 4) chest = HouseItem("衣柜", 2) table = HouseItem("餐桌", 1.5) print(bed) print(chest) print(table)
小结
__init__
和 __str__
两个内置方法class House: def __init__(self, house_type, area): """ :param house_type: 户型 :param area: 总面积 """ self.house_type = house_type self.area = area # 剩余面积默认和总面积一致 self.free_area = area # 默认没有任何的家具 self.item_list = [] def __str__(self): # Python 能够自动的将一对括号内部的代码连接在一起 return ("户型:%s\n总面积:%.2f[剩余:%.2f]\n家具:%s" % (self.house_type, self.area, self.free_area, self.item_list)) def add_item(self, item): print("要添加 %s" % item) ... # 2. 创建房子对象 my_home = House("两室一厅", 60) my_home.add_item(bed) my_home.add_item(chest) my_home.add_item(table) print(my_home)
小结
__init__
和 __str__
两个内置方法add_item
方法 准备添加家具add_item
方法,将 三件家具 以实参传递到 add_item
内部需求
def add_item(self, item): print("要添加 %s" % item) # 1. 判断家具面积是否大于剩余面积 if item.area > self.free_area: print("%s 的面积太大,不能添加到房子中" % item.name) return # 2. 将家具的名称追加到名称列表中 self.item_list.append(item.name) # 3. 计算剩余面积 self.free_area -= item.area
add_item
方法 将家具添加到房子中一个对象的 属性 可以是 另外一个类创建的对象
需求
shoot
方法需求
print
提示射击,并且输出子弹数量class Gun: def __init__(self, model): # 枪的型号 self.model = model # 子弹数量 self.bullet_count = 0 def add_bullet(self, count): self.bullet_count += count def shoot(self): # 判断是否还有子弹 if self.bullet_count <= 0: print("没有子弹了...") return # 发射一颗子弹 self.bullet_count -= 1 print("%s 发射子弹[%d]..." % (self.model, self.bullet_count)) # 创建枪对象 ak47 = Gun("ak47") ak47.add_bullet(50) ak47.shoot()
假设:每一个新兵 都 没有枪
定义没有初始值的属性
在定义属性时,如果 不知道设置什么初始值,可以设置为 None
None
关键字 表示 什么都没有None
赋值给任何一个变量fire
方法需求
class Soldier: def __init__(self, name): # 姓名 self.name = name # 枪,士兵初始没有枪 None 关键字表示什么都没有 self.gun = None def fire(self): # 1. 判断士兵是否有枪 if self.gun is None: print("[%s] 还没有枪..." % self.name) return # 2. 高喊口号 print("冲啊...[%s]" % self.name) # 3. 让枪装填子弹 self.gun.add_bullet(50) # 4. 让枪发射子弹 self.gun.shoot()
小结
__init__
内置方法None
身份运算符用于 比较 两个对象的 内存地址 是否一致 —— 是否是对同一个对象的引用
is
用于判断 两个变量 引用对象是否为同一个
==
用于判断 引用变量的值 是否相等
>>> a = [1, 2, 3] >>> b = [1, 2, 3] >>> b is a False >>> b == a True
应用场景
定义方式
class Women: def __init__(self, name): self.name = name # 不要问女生的年龄 self.__age = 18 def __secret(self): print("我的年龄是 %d" % self.__age) xiaofang = Women("小芳") # 私有属性,外部不能直接访问 # print(xiaofang.__age) # 私有方法,外部不能直接调用 # xiaofang.__secret()
提示:在日常开发中,不要使用这种方式,访问对象的 私有属性 或 私有方法
Python
中,并没有 真正意义 的 私有
_类名
=> _类名__名称
# 私有属性,外部不能直接访问到 print(xiaofang._Women__age) # 私有方法,外部不能直接调用 xiaofang._Women__secret()
__new__
方法设计模式
单例设计模式
类名()
返回的对象,内存地址是相同的__new__
方法Python
的解释器 首先 会 调用 __new__
方法为对象 分配空间__new__
是一个 由 object
基类提供的 内置的静态方法,主要作用有两个:
Python
的解释器获得对象的 引用 后,将引用作为 第一个参数,传递给 __init__
方法重写
__new__
方法 的代码非常固定!
__new__
方法 一定要 return super().__new__(cls)
__new__
是一个静态方法,在调用时需要 主动传递 cls
参数示例代码
class MusicPlayer(object): def __new__(cls, *args, **kwargs): # 如果不返回任何结果, return super().__new__(cls) def __init__(self): print("初始化音乐播放对象") player = MusicPlayer() print(player)
None
,用于记录 单例对象的引用__new__
方法is None
,调用父类方法分配空间,并在类属性中记录结果class MusicPlayer(object): # 定义类属性记录单例对象引用 instance = None def __new__(cls, *args, **kwargs): # 1. 判断类属性是否已经被赋值 if cls.instance is None: cls.instance = super().__new__(cls) # 2. 返回类属性的单例引用 return cls.instance
类名()
创建对象时,Python
的解释器都会自动调用两个方法:
__new__
分配空间__init__
对象初始化__new__
方法改造之后,每次都会得到 第一次被创建对象的引用需求
解决办法
init_flag
标记是否 执行过初始化动作,初始值为 False
__init__
方法中,判断 init_flag
,如果为 False
就执行初始化动作init_flag
设置为 True
__init__
方法时,初始化动作就不会被再次执行 了class MusicPlayer(object): # 记录第一个被创建对象的引用 instance = None # 记录是否执行过初始化动作 init_flag = False def __new__(cls, *args, **kwargs): # 1. 判断类属性是否是空对象 if cls.instance is None: # 2. 调用父类的方法,为第一个对象分配空间 cls.instance = super().__new__(cls) # 3. 返回类属性保存的对象引用 return cls.instance def __init__(self): if not MusicPlayer.init_flag: print("初始化音乐播放器") MusicPlayer.init_flag = True # 创建多个对象 player1 = MusicPlayer() print(player1) player2 = MusicPlayer() print(player2)
面向对象三大特性
封装 根据 职责 将 属性 和 方法 封装 到一个抽象的 类 中
继承 实现代码的重用,相同的代码不需要重复的编写
多态 不同的 子类对象 调用相同的 父类方法,产生不同的执行结果
需求
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() # 1. 创建一个狗对象 # wangcai = Dog("旺财") wangcai = XiaoTianDog("飞天旺财") # 2. 创建一个小明对象 xiaoming = Person("小明") # 3. 让小明调用和狗玩的方法 xiaoming.game_with_dog(wangcai)
面向对象三大特性
继承的概念:子类 拥有 父类 的所有 方法 和 属性
class 类名(父类名): pass
Dog
类是 Animal
类的子类,Animal
类是 Dog
类的父类,Dog
类从 Animal
类继承Dog
类是 Animal
类的派生类,Animal
类是 Dog
类的基类,Dog
类从 Animal
类派生C
类从 B
类继承,B
类又从 A
类继承C
类就具有 B
类和 A
类的所有属性和方法子类 拥有 父类 以及 父类的父类 中封装的所有 属性 和 方法
提问
哮天犬 能够调用 Cat
类中定义的 catch
方法吗?
答案
不能,因为 哮天犬 和 Cat
之间没有 继承 关系
应用场景
重写 父类方法有两种情况:
具体的实现方式,就相当于在 子类中 定义了一个 和父类同名的方法并且实现
重写之后,在运行时,只会调用 子类中重写的方法,而不再会调用 父类封装的方法
super().父类方法
来调用父类方法的执行super
Python
中 super
是一个 特殊的类super()
就是使用 super
类创建出来的对象在
Python 2.x
时,如果需要调用父类的方法,还可以使用以下方式:
父类名.方法(self)
Python 3.x
还支持这种方式提示
父类名
和 super()
两种方式不要混用
- 私有属性、方法 是对象的隐私,不对外公开,外界 以及 子类 都不能直接访问
- 私有属性、方法 通常用于做一些内部的事情
示例
B
的对象不能直接访问 __num2
属性B
的对象不能在 demo
方法内访问 __num2
属性B
的对象可以在 demo
方法内,调用父类的 test
方法test
方法内部,能够访问 __num2
属性和 __test
方法概念
语法
class 子类名(父类名1, 父类名2...) pass
问题的提出
提示:开发时,应该尽量避免这种容易产生混淆的情况! —— 如果 父类之间 存在 同名的属性或者方法,应该 尽量避免 使用多继承
Python
中针对 类 提供了一个 内置属性 __mro__
可以查看 方法 搜索顺序method resolution order
,主要用于 在多继承时判断 方法、属性 的调用 路径print(C.__mro__)
输出结果
(<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>)
__mro__
的输出结果 从左至右 的顺序查找的
object
是Python
为所有对象提供的 基类,提供有一些内置的属性和方法,可以使用dir
函数查看
新式类:以 object
为基类的类,推荐使用
经典类:不以 object
为基类的类,不推荐使用
在 Python 3.x
中定义类时,如果没有指定父类,会 默认使用 object
作为该类的 基类 —— Python 3.x
中定义的类都是 新式类
在 Python 2.x
中定义类时,如果没有指定父类,则不会以 object
作为 基类
新式类 和 经典类 在多继承时 —— 会影响到方法的搜索顺序
为了保证编写的代码能够同时在 Python 2.x
和 Python 3.x
运行!
今后在定义类时,如果没有父类,建议统一继承自 object
class 类名(object): pass
__init__
为 对象初始化因此,通常也会把:
在程序执行时:
self.
结论
Python
中 一切皆对象:
class AAA:
定义的类属于 类对象obj1 = AAA()
属于 实例对象
Python
中,类 是一个特殊的对象 —— 类对象示例需求
name
class Tool(object): # 使用赋值语句,定义类属性,记录创建工具对象的总数 count = 0 def __init__(self, name): self.name = name # 针对类属性做一个计数+1 Tool.count += 1 # 创建工具对象 tool1 = Tool("斧头") tool2 = Tool("榔头") tool3 = Tool("铁锹") # 知道使用 Tool 类到底创建了多少个对象? print("现在创建了 %d 个工具" % Tool.count)
在 Python
中 属性的获取 存在一个 向上查找机制
因此,要访问类属性有两种方式:
注意
对象.类属性 = 值
赋值语句,只会 给对象添加一个属性,而不会影响到 类属性的值class
关键字下方可以定义 类属性语法如下
@classmethod def 类方法名(cls): pass
@classmethod
来标识,告诉解释器这是一个类方法cls
cls
就是 哪一个类的引用self
类似cls
cls
参数cls.
访问类的属性cls.
调用其他的类方法示例需求
name
show_tool_count
的类方法,输出使用当前这个类,创建的对象个数@classmethod def show_tool_count(cls): """显示工具对象的总数""" print("工具对象的总数 %d" % cls.count)
在类方法内部,可以直接使用
cls
访问 类属性 或者 调用类方法
在开发时,如果需要在 类 中封装一个方法,这个方法:
这个时候,可以把这个方法封装成一个 静态方法
语法如下
@staticmethod def 静态方法名(): pass
@staticmethod
来标识,告诉解释器这是一个静态方法class Dog(object): # 狗对象计数 dog_count = 0 @staticmethod def run(): # 不需要访问实例属性也不需要访问类属性的方法 print("狗在跑...") def __init__(self, name): self.name = name
需求
Game
类top_score
记录游戏的 历史最高分player_name
记录 当前游戏的玩家姓名show_help
显示游戏帮助信息show_top_score
显示历史最高分start_game
开始当前玩家的游戏提问
如果方法内部 即需要访问 实例属性,又需要访问 类属性,应该定义成什么方法?
答案
class Game(object): # 游戏最高分,类属性 top_score = 0 @staticmethod def show_help(): print("帮助信息:让僵尸走进房间") @classmethod def show_top_score(cls): print("游戏最高分是 %d" % cls.top_score) def __init__(self, player_name): self.player_name = player_name def start_game(self): print("[%s] 开始游戏..." % self.player_name) # 使用类名.修改历史最高分 Game.top_score = 999 # 1. 查看游戏帮助 Game.show_help() # 2. 查看游戏最高分 Game.show_top_score() # 3. 创建游戏对象,开始游戏 game = Game("小明") game.start_game() # 4. 游戏结束,查看游戏最高分 Game.show_top_score()
In [1]: eval("1 + 1")
Out[1]: 2
In [2]: eval("'' * 10")
Out[2]: '*********'
In [3]: type(eval("[1, 2, 3, 4, 5]"))
Out[3]: list
In [4]: type(eval("{'name': 'xiaoming', 'age': 18}"))
Out[4]: dict
模块是 Python 程序架构的一个核心概念
py
结尾的 Python
源代码文件都是一个 模块import 模块名1, 模块名2
提示:在导入模块时,每个导入应该独占一行
import 模块名1 import 模块名2
模块名.
使用 模块提供的工具 —— 全局变量、函数、类as
指定模块的别名如果模块的名字太长,可以使用
as
指定模块的名称,以方便在代码中的使用
import 模块名1 as 模块别名
注意:模块别名 应该符合 大驼峰命名法
from ... import
的方式import 模块名
是 一次性 把模块中 所有工具全部导入,并且通过 模块名/别名 访问# 从 模块 导入 某一个工具 from 模块名1 import 工具名
模块名.
注意
如果 两个模块,存在 同名的函数,那么 后导入模块的函数,会 覆盖掉先导入的函数
import
代码应该统一写在 代码的顶部,更容易及时发现冲突as
关键字 给其中一个工具起一个别名# 从 模块 导入 所有工具 from 模块名1 import *
注意
这种方式不推荐使用,因为函数重名并没有任何的提示,出现问题不好排查
Python
的解释器在 导入模块 时,会:
在开发时,给文件起名,不要和 系统的模块文件 重名
Python
中每一个模块都有一个内置属性 __file__
可以 查看模块 的 完整路径
示例
import random # 生成一个 0~10 的数字 rand = random.randint(0, 10) print(rand)
注意:如果当前目录下,存在一个
random.py
的文件,程序就无法正常执行了!
Python
的解释器会 加载当前目录 下的 random.py
而不会加载 系统的 random
模块Python
文件 就是一个 模块实际开发场景
__name__
属性
__name__
属性可以做到,测试模块的代码 只在测试情况下被运行,而在 被导入时不会被执行!
__name__
是 Python
的一个内置属性,记录着一个 字符串__name__
就是 模块名__name__
是 __main__
在很多 Python
文件中都会看到以下格式的代码:
# 导入模块 # 定义全局变量 # 定义类 # 定义函数 # 在代码的最下方 def main(): # ... pass # 根据 __name__ 判断是否执行下方代码 if __name__ == "__main__": main()
__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
setup.py
的文件from distutils.core import setup setup(name="hm_message", # 包名 version="1.0", # 版本 description="itheima's 发送和接收消息模块", # 描述信息 long_description="完整的发送和接收消息模块", # 完整描述信息 author="itheima", # 作者 author_email="itheima@itheima.com", # 作者邮箱 url="www.itheima.com", # 主页 py_modules=["hm_message.send_message", "hm_message.receive_message"])
有关字典参数的详细信息,可以参阅官方网站:
https://docs.python.org/2/distutils/apiref.html
$ python3 setup.py build
$ python3 setup.py sdist
注意:要制作哪个版本的模块,就使用哪个版本的解释器执行!
$ tar -zxvf hm_message-1.0.tar.gz $ sudo python3 setup.py install
卸载模块
直接从安装目录下,把安装模块的 目录 删除就可以
$ cd /usr/local/lib/python3.5/dist-packages/ $ sudo rm -r hm_message*
pip
安装第三方模块Python
包 / 模块
pygame
就是一套非常成熟的 游戏开发模块pip
是一个现代的,通用的 Python
包管理工具Python
包的查找、下载、安装、卸载等功能安装和卸载命令如下:
# 将模块安装到 Python 2.x 环境 $ sudo pip install pygame $ sudo pip uninstall pygame # 将模块安装到 Python 3.x 环境 $ sudo pip3 install pygame $ sudo pip3 uninstall pygame
Mac
下安装 iPython
$ sudo pip install ipython
Linux
下安装 iPython
$ sudo apt install ipython $ sudo apt install ipython3
文件的作用
将数据长期保存下来,在需要的时候使用
文本文件
二进制文件
在 计算机 中要操作文件的套路非常固定,一共包含三个步骤:
Python
中要操作文件需要记住 1 个函数和 3 个方法序号 | 函数/方法 | 说明 |
---|---|---|
01 | open | 打开文件,并且返回文件操作对象 |
02 | read | 将文件内容读取到内存 |
03 | write | 将指定内容写入文件 |
04 | close | 关闭文件 |
open
函数负责打开文件,并且返回文件对象read
/write
/close
三个方法都需要通过 文件对象 来调用open
函数的第一个参数是要打开的文件名(文件名区分大小写)
read
方法可以一次性 读入 并 返回 文件的 所有内容close
方法负责 关闭文件
read
方法执行后,会把 文件指针 移动到 文件的末尾# 1. 打开 - 文件名需要注意大小写 file = open("README") # 2. 读取 text = file.read() print(text) # 3. 关闭 file.close()
提示
read
方法后,文件指针 会移动到 读取内容的末尾
思考
read
方法,读取了所有内容,那么再次调用 read
方法,还能够获得到内容吗?答案
open
函数默认以 只读方式 打开文件,并且返回文件对象语法如下:
f = open("文件名", "访问方式")
访问方式 | 说明 |
---|---|
r | 以只读方式打开文件。文件的指针将会放在文件的开头,这是默认模式。如果文件不存在,抛出异常 |
w | 以只写方式打开文件。如果文件存在会被覆盖。如果文件不存在,创建新文件 |
a | 以追加方式打开文件。如果该文件已存在,文件指针将会放在文件的结尾。如果文件不存在,创建新文件进行写入 |
r+ | 以读写方式打开文件。文件的指针将会放在文件的开头。如果文件不存在,抛出异常 |
w+ | 以读写方式打开文件。如果文件存在会被覆盖。如果文件不存在,创建新文件 |
a+ | 以读写方式打开文件。如果该文件已存在,文件指针将会放在文件的结尾。如果文件不存在,创建新文件进行写入 |
提示
写入文件示例
# 打开文件 f = open("README", "w") f.write("hello python!\n") f.write("今天天气真好") # 关闭文件 f.close()
read
方法默认会把文件的 所有内容 一次性读取到内存readline
方法readline
方法可以一次读取一行内容读取大文件的正确姿势
# 打开文件 file = open("README") while True: # 读取一行内容 text = file.readline() # 判断是否读到内容 if not text: break # 每读取一行的末尾已经有了一个 `\n` print(text, end="") # 关闭文件 file.close()
目标
用代码的方式,来实现文件复制过程
# 1. 打开文件 file_read = open("README") file_write = open("README[复件]", "w") # 2. 读取并写入文件 text = file_read.read() file_write.write(text) # 3. 关闭文件 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()
Python
中,如果希望通过程序实现上述功能,需要导入 os
模块序号 | 方法名 | 说明 | 示例 |
---|---|---|---|
01 | rename | 重命名文件 | os.rename(源文件名, 目标文件名) |
02 | remove | 删除文件 | os.remove(文件名) |
序号 | 方法名 | 说明 | 示例 |
---|---|---|---|
01 | listdir | 目录列表 | os.listdir(目录名) |
02 | mkdir | 创建目录 | os.mkdir(目录名) |
03 | rmdir | 删除目录 | os.rmdir(目录名) |
04 | getcwd | 获取当前目录 | os.getcwd() |
05 | chdir | 修改工作目录 | os.chdir(目标目录) |
06 | path.isdir | 判断是否是文件 | os.path.isdir(文件路径) |
提示:文件或者目录操作都支持 相对路径 和 绝对路径
ASCII
编码,UNICODE
编码等Python 2.x 默认使用
ASCII
编码格式
Python 3.x 默认使用UTF-8
编码格式
ASCII
编码256
个 ASCII
字符ASCII
在内存中占用 1 个字节 的空间
8
个 0/1
的排列组合方式一共有 256
种,也就是 2 ** 8
UTF-8
编码格式UTF-8
字符,涵盖了 地球上几乎所有地区的文字UTF-8
是 UNICODE
编码的一种编码格式Python 2.x 默认使用
ASCII
编码格式
Python 3.x 默认使用UTF-8
编码格式
utf-8
编码来处理 python 文件# *-* coding:utf8 *-*
这方式是官方推荐使用的!
# coding=utf8
Python 2.x
中,即使指定了文件使用 UTF-8
的编码格式,但是在遍历字符串时,仍然会 以字节为单位遍历 字符串u
,告诉解释器这是一个 unicode
字符串(使用 UTF-8
编码格式的字符串)# *-* coding:utf8 *-* # 在字符串前,增加一个 `u` 表示这个字符串是一个 utf8 字符串 hello_str = u"你好世界" print(hello_str) for c in hello_str: print(c)
Python 解释器
遇到 到一个错误,会停止程序的执行,并且提示一些错误信息,这就是 异常程序开发时,很难将 所有的特殊情况 都处理的面面俱到,通过 异常捕获 可以针对突发事件做集中的处理,从而保证程序的 稳定性和健壮性
try(尝试)
来 捕获异常try: 尝试执行的代码 except: 出现错误的处理
try
尝试,下方编写要尝试代码,不确定是否能够正常执行的代码except
如果不是,下方编写尝试失败的代码try: # 提示用户输入一个数字 num = int(input("请输入数字:")) except: print("请输入正确的数字")
在程序执行时,可能会遇到 不同类型的异常,并且需要 针对不同类型的异常,做出不同的响应,这个时候,就需要捕获错误类型了
语法如下:
try: # 尝试执行的代码 pass except 错误类型1: # 针对错误类型1,对应的代码处理 pass except (错误类型2, 错误类型3): # 针对错误类型2 和 3,对应的代码处理 pass except Exception as result: print("未知错误 %s" % result)
Python
解释器 抛出异常 时,最后一行错误信息的第一个单词,就是错误类型需求
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: # 尝试执行的代码 pass except 错误类型1: # 针对错误类型1,对应的代码处理 pass except 错误类型2: # 针对错误类型2,对应的代码处理 pass except (错误类型3, 错误类型4): # 针对错误类型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("执行完成,但是不保证正确")
提示
需求
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)
raise
异常Python
解释器会 抛出 异常之外示例
注意
Python
中提供了一个 Exception
异常类Exception
的 对象raise
关键字 抛出 异常对象需求
input_password
函数,提示用户输入密码def input_password(): # 1. 提示用户输入密码 pwd = input("请输入密码:") # 2. 判断密码长度,如果长度 >= 8,返回用户输入的密码 if len(pwd) >= 8: return pwd # 3. 密码长度不够,需要抛出异常 # 1> 创建异常对象 - 使用异常的错误信息字符串作为参数 ex = Exception("密码长度不够") # 2> 抛出异常对象 raise ex try: user_pwd = input_password() print(user_pwd) except Exception as result: print("发现错误:%s" % result)
演练步骤
04_函数
项目multiple_table():
import
导入并且调用函数定义函数的格式如下:
def 函数名(): 函数封装的代码 ……
def
是英文 define
的缩写调用函数很简单的,通过 函数名()
即可完成对函数的调用
需求
say_hello
的函数,封装三行打招呼的代码name = "小明" # 解释器知道这里定义了一个函数 def say_hello(): print("hello 1") print("hello 2") print("hello 3") print(name) # 只有在调用函数时,之前定义的函数才会被执行 # 函数执行完成之后,会重新回到之前的程序中,继续执行后续的代码 say_hello() print(name)
用 单步执行 F8 和 F7 观察以下代码的执行过程
能否将 函数调用 放在 函数定义 的上方?
Python
已经知道函数的存在NameError: name 'say_hello' is not defined
(名称错误:say_hello 这个名字没有被定义)CTRL + Q
可以查看函数的说明信息注意:因为 函数体相对比较独立,函数定义的上方,应该和其他代码(包括注释)保留 两个空行
演练需求
sum_2_num
的函数演练代码如下:
def sum_2_num(): num1 = 10 num2 = 20 result = num1 + num2 print("%d + %d = %d" % (num1, num2, result)) sum_2_num()
思考一下存在什么问题
函数只能处理 固定数值 的相加
如何解决?
,
分隔def sum_2_num(num1, num2): result = num1 + num2 print("%d + %d = %d" % (num1, num2, result)) sum_2_num(50, 20)
return
关键字可以返回结果注意:
return
表示返回,后续的代码都不会被执行
def sum_2_num(num1, num2): """对两个数字的求和""" return num1 + num2 # 调用函数,并使用 result 变量接收计算结果 result = sum_2_num(10, 20) print("计算结果是 %d" % result)
test2
中,调用了另外一个函数 test1
test1
函数时,会先把函数 test1
中的任务都执行完test2
中调用函数 test1
的位置,继续执行后续的代码def test1(): print("*" * 50) print("test 1") print("*" * 50) def test2(): print("-" * 50) print("test 2") test1() print("-" * 50) test2()
体会一下工作中 需求是多变 的
需求 1
print_line
函数能够打印 *
组成的 一条分隔线def print_line(char): print("*" * 50)
需求 2
def print_line(char): print(char * 50)
需求 3
def print_line(char, times): print(char * times)
需求 4
提示:工作中针对需求的变化,应该冷静思考,不要轻易修改之前已经完成的,能够正常执行的函数!
def print_line(char, times): print(char * times) def print_lines(char, times): row = 0 while row < 5: print_line(char, times) row += 1
模块是 Python 程序架构的一个核心概念
py
结尾的 Python
源代码文件都是一个 模块步骤
hm_10_分隔线模块.py
hm_09_打印多条分隔线.py
中的内容,最后一行 print
代码除外name = "黑马程序员"
hm_10_体验模块.py
文件,并且编写以下代码:import hm_10_分隔线模块 hm_10_分隔线模块.print_line("-", 80) print(hm_10_分隔线模块.name)
import
导入这个模块模块名.变量
/ 模块名.函数
的方式,使用这个模块中定义的变量或者函数模块可以让 曾经编写过的代码 方便的被 复用!
注意:如果在给 Python 文件起名时,以数字开头 是无法在
PyCharm
中通过导入这个模块的
C
是compiled
编译过 的意思
操作步骤
__pycache__
的目录hm_10_分隔线模块.cpython-35.pyc
文件,cpython-35
表示 Python
解释器的版本pyc
文件是由 Python 解释器将 模块的源码 转换为 字节码
Python
这样保存 字节码 是作为一种启动 速度的优化字节码
Python
在解释源程序时是分成两个步骤的
有了模块的字节码文件之后,下一次运行程序时,如果在 上次保存字节码之后 没有修改过源代码,Python 将会加载 .pyc 文件并跳过编译这个步骤
当 Python
重编译时,它会自动检查源文件和字节码文件的时间戳
如果你又修改了源代码,下次程序运行时,字节码将自动重新创建
提示:有关模块以及模块的其他导入方式,后续课程还会逐渐展开!
模块是 Python 程序架构的一个核心概念
- 变量 和 数据 都是保存在 内存 中的
- 在
Python
中 函数 的 参数传递 以及 返回值 都是靠 引用 传递的
在 Python
中
id()
函数可以查看变量中保存数据所在的 内存地址注意:如果变量已经被定义,当给一个变量赋值的时候,本质上是 修改了数据的引用
- 变量 不再 对之前的数据引用
- 变量 改为 对新赋值的数据引用
变量引用
的示例在 Python
中,变量的名字类似于 便签纸 贴在 数据 上
a
,并且赋值为 1
变量
b
是第 2 个贴在数字2
上的标签
在 Python
中,函数的 实参/返回值 都是是靠 引用 来传递来的
def test(num): print("-" * 50) print("%d 在函数内的内存地址是 %x" % (num, id(num))) result = 100 print("返回值 %d 在内存中的地址是 %x" % (result, id(result))) print("-" * 50) return result a = 10 print("调用函数前 内存地址是 %x" % id(a)) r = test(a) print("调用函数后 实参内存地址是 %x" % id(a)) print("调用函数后 返回值内存地址是 %x" % id(r))
不可变类型,内存中的数据不允许被修改:
int
, bool
, float
, complex
, long(2.x)
str
tuple
可变类型,内存中的数据可以被修改:
list
dict
a = 1 a = "hello" a = [1, 2, 3] a = [3, 2, 1]
demo_list = [1, 2, 3] print("定义列表后的内存地址 %d" % id(demo_list)) demo_list.append(999) demo_list.pop(0) demo_list.remove(2) demo_list[0] = 10 print("修改数据后的内存地址 %d" % id(demo_list)) demo_dict = {"name": "小明"} print("定义字典后的内存地址 %d" % id(demo_dict)) demo_dict["age"] = 18 demo_dict.pop("name") demo_dict["name"] = "老王" print("修改数据后的内存地址 %d" % id(demo_dict))
注意:字典的
key
只能使用不可变类型的数据
注意
(hash)
Python
中内置有一个名字叫做 hash(o)
的函数
哈希
是一种 算法,其作用就是提取数据的 特征码(指纹)
Python
中,设置字典的 键值对 时,会首先对 key
进行 hash
已决定如何在内存中保存字典的数据,以方便 后续 对字典的操作:增、删、改、查
key
必须是不可变类型数据value
可以是任意类型的数据提示:在其他的开发语言中,大多 不推荐使用全局变量 —— 可变范围太大,导致程序不好维护!
def demo1(): num = 10 print(num) num = 20 print("修改后 %d" % num) def demo2(): num = 100 print(num) demo1() demo2() print("over")
# 定义一个全局变量 num = 10 def demo1(): print(num) def demo2(): print(num) demo1() demo2() print("over")
注意:函数执行时,需要处理变量时 会:
全局变量的引用
提示:在其他的开发语言中,大多 不推荐使用全局变量 —— 可变范围太大,导致程序不好维护!
num = 10 def demo1(): print("demo1" + "-" * 50) # 只是定义了一个局部变量,不会修改到全局变量,只是变量名相同而已 num = 100 print(num) def demo2(): print("demo2" + "-" * 50) print(num) demo1() demo2() print("over")
注意:只是在函数内部定义了一个局部变量而已,只是变量名相同 —— 在函数内部不能直接修改全局变量的值
global
进行声明num = 10 def demo1(): print("demo1" + "-" * 50) # global 关键字,告诉 Python 解释器 num 是一个全局变量 global num # 只是定义了一个局部变量,不会修改到全局变量,只是变量名相同而已 num = 100 print(num) def demo2(): print("demo2" + "-" * 50) print(num) demo1() demo2() print("over")
a = 10 def demo(): print("%d" % a) print("%d" % b) print("%d" % c) b = 20 demo() c = 30
注意
g_
或者 gl_
的前缀提示:具体的要求格式,各公司要求可能会有些差异
函数根据 有没有参数 以及 有没有返回值,可以 相互组合,一共有 4 种 组合形式
定义函数时,是否接收参数,或者是否返回结果,是根据 实际的功能需求 来决定的!
此类函数,不接收参数,也没有返回值,应用场景如下:
注意:
此类函数,不接收参数,但是有返回值,应用场景如下:
此类函数,接收参数,没有返回值,应用场景如下:
此类函数,接收参数,同时有返回值,应用场景如下:
return
关键字可以返回结果问题:一个函数执行后能否返回多个结果?
def measure(): """返回当前的温度""" print("开始测量...") temp = 39 print("测量结束...") return temp result = measure() print(result)
def measure(): """返回当前的温度""" print("开始测量...") temp = 39 wetness = 10 print("测量结束...") return (temp, wetness)
提示:如果一个函数返回的是元组,括号可以省略
技巧
Python
中,可以 将一个元组 使用 赋值语句 同时赋值给 多个变量result = temp, wetness = measure()
题目要求
a = 6
, b = 100
# 解法 1 - 使用临时变量 c = b b = a a = c
# 解法 2 - 不使用临时变量 a = a + b b = a - b a = a - b
a, b = b, a
问题 1:在函数内部,针对参数使用 赋值语句,会不会影响调用函数时传递的 实参变量? —— 不会!
def demo(num, num_list): print("函数内部") # 赋值语句 num = 200 num_list = [1, 2, 3] print(num) print(num_list) print("函数代码完成") gl_num = 99 gl_list = [4, 5, 6] demo(gl_num, gl_list) print(gl_num) print(gl_list)
问题 2:如果传递的参数是 可变类型,在函数内部,使用 方法 修改了数据的内容,同样会影响到外部的数据
def mutable(num_list): # num_list = [1, 2, 3] num_list.extend([1, 2, 3]) print(num_list) gl_list = [6, 7, 8] mutable(gl_list) print(gl_list)
+=
python
中,列表变量调用 +=
本质上是在执行列表变量的 extend
方法,不会修改变量的引用def demo(num, num_list): print("函数内部代码") # num = num + num num += num # num_list.extend(num_list) 由于是调用方法,所以不会修改变量的引用 # 函数执行结束后,外部数据同样会发生变化 num_list += num_list print(num) print(num_list) print("函数代码完成") gl_num = 9 gl_list = [1, 2, 3] demo(gl_num, gl_list) print(gl_num) print(gl_list)
gl_num_list = [6, 3, 9] # 默认就是升序排序,因为这种应用需求更多 gl_num_list.sort() print(gl_num_list) # 只有当需要降序排序时,才需要传递 `reverse` 参数 gl_num_list.sort(reverse=True) print(gl_num_list)
def print_info(name, gender=True): gender_text = "男生" if not gender: gender_text = "女生" print("%s 是 %s" % (name, gender_text))
提示
def print_info(name, gender=True, title):
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)
有时可能需要 一个函数 能够处理的参数 个数 是不确定的,这个时候,就可以使用 多值参数
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)
提示:多值参数 的应用会经常出现在网络上一些大牛开发的框架中,知道多值参数,有利于我们能够读懂大牛的代码
需求
sum_numbers
,可以接收的 任意多个整数def sum_numbers(*args): num = 0 # 遍历 args 元组顺序求和 for n in args: num += n return num print(sum_numbers(1, 2, 3))
args
kwargs
*
*
def demo(*args, **kwargs): print(args) print(kwargs) # 需要将一个元组变量/字典变量传递给函数对应的参数 gl_nums = (1, 2, 3) gl_xiaoming = {"name": "小明", "age": 18} # 会把 num_tuple 和 xiaoming 作为元组传递个 args # demo(gl_nums, gl_xiaoming) demo(*gl_nums, **gl_xiaoming)
函数调用自身的 编程技巧 称为递归
特点
代码特点
示例代码
def sum_numbers(num): print(num) # 递归的出口很重要,否则会出现死循环 if num == 1: return sum_numbers(num - 1) sum_numbers(3)
需求
sum_numbers
num
的整数参数def sum_numbers(num): if num == 1: return 1 # 假设 sum_numbers 能够完成 num - 1 的累加 temp = sum_numbers(num - 1) # 函数内部的核心算法就是 两个数字的相加 return num + temp print(sum_numbers(2))
提示:递归是一个 编程技巧,初次接触递归会感觉有些吃力!在处理 不确定的循环条件时,格外的有用,例如:遍历整个文件目录的结构