其实本来不打算把这些发上来的,但有时候做东西做着做着就被苟到了。

但是有些经历还是想记录下来。

在这里我不想吐槽,未来某年某天看到这些,我也会有所回忆,有所感念

而不再在意那些岁月的酸甜。


Ps: 当然这个东西未来真的要做教学教材的素材时,这篇博文会删掉。

此文引用的图片懒得上传,就不管它了。。。


第二章 计算与人工智能基础

​ 随着计算机、人工智能和大数据的普遍应用,各种AI程序已和我们的生活、工作和学习息息相关。人脸识别、语音识别、购物推荐、无人驾驶等 AI 应用构成了我们生活的一部分。目前市面上大部分的人工智能的代码都是用 Python 来编写。

​ 10年来,Python一直是AI研究的最前沿语言。它是一种面向对象的、动态的程序设计语言。具有非常简洁而清晰的语法,适合于完成各种高层任务。它既可以用来快速开发程序脚本,也可以用来开发大规模的软件。随着 NumPy, SciPy, Matplotlib, scikit-learn 等众多程序库的开发,Python 在科学计算、 数据分析的应用越来越广泛,也是实现人工智能机器学习、神经网络、深度学习的主流编程语言。

​ 本章以 Python 作为实现计算与人工智能的工具语言,通过实现一个计算与人工智能的案例来学习Python的基本语法;


2.1 计算与人工智能编程环境的搭建与编码规范

本节知识点的学习目标:

1、 初识Python,了解Python语言的历史与特点

2、 了解Python的应用领域

3、 掌握Python开放环境的搭建与配置

4、 掌握Python的编程规范

5、 掌握Python标准库、扩展库的导入

6、 掌握 spyder IDE的调试方法

7、 熟悉本章要解决的案例任务


2.1.1 Python简介

Python 是由 Guido van Rossum 在八十年代末和九十年代初,在荷兰国家数学和计算机科学研究所设计出来的。Python的名字不是来源于蟒蛇,而是源于Guido是一个名为”Monty Python”的飞行戏团的爱好者。

Python是在ABC语言的基础上开发的,它继承了ABC语言的优点,受到了Modula-3的影响,结合了C/C++语言的用户习惯。它支持命令式编程与函数式编程两种模式,完全支持面向对象程序设计,语法简洁清晰,功能强大且易学易用,一度成为了Unix和Linux开发者所青睐的开发语言。

Python 是一门跨平台、开源、免费的,结合了解释性、编译性、互动性和面向对象的脚本语言,其关键特性如下:

简单易学 – Python 是一种代表简单主义思想的语言。阅读一个良好的 Python 程序就感觉像是在读英语一样,尽管这个英语的要求非常严格!Python 的这种伪代码本质是它最大的优点之一。Python 有极其简单的语法, 容易上手。

免费开源 – Python 是 FLOSS(自由/开放源码软件)之一。简单地说,你可以自由地发布这个软件的拷贝、阅读它的源代码、对它做改动、把它的一部分用于新的自由软件中。

可移植可扩展性 – 由于它的开源本质,Python 已经被移植在许多平台上(经过改动使它能够工作在不同平台上)。 如果你需要你的一段关键代码运行得更快或者希望某些算法不公开,你可以把你的部分程序用 C 或 C++ 编写,然后在你的 Python 程序中使用它们。

解释性 – Python 语言写的程序不需要编译成二进制代码。你可以直接从源代码运行程序。在计算机内部,Python 解释器把源代码转换成称为字节码的中间形式,然后再把它翻译成计算机使用的机器语言并运行。

面向对象 – Python 既支持面向过程的编程也支持面向对象的编程。在“面向过程”的语言中,程序是由过程或仅仅是可重用代码的函数构建起来的。在“面向对象”的语言中,程序是由数据和功能组合而成的对象构建起来的。与其他主要的语言如 C++ 和 Java 相比,Python 以一种非常强大又简单的方式实现面向对象编程。

丰富的库 – Python 标准库确实很庞大。它可以帮助你处理各种工作,包括正则表达式、文档生成、单元测试、线程、数据库、网页浏览器、CGI、FTP、电子邮件、XML、XML-RPC、HTML、WAV 文件、密码系统、GUI(图形用户界面)、Tk 和其他与系统有关的操作。

规范的代码 – Python 采用强制缩进的方式使得代码具有极佳的可读性。

客观地说相较于C++, Java,Python的运行速度相对慢许多。这也是许多公司不使用Python作为软件开发工具语言的很重要的原因。但随着互联网,大数据与人工智能的发展,Python 在网络爬虫、大数据分析与处理,科学计算可视化,自然语言处理、游戏设计与开发等领域获得了广泛的应用。在本教材中,Python作为一个实现计算与人工智能的工具语言,主要解决如下问题:

• 在介绍计算机系统思维的用Python实现模拟冯诺依曼计算机体系结构,利用Python实现文件与文件夹的管理与操作

网络爬虫 -学习如何在互联网上爬取免费的数据,调用request库请求网页,用beautifulsoup库来解析网页数据。学习使用Scripy爬虫框架爬取网络数据。

数据分析-在获取了大量数据的基础上,利用Python结合科学计算numpy库、机器学习等技术,对数据进行清洗、去重、规格化处理,实现数据的分类与预测等数据分析与处理方法

自然语言处理**-** 利用Python实现文字识别,词频统计,词云生成,语音识别等自然语言处理操作

科学计算 – 利用Pandas,NumPy,Matplotlib等众多程序库 ,用Python实现科学计算、数据表与数据库的处理,以及数据可视化的操作。

人工智能 – 利用百度平台,实现人工智能领域内的机器学习、神经网络、深度学习 ,学习人脸识别,图像识别和 。


2.1.2 Python开发环境的搭建

常用的Python开发环境除了Python官方安装包自带的IDLE,还有Anaconda3、 PyCharm和Visual Studio Code等,本书主要通过Anaconda3 提供的Spyder和Jupyter Notebook开发环境来介绍Python的基本语法和在网络爬虫、数据分析、数据挖掘与可视化、算法设计,人工智能深度学习等方面的应用。本书的代码同样可以在其他的开发环境中运行。

anaconda3环境安装搭建

Conda 是一个开源的软件包管理系统和环境管理系统,用于安装多个版本的软件包及其依赖关系,并在它们之间轻松切换。是当下最流行的 Python 环境管理工具。

Anaconda指的是一个开源的Python发行版本,其包含了conda、Python等180多个科学包及其依赖项。 [1] 因为包含了大量的科学包,Anaconda 的下载文件比较大(约 531 MB),如果只需要某些包,或者需要节省带宽或存储空间,也可以使用Miniconda这个较小的发行版(仅包含conda和 Python)。

Anaconda 官网地址:https://www.anaconda.com/

下载后,可以以默认的选项安装,建议在下图步骤时将Add Anaconda3 to my PATH environment variable勾上)

在安装结束后,你将会在windows的开始面板处找到Anaconda的文件夹

其中的Jupyter NotebookSpyder就是我们课程中所使用的两个软件.

Spyder

1.简介

Spyder 是一个强大的Python科学环境。它具有综合开发工具的高级编辑、分析、调试和分析功能与科学包的数据探索、交互执行、深度检查和优美的可视化功能的独特组合。

2.界面

左侧是我们写Python代码的窗口;右上角是变量生成区,同时绘图结果会在这里;右下角是控制台,代码运行结果也在这里显示。

Jupyter Notebook

Jupyter Notebook是基于网页的用于交互计算的应用程序。是以网页的形式打开,可以在网页页面中直接编写代码和运行代码,代码的运行结果也会直接在代码块下显示。如在编程过程中需要编写说明文档,可在同一个页面中直接编写,便于作及时的说明和解释。

Jupyter Notebook文档中包含了交互计算、说明文档、数学公式、图片以及其他富媒体形式的输入和输出, 文档的后缀名为.ipynb的JSON格式文件, 文档也可以导出为:HTML、LaTeX、PDF等格式,可以很方便与他人共享使用。

Jupyter Notebook的主要特点
  • 编程时具有语法高亮、缩进、tab补全的功能。

  • 可直接通过浏览器运行代码,同时在代码块下方展示运行结果。

  • 以富媒体格式展示计算结果。富媒体格式包括:HTML,LaTeX,PNG,SVG等。

  • 对代码编写说明文档或语句时,支持Markdown语法。

  • 支持使用LaTeX编写数学性说明。

Jupyter Notebook的运行界面如下:

运行后,软件将打开一个本地网页(注意运行期间不要关闭电脑打开的命令行)

用 Jupyter Notebook打开.ipynb文件后

文档中的每一个单元格称为一个cell,每一个cell都可以独立运行并显示结果,但前面cell的运行结果会影响到后面的cell,也就是说前面cell定义的变量,导入的库,后面的cell仍可以访问与使用,这一点要特别注意。

扩展库的安装

这里只介绍 Anaconda 图形化界面的安装方法,另外还有命令行的安装方法自行了解

打开文件夹中的Anaconda Navigator,在Not installed选项卡中搜索你要安装的库即可


2.1.3 Python的编码规范

默认情况下,Python 3 源码文件以 UTF-8 编码,所有字符串都是 unicode 字符串。

行与缩进

Python最具特色的就是使用缩进来表示代码块,不需要使用大括号 {}

缩进的空格数是可变的,但是同一个代码块的语句必须包含相同的缩进空格数。如:

if True:
print ("True")
else:
print ("False")

以下代码最后一行语句缩进数的空格数不一致,会导致运行错误:

if True:
print ("Answer")
print ("True")
else:
print ("Answer")
print ("False") # 缩进不一致,会导致运行错误

注释

Python中的注释有单行注释和多行注释,只起一个说明的作用。

Python注释有单行注释与多行注释,一般单行注释用#开头,可写在单行代码后,也可以单列一行,置于代码前说明。

同时Python 中也提供多行注释,使用三个单引号(‘’’)或三个双引号(“””),例如:

# 第一个注释
# 第二个注释

'''
第三注释
第四注释
'''

"""
第五注释
第六注释
"""
print ("Hello, Python!")

标识符和保留字

Python保留字

保留字即关键字,我们不能把它们用作任何标识符名称。Python 的标准库提供了一个 keyword 模,可以输出当前版本的所有关键字:

import keyword
print(keyword.kwlist)

【运行结果】

['False', 'None', 'True', 'and', 'as', 'assert', 'break', 'class', 'continue', 'def', 'del', 'elif', 'else', 'except', 'finally', 'for', 'from', 'global', 'if', 'import', 'in', 'is', 'lambda', 'nonlocal', 'not', 'or', 'pass', 'raise', 'return', 'try', 'while', 'with', 'yield']

续行

Python 通常是一行写完一条语句,但如果语句很长,我们可以使用反斜杠()来实现多行语句,例如:

total = item_one + \
item_two + \
item_three

在 [], {}, 或 () 中的多行语句,不需要使用反斜杠(),例如:

total = ['item_one', 'item_two', 'item_three',
'item_four', 'item_five']

2.1.4 Python标准库、扩展库(模块)的导入

import 与 from…import

在 Python 用 import 或者 from...import 来导入相应的模块。

将整个模块导入,格式为: import 模块名

将整个模块导入并设置别名,格式为:import 模块名 as 别名

从某个模块中导入某个函数,格式为: from 模块名 import 对象

从某个模块中导入多个函数,格式为: from 模块名 import 对象一, 对象二, 对象三

将某个模块中的全部函数导入,格式为: from 模块名 import *

导入 math 模块
import math

x = int(input("输x:"))
print(math.sqrt(x)) #输出x的开方
导入 math 模块的 sqrt 成员
from math import sqrt  		#导入特定的成员

x = int(input("输入x:"))
print(sqrt(x)) #因为已经导入sqrt成员,所以此处引用时不需要加math.sqrt
导入 numpy 模块并为其设置别名
import numpy as np

x = int(input("输入x:"))
print(np.sqrt(x)) #以 别名.对象名 的方式引用

在后面的章节学习时,我们会导入其他的模块,如matplotlib, numpy等等。


2.1.5 Python程序调试简介

学会调试程序是学好python编程的最基本的技能,首先在编程过程中学会快捷键的使用

其次学会用debug可实现单步、设置断点调试程序, 通过ctrl+F5进入调试状态,ctrl+F12可进入下一步,ctrl+shift+F12可结束调试,运行到对应的语句就能够在右侧的窗口看到对应的变量和对象的值的变化,见下图*****,具体的调试过程可自行上网查询资料。

最后,程序调试的过程中可以多次使用print()函数输出程序执行过程中的中间结果,可以最直观地查看程序代码的编写是否正确,这也是初学者最常用的程序调试方法。


2.1.6 本章要解决的案例

案例问题描述:

如图所示,世界机器人大赛比赛项目:机器人定点投篮比赛正在如火如荼地进行中,已知某机器人以14m/s 的出手速度V0,1.9m的出手高度H和40°的出手角度θ投出篮球,在不考虑空气阻力和落点有效区域,请建立坐标系并绘制蓝球的运动轨迹。(S点为蓝球离手瞬间位置,g=9.8m/s2),并计算在V0和高度H一定的情况下确定投球角度θ的范围,使其投球命中篮筐。

为解决上述问题,不管是人工还是计算,都得知道球水平和垂直方向的坐标系,假设投出铅球的时刻为0,则在t 时刻,铅球的坐标(xt,yt)如下:
$$
x_t=v_0cos\theta t
\
y_t = h+v_0sin\theta t-\frac{1}{2}gt^2
$$
有了此方程就可以计算一个时间范围内的n个坐标点,将这n个坐标点连在一起就可得到篮球的运行轨迹,当n值足够多的时候,轨迹越接近真实的轨迹。在 θ 的取值范围内,通过不断的尝试可找出投掷篮球距离最远的θ值 。

利用同样的方法,也可在V0和高度H一定的情况下,通过确定投球角度θ的范围,使机器人能百分百投球命中篮筐。

2.1.6 本节小结


2.1.7 习题


2.2 简单计算问题的求解

为了在计算机内要绘制篮球在某一时间点坐标的数据,需要知道数据在计算机内是如何表示,各种数据信息是如何命名,数据是如何获取的,如何根据数据计算各种数学公式的值,且计算的结果要在屏幕上显示等等。

本节知识点的学习目标:

1、 掌握Python数据的输出

2、 掌握变量的赋值

3、了解Python基本的数据类型及不同基本数据类型之间的转换

4、 掌握数据的输入

5、掌握算术运算符与表达式的表示

5、 掌握简单计算问题的求解,

6、掌握顺序结构程序设计的基本方法

本节案例任务:如何在屏幕上绘制篮球在某一个时间点的坐标

任务分析:

要分析如何建立球水平与垂直方向的坐标方程,进而推广到可实现求解简单的计算问题。

从计算球的横坐标的数学公式
$$
x_t=v_0cos\theta t
$$
可知,v0,t,和θ对应着抛出球的初速度,角度和某个时间点,如果能确定这3个参数的值,并通过计算
$$
cos\theta
$$
函数,则水平方向的坐标位置就确定好了,同理
$$
y_t = h+v_0sin\theta t-\frac{1}{2}gt^2
$$
的计算方法也类似,但在此公式中g的值为9.8的重力加速度,所以在开始计算之前,要想办法确定这3个参数的值,则水平和垂直方向的坐标就确定下来了。

站在计算机的角度来看,其值不能改变的数据对象称为“常量”,例如公式中重力加速度9.8,圆周率pi的值等;凡是计算机不知道的量都叫“变量”,如上述公式中的三个参系数v0,t,和θ的值,显然,求 (xt,yt) 的值还需要计算三角函数cos(θ)和sin(θ)的值,计算机中通过装载数学公式库运行库中的函数来完成操作。

计算机不知道的量即变量,要么开始计算前想办法告诉计算机,要么让计算机根据公式自己算出来。此例中v0,t,和θ的值只能告诉计算机。告诉计算机的方法有很多,最简单的方式在程序中直接写明(称为给变量赋值,简称赋值);较复杂一点是在程序运行时,通过键盘输入,就是我们用微信或QQ一样;再复杂一点将数据事先在一个文件中,程序运行时自动从这个文件中取,这个文件可以为普通的文本文件,EXCEL文件、WORD文件、数据库文件等。

要解决上述问题,可通过以下几个步骤来完成:

1、 如何输出水平与垂直方向坐标数据

2、 如何表示水平与垂直方向坐标数据

3、 如何解决关键数据的输入问题

4、 数学公式的Python表示问题

5、 如何建立坐标轴并输出一个坐标点


2.2.1 数据的输出

根据题面案例给定的初始化值:14m/s 的出手速度v0,1.9m的出手高度h和40°的出手角度θ,g 为9.8,假设t=1.5秒时,我们可以手动计算求出屏幕上篮球的坐标点xt=16.0869 ,yt=4.3735,(保留小数点后4位) ,若想在计算机屏幕上输出两坐标点的值,需要用到Python的输出方式 。

Python有两种输出值的方式: 表达式语句和 print() 函数。

表达式语句输出:

例如:

同时,print()函数也可以直接输出括号内的表达式结果。如:

#输出表达式
print(1+2+3+4+5)
print(25>24)
print(15==46)

【运行结果】

15
True
False

例:Print()函数输出

#简单输出
s=10
print(s) #输出s的值
print('s') #输出字符串s

【运行结果】

10
s

print()语句默认输出换行,如果你希望运行时不直接换行,在该行结尾继续输出下一次print()的结果,可以加入end参数

#多print()单行输出
print(1+2, end=" ")
print("Python")
print(1, end="+")
print(2, end="=")
print(3)

【运行结果】

3 Python
1+2=3

如果你希望输出的形式更加多样,可以使用 str.format() 函数或者Python2以上支持的%格式化来进行格式化输出值。

Python的格式化输出

%方式

这个方法沿用的是c语言的方式,在很多语言中都采用这个方式进行格式化输出

格式化字符串:

1.在%操作符的左侧放置一个需要进行格式化的字符串,这个字符串带有一个或多个嵌入的转换目标,都以%开头(例如,%d)。

2.在%操作符右侧放置一个(或多个,嵌入到元组中)对象,这些对象将会插入到左侧想让Python进行格式化字符串的一个(或多个)转换目标的位置上去。

例如:

# %方式格式化输出
i, j = 5, 10
print("%d*%d=%2d" % (i, j, i*j))

【运行结果】

5*10=50

这里涉及到的%d是格式控制符,如:

格式化符号 说明
%c 转换成字符(ASCII 码值,或者长度为一的字符串)
%r 优先用repr()函数进行字符串转换
%s 优先用str()函数进行字符串转换
%d / %i 转成有符号十进制数
%u 转成无符号十进制数
%o 转成无符号八进制数
%x / %X 转成无符号十六进制数(x / X 代表转换后的十六进制字符的大小写)
%e / %E 转成科学计数法(e / E控制输出e / E)
%f / %F 转成浮点数(小数部分自然截断)
%g / %G %e和%f / %E和%F 的简写
%% 输出% (格式化字符串里面包括百分号,那么必须使用%%)

你可以补充参数来进行相关的补位等操作,例如:

i, j = 5, 1
print("%d*%d=%2d" % (i, j, i*j))
t = 1.5565656565
print("%.5f" % (t))

【运行结果】

5*1= 5
1.5565

可以看到等号右边的5左边多了个空格补成两位。

第三种方式是使用文件对象的 write() 方法,标准输出文件可以用 sys.stdout 引用,这里不做介绍,请感兴趣的读者查阅相关资料。


2.2.2 变量和赋值

变量是用来存储程序中的各种数据,例如案例公式中的初速度用V0表示,高度用H来表示,时间用T来表示,而xt,yt分别代表某一t时刻的横坐标与纵坐标;变量有不同的数据类型,在内存中会有一片区域,变量具有唯一的标识符,且标识符遵循一定命名规则,变量可以赋值例如V0初值为14m/s,H的高度为1 m,变量可也以用来保存数学公式的计算结果

1、 变量的命名规则:

• 第一个字符必须是字母表中字母或下划线 _

• 变量的其他的部分由字母、数字和下划线组成。

• 变量对大小写敏感。

• 变量名不能是保留字或关键字

2、变量的赋值:

#变量的赋值
v0=1.4
h=10
g=9.8
t=1
print(v0,h,g,t)

【输出结果】

1.4 10 9.8 1

2.2.3 基本的数据类型

计算机利用一系列的0、1来表示数字、图形符号与语音等数据信息,不同的数据需要定义不同的数据类型来存储,数据类型也决定了如何将代表这些数据位存储到计算机内存中。Python的基本数据类型包括: 整型、浮点型,复数、字符串型,布尔型True,False和空值None等

1、 整型数据

Python中可以处理任意大小的整数,包括负整数,十进制整数的表示方式与数学上的写法一样,如255,0,2014等。此外Python还支持十六进制、八进制和二进制整数。

十六进制需要用0x或0X作为前缀,用0-9和a-f作为基本的16个数字,如0xffff,0X123afe等,八进制数需要用0o或0O作为前缀,用0-7作为基本的8个字符,如:0o127、0O376等,二进制数需要用0b或0B作为前缀,用0-1作为基本数字,0b1011,0B0100001等。

#整数类型
print(255)#十进制整数
print(0xff) #十六进制整数
print(0o123) #八进制整数
print(0b0100001)#二进制整数
print(-123) #负整数表示
print(-0xff) #负十六进制整数

【运行结果】

255
255
83
33
-123
-255

从输出结果可知,不同进制表示的整数经过print()函数输出为十进制数值。

2、浮点型

在Python中,浮点型用来表示实数,一般情况下用来表示小数,浮点数可以使用普通的数学写法,如0.1234,-3.14159等,对于特别大或特别小的浮点数,使用科学计数法表示,用E或e来表示10的幂。如1.234e-1,-1.24E3代表-1240

#浮点型
print(0.1234) #普通浮点数表示
print(1.234e-1)#科学计数法浮点数表示
print(-3.1415926)#负浮点数表示

【运行结果】

0.1234
0.1234
-3.1415926

整数与浮点数的输出都可进行格式化处理,例如指定整数数据的宽度,浮点数的小数点后面的位数,特殊符号的输出等,数据的格式化处理会在后面的章节中会重点介绍,在这里我们先体验见识一下。

#格式化输出整数与浮点数
print("%8d" %-123) #整数的输出宽度为8 ,不够左边补空格(默认)
print("%8.2f" %-3.1415926) #浮点数的输出宽度为8,保留2位小数,不够左边补空格

【运行结果】

-123
-3.14

上述代码 % 为格式数据的分隔符,左边为格式控制字符串,右边为要输出的数据,可以是1到多个不等,从输出结果来看,数据的宽度为数据的显示长度,如果数据的长度大于设置显示的宽度,则数据原样输出,如果数据的长度小于设置显示的宽度,默认情况下不够的左边补空格显示。

3、复数:Python提供复数的表示,其中j 代表虚数单位

#复数输出
print(1+2j)
print(7-3j)

【运行结果】

(1+2j)
(7-3j)

复数类型的实数与虚数部分的数值都是浮点类型,可通过复数.real和复数.imag分别获得复数的实数部分与虚数部分,例如:

#复数实数与虚数获取
print((1+2j).real)
print((1+2j).imag)
print((7-3j).real)
print((7-3j).imag)

【运行结果】

1.0
2.0
7.0
-3.0

4、字符串类型

字符串是由字符组成的序列,是用一对单引号(‘)、双引号(“)或者三引号(‘’‘)括起来的一个或多个字符。其中单引号和双引号都可以表示单行字符串,两者作用相同。单引号与双引号都可以作为字符串的一部分,一般当字符串中有单引号时,使用双引号作为界定符,反之,当双引号是字符的一部分时,用单引号作为界定符。三引号可以表示单行或多行字符串。

#字符串组成
print ('hello world') #显示英文字符
print ("Hello guys,Welcom to Hu Nan University !")
print ("湖南大学") #显示中文字符
print ('let\'s go to school\n') #带转义字符的字符串
print ('''重要的事情说三遍:
符号与标点要使用英文输入法
符号与标点要使用英文输入法
符号与标点要使用英文输入法''') #多行字符串的显示

【运行结果】

hello world
Hello guys,Welcom to Hu Nan University !
湖南大学
let's go to school

重要的事情说三遍:
符号与标点要使用英文输入法
符号与标点要使用英文输入法
符号与标点要使用英文输入法

从前面的程序段语句中出现了带‘\’的特殊字符,俗称Python的转义字符,见表2-1

见表2-1 转义字符

转义字符 描述
(在行尾时) 续行符
\ 反斜杠符号
' 单引号
" 双引号
\a 响铃
\b 退格(Backspace)
\e 转义
\000
\n 换行
\v 纵向制表符
\t 横向制表符
\r 回车
\f 换页
\oyy 八进制数,y 代表 0~7 的字符,例如:\012 代表换行。
\xyy 十六进制数,以 \x 开头,yy代表的字符,例如:\x0a代表换行
\other 其它的字符以普通格式输出

例如:

#转义字符的使用
print ("\101 \x42 C\n")
print("I say:\"How are you?\"\n")
print("\\Python Program\\\n")
print(' ')
print("Python \'C\'")

【运行结果】

A B C

I say:"How are you?"

\Python Program\


Python 'C'

其中’ ‘代表空字符,’\’输出一个(\),’\’‘输出一个单引号(‘),’\”’输出一个(“),’\n’代表换行,本身print()函数默认输出数据后换行,故输出字符串有多行输出。

如果程序不让转义字符生效,要显示字符串原来的意思,则可通过R或r 来定义原始字符串,例如:

#原始字符串
print (r"\101 \x42 C\n")
print(R"\\Python Program\\\n")

【运行结果】

\101 \x42 C\n
\\Python Program\\\n

另外字符串与数字一样,是不可变对象。所谓不可变,是指不能原样修改对象的内容。

#字符类型为不可变对象
a=b='123' #字符变量a,b赋初值为123,指向同一对象
print(id(a)) #输出字符变量a对象的内存地址。
print(id(b)) #输出字符变量b对象的内存地址。
a='abc' #字符变量a重新赋值
print(a) #输出字符变量a的值
print(id(a)) #输出字符变量a对象的内存地址。
print(id(b)) #输出字符变量b对象的内存地址。
print(b) #输出字符变量b的值

【运行结果】

2609381795696
2609381795696
abc
2609286044336
2609381795696
123

从代码的输出结果可知: a,b 初始化为同一对象,有相同的内存地址,但当修改a的值时,a引用了另一个空间,其地址发生了改变,b的内容地址都不变,可见对字符变量a的修改不是原地修改,这也说明了字符串是不可变对象。

5、不同数据类型之间的转换

函数 解释
int(x [,base]) 将 x 转换为一个整数
long(x [,base]) 将 x 转换为一个长整数
float(x) 将 x 转换到一个浮点数
complex(real [,imag]) 创建一个复数
str(x) 将对象 x 转换为字符串
chr(x) 将一个整数转换为一个字符
unichr(x) 将一个整数转换为 Unicode 字符
ord(x) 将一个字符转换为它的整数值
hex(x) 将一个整数转换为一个十六进制字符串
oct(x) 将一个整数转换为一个八进制字符串

例如:

int()函数的使用

s = '101010'
print(type(s)) #s是一个字符串

print(int(s))
print(type(int(s))) #int(s)将s转化成整数

print(int(s,2)) #int(s,2)将2进制的s字符串转化成10进制

【运行结果】

<class 'str'>
101010
<class 'int'>
42

float()函数的使用

s = '123.456'
print(float(s))
print(type(float(s))) #float(s)将s转化成浮点数

【运行结果】

123.456
<class 'float'>

str()函数的使用

a = 123
b = 123.456
print(type(a), type(b)) #a,b分别为整数和浮点数
print(str(a), str(b))
print(type(str(a)), type(str(b))) #str(a),str(b)分别把a,b转化成字符串

【运行结果】

<class 'int'> <class 'float'>
123 123.456
<class 'str'> <class 'str'>

2.2.4 数据的输入

案例中时间T 的值可直接赋值获得,但更多的时侯是需要从键盘输入。变量可在程序中直接赋值,但有时候,编写程序时并不能确定变量的值,而是在程序运行过程中由用户输入,Python提供了用来从键盘输入数据的input()函数,它一般与Python的另一常用的内置函数 eval()同时使用,以满足用户单个数据,多个数据,按某一格式输入数据的要求;

Python中的标准化输入由函数input()来完成。格式如下:

input([提示性语句])

Python提供了 input() 内置函数从标准输入读入一行文本,默认的标准输入是键盘。

str = input("请输入:");

注意,input读入的是一个字符串

如果键盘读入的是123,它并不能被直接运算,需要经过int(),float()或eval()函数的转换才可变成整形,如:

num = int(input("假设输入的是一个整数"))
num = float(input("假设输入的是一个小数"))
num = eval(input("假设输入的是一个数字"))

多个数据的输入

input()函数支持多个数据同时输入,例如:

a,b,c = eval(input("请输入a,b,c的值,中间以‘,’隔开: "))
print(a,b,c)
print(type(a),type(b),type(c))

【运行结果】

请输入a,b,c的值,中间以‘,’隔开: 7,'sss',2.7
7 sss 2.7
<class 'int'> <class 'str'> <class 'float'>

还可以以字符串的方式读入后再分割,例如:

a,b,c = input("请输入a,b,c的值,中间以‘ ’隔开: ").split()
#split()方法默认以空格作为分割符,如果你想以‘,’分割,可以写作split(',')
print(a,b,c)
print(type(a),type(b),type(c))

【运行结果】

请输入a,b,c的值,中间以‘ ’隔开: 7 8 2.5
7 8 2.5
<class 'str'> <class 'str'> <class 'str'>

可以看到,用split()方法得到的数据都是字符串,如果要进行进一步数学运算,必须用int()float()eval()等函数转化。


2.2.5 运算符与表达式

案例中的某一时刻(如t=2.5)的坐标值(xt,yt),在数学公式中,只要将各种参数值代入公式计算即可,而在计算机中可利用Python的变量、运算符、括号和各种函数一起构造的表达式来完成。

运算符:乘法×符号与字母x非常类似,%运算符在计算语言另有任用,因此计算机语言的四则运算为“+-*/”。%取余数,如14%3为2即14被3除后余数为2,此外常见的运算符有前面用来赋值的赋值运算符、关系运算符,逻辑运算符、位运算符、成员运算符等,在这里先介绍解决简单问题的算术运算符,其他运算符后面的内容会一一道来,请大家找百度或跟着我们慢慢地积累吧

Python算术运算符

以下假设变量a为10,变量b为21:

运算符 描述 实例
+ 加 - 两个对象相加 a + b 输出结果 31
- 减 - 得到负数或是一个数减去另一个数 a - b 输出结果 -11
* 乘 - 两个数相乘或是返回一个被重复若干次的字符串 a * b 输出结果 210
/ 除 - x 除以 y b / a 输出结果 2.1
% 取模 - 返回除法的余数 b % a 输出结果 1
** 幂 - 返回x的y次幂 a**b 为10的21次方
// 取整除 - 向下取接近商的整数 >>> 9//2
4
>> -9//2
-5

表达式

表达式是由数字、变量、运算符、括号等组成的组合,最常见最基础的表达式是算术表达式

#算术表达式
import math #导入math库
a,b,c=1,2,1
delta=b*b-4*a*c #变量的赋值
x1=(-b+math.sqrt(delta))/(2*a) #求一元二次方程的根
x2=(-b-math.sqrt(delta))/(2*a) #求一元二次方程的根
print(x1,x2)

【输出结果】

-1.0 -1.0

程序代码中出现的sqrt(),cos()和sin(),sqrt是开平方的函数,而计算机的硬件本质上只能做“加减乘除”运算,像开平方这种复杂的运算,必须有人去编写代码。正如我们有“数学用表”一样,计算机就有“数学函数库”,这里面已经有很多数学函数的实现代码,如开平方就是sqrt(),求正弦用sin(), 求余弦用cos()

当想调用函数时需在最前面写上import math,也可以写上from math import *

math是mathematics(数学)的缩写,

import是导入的意思,import math 导入数学库,则数学库中的函数可以拿来直接使用而不需要我们自己去编写。

这样的函数扩展库还有很多,有兴趣的同学可以自行搜索,或到官方https://pypi.org/查找需要的库及其说明。

一些常用的math库函数如下表:

函数 说明
e 自然常数e
pi 圆周率pi
degrees(x) 弧度转角度
radians(x) 角度转弧度
exp(x) 返回e的x次方
log(x[, base]) 返回x的以base为底的对数,base默认为e
log10(x) 返回x的以10为底的对数
pow(x, y) 返回x的y次方
sqrt(x) 返回x的平方根
ceil(x) 返回不小于x的整数
floor(x) 返回不大于x的整数
trunc(x) 返回x的整数部分
fabs(x) 返回x的绝对值
sin(x) 返回x(弧度)的三角正弦值
asin(x) 返回x的反三角正弦值
cos(x) 返回x(弧度)的三角余弦值
acos(x) 返回x的反三角余弦值
tan(x) 返回x(弧度)的三角正切值
atan(x) 返回x的反三角正切值
atan2(x, y) 返回x/y的反三角正切值
sinh(x) 返回x的双曲正弦函数
asinh(x) 返回x的反双曲正弦函数
cosh(x) 返回x的双曲余弦函数
acosh(x) 返回x的反双曲余弦函数
tanh(x) 返回x的双曲正切函数
atanh(x) 返回x的反双曲正切函数

此外表达式中除了变量、运算符和函数外,还有括号:数学中有大括号{}、中括号[]与小括号(),在计算机语言中这些符号另有重用。圆括号跟数学中圆括号一样,当“先乘除后加减”不够用即不能清晰的表明运算次序时,需要用圆括号来帮忙,如前面计算一元二次方程的根的公式,如果写是x1=-b+sqrt(delta)/2*a,它实质是表示*
$$
x_1=-b+\frac{\sqrt\delta}{2}a
$$
则 x1=-2+0/2
1=-2,而不是正确答案-1。圆括号可多重嵌套但必须成对出现。


2.2.6 建立坐标轴完成屏幕上输出一个坐标点的任务

案例2-3 在屏幕上绘制一个点的坐标

案例描述与分析见前面内容所述,在此不再重述。

案例代码实现:

#求某一时间点的坐标数据并在屏幕上输出
import math #导入math库
h, v0, g, θ = 1.9, 14, 9.8, 40 #变量的多重赋值
t=eval(input('请输入时间t=')) #为变量t赋值
θ=math.radians(θ) #角度转变为弧度
xt=v0*math.cos(θ)*t #横坐标
yt=h+ v0*math.sin(θ)*t -1/2*g*t**2 #纵坐标
print(xt,yt) #打印显示

【运行结果】

-32.67996502096083
-21.614455136511914

2.2.7 简单计算问题求解

通过前面的案例分析,我们浅试了一把Python语言,学习如何给变量赋值,如何通过键盘给变量赋值、如何显示结果、如何计算数学公式或数学表达式、如何确定变量的数据类型、如何调用Python语言提供的函数库、如何加上注释等,下面我们来试水Python编程,先来解决计算与人工智能简单的计算问题。

案例2-1 银行存款本息计算法

案例描述:若干美元假设是(1万美元)以固定利率存入银行账户,存5年后银行账户变为多少美金?

案例分析:由2021年最新中国银行美元存款利率可知,5年期美元的年利率为0.75% 。通过如下步骤可实现案例任务:

(1) 从键盘输入美元数,以$为美元结尾符

(2) 5年利率赋值为0.75%

(3) 根据公式计算利息=本金年利率年数

(4) 根据公式本息和=本金+利息

(5) 输出5年本息和

案例代码实现:

#案例2-1 银行存款计算本息和
B_money = input("请输入一个带$符号的美元存款数:" )
B_money = B_money[:-1] #取美元数字字符串
Rate_5 = 0.0075 #5年固定利率
Lx_money = eval(B_money)*Rate_5*5 #5年利息
Bx_money = eval(B_money)+Lx_money #5年本息和
print("5年后的美元存款本息和:{:.2f}$".format(Bx_money)) #输出结果

【运行结果】

请输入一个带$符号的美元存款数:10000$
5年后的美元存款本息和:10375.00$

案例2-2 表达式的计算问题

案例描述:编程求lg(3x2+x4)*ln(|cos(x)|*esin(x))的值,x从键盘输入

案例分析:本案例需要获得x的值,可以直接赋值或从键盘输入,各种数学函数的调用可通过导入math库来实现,运用运算符可构造公式表达式,最后通过print函数打印输出结果。

案例代码实现:

#求f(x)=lg(3x2+x4)*ln(|cos(x)|*esin(x))的值
import math #导入math库
x=eval(input('请输入x=')) #为变量x赋值
fx=math.log(3*x**2+x**4,10)*math.log(math.abs(math.cos(x))*math.e*math.sin(x))
#注意math.log(x)的底数默认为e,lg(x)应该为math.log(x,10)
print(fx)

案例2-3 顺序程序设计举例


2.2.8 本节小结


2.2.9 习题


2.3 基本的程序设计方法

从前一章的求解简单计算问题的过程中,所编写的代码的执行过程为从上到下,依次执行的顺序结构,计算机要解决稍复杂的问题,还需要让程序具有选择判断、重复迭代的能力,这也是结构化程序设计的三种基本程序设计方法

本节知识点的学习目标:

1、 掌握if、else和elif语句的基本结构与语法

2、 掌握for、while循环语句的基本结构与用法

3、 掌握循环语句中常用的range()函数,break、continue、pass 等

4、 掌握循环的嵌套以及条件与循环的组合

5、掌握数列问题、迭代问题、图形问题和列表迭代等循环的典型应用

本节案例任务:如何在合理范围内获得1到n个数据点

任务描述 :

从机器人抛球的初始状态(出手速度V0,出手角度θ,抛球高度H),根据初等物理知识可得知,球从抛出T=0到落地的时间Tmax可通过公式计算得知,在0-Tmax的范围内可以获得n个数据点,这些数据点组成了球的运动轨迹,如何通过Python的条件分支if 来保证球的运动轨迹范围,这是本节要解决的第一个问题。

任务分析:通过如下步骤可以实现上述任务:

(1) 通过前面所学的表达式的计算求解Tmax的值

(2) 设置条件分支判断时间T的取值范围

(3) 在有效的范围内输出结果


2.3.1 让程序具有选择判读的能力

Python提供了if单分支语句、if-else二分支语句以及if-elif-else多分支语句来支持程序具有选择与判断的能力。

Python比较运算符

以下假设变量a为10,变量b为20:

运算符 描述 实例
== 等于 - 比较对象是否相等 (a == b) 返回 False。
!= 不等于 - 比较两个对象是否不相等 (a != b) 返回 True。
> 大于 - 返回x是否大于y (a > b) 返回 False。
< 小于 - 返回x是否小于y。所有比较运算符返回1表示真,返回0表示假。这分别与特殊的变量True和False等价。注意,这些变量名的大写。 (a < b) 返回 True。
>= 大于等于 - 返回x是否大于等于y。 (a >= b) 返回 False。
<= 小于等于 - 返回x是否小于等于y。 (a <= b) 返回 True。

Python逻辑运算符

Python语言支持逻辑运算符,以下假设变量 a 为 10, b为 20:

运算符 逻辑表达式 描述 实例
and x and y 布尔”与” - 如果 x 为 False,x and y 返回 x 的值,否则返回 y 的计算值。 (a and b) 返回 20。
or x or y 布尔”或” - 如果 x 是 True,它返回 x 的值,否则它返回 y 的计算值。 (a or b) 返回 10。
not not x 布尔”非” - 如果 x 为 True,返回 False 。如果 x 为 False,它返回 True。 not(a and b) 返回 False

1、单分支语句-if语句

例1:

a = 1
if a % 2 == 0:
print("a是偶数") #显然这行是不输出的
print("这行输出")

【 运行结果】

这行输出

例2:

a = 2
if a % 2 == 0:
print("a是偶数") #显然这行是输出的
print("这行输出")

【运行结果】

a是偶数
这行输出

2、二分支语句-if-else语句

a = int(input("a="))
if a % 2 == 0:
print("a是偶数")
else:
print("a是奇数")

【运行结果】

a=15
a是奇数
a=20
a是偶数

3、多分支语句-if-elif-else语句

age = int(input("请输入你家狗狗的年龄: "))
print("")
if age <= 0:
print("你是在逗我吧!")
elif age == 1:
print("相当于 14 岁的人。")
elif age == 2:
print("相当于 22 岁的人。")
else:
human = 22 + (age -2)*5
print("对应人类年龄: ", human)

【运行结果】

请输入你家狗狗的年龄: 1
相当于 14 岁的人。

4、分支综合案例应用

案例2-4:在合理的范围内输出一个坐标的数据点

案例描述与分析如前所述,这里不再重述;

案例代码实现:

#在合理的范围内输出一个数据点
import math
h, v0, g, θ = 1.9, 14, 9.8, 40
θ=math.radians(θ)
Tmax=(2*v0*math.sin(θ)+math.sqrt(4*v0**2*math.sin(θ)**2+8*g*h))/(2*g)
print('最大时间为:',Tmax,end=’ ‘)
t=eval(input('请输入时间t, t的取值范围0~tmax:’))
if t<0 or t>Tmax:
print('输入错误')
else:
xt=v0*math.cos(θ)*t #横坐标
yt=h+ v0*math.sin(θ)*t -1/2*g*t**2 #纵坐标
print(xt,yt) #打印

【运行结果】

最大时间为: 2.0277594501629714
请输入时间t, t的取值范围0~tmax:2
21.449244407331385 0.29805307122309443

案例2-4代码实现了在合理的范围内输出1个数据点的任务,但如果要在屏幕上输出n 个点,则要在合理的范围内生成n个数据点,这需要程序执行一些重复迭代的操作,这也是本节案例要解决的第二个问题。

任务分析:通过如下步骤可以实现上述任务:

(1)根据表达式的计算求解Tmax的值

(2)设置时间T的初值为0 ,时间的递增delta的初始值

(3)当T<=Tmax 的范围内 :

重复计算坐标点的(xt,yt),

重复输出坐标点

T的值递增即T+=delta

(4) 当T>Tmax后,程序执行完毕,完成任务。


2.3.2 让程序具有重复迭代的能力

让程序具有重复迭代的能力,编程语言提供了循环的概念。在循环结构中,满足某些条件,程序重复执行某一过程,直到条件不满足退出循环。

Python提供了两种类型的循环:while循环和for循环。While循环是一种条件控制循环,它通过某一条件的真假来控制循环;而for循环是一种计数控制循环,它将循环体内的语句块重复执行特定的次数。

1、while循环

Python 中 while 语句的一般形式:

while 条件表达式:
语句块

执行流程图如下:

同样需要注意冒号和缩进。另外,在 Python 中没有 do..while 循环。

以下实例使用了 while循环 来计算 1 到 100 的总和:

n = 100

sum = 0
counter = 1
while counter <= n:
sum = sum + counter
counter += 1

print("1 到 %d 之和为: %d" % (n,sum))

【运行结果】

1100 之和为: 5050

2、for循环

Python for循环可以遍历任何序列的项目,如一个列表或者一个字符串。

for循环的一般格式如下:

for 循环变量 in 对象:
语句块

这里介绍range()函数

如果你需要遍历数字序列,可以使用内置range()函数。它会生成数列,例如:

for i in range(5):
print(i)

【运行结果】

1
2
3
4
5

你也可以使用range指定区间的值:

for i in range(5,9):
print(i)

【运行结果】

5
6
7
8

也可以使range以指定数字开始并指定不同的增量(甚至可以是负数,有时这也叫做’步长’):

for i in range(0,10,3):
print(i)

【运行结果】

0
3
6
9

负数:

for i in range(-10,-100,-30):
print(i)

【运行结果】

-10
-40
-70

您可以结合range()和len()函数以遍历一个序列的索引,如下所示:

a = ['Huawei', 'Baidu', 'HNU', 'Ali', 'Tencent']
for i in range(len(a)):
print(i, a[i])

【运行结果】

0 Huawei
1 Baidu
2 HNU
3 Ali
4 Tencent

还可以使用range()函数来创建一个列表:

print(list(range(5)))

【运行结果】

[0, 1, 2, 3, 4]

之后在序列一节中我们会详细介绍for循环在序列中的应用

3、break与continue在循环中的应用

break语句

break用于中断当前循环的执行,提前退出循环结构。一般与if语句一起使用。对于包含else子句的while循环和for循环而言,在while或for子句中一旦执行break语句,else语句将没有机会执行。

break 执行流程图:

例:

n = 5
while n > 0:
n -= 1
if n == 2:
break
print(n)
print('循环结束。')

【运行结果】

4
3
循环结束。

可以看到,当break执行时,会跳出当前循环,使循环直接结束。

continue语句

与break不同的是,continue语句用于中止本次循环的执行,开始下一次循环

continue 执行流程图:

例:

n = 5
while n > 0:
n -= 1
if n == 2:
continue
print(n)
print('循环结束。')

【运行结果】

4
3
1
0
循环结束。

可以看到,当continue执行时,会直接进入下一次循环,而不运行该次循环中continue之后的代码。

4、循环结构的嵌套

为了解决复杂的问题,可以使用循环的嵌套来完成,循环嵌套层数不限,但循环嵌套的内层循环与外层循环不能交叉。最常用的循环嵌套是双层循环嵌套,内层循环执行完,外循环执行一次,总的循环次数为内外层循环次数相乘获得。

Python for 循环嵌套语法:

for 循环变量 in 对象:
for 循环变量 in 对象:
语句块A
语句块B

Python while 循环嵌套语法:

while 表达式:
while 表达式:
语句块A
语句块B

你可以在循环体内嵌入其他的循环体,如在while循环中可以嵌入for循环, 反之,你可以在for循环中嵌入while循环。

例如:使用嵌套循环输出50~100之间的素数:

i = 50
while(i < 100):
j = 2
while(j <= (i/j)):
if not(i%j): break
j = j + 1
if (j > i/j) : print(i, "is Prime")
i = i + 1

【运行结果】

53 is Prime
59 is Prime
61 is Prime
67 is Prime
71 is Prime
73 is Prime
79 is Prime
83 is Prime
89 is Prime
97 is Prime

5、循环综合实例

案例2-5 屏幕上打印九九乘法表

for i in range(1,10):
for j in range(1,i+1):
print("{}*{}={:2d}".format(j, i, i*j), end=" ")
print()

【运行结果】

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

案例2-6 求cos(x) 的值,直到最后一项绝对值小于1e-7(即10-7 )为止(注:,x为弧度值)

from math import radians
x = eval(input("输入x的值(0~90):"))
x = radians(x)
an, cosx = 1, 1
jc, i = 1, 2
flag = -1
while(abs(an) >= 1e-7):
jc = jc * (i-1) * i;
an = flag * 1 / jc * x ** i
i += 2
flag = -flag
cosx += an
print(cosx)

【运行结果】

输入x的值(0~90):60
0.5000000000217777

2.3.3 在屏幕上绘制篮球N个坐标点的轨迹的任务

案例描述分析见前2.3.1

案例代码实现:

import math
import matplotlib.pyplot as plt

h, v0, g, θ = 1.9, 14, 9.8, 40
θ=math.radians(θ)
tmax=(2*v0*math.sin(θ)+math.sqrt(4*v0**2*math.sin(θ)**2+8*g*h))/(2*g)
t,delta=0,0.1
while t<tmax:
xt=v0*math.cos(θ)*t
yt=h+ v0*math.sin(θ)*t -1/2*g*t**2
plt.plot(xt,yt,'ro')
t=t+delta
plt.grid(True)
plt.axis([0,30,0,3])
plt.show()

2.3.4 小结


2.3.5 习题


2.4 程序内置的数据结构

在2.3节中案例通过分支与循环已经实现了在合理的范围内输出N个点的任务,接下来我们要完成在屏幕上绘制球的运动轨迹的任务,先将N个数据点保存到数据容器中,通过对数据容器的访问,导入提供数据绘图功能的第三方库matplotlib,其子库pyplot字库可实现在屏幕上可视化程序的运行结果。

本节知识点的学习目标:

1、 掌握序列数据容器:字符串基本语法与操作

2、 掌握序列数据容器:列表基本语法与操作

3、 了解序列数据容器:元组基本语法与操作

4、 了解映射数据容器:集合的基本语法与操作

5、 掌握映射数据容器:字典的基本语法与操作

6、 掌握字符串的典型应用

7、 掌握与列表与字典有关的工程问题的典型应用

本节案例任务:如何在坐标轴内可视化球的运动轨迹

任务描述:

从机器人抛球的初始状态(出手速度V0,出手角度θ,抛球高度H),根据初等物理知识可得知,球从抛出T=0到落地的时间Tmax可通过公式计算得知,在0-Tmax的范围内可以获得n个数据点,将这n个数据点保存到数据容器-列表中,并通过导入数据可视化库建立坐标轴并在坐标轴内绘制球的运动轨迹

通过如下步骤可以实现上述任务:

(1)导入math库和可视化库matplotlib.pyplot

(2)公式计算Tmax的值

(3)建立xt,yt 两个空列表 xt=[ ],yt=[ ],初始化T=0

(4)当T<=Tmax:

​ 计算每一个T的横坐标与纵坐标

添加横坐标与纵坐标到xt[],yt[]列表中

(5)调用可视化函数生成坐标轴并绘制显示球的轨迹


2.4.1 不同数据容器简介:

从前面几章学习的程序设计知识可知,程序设计需要多个数据进行运算与操作时,数据的表示与存储是通过定义变量来实现,例如:假设要求2个数的最大值,3个数的最大值,可以定义3个变量来存储数据,通过比较可得到结果。但是,假如要求100个,1000个甚至更多的数据的最大值,在程序中手动定义那么多的变量显然是不合适的。因此:Python程序设计语言提供了可存储大量数据的数据容器-列表、元组、集合与字典。

列表、元组和集合都可以用来存储大量的数值数据,也可以存储字符数据。列表、元组中的数据是有序的,多个字符组成的字符串也是有序的 ,序列中的每个元素被分配了一个序列号,即元素的位置,也称索引,故它们具有许多相同的属性与操作方法。

与列表类似,集合也可以存储多个数据,不同于列表的是:集合中的元素是彼此不能相同并且不按照任何特定的顺序存放。集合的这种属性对应于数学中集合的三大特性:确定性、互异性和无序性.

在许多的应用中需要通过关键字来查找信息,例如:通过学生的学号来查找学生的信息,通过公民的身份证号码查找公民的个人信息。在编程术语中,根据一个信息查找另一个信息的方式构成了“键值对“的”映射“关系,Python语言的字典就是一种映射数据容器。

关于可变与不可变数据容器简介


2.4.2 序列的基本操作

所谓序列,指的是一块可存放多个值的连续内存空间,这些值按一定顺序排列,可通过每个值所在位置的编号(称为索引)访问它们。

为了更形象的认识序列,可以将它看做是一个学校的教学楼,那么教学楼的每个教室就如同序列存储数据的一个个内存空间,每个教室所特有的房间号就相当于索引值。也就是说,通过房间号(索引)我们可以找到教学楼(序列)中的每个教室(内存空间)。

在 Python 中,序列类型包括字符串、列表、元组、集合和字典,这些序列支持以下几种通用的操作,但比较特殊的是,集合和字典不支持索引、切片、相加和相乘操作。

字符串也是一种常见的序列,它也可以直接通过索引访问字符串内的字符。

1、序列索引

序列中,每个元素都有属于自己的编号(索引)。从起始元素开始,索引值从 0 开始递增

除此之外,Python 还支持索引值是负数,此类索引是从右向左计数,换句话说,从最后一个元素开始计数,从索引值 -1 开始

注意,在使用负值作为列序中各元素的索引值时,是从 -1 开始,而不是从 0 开始。

无论是采用正索引值,还是负索引值,都可以访问序列中的任何元素。以字符串为例,访问“Python语言中文网”的首元素和尾元素,可以使用如下的代码:

##字符串下标访问
str1="Python语言中文网"
print(len(str1)) #输出字符长度
print(str1[0],"==",str1[-11]) #首字符下标访问
print(str1[10],"==",str1[-1]) #尾字符下标访问

【运行结果】

11
p == p
网 == 网

2、序列切片

切片操作是访问序列中元素的另一种方法,它可以访问一定范围内的元素,通过切片操作,可以生成一个新的序列
序列实现切片操作的语法格式如下:

sname[start : end : step]

其中,各个参数的含义分别是:

  • sname:表示序列的名称;

  • start:表示切片的开始索引位置(包括该位置),此参数也可以不指定,会默认为 0,也就是从序列的开头进行切片;

  • end:表示切片的结束索引位置(不包括该位置),如果不指定,则默认为序列的长度;

  • step:表示在切片过程中,隔几个存储位置(包含当前位置)取一次元素,也就是说,如果 step 的值大于 1,则在进行切片去序列元素时,会“跳跃式”的取元素。如果省略设置 step 的值,则最后一个冒号就可以省略。

例如:对字符串“Python语言中文网”进行切片:

#字符串的切片访问
str1="Python语言中文网"
print(str1[:2]) #取索引区间为[0,2]之间(不包括索引2处的字符)的字符串
print(str1[::2]) #隔 1 个字符取一个字符,区间是整个字符串
print(str1[:]) #取整个字符串,此时 [] 中只需一个冒号即可
print(str1[2:5])# 取字符串的索引为[2:4] 的字符

【运行结果】

py
pto语中网
Python语言中文网
tho

3、序列相加

Python中,支持两种类型相同的序列使用“+”运算符做相加操作,它会将两个序列进行连接,但不会去除重复的元素。

这里所说的“类型相同”,指的是“+”运算符的两侧序列要么都是列表类型,要么都是元组类型,要么都是字符串。

例如:

a, b = '123', '456'			# a,b均为字符串
print(a+b)

l1, l2 = [1,2,3], [1,4,5] # l1,l2为列表
print(l1+l2)

【运行结果】

123456
[1, 2, 3, 1, 4, 5]

4、序列相乘

Python 中,使用数字 n 乘以一个序列会生成新的序列,其内容为原来序列被重复 n 次的结果。例如:

str="Python语言中文网"
print(str*3)

【运行结果】

Python语言中文网Python语言中文网Python语言中文网

比较特殊的是,列表类型在进行乘法运算时,还可以实现初始化指定长度列表的功能。例如如下的代码,将创建一个长度为 5 的列表,列表中的每个元素都是 None,表示什么都没有。

#列表的创建用 [],后续讲解列表时会详细介绍
list = [None]*5
print(list)

【运行结果】

[None, None, None, None, None]

5、检查元素是否存在序列中

Python 中,可以使用 in 关键字检查某元素是否为序列的成员,其语法格式为:

value in sequence

其中,value 表示要检查的元素,sequence 表示指定的序列。

例如,检查字符‘c’是否包含在字符串“c.biancheng.net”中,可以执行如下代码:

str="c.biancheng.net"
print('c' in str)

【运行结果】

True

和 in 关键字用法相同,但功能恰好相反的,还有 not in 关键字,它用来检查某个元素是否不包含在指定的序列中,比如说:

str="c.biancheng.net"
print('c' not in str)

输出结果为:

False

6、和序列相关的内置函数

Python提供了几个内置函数(表 * 所示),可用于实现与序列相关的一些常用操作。

函数 功能
len() 计算序列的长度,即返回序列中包含多少个元素。
max() 找出序列中的最大元素。注意,对序列使用sum()函数时,做加和操作的必须都是数字,不能是字符或字符串,否则该函数将抛出异常,因为解释器无法判定是要做连接操作(+运算符可以连接两个序列),还是做加和操作。
min() 找出序列中的最小元素。
list() 将序列转换为列表。
str() 将序列转换为字符串。
sum() 计算元素和。
sorted() 对元素进行排序。
reversed() 反向序列中的元素。
enumerate() 将序列组合为一个索引序列,多用在for循环中。

例如:

str="c.biancheng.net"
#找出最大的字符
print(max(str))
#找出最小的字符
print(min(str))
#对字符串中的元素进行排序
print(sorted(str))

【运行结果】:

t
.
['.', '.', 'a', 'b', 'c', 'c', 'e', 'e', 'g', 'h', 'i', 'n', 'n', 'n', 't']

2.4.3 字符串

在2.2.1 基本的数据类型中我们学习了字符类型的基本表示,字符串(sting)通过任意字符组成的序列数据,序列中的数据可能是一个字母、数值、符号或者标点符号,是用来记录文本信息的有序集合。本节重点介绍字符的基本操作和方法。

1、字符串的基本操作

(1)字符串的访问、连接与复制

一个字符串就是一个序列,因此可以将序列的一些基本操作应用在字符串上。

字符串包含两种序号体现:正向递增序号和反向递减序号,如果字符串长度为L,正向递增以最左侧字符序号为0,向右依次递增,最右侧字符序号为L-1;反向递减序号以最右侧字符序号为-1,向左依次递减,最左侧字符序号为-L。这两种索引方式可以在一个字符表示中使用。

Python字符串也提供区间访问方式:采用[start : end : step]格式,表示字符串中从start到end(不包含end)的子字符串,其中start和end为字符串的索引序号,可以混合使用正向递增序号和方向递减序号。如果表示中start或end缺失,则表示字符串把开始或者结束索引值设为默认值, step表示在字符访问过程中,隔几个存储位置(包含当前位置)取一次元素。step可省略,省略后默认值为1,具体的实例可参照序列的基本操作中,这里不再重复。

字符串以Unicode方式编码存储,因此中文与英文字符都算一个字符。

此外:使用+和*可实现字符串的拼接与复制,例如定义str1=’Hello’+’ ‘+’HuNanUniversity’ , str2=‘*’*5,则str1的值为Hello HuNanUniversity,str2的值为*****。另外,使用增强赋值运算符+=*=也是可以的。

使用in/not in运算符可以判断一个字符串是否是另一个字符串的子串,例如表达式 ‘th’ in ‘Python’将返回True.

(2)字符串的内置函数

Python提供了一些内置函数,有6个函数与字符串处理有关,例如:

#字符串内置函数
str1="Python语言中文网"
print(len(str1)) #len(x) #返回x的长度
print(str(3.1415926)) #str(x)返回x对应的字符串形式
print(chr(65)) #chr(x)返回Unicode编码X对应的单字符
print(ord('A')) #ord(x)返回单字符表示的Unicode编码
print(hex(100)) #hex(x) 返回整数X对应的16进制的小写形式字符串
print(oct(100)) #oct(x) 返回整数X对应的8进制的小写形式字符串

【运行结果】

11
3.1415926
A
65
0x64
0o144
(3)字符串的增删改

我们已经知道可以用 s=’ ‘的方式来表示一个空的字符串,用 ‘abc’+’def’可得到连接后的字符串‘abcdef’,如果想修改一个字符串的内容时该如何操作呢?

同时我们清楚,字符串是不可变的对象,即不可以直接修改字符串的内容。所以需要通过字符串切片的方式将不同的字符串连接起来,从而形成新的字符串。例如若想将字符串 str1中的‘Python’修改为’C’,如果采用直接赋值的方式: str1[0:5]=’C’,Python会报错,正确的操作方式为:

#字符串的修改
str1="Python语言中文网"
str2='C'+str1[6:]
print(str2)

【运行结果】

C语言中文网

若要删除str1字符串中的“语言”字符,正确的操作方式为:

#字符串的删除 
str1="Python语言中文网"
str2=str1[0:6]+str1[8:]
print(str2)

【运行结果】

Python中文网

2、字符串的函数与方法

在Python解释器中,所有的数据类型都采用面向对象的方式实现,封装为一个类。字符串也是一个类,它具有类似对象.方法() 形式的字符串处理函数,在面向对象中,这些函数被称为“方法”,字符串共有43个内置方法,鉴于部分方法不常用,限于篇幅,这里仅介绍其中的18个方法,见表

字符串方法 功能
s.upper() 返回字符串s的副本,全部字符大写
s.lower() 返回字符串s的副本,全部字符小写
s.islower() 如果 s 中包含至少一个区分大小写的字符,并且所有这些(区分大小写的)字符都是小写,则返回 True,否则返回 False
s.isdigit() 如果 s只包含数字则返回 True 否则返回 False.
s.isalpha() 如果 s 至少有一个字符并且所有字符都是字母则返回 True,否则返回 False
s.isnumeric() 如果 s 中只包含数字字符,则返回 True,否则返回 False
s.find(substr,[start:[end]]) 返回 substr串在s串中的第一个字符的下标, start和 end表示查找的范围,没有找到返回-1
s.index(substr,[start:[end]]) 用法同s.find()相同,substr不在返回则会报异常
s.count(str,[start=0,end=len(s))]) 返回 str 在 s 里面出现的次数,如果 start 或者 end 指定则返回指定范围内 str 出现的次数
s.replace(oldstr,newstr,[count]), 用newstr替换oldstr,,count为替换次数
s.split([sep,[maxsplit]) 以sep为分隔符,把字符串s拆分为一个列表,默认的以空格作为分隔符
s.strip([chars])
s.lstrip()
s.rstrip()
删除s前后[chars]字符串,默认是删除首尾空格 删除s左边前后[chars]字符串,默认是删除左边空格 删除s右边前后[chars]字符串,默认是删除右边空格
s.join(seq) 把seq代表的序列组合成字符串,用s将序列各元素连接起来
s.format() 格式化字符串
s.center(width) 返回一个原字符串居中,并使用空格填充至长度 width 的新字符串
s.zfill(width) 返回长度为 width 的字符串,原字符串 s 右对齐,前面填充0

表格中对字符串方法的功能和参数做了简单的说明,各个参数的具体用法,具体的使用请查阅相关手册,这里仅给出少数的例子,请读者在应用中逐步实践。

#字符串的函数与方法

str3="Python.biancheng.net"

#找出子串在str3中的位置

print(str3.find('bian')) #在整字符串中查找子串在字符串中的位置
print(str3.find('bian',6,len(str3)))#从字符串下标为6的位置开始查找
print(str3.find('bian',10)) #从字符串下标为10的位置开始查找,没找到返回-1

#统计某一字串在字符串中出现的次数
print(str3.count('e'))

#字符串的替换

print(str3.replace('Python','c'))
print(str3)#原字符串不改变

#字符串切割,生成列表

print(str3.split('.'))
print(str3)#原字符串不改变

#删除字符串中某一字符

print(str3.strip(' '))#去掉首尾空格
print(str3)#原字符串不改变

【运行结果】

7
7
-1
2
c.biancheng.net
Python.biancheng.net
['Python', 'biancheng', 'net']
Python.biancheng.net
Python.biancheng.net
Python.biancheng.net

3、字符串的格式化

format方式

这个方法是Python独有的,但也更推荐使用str.format()函数进行格式化。例如:

#format方式格式化输出
i, j = 5, 10
print("{}*{}={:2d}".format(i,j,i*j))

【运行结果】

5*10=50

format的优秀之处在于,它不再需要指定变量的输出类型%d, %f等等,只需要使用{}占位

当然如果要特定位数还是需要指定类型的

format本身还能支持左右对齐等操作,这里不进行介绍,请同学们自行查阅资料尝试。

format还支持变量的复用,如果你想在一句话中重复输出10次s变量,就无需像使用%方式那样需要在右侧括号处打十次s

ss="@xxx"
print("你不要再%s啦,%s很忙的,天天在评论区%s,%s会生气的啦" % (ss,ss,ss,ss))

而format方法则可有效避免重复书写变量

ss="@xxx"
print("你不要再{0}啦,{0}很忙的,天天在评论区{0},{0}会生气的啦".format(ss))

同时format也可以指定{}输出的是第几个变量,如上文的{0}指定输出了第一个变量ss

在索引后接上:一样可以对该位置变量做格式化控制,如:

name = '小明'
alipay = '5426.53'
print("{1}的支付宝中有{0:.1f}元".format(alipay,name))

【运行结果】

小明的支付宝有5426.5

4、 字符串的操作实例

案例2-8 进制转换:二进制整数转换成十进制整数的程序

案例描述:不使用Python转换函数int(“ “,base=10,2,8,16),实现二进制转换成十进制的程序

案例分析: 二进制或其他进制整数转换成十进制整数的方法为按权展开相加可得。

二进制的权为2,表示数的数码为0,1;八进制权为8,表示数的数码为0,1,2,3,4,5,6,7;十六进制权为16,表示数的数码为:0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F。例如:(1011011)2 = (126+025+124+123+022+121+1*20) =( 91)10

通过以下步骤可实现上述任务:

(1)设置输入语句,输入0、1 组成的二进制字符串

(2) 求字符串的长度

(3)依次读取字符序列的每个字符,如果为1,则累加该项的权

(4) 输出结果

案例实现:(代码清单)

# 字符串案例2-8 二进制整数转换成十进制整数
b_num=input("请输入二进制数:")
dec_num=0 #十进制数变量
for i in range(len(b_num)):
if b_num[i]=='1':
dec_num+=2**(len(b_num)-1-i)
print("输出十进制数为:%d" %(dec_num))

【运行结果】

请输入二进制数:1011011
输出十进制数为:91

请读者不妨一试:如果要实现八/十六进制整数转换成十进制整数,该如何修改程序?对于十六进制整数要转换成十进制整数,待我们学完字典后,可以选择另一种方法来完成。

案例2-9 密码加密

案例描述: 某用户在存储自己的密码时(密码由大小写英文字母和数字组成),为了不被别人知晓,将其按下列规则加密:

数字字符0用9替换,1用8替换,2用7替换,3用6替换,4用5替换,5用4替换,6用3替换,7用2替换,8用1替换

英文字母a用z替换,b用y替换,c用x替换,d用w替换,e用v替换,f用u替换,g用n替换,以此类推

英文字母A用Z替换,B用Y替换,C用X替换,D用W替换,E用V替换,F用U替换,G用N替换,以此类推

输入用户的密码,输出其加密后的密码

例如:输入0012xyzM>

输出9987cbaN

案例分析:

本案例输入的字符串中包含大小写英文字符和数字字符,要用ord()和chr()函数的方式来完成,ord(x)的功能:返回单字符表示的Unicode编码 ;chr(x)的功能:返回Unicode编码X对应的单字符。

通过以下步骤可实现上述任务:

(1)设置输入语句,输入包含大小写和数字字符组成的要加密的密码,俗称原码

(2) 求原码字符串的长度,确定了加密循环的次数

(3)依次读取字符序列的每个字符,通过多分支语句按3种方式加密

(4) 输出加密后的结果

案例实现:(代码清单)

#字符案例2-9 字符加密

Y_word=input("请输入原码:")
M_word=' ' #初始化密码变量为空字符
for i in range(len(Y_word)):
if 0<=ord(Y_word[i])-ord('0')<=9:
M_word+=str(9-int(Y_word[i]))
elif 'a'<=Y_word[i]<='z':
M_word+=chr(ord('a')+ord('z')-ord(Y_word[i]))
else:
M_word+=chr(ord('A')+ord('Z')-ord(Y_word[i]))
print("请输出密码:%s" %M_word)

【运行结果】

请输入原码:0012xyzM
请输出密码:9987cbaN

请读者不妨想一想:还有其他的方式来实现字符串的加密吗?待我们学完列表后,可以试着用列表来完成加密过程。


2.4.4 列表

列表(list)是包含0个或多个对象引用的有序序列,属于序列类型。从形式上看,列表会将所有元素都放在一对中括号[ ]里面,相邻元素之间用逗号,分隔,如下所示:

[element1, element2, element3, ..., elementn]

格式中,element1 ~ elementn 表示列表中的元素,个数没有限制,只要是 Python 支持的数据类型就可以。其长度与内容也是可变的,可自由地对列表中的元素进行增加、删除或替换

从内容上看,列表可以存储整数、小数、字符串、列表、元组等任何类型的数据,并且同一个列表中元素的类型也可以不同,使用非常灵活。例如:

["http://c.biancheng.net/Python/", 1, [2,3,4] , 3.0]

可以看到,列表中同时包含字符串、整数、列表、浮点数这些数据类型。

注意,在使用列表时,虽然可以将不同类型的数据放入到同一个列表中,但通常情况下不这么做,同一列表中只放入同一类型的数据,这样可以提高程序的可读性。

另外,在其它 Python 教程中,经常用 list 代指列表,这是因为列表的数据类型就是 list,通过 type() 函数就可以知道,例如:

>>> type( ["http://c.biancheng.net/Python/", 1, [2,3,4] , 3.0] )

<class 'list'>

可以看到,它的数据类型为 list,就表示它是一个列表。

此外,列表除了可用[ ]来表示,还可以通过list()函数将元组或字符串转化而成

1、列表的创建

print( [231, 3.14, 'ab', 0]) # 直接用[ ]定义列表

print(list('欢迎来到美丽的星城长沙') ) #将字符串转化成列表

print (list((123,[456,'a'],'abc'))) #将元组转化成列表

【运行结果】:

[231, 3.14, 'ab', 0]
['欢', '迎', '来', '到', '美', '丽', '的', '星', '城', '长', '沙']
[123, [456, 'a'], 'abc']

由于列表是序列类型,序列数据的索引、切片,相加、相乘、成员关系操作符(in),长度计算函数(len())、求最大最小值函数max(),min()等序列函数都支持。

2、列表的访问:

使用下标索引来访问列表中的值,同样你也可以使用方括号的形式截取字符,如下所示:

list1 = ['physics', 'chemistry', 1997, 2000]
list2 = [1, 2, 3, 4, 5, 6, 7 ]

print "list1[0]: ", list1[0]
print "list2[1:5]: ", list2[1:5]

【运行结果】

list1[0]:  physics
list2[1:5]: [2, 3, 4, 5]

同样,列表也支持负数索引

3、列表的拼接和复制

列表对 + 和 * 的操作符与字符串相似。+ 号用于组合列表,* 号用于重复列表。

如下所示:

Python 表达式 结果 描述
len([1, 2, 3]) 3 长度
[1, 2, 3] + [4, 5, 6] [1, 2, 3, 4, 5, 6] 组合
[‘Hi!’] * 4 [‘Hi!’, ‘Hi!’, ‘Hi!’, ‘Hi!’] 重复

4、列表的遍历

列表可以采用下标进行索引遍历,也可以采用in操作进行索引,例如:

a = [78, 65, 89, 26]
for i in range(len(a)):
print(a[i])

【运行结果】

78
65
89
26

a = [78, 65, 89, 26]
for num in a:
print(num)

【运行结果】

78
65
89
26

5、列表的in/not in运算符

Python 表达式 结果 描述
if 3 in [1, 2, 3]: print(True) True 元素是否存在于列表中
for x in [1, 2, 3]: print(x) 1 2 3 迭代
if 3 not in [1, 2]: print(True) True 元素是否不存在于列表中

6、列表的切片

正值索引

nums = [10, 20, 30, 40, 50, 60, 70, 80, 90]
print(nums[0:4])

【运行结果】

[10, 20, 30, 40]

负值索引

list = ['Huawei', 'HNU', "Zhihu", "Ali", "Wiki"]

# 读取第二位
print ("list[1]: ", list[1])
# 从第二位开始(包含)截取到倒数第二位(不包含)
print ("list[1:-2]: ", list[1:-2])

【运行结果】

list[1]:  HNU
list[1:-2]: ['HNU', 'Zhihu']

7、列表的推导式(列表解析)

列表推导式(又称列表解析式)提供了一种简明扼要的方法来创建列表。

它的结构是在一个中括号里包含一个表达式,然后是一个for语句,然后是 0 个或多个 for 或者 if 语句。那个表达式可以是任意的,意思是你可以在列表中放入任意类型的对象。返回结果将是一个新的列表,在这个以 if 和 for 语句为上下文的表达式运行完成之后产生。

列表推导式的执行顺序:各语句之间是嵌套关系,左边第二个语句是最外层,依次往右进一层,左边第一条语句是最后一层。

例如:

list = [x*x for x in range(10)]
print(list)

【运行结果】

[0, 1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

进行嵌套后

[x*y for x in range(1,5) if x > 2 for y in range(1,4) if y < 3]

他的执行顺序是:

for x in range(1,5)
if x > 2
for y in range(1,4)
if y < 3
x*y

8、列表的增删查改排序与统计等操作

向列表中增加元素

l = []
l.append(1)
l.append('string')
print(l)

【运行结果】

[1, 'string']

删除列表中的元素

list = ['Huawei', 'HNU', 1997, 2000]

print ("原始列表 : ", list)
del list[2]
print ("删除第三个元素 : ", list)

【运行结果】

原始列表 :  ['Huawei', 'HNU', 1997, 2000]
删除第三个元素 : ['Huawei', 'HNU', 2000]

查找列表中的元素位置

list = ['Huawei', 'HNU', 1997, 2000]
print(list.index(1997))

【运行结果】

2

对列表中某一位置元素进行修改

list = ['Huawei', 'HNU', 1997, 2000]
list[2] = 1998
print(list)

【运行结果】

['Huawei', 'HNU', 1998, 2000]

统计列表中某个元素出现次数

list = [1, 1, 1, 2, 3]
print(list.count(1), list.count(3))

【运行结果】

3 1

列表的排序

sort方法(改变原序列)

list = [45, 78, 98, 12]
list.sort()
print(list)

【运行结果】

[12, 45, 78, 98]

sorted函数(不改变原序列)

list = [45, 78, 98, 12]
new_list = sorted(list)
print(list)
print(new_list)

【运行结果】

[45, 78, 98, 12]
[12, 45, 78, 98]

9、多维列表(嵌套列表)

使用嵌套列表即在列表里创建其它列表,例如:

a = ['a', 'b', 'c']
n = [1, 2, 3]
x = [a, n]
print(x)

print(x[0])

print(x[0][1])

【运行结果】

[['a', 'b', 'c'], [1, 2, 3]]
['a', 'b', 'c']
'b'

10、列表函数方法

Python包含以下函数:

序号 函数
1 len(list)列表元素个数
2 max(list) 返回列表元素最大值
3 min(list) 返回列表元素最小值
4 list(seq)将元组转换为列表

Python包含以下方法:

序号 方法
1 list.append(obj) 在列表末尾添加新的对象
2 list.count(obj)统计某个元素在列表中出现的次数
3 list.extend(seq)在列表末尾一次性追加另一个序列中的多个值(用新列表扩展原来的列表)
4 list.index(obj) 从列表中找出某个值第一个匹配项的索引位置
5 list.insert(index, obj) 将对象插入列表
6 list.pop([index=-1])移除列表中的一个元素(默认最后一个元素),并且返回该元素的值
7 list.remove(obj)移除列表中某个值的第一个匹配项
8 list.reverse()反向列表中元素
9 list.sort( key=None, reverse=False)对原列表进行排序
10 list.clear() 清空列表
11 list.copy() 复制列表

11、列表综合案例

案例2-10 for循环与列表解决查找暴力搜索问题

案例2-11 “密码问题”

问题描述:编号为1、2、3、…、N的N个人按顺时针方向围坐一圈,每人持有一个密码(正整数)。从指定

编号为1的人开始,按顺时针方向自1开始顺序报数,报到指定数M时停止报数,报M的人出列,并将

他的密码作为新的M值,从他在顺时针方向的下一个人开始,重新从1报数,依此类推,直至所有的

人全部出列为止。请设计一个程序求出出列的顺序,其中N≤30,M及密码值从键盘输入。

案例2-12 用列表解决本节案例任务:实现绘制N个坐标点的篮球飞行轨迹


2.4.5 元组

元组(tuple)是Python中的另一种内置的存储有序数据容器。与列表类似,可存储不同类型的数据,如字符数据、数值数据、甚至元组,然而,元组是不可改变的数据容器,创建后不能作任何的修改操作。因此,元组的主要作用是用来存放那些在程序的执行过程中不能被破坏的数据。

1、元组的创建

Python 的元组与列表类似,不同之处在于元组的元素不能修改。

元组使用小括号 (),列表使用方括号 []

元组创建很简单,只需要在括号中添加元素,并使用逗号隔开即可。

tup1 = ('Huawei', 'HNU', 1997, 2000)
tup2 = (1, 2, 3, 4, 5 )
tup3 = "a", "b", "c", "d" # 不需要括号也可以
print(type(tup3))

【运行结果】

<class 'tuple'>

创建空元组

tup1 = ()

元组中只包含一个元素时,需要在元素后面添加逗号,否则括号会被当作运算符使用:

tup1 = (50)
print(type(tup1)) # 不加逗号,类型为整型

tup1 = (50,)
print(type(tup1)) # 加上逗号,类型为元组

【运行结果】

<class 'int'>
<class 'tuple'>

2、元组的基本操作

访问元组

元组可以使用下标索引来访问元组中的值,如下实例:

tup1 = ('Huawei', 'HNU', 1997, 2000)
tup2 = (1, 2, 3, 4, 5, 6, 7 )
print("tup1[0]: ", tup1[0])
print("tup2[1:5]: ", tup2[1:5])

以上实例输出结果:

tup1[0]:  Huawei
tup2[1:5]: (2, 3, 4, 5)
修改元组

元组中的元素值是不允许修改的,但我们可以对元组进行连接组合,如下实例:

tup1 = (12, 34.56) 
tup2 = ('abc', 'xyz')
# 以下修改元组元素操作是非法的。
# tup1[0] = 100
# 创建一个新的元组
tup3 = tup1 + tup2
print (tup3)

以上实例输出结果:

(12, 34.56, 'abc', 'xyz')
删除元组

元组中的元素值是不允许删除的,但我们可以使用del语句来删除整个元组,如下实例:

tup = ('Huawei', 'HNU', 1997, 2000)
print (tup)
del tup
print ("删除后的元组 tup : ")
print (tup)

以上实例元组被删除后,输出变量会有异常信息,输出如下所示:

删除后的元组 tup : 
Traceback (most recent call last):
File "test.py", line 8, in <module>
print (tup)
NameError: name 'tup' is not defined
元组运算符

与字符串一样,元组之间可以使用 + 号和 * 号进行运算。这就意味着他们可以组合和复制,运算后会生成一个新的元组。

Python 表达式 结果 描述
len((1, 2, 3)) 3 计算元素个数
(1, 2, 3) + (4, 5, 6) (1, 2, 3, 4, 5, 6) 连接
(‘Hi!’,) * 4 (‘Hi!’, ‘Hi!’, ‘Hi!’, ‘Hi!’) 复制
3 in (1, 2, 3) True 元素是否存在
for x in (1, 2, 3): print (x,) 1 2 3 迭代
元组切片

元组作为一种序列,也是可以进行序列切片操作的,如

tup = (123, 456, 789, 'Python')
print(tup[1:3])
print(tup[-2:])

【运行结果】

(456, 789)
(789, 'Python')
元组内置函数

Python元组包含了以下内置函数

序号 方法及描述 实例
1 len(tuple) 计算元组元素个数。 tuple1 = (‘Huawei’, ‘HNU’, ‘Ali’)
print(len(tuple1))
【结果】3
2 max(tuple) 返回元组中元素最大值。 tuple2 = (‘5’, ‘4’, ‘8’)
print(max(tuple2))
【结果】’8’
3 min(tuple) 返回元组中元素最小值。 tuple2 = (‘5’, ‘4’, ‘8’)
print(min(tuple2))
【结果】’4’
4 tuple(iterable) 将可迭代系列转换为元组。 list1= [‘Huawei’, ‘Ali’, ‘HNU’, ‘Baidu’]
tuple1=tuple(list1)
print(tuple1)
【结果】(‘Huawei’, ‘Ali’, ‘HNU’, ‘Baidu’)`

3、元组与列表比较

  • 元组和列表都属于序列。
  • 列表属于可变序列,它的元素可以随时修改或者删除,而元组属于不可变序列,其中的元素是不能修改的,除非整体重新赋值。
  • 列表可以使用多种方法实现添加和修改列表元素,而元组没有办法,因为不能想元组中添加或修改元素,同样也不能删除元素
  • 列表可以使用切片方法访问和修改列表中的元素,元组也支持切片,但是它只支持通过切片访问元组中的元素,不支持修改
  • 元组比列表中的访问和处理速度更快,所以如果只需要对其中的元素进行访问,而不进行任何修改,建议使用元组。
  • 列表不能作为字典类型中的键,而元组是可以的。

2.4.6 集合

与列表、元组相比,集合(set)是无序可变的数据容器,所有的元素放在一对“{ }”中,元素之间用逗号隔开,同一个集合内的元素都是唯一的,不允许重复。

1、集合简介

集合(set)是一个无序的不重复元素序列。

可以使用大括号 {} 或者 set() 函数创建集合,注意:创建一个空集合必须用 set() 而不是 {},因为 {} 是用来创建一个空字典。

2、集合的基本运算

集合的创建
set1 = {value1,value2,...}

以现有的序列进行创建

set1 = set('abcdefg')

创建一个空集合

set1 = set()
集合的去重功能

在前面列表的求并集差集时,我们已初步接触过集合的去重功能,如:

set1 = set('caaaaaaaab')
print(set1)

【运行结果】

{'a', 'b', 'c'}

可以看到,重复的a被剔除,同时剩下的元素以字典序排列。

添加元素

语法格式如下:

s.add( x )

将元素 x 添加到集合 s 中,如果元素已存在,则不进行任何操作。

set1 = set(("Huawei", "HNU", "Ali"))
set1.add("Facebook")
print(set1)

【运行结果】

{'Ali', 'Facebook', 'Huawei', 'HNU'}

还有一个方法,也可以添加元素,且参数可以是列表,元组,字典等,语法格式如下:

s.update( x )

x 可以有多个,用逗号分开。

set1 = set(("Huawei", "HNU", "Ali"))
set1.update({1,3})
print(set1)

set1.update([1,4],[5,6])
print(set1)

【运行结果】

{1, 3, 'Huawei', 'Ali', 'HNU'}
{1, 3, 4, 5, 6, 'Huawei', 'Ali', 'HNU'}
移除元素

语法格式如下:

s.remove( x )

将元素 x 从集合 s 中移除,如果元素不存在,则会发生错误。

set1 = set(("Huawei", "HNU", "Ali"))
set1.remove("Ali")
print(set1)

【运行结果】

{'Huawei', 'HNU'}

如果移除一个不存在的元素

set1 = set(("Huawei", "HNU", "Ali"))
set1.remove("Facebook") # 不存在会发生错误

【运行结果】

Traceback (most recent call last):
File "<stdin>", line 1, **in** <module>
KeyError: 'Facebook'

此外还有一个方法也是移除集合中的元素,且如果元素不存在,不会发生错误。格式如下所示:

s.discard( x )

例如

set1 = set(("Huawei", "HNU", "Ali"))
set1.discard("Facebook") # 不存在不会发生错误
print(set1)

【运行结果】

{'Ali', 'Huawei', 'HNU'}

我们也可以设置随机删除集合中的一个元素,语法格式如下:

s.pop() 

例如:

set1 = set(("Huawei", "HNU", "Ali", "Facebook"))
x = set1.pop()
print(x)

输出结果:

HNU

多次执行测试结果都不一样。

set 集合的 pop 方法会对集合进行无序的排列,然后将这个无序排列集合的左面第一个元素进行删除。

3、集合的常用方法


2.4.7 字典

不同于字符串、列表、元组和集合等序列数据容器,Python字典所存储的数据元素之间与顺序无关。它是一种映射类型的数据容器,所谓映射类型的数据结构,存在一对一的映射关系,例如:每个居民都有唯一的身份证号码,表示为身份证:身份证号码,也存在多对一的映射关系,例如多个学生有相同的成绩,表示为学生学号:成绩,一对多的关系不能称为映射。

1、字典的概念与特性

Python字典中的每个数据元素包含“键”和“值”,这两部分以冒号分隔,表示一种对应关系(键:值),英文为(key:value)。不同元素之间以逗号隔开,所有元素放在一对大括号里面。格式如下表示:

d = {key1 : value1, key2 : value2,…… }

字典元素中的“键”可以是Python中的不可变类型,例如整数,浮点数、字符串、元组等类型,但不可以是列表、集合和字典或其他可变类型,而且包含了列表元素的元组也不能作为字典的“键”,字典的键(key)是唯一的,不能重复,而值(value)是可以重复的。

2、字典的创建

字典可以用若干"键: 值"元素放在一对大括号中进行创建,也可以使用dict类的不同形式或字典推导式

以键值对定义字典,例如:

dict1 = {}				#创建空字典
dict2 = {'name': 'Hunan University', 'url': 'http://www.hnu.edu.cn/', 'create_time': 1903}
print(dict1)
print(dict2)

【运行结果】

{}
{'name': 'Hunan University', 'url': 'http://www.hnu.edu.cn/', 'create_time': 1903}

以其他序列作为字典的“键”进行创建(“值”为空 ),例如:

dict1 = dict.fromkeys(['name', 'url', 'create_time'])	#以列表的值作为字典的键来创建字典
print(dict1)

【运行结果】

{'name': None, 'url': None, 'create_time': None}

使用字典推导式创建字典,例如:

dict1 = {ch: ord(ch) for ch in "Hunan University"}
print(dict1)

【运行结果】

{'H': 72, 'u': 117, 'n': 110, 'a': 97, ' ': 32, 'U': 85, 'i': 105, 'v': 118, 'e': 101, 'r': 114, 's': 115, 't': 116, 'y': 121}

3、字典元素的访问

字典支持下标操作,把“键”作为下标可以返回对应的”值“,如果字典中不包含这个“键”会抛出异常。例如:

dict1 = {'name': 'Hunan University', 'url': 'http://www.hnu.edu.cn/', 'create_time': 1903}
print(dict1['name'])

【运行结果】

Hunan University

字典的get()方法用于获取指定的“键”对应的“值”,如果指定的”键“不存在,get()方法会返回控制或指定的值。例如:

dict1 = {'name': 'Hunan University', 'url': 'http://www.hnu.edu.cn/', 'create_time': 1903}
print(dict1.get('name'))
print(dict1.get('address', '不存在这个键'))

【运行结果】

Hunan University
不存在这个键

字典对象支持元素迭代,可以将其转换为列表或元组,也可以用 for 循环遍历其中的元素,默认情况下是遍历字典的“键”,如果需要遍历字典中的元素,则必须使用字典对象的items()方法明确说明;如果需要遍历字典中的“值”,则必须使用字典对象的values()方法明确说明。当我们在使用len(), max(), min(), sum(), sorted(), enumerate(), map()等内置函数以及in成员操作符时也遵循同样的约定。例如:

dict1 = {'name': 'Hunan University', 'url': 'http://www.hnu.edu.cn/', 'create_time': 1903}
#把所有的“键”转换为列表
print(list(dict1))
print(list(dict1.keys()))
#把所有的“值”转换成列表
print(list(dict1.values()))
#把所有的元素转换成列表
print(list(dict1.items()))

【运行结果】

['name', 'url', 'create_time']
['name', 'url', 'create_time']
['Hunan University', 'http://www.hnu.edu.cn/', 1903]
[('name', 'Hunan University'), ('url', 'http://www.hnu.edu.cn/'), ('create_time', 1903)]

用for循环遍历字典,例如:

dict1 = {'name': 'Hunan University', 'url': 'http://www.hnu.edu.cn/', 'create_time': 1903}
#遍历所有的“键”
for key in dict1:
print(key, end=" ")
print()
#或者
for key in dict1.keys():
print(key, end=" ")
print("\n--------")

#遍历所有的“值”
for value in dict1.values():
print(value, end=" ")
print("\n--------")

#遍历所有的元素
for key, value in dict1.items():
print(key, value)

【运行结果】

name url create_time 
name url create_time
--------
Hunan University http://www.hnu.edu.cn/ 1903
--------
name Hunan University
url http://www.hnu.edu.cn/
create_time 1903

4、字符元素的增、删和改操作

字典可以以“键”的方式进行修改赋值。若该”键“存在,则修改该”键“对应的”值“;若该”键“不存在,则想字典中添加新的键值对。例如:

dict1 = {'name': 'Hunan University', 'url': 'http://www.hnu.edu.cn/', 'create_time': 1903}
dict1["type"] = "985" #新增键值对
print(dict1)
dict1["type"] = "985,211" #修改键“type”的值
print(dict1)

【运行结果】

{'name': 'Hunan University', 'url': 'http://www.hnu.edu.cn/', 'create_time': 1903, 'type': '985'}
{'name': 'Hunan University', 'url': 'http://www.hnu.edu.cn/', 'create_time': 1903, 'type': '985,211'}

使用字典对象的upload()方法可以将另一个字典的元素一次性全部添加到当前字典中,如果两个字典中存在相同的“键”,则以另一个字典的“值”为准对当前字典进行更新。例如:

dict1 = {'name': 'HNU', 'url': 'http://www.hnu.edu.cn/', 'create_time': 1903}
dict2 = {'name': 'Hunan University', 'type': '985,211'}
dict1.update(dict2)
print(dict1)

【运行结果】

{'name': 'Hunan University', 'url': 'http://www.hnu.edu.cn/', 'create_time': 1903, 'type': '985,211'}

使用字典对象的pop()方法可以删除字典指定的“键”对应的元素,同时返回对应的值。而popitem()方法删除字典最后一个元素并返回一个包含“键”和“值”的元组。例如:

dict1 = {'name': 'Hunan University', 'url': 'http://www.hnu.edu.cn/', 'create_time': 1903}
print(dict1.pop('create_time')) #删除键'create_time'并返回其值

dict1 = {'name': 'Hunan University', 'url': 'http://www.hnu.edu.cn/', 'create_time': 1903}
print(dict1.popitem()) #删除最后一个元素并返回其键值对的元组形式

【运行结果】

1903
('create_time', 1903)

另外,也可以用del函数删除指定的“键”对应的元素。例如:

dict1 = {'name': 'Hunan University', 'url': 'http://www.hnu.edu.cn/', 'create_time': 1903}
del dict1['url']
print(dict1)

【运行结果】

{'name': 'Hunan University', 'create_time': 1903}

5、字典与列表的嵌套

在字典中,我们允许以列表作为字典的“键”或“值”,并以多层下标的方式调用。例如:

class1 = {'names': ['张三', '李四', '王五'], 'scores': [95, 98, 75]}
print(class1['names'])
print(class1['scores'][1])

【运行结果】

['张三', '李四', '王五']
98

也允许在列表中嵌入字典。例如:

class1 = [{'name': '张三', 'score': 95}, {'name': '李四', 'score': 98}, {'name': '王五', 'score': 75}]
print(class1[0])
print(class1[1]['name'])

【运行结果】

{'name': '张三', 'score': 95}
李四

6、字典应用综合案例

案例2-12 进制转换

案例2-13 词频统计

案例2-14


2.4.9 小结


2.4.10 习题


2.5 模块化程序构造:函数

本节知识点的学习目标:

1、 掌握函数的基本概念

2、 掌握函数不同参数的使用方法

3、 掌握lambda 函数的使用

4、 理解递归函数的执行过程

5、 理解变量的作用域

6、 掌握用函数实现模块化程序设计方法

本节案例任务:如何绘制不同条件下的球的运动轨迹-Python函数与模块化编程

本关任务:完成代码的复用,封装性,实现模块化程序设计方法

案例任务:根据不同的高度初速度和不同的角度绘制铅球的飞行轨迹

案例分析:

2.5.1 函数的基本概念

在Python中,函数的含义不是数学上的函数值与表达式之间的对应关系,而是一种运算或处理过程,是一段具有特定功能的、可重用的语句组,用函数名来表示并通过函数名来进行功能调用。函数也可以看作一段具有名字的子程序,可以在需要的地方调用执行,从而避免编写大段的重复代码。

每次使用函数时,可以提供一些不同的参数作为输入,来实现不同的数据处理;函数执行后,可以反馈一个或多个变量以表示函数处理的结果。

函数能够完成特定的功能,用户在使用函数时并不需要了解函数内部实现相应功能的原理,只需了解函数的调用输出方式。严格地说,函数是一种功能抽象。

使用函数是为了降低编程难度和代码重用。利用函数,我们可以将复杂的大问题分解成一系列简单的小问题并逐步解决。函数可以在一个程序中使用,也可以用于多个程序。当需要修改时,只需修改相应的函数即可,其他调用到函数的地方也会做相应的更新,这样也降低了代码的维护难度。

内置函数与自定义函数

在Python中,有些函数为用户自行编写的,称为自定义函数;还有一部分函数和方法是Python安装包自带的,包括Python内置的函数(例如abs()eval()等)和Python标准库中的函数(例如math库的sqrt()等)

下表列出了我们在Python中较为常用的内置函数:

函数名 用途
all() 全部值为真,返回True
any() 任意一个值为真,返回True
abs(x) 绝对值
divmod(x,y) 两个数字相除,取商和余的元组
len() list长度
max(iterable) 返回参数中最大值
min(iterable) 返回参数中最小值
pow(x,y) 返回x的y次方的值
range(x,y) 生成一个数值序列
round(x,y) 获取指定位数的小数
sum(iterable) 返回集合中数字的总和
bool() 转换为布尔类型
bin(int) 十进制转换为二进制
oct(int) 十进制转换为八进制
hex(int) 十进制转换为十六进制
float(int/str) 将数字转换为浮点数
int(str) 将数字转换为整数
str(int) 转换为字符串
bytes(str,code) 接收一个字符串,与所要编码的格式,返回一个字节流类型。
iter(iterable) 返回一个迭代器
dict(iterable) 转换为数据字典
list(iterable) 转换为列表类型
tuple(iterable) 转换为元组类型
set(iterable) 创建一个无序不重复元素的集合
complex() 创建一个复数
enumerate() 返回一个枚举对象
ord(str) 返回ASCII对应的十进制整数
chr(int) 返回整数对应的ASCII字符
ascii() 判断参数是否ascii编码,如果不是,输出字节码
map(func,*iterable) 处理序列中的每个元素,得到的结果是一个‘list’,该‘list’元素个数及位置与原来一样。
lambda()
reduce() 处理一个序列,然后把序列进行合并操作
reversed(sequence) 生成一个反转序列的迭代器
sorted() 对一个集合进行排序,返回排序后的list
zip() 拉链形式,将对象中的多个可迭代序列输出成一一对应的元组
next() 接受一个迭代器,返回迭代器中的值
id() 返回一个对象的唯一标识值。
help() 调用系统内置的帮助系统
type() 返回一个对象的数据类型
input() 获取用户输入信息
open() 打开文件
print() 打印函数
format() 字符串格式化
eval() 1、将字符串中数据结构提取出来 2、将字符串中数据运算计算出结果

而用户自定义的函数包括函数名,函数参数与实现函数功能的语句块等,一般形式为:

def 函数名([形式参数表]):
函数体(语句块)

例如:定义一个实现平面直角坐标系中求两点距离的函数

def dis(x1, y1, x2, y2):
from math import sqrt
return sqrt((x1-x2)**2+(y1-y2)**2)

该函数接受4个参数:x1,y1x2,y2,返回两点的距离。

一样需要注意的是,函数和判断循环一样严格缩进。

如果你想要先定义一个函数,但还未想好函数体,可以这样写:

def 函数名():
pass

使用pass语句以确保函数在运行程序或调用函数时不会报错。

函数的调用及其返回值

1.函数的调用

我们定义函数之后,在我们需要函数的地方以如下方式调用:

函数名(实际参数表)

调用函数时,和形式参数对应的参数因为有实际值,所以称之为实际参数,简称”实参“。当有多个实参时,实参之间以,分隔。例如:

def dis(x1, y1, x2, y2):
from math import sqrt
return sqrt((x1-x2)**2+(y1-y2)**2)

d = dis(1, 1, 2, 2)
print(d)

【运行结果】

1.4142135623730951

如果调用的是无参数函数,则调用形式为:

函数名()

例如:输入直角三角形的两条直角边长度,求第三条边(勾股定理)

def Pythagoras_theorem():
a,b = eval(input('请输入两条直角边长度: '))
from math import sqrt
return sqrt(a*a+b*b)

c = Pythagoras_theorem()
print(c)

【运行结果】

请输入两条直角边长度: 3,4
5.0
2.函数的返回值

return语句用来退出程序并将程序返回到函数被调用的位置继续执行。return语句可以同时将0个、1个或多个函数运算后的结果返回给函数被调用处的变量,例如:

#交换a,b
def swap(a, b):
return b,a

a = 1
b = 2
a,b = swap(a, b)
print(a,b)

【运行结果】

2 1

如果你输出返回单个变量的函数的类型,会得到对应返回的单变量的数据类型

def dis(x1, y1, x2, y2):
from math import sqrt
return sqrt((x1-x2)**2+(y1-y2)**2)

print(type(dis(1, 1, 2, 2)))

【运行结果】

<class 'float'>

而当函数有多个返回值时,返回的是一个元组

def swap(a, b):
return b, a

a = 1
b = 2
print(type(swap(a, b)))

【运行结果】

<class 'tuple'>

一个函数一旦运行了return语句,即意味着这个函数结束运算,函数内return语句以下的语句块将不被运行。


2.5.2 函数的不同参数调用

我们在使用Python函数时,经常需要传入一些参数来进行函数的运算,从而得到对应的结果。而Python中的函数参数又可以具体分类为以下四种:位置参数,默认参数,关键参数和可变参数。

位置参数

位置参数是函数最基本的参数,指调用函数传递参数时没有任何多余的说明,多个实参会依次按顺序传递给对应的形参。这时实参和形参是按顺序一一对应的,否则会抛出异常。例如:

def function(a, b):
print(a, b, a+b)

function(8, 5)

【运行结果】

8 5 13

可以看到形式参数a对应的是传入的第一个实际参数8,形式参数b对应的是传入的第二个实际参数5。

默认参数

在函数定义的时候,我们可以将一些形式参数设置默认值,这样的参数就是默认参数。在调用函数时,如果不为已经设置了默认值的形参传递实参则使用设置的默认值,如果传递了实参则使用传递的实参。例如:

def function(a, b=100):
print(a, b, a+b)

function(8)
function(8, 5)

【运行结果】

8 100 108
8 5 13

同时需要注意的是,在定义函数形参时,任何带默认值的形参右侧都不能再有不带默认值的位置参数。例如:

def function(a=100, b):		#定义是非法的,会抛出错误
pass

关键参数

关键参数是指,在调用参数时明确指定给哪个形参传递实参,这时实参的顺序可以与形参不对应,而不影响传递的最终结果。例如:

def function(a,b):
print(a, b, a+b)

function(b=5, a=8)

【运行结果】

8 5 13

可变参数

定义函数时需要定义函数的参数个数,通常情况下这些参数个数表示了函数可调用的参数个数上限。但是有时在定义函数时无法得知参数个数的情况,在Python中使用*args*kwargs可以定义可变参数。

1,*args 方式传入

在形参前面加一个星号*,表示可以接收多个位置参数并把它们放到一个元组中。例如:

def function(a,b,*args):
print(a,b)
print(args)

function(1, 2, 3, 4, 5)

【运行结果】

1 2
(3, 4, 5)
2,**kwargs 方式传入

在形参前面加两个星号**,表示可以接受多个关键参数并把它们房贷一个字典中。例如:

def function(a,b,**kwargs):
print(a,b)
print(kwargs)

function(1, 2, x=3, y=4, z=5)

【运行结果】

1 2
{'x': 3, 'y': 4, 'z': 5}

2.5.3 lambda函数—匿名函数

Python允许使用lambda语句创建匿名函数,也就是说函数没有具体的名称。当需要定义一个功能简单但不经常使用的函数来运行代码时,就可以使用lambda定义匿名函数,从而省去定义函数的过程。对于一些抽象的、不会再其他地方重复使用的函数,给函数命名也是一件麻烦的事(需要避免函数重名),而使用lambda语句则不需要考虑函数命名的问题。

lambda语句中,冒号前是函数的参数,多个参数则以逗号隔开;冒号后是返回值而不需要写return语句。lambda语句的基本形式如下:

ans = lambda 参数表 : 返回值

lambda语句有个十分明显的缺点:只能使用一个表达式。多个表达式的函数仍然需要用def定义。

下面以关键字排序为例:

l=[{"age": 20, "name": "张三"}, {"age": 25,"name": "李四"}, {"age": 10,"name": "王五"}]
l=sorted(l, key=lambda x: x["age"])
print(l)

如果不用Lambda表达式,而要写成常规的函数,那么需要这么写:

def comp(x):
return x["age"]
l=[{"age": 20, "name": "张三"}, {"age": 25, "name": "李四"}, {"age": 10, "name": "王五"}]
l=sorted(l, key=comp)
print(l)

以上用匿名函数和常规函数分别实现了对列表内各项对年龄进行关键字排序,可以看到,使用lambda语句使代码显得更加简洁。

此外,匿名函数也是一个函数对象,也可以把匿名函数赋值给一个变量,再利用变量来调用该函数:

f = lambda x: x*x
print(f(5))

【运行结果】

25

同样,也可以把匿名函数作为返回值返回,比如:

def function(x, y):
return lambda: x*x+y*y

2.5.4 变量的作用域

1、作用域介绍

变量的作用域是指在程序中能够对改变量进行读取修改的范围。根据作用域不同,可以将变量分为以下四种:

  • L:local,局部作用域,即函数中定义的变量;

  • E:enclosing,嵌套的父级函数的局部作用域,即包含此函数的上级函数的局部作用域,但不是全局的;

  • G:global,全局变量,就是模块级别定义的变量;

  • B:built-in,系统固定模块里面的变量。

当然,local和enclosing是相对的,enclosing变量相对上层来说也是local。

当不同作用域的变量同名时,优先级由近及远,从函数最内层逐渐向外,即:作用域局部>外层作用域>当前模块中的全局>Python内置作用域,也就是LEGB。例如:

x = int(2.9) # int built-in
g_count = 0 # global

def outer():
o_count = 1 # enclosing
def inner():
i_count = 2 # local

2、作用域产生

在Python中,只有模块(module),类(class)以及函数(def、lambda)才会引入新的作用域,其它的代码块(如if、try、for等)是不会引入新的作用域的,如下代码:

if True:
x = 1
print(x)

【运行结果】

1

if并没有引入一个新的作用域,x仍处在当前作用域中,后面代码可以直接使用。

而调用下一作用域的变量时,运行则会出错,例如:

def test():
x2 = 2
print(x2)

【运行结果】

NameError: name 'x2' is not defined

def、class、lambda是可以引入新作用域的。

3、局部作用域变量的修改

一个不在局部作用域里的变量默认是只读的,如果试图为其绑定一个新的值,Python认为是在当前的局部作用域里创建一个新的变量,也就是说在当前局部作用域中,如果直接使用外部作用域的变量,那么这个变量是只读的,不能修改,如:

count = 10
def outer():
print(count)
count = 100
print(count)
outer()

【运行结果】

UnboundLocalError: local variable 'count' referenced before assignment

这里第一个print中,使用到了外部作用域的count,这样后面count就指外部作用域中的count了,再修改就会报错。 如果没使用过这个变量,而直接赋值,会认为是新定义的变量,此时会覆盖外部作用域中变量,如:

count = 10
def outer():
count = 100
print(count)
print(count)
outer()

【运行结果】

100
10

内部作用域中直接声明了count=100,此后在函数内使用的count都是内部作用域的count,同时外部的count变量并不被改变。也就是说,函数内外各有一个独立的count变量。

4、全局变量和外部作用域变量的修改

global关键字

当内部作用域想修改外部作用域的变量时,就要用到global和nonlocal关键字了,当修改的变量是在全局作用域(global作用域)上的,就要使用global先声明一下,例如:

count = 10
def outer():
global count
print(count)
count = 100
print(count)
outer()
print(count)

【运行结果】

10
100
100
nonlocal关键字

global关键字声明的变量必须在全局作用域上,不能嵌套作用域上,当需要修改嵌套作用域(enclosing作用域,外层非全局作用域)中的变量时,这时就需要使用nonlocal关键字,例如:

def outer():
count = 10
def inner():
nonlocal count
count = 20
print(count)
inner()
print(count)
outer()

【运行结果】

20
20

2.5.5 同一机器人投篮,不同角度与初速度,绘制多条篮球运动轨迹图像的任务

案例 以不同角度与初速度绘制多条篮球运动轨迹

案例描述与分析见前面内容所述,在此不再重述。

案例代码实现:

import math
import numpy as np
import matplotlib.pyplot as plt

def shot_trace(v0, theta): # 运用函数将计算坐标点任务模块化
theta = math.radians(theta) # 将角度改成弧度制
tmax = (v0 * math.sin(theta) + math.sqrt(v0 ** 2 * math.sin(theta) ** 2 + 2 * g * h)) / g
# 计算篮球抛出后滞空时间
t = np.linspace(0, tmax) # 将总时间平均划分,linspace函数默认分割成50等分,产生50个时间点
xt = v0*math.cos(theta) * t
yt = h + v0 * math.sin(theta) * t - 1 / 2 * g * t ** 2
# 对每个时间点计算对应的x值和y值
return xt, yt # 返回xt和yt

h, g = 1.9, 9.8 # 定义常量:机器人的身高h和g
v0_list = [13, 14] # 篮球抛出初速度大小取值
theta_list = [40, 50, 30] # 篮球抛出角度取值
legend_list = [] # 用于存储每组的参数,便于绘制图例

for v0 in v0_list:
for theta in theta_list:
xt, yt = shot_trace(v0, theta) # 引用函数并传入参数初速度和角度,得到xt,yt的数组对象
plt.plot(xt, yt, '-') # 根据xt,yt数组绘图,以实线(-)连接坐标点
legend_list.append((v0, theta)) # 将参数存储

plt.grid(True) # 启用坐标网格线
plt.axis([0, 30, 0, 8]) # 定义x轴范围为[0, 30],y轴范围为[0, 8]
plt.legend(legend_list) # 绘制图例
plt.show() # 在窗口中展示绘制结果

2.5.6 同一机器人,不同角度范围解决定点投篮问题

案例描述:
以上我们求得机器人固定速度和角度投篮的曲线。但对于实现精准投篮尚有不足。现在我们来想想,如果知道篮筐的坐标点和投篮的出球速度,是否能求出投篮的角度?
已知机器人投篮出球速度为10m/s,出手位置高度1.9m,篮筐中心和机器人水平距离相差7.25m(三分线),离地3.05m,直径0.45m。求机器人的出手投球角度范围并据此绘制曲线。
提示,篮球不可以从下往上穿过篮筐

案例代码实现:

import math
import numpy as np
import matplotlib.pyplot as plt

def shot_trace(v0, theta, delta_h): # 运用函数将计算坐标点任务模块化
tmax = (v0 * math.sin(theta) + math.sqrt(v0 ** 2 * math.sin(theta) ** 2 - 2 * g * delta_h)) / g
# 计算篮球抛出后滞空时间
t = np.linspace(0, tmax) # 将总时间平均划分,linspace函数默认分割成50等分,产生50个时间点
xt = v0*math.cos(theta) * t
yt = h + v0 * math.sin(theta) * t - 1 / 2 * g * t ** 2
# 对每个时间点计算对应的x值和y值
return xt, yt # 返回xt和yt

def seek_theta(v0, delta_h, x, r): # 运用函数将寻找角度范围任务模块化
theta_range = [] # 存储合适的角度
for theta in np.arange(0, 90, 0.01): # 每0.01°测验
theta = math.radians(theta) # 将角度改成弧度制
if v0 ** 2 * math.sin(theta) ** 2 - 2 * g * delta_h < 0:
continue # 如果该角度无法到达指定高度,则二次函数无解,直接尝试下一个角度
t = (v0 * math.sin(theta) + math.sqrt(v0 ** 2 * math.sin(theta) ** 2 - 2 * g * delta_h)) / g
# 计算到达篮筐所需时间
if x - r <= v0 * math.cos(theta) * t <= x + r:
theta_range.append(theta) # 如果可以命中篮筐,则存储改角度
return theta_range # 返回可进球的角度范围

v0, h, x, H, r, g = 10, 1.9, 7.25, 3.05, 0.45 / 2, 9.8
# 已知机器人投篮出球速度为10m/s,出手位置高度1.9m,篮筐中心和机器人水平距离相差7.25m(三分线),离地3.05m,直径0.45m。

theta_range = seek_theta(v0, H - h, x, r) # 引用函数求角度范围

for theta in theta_range: # 遍历可用角度并绘图
xt, yt = shot_trace(v0, theta, H-h) # 引用函数并传入参数初速度和角度,得到xt,yt的数组对象
plt.plot(xt, yt, 'r-') # 根据xt,yt数组绘图,以实线(红色-)连接坐标点

plt.grid(True) # 启用坐标网格线
plt.axis([0, 8, 0, 7]) # 定义x轴范围为[0, 8],y轴范围为[0, 7]
plt.show()

2.5.7 本节小结

2.5.8 习题