python基础入门
序列
python 中常用的序列结构有:
字符串、列表、元组、字典、集合
列表简介
列表:用于存储任意数目、任意类型的数据集合。
列表是内置可变序列,是包含多个元素的有序连续的内存空间。列表定义的标准语法格式: a = [10,20,30,40]
其中,10,20,30,40 这些称为:列表 a 的元素。 列表中的元素可以各不相同,可以是任意类型。比如:
a = [10,20,’abc’,True] 列表对象的常用方法汇总如下,方便大家学习和查阅。
方法 | 要点 | 描述 |
---|---|---|
list.append(x) | 增加元素 | 将元素 x 增加到列表 list 尾部 |
list.extend(aList) | 增加元素 | 将列表 alist 所有元素加到列表 list 尾部 |
list.insert(index,x) | 增加元素 | 在列表 list 指定位置 index 处插入元素 x |
list.remove(x) | 删除元素 | 在列表 list 中删除首次出现的指定元素 x |
list.pop([index]) | 删除元素 | 删除并返回列表 list 指定为止 index 处的元素,默认是 最后一个元素 |
list.clear() | 删除所有元素 | 删除列表所有元素,并不是删除列表对象 |
list.index(x) | 访问元素 | 返回第一个 x 的索引位置,若不存在 x 元素抛出异常 |
list.count(x) | 计数 | 返回指定元素 x 在列表 list 中出现的次数 |
len(list) | 列表长度 | 返回列表中包含元素的个数 |
list.reverse() | 翻转列表 | 所有元素原地翻转 |
list.sort() | 排序 | 所有元素原地排序 |
list.copy() | 浅拷贝 | 返回列表对象的浅拷贝 |
Python 的列表大小可变,根据需要随时增加或缩小。
字符串和列表都是序列类型,一个字符串是一个字符序列,一个列表是任何元素的序列。我 们前面学习的很多字符串的方法,在列表中也有类似的用法,几乎一模一样。
列表的创建
基本语法[]创建
list()创建: 使用 list()可以将任何可迭代的数据转化成列表。
1 | >>> a = list("gaoqi,sxt") |
range()创建整数列表
range()可以帮助我们非常方便的创建整数列表,这在开发中及其有用。语法格式为: range([start,] end [,step])
start 参数:可选,表示起始数字。默认是 0
end 参数:必选,表示结尾数字。
step 参数:可选,表示步长,默认为 1
python3 中 range()返回的是一个 range 对象,而不是列表。我们需要通过 list()方法将其 转换成列表对象。
1 | >>> list(range(3,15,2)) |
推导式生成列表
使用列表推导式可以非常方便的创建列表,在开发中经常使用。
1 | a = [x*2 for x in range(100) if x%9==0] #通过 if 过滤元素 |
列表元素的增加和删除
当列表增加和删除元素时,列表会自动进行内存管理,大大减少了程序员的负担。但这 个特点涉及列表元素的大量移动,效率较低。除非必要,我们一般只在列表的尾部添加元素 或删除元素,这会大大提高列表的操作效率。
append()方法
原地修改列表对象,是真正的列表尾部添加新的元素,速度最快,推荐使用。 a.append(80)
+运算符操作
并不是真正的尾部添加元素,而是创建新的列表对象;将原列表的元素和新列表的元素依次 复制到新的列表对象中。这样,会涉及大量的复制操作,对于操作大量元素不建议使用。a = a+[50]
extend()方法
将目标列表的所有元素添加到本列表的尾部,属于原地操作,不创建新的列表对象。
1 | >>> a = [20,40] |
insert()插入元素
使用 insert()方法可以将指定的元素插入到列表对象的任意制定位置。这样会让插入位置后 面所有的元素进行移动,会影响处理速度。涉及大量元素时,尽量避免使用。类似发生这种 移动的函数还有:remove()、pop()、del(),它们在删除非尾部元素时也会发生操作位置后 面元素的移动。
a.insert(2,100)#在index2处加入100
乘法扩展
使用乘法扩展列表,生成一个新列表,新列表元素时原列表元素的多次重复。
1 | b = a*3 |
列表元素的删除 del 删除
删除列表指定位置的元素。del a[1]
pop()方法
pop()删除并返回指定位置元素,如果未指定位置则默认操作列表最后一个元素。
a.pop(1)#返回index 1的元素
remove()方法
删除首次出现的指定元素,若不存在该元素抛出异常。a.remove(20)
列表元素访问和计数
我们可以通过索引直接访问元素。索引的区间在[0, 列表长度-1]这个范围。超过这个范围则 会抛出异常。
index()获得指定元素在列表中首次出现的索引
index()可以获取指定元素首次出现的索引位置。语法是:index(value,[start,[end]])。其中, start 和 end 指定了搜索的范围。
1 | >>> a.index(30,5,7) #从索引位置 5 到 7 这个区间,第一次出现 30 元素的位置 |
count()获得指定元素在列表中出现的次数
count()可以返回指定元素在列表中出现的次数。 a.count(20)
len()返回列表长度
len()返回列表长度,即列表中包含元素的个数。 len(a)
成员资格判断
判断列表中是否存在指定的元素,我们可以使用 count()方法,返回 0 则表示不存在,返回 大于 0 则表示存在。但是,一般我们会使用更加简洁的 in 关键字来判断,直接返回 True 或 False。100 not in a
切片操作
我们在前面学习字符串时,学习过字符串的切片操作,对于列表的切片操作和字符串类似。 切片是 Python 序列及其重要的操作,适用于列表、元组、字符串等等。切片的格式如下:
切片 slice 操作可以让我们快速提取子列表或修改。标准格式为: [起始偏移量 start:终止偏移量 end[:步长 step]]
注:当步长省略时顺便可以省略第二个冒号
操作和说明 示例 结果 [:] 提取整个列表 [10,20,30][:] [10,20,30] [start:]从 start 索引开始到 结尾 [10,20,30][1:] [20,30] [:end]从头开始知道 end-1 [10,20,30][:2] [10,20] [start:end]从 start 到 end-1 [10,20,30,40][1:3] [20,30] [start:end:step] 从 start 提 取到 end-1,步长是 step [10,20,30,40,50,60,70][1:6: 2] [20, 40, 60]
示例 | 说明 | 结果 |
---|---|---|
[10,20,30,40,50,60,70][-3:] | 倒数三个 | [50,60,70] |
10,20,30,40,50,60,70][-5:-3] | 倒数第五个到倒数 第三个(包头不包尾) | [30,40] |
[10,20,30,40,50,60,70][::-1] | 步长为负,从右到左 反向提取 | [70, 60, 50, 40, 30, 20, 10] |
切片操作时,起始偏移量和终止偏移量不在[0,字符串长度-1]这个范围,也不会报错。起始 偏移量小于 0 则会当做 0,终止偏移量大于“长度-1”会被当成”长度-1”
列表的遍历
for obj in listObj: print(obj)
复制列表所有的元素到新列表对象
我们可以通过如下简单方式,实现列表元素内容的复制: list1 = [30,40,50]
list2 = [] + list1
*列表排序 *
修改原列表,不建新列表的排序
1 | >>> a = [20,10,30,40] |
建新列表排序
我们也可以通过内置函数 sorted()进行排序,这个方法返回新列表,不对原列表做修改。
a = sorted(a)
c = sorted(a,reverse=True) #降序
reversed()返回迭代器
内置函数 reversed()也支持进行逆序排列,与列表对象 reverse()方法不同的是,内置函数 reversed()不对原列表做任何修改,只是返回一个逆序排列的迭代器对象。
1 | 20,10,30,40] a = [ |
列表相关的其他内置函数汇总 max 和 min
用于返回列表中最大和最小值。max(a)
sum
对数值型列表的所有元素进行求和操作,对非数值型列表运算则会报错。
元组 tuple
列表属于可变序列,可以任意修改列表中的元素。元组属于不可变序列,不能修改元组中的 元素。因此,元组没有增加元素、修改元素、删除元素相关的方法。
因此,我们只需要学习元组的创建和删除,元组中元素的访问和计数即可。元组支持如 下操作:
\1. 索引访问
\2. 切片操作
\3. 连接操作
\4. 成员关系操作
\5. 比较运算操作
\6. 计数:元组长度 len()、最大值 max()、最小值 min()、求和 sum()等。
元组的创建
\1. 通过()创建元组。小括号可以省略。
如果元组只有一个元素,则必须后面加逗号。这是因为解释器会把(1)解释为整数 1,(1,) 解释为元组。
a = (10,20,30) 或者 a = 10,20,30
\2. 通过 tuple()创建元组 tuple(可迭代的对象)
tuple()可以接收列表、字符串、其他序列类型、迭代器等生成元组。 list()可以接收元组、字符串、其他序列类型、迭代器等生成列表。
元组的元素访问和计数
\1. 元组的元素不能修改
\2. 元组的元素访问和列表一样,只不过返回的仍然是元组对象。
\3. 列表关于排序的方法 list.sorted()是修改原列表对象,元组没有该方法。如果要对元组排 序,只能使用内置函数 sorted(tupleObj),并生成新的列表对象。 sorted(a)
zip
zip(列表 1,列表 2,…)将多个列表对应位置的元素组合成为元组,并返回这个 zip 对象。
1 | >>> a = [10,20,30] |
生成器推导式创建元组
从形式上看,生成器推导式与列表推导式类似,只是生成器推导式使用小括号。列表推 导式直接生成列表对象,生成器推导式生成的不是列表也不是元组,而是一个生成器对象。
我们可以通过生成器对象,转化成列表或者元组。也可以使用生成器对象的next() 方法进行遍历,或者直接作为迭代器对象来使用。不管什么方式使用,元素访问结束后,如 果需要重新访问其中的元素,必须重新创建该生成器对象。
1 | >>> s = (x*2 for x in range(5)) |
元组总结
\1. 元组的核心特点是:不可变序列。
\2. 元组的访问和处理速度比列表快。
\3. 与整数和字符串一样,元组可以作为字典的键,列表则永远不能作为字典的键使用。
字典介绍
字典是“键值对”的无序可变序列,字典中的每个元素都是一个“键值对”,包含:“键 对象”和“值对象”。可以通过“键对象”实现快速获取、删除、更新对应的“值对象”。
列表中我们通过“下标数字”找到对应的对象。字典中通过“键对象”找到对应的“值 对象”。“键”是任意的不可变数据,比如:整数、浮点数、字符串、元组。但是:列表、 字典、集合这些可变对象,不能作为“键”。并且“键”不可重复。
“值”可以是任意的数据,并且可重复。
字典的创建
\1. 我们可以通过{}、dict()来创建字典对象。
\2. 通过 zip()创建字典对象
1 | >>> k = ['name','age','job'] |
\3. 通过 fromkeys 创建值为空的字典
1 | >>> a = dict.fromkeys(['name','age','job']) |
字典元素的访问
\1. 通过 [键] 获得“值”。若键不存在,则抛出异常。 a[‘name’]
\2. 通过 get()方法获得“值”。推荐使用。优点是:指定键不存在,返回 None;也可以设 定指定键不存在时默认返回的对象。推荐使用 get()获取“值对象”。a.get(‘name’)
\3. 列出所有的键值对
1 | >>> a.items() |
\4. 列出所有的键,列出所有的值
1 | >>> a.keys() |
\5. len() 键值对的个数
\6. 检测一个“键”是否在字典中 用in
字典元素添加、修改、删除
\1. 给字典新增“键值对”。如果“键”已经存在,则覆盖旧的键值对;如果“键”不存在, 则新增“键值对”。a[‘age’]=16
\2. 使用 update()将新字典中所有键值对全部添加到旧字典对象上。如果 key 有重复,则直 接覆盖。
1 | >>> a = {'name':'gaoqi','age':18,'job':'programmer'} |
\3. 字典中元素的删除,可以使用 del()方法;或者 clear()删除所有键值对;pop()删除指定 键值对,并返回对应的“值对象”;
1 | >>> del(a['name']) |
\4. popitem() :随机删除和返回该键值对。字典是“无序可变序列”,因此没有第一个元 素、最后一个元素的概念;popitem 弹出随机的项,因为字典并没有”最后的元素”或者其 他有关顺序的概念。若想一个接一个地移除并处理项,这个方法就非常有效(因为不用首先获取键的列表)。
a.popitem()
序列解包
序列解包可以用于元组、列表、字典。序列解包可以让我们方便的对多个变量赋值。
1 | >>> x,y,z=(20,30,10) |
序列解包用于字典时,默认是对“键”进行操作; 如果需要对键值对操作,则需要使用 items();如果需要对“值”进行操作,则需要使用 values();
1 | >>> s = {'name':'gaoqi','age':18,'job':'teacher'} |
字典核心底层原理(重要)
字典对象的核心是散列表。散列表是一个稀疏数组(总是有空白元素的数组),数组的 每个单元叫做 bucket。每个 bucket 有两部分:一个是键对象的引用,一个是值对象的引 用。
由于,所有 bucket 结构和大小一致,我们可以通过偏移量来读取指定 bucket。
扩容
python 会根据散列表的拥挤程度扩容。“扩容”指的是:创造更大的数组,将原有内容 拷贝到新数组中。
接近 2/3 时,数组就会扩容。
用法总结:
\1. 键必须可散列
(1) 数字、字符串、元组,都是可散列的。
(2) 自定义对象需要支持下面三点:
- 1 支持 hash()函数
- 2 支持通过eq()方法检测相等性。
- 3 若 a==b 为真,则 hash(a)==hash(b)也为真。
\2. 字典在内存中开销巨大,典型的空间换时间。
\3. 键查询速度很快
\4. 往字典里面添加新建可能导致扩容,导致散列表中键的次序变化。因此,不要在遍历字 典的同时进行字典的修改。
集合
集合创建和删除
\1. 使用{}创建集合对象,并使用 add()方法添加元素
\2. 使用 set(),将列表、元组等可迭代对象转成集合。如果原来数据存在重复数据,则只保 留一个。
\3. remove()删除指定元素;clear()清空整个集合
集合相关操作
1 | >>> a = {1,3,'sxt'} |
控制语句
三元条件运算符
Python 提供了三元运算符,用来在某些简单双分支赋值情况。三元条件运算符语法格式如 下:
条件为真时的值 if (条件表达式) else 条件为假时的值
1 | print( num if int(num)<10 else "数字太大") |
可迭代对象
Python 包含以下几种可迭代对象:
- 序列。包含:字符串、列表、元组
- 字典
- . 迭代器对象(iterator)
\4. 生成器函数(generator)
\5. 文件对象
1 |
|
1 | 【操作】利用嵌套循环打印九九乘法表 |
1 | for m in range(1,10): |
使用 zip()并行迭代
我们可以通过 zip()函数对多个序列进行并行迭代,zip()函数在最短序列“用完”时就会停止。
1 | names = ("高淇","高老二","高老三","高老四") |
推导式创建序列
推导式是从一个或者多个迭代器快速创建序列的一种方法。它可以将循环和条件判断结合, 从而避免冗长的代码。推导式是典型的 Python 风格,会使用它代表你已经超过 Python 初 学者的水平。
列表推导式
列表推导式生成列表对象,语法如下:
[表达式 for item in 可迭代对象 ]
或者:{表达式 for item in 可迭代对象 if 条件判断}
1 |
|
字典推导式
row in range(1,10) for col in range(1,10)]
#可以使用两
字典的推导式生成字典对象,格式如下:
{key_expression : value_expression for 表达式 in 可迭代对象}
类似于列表推导式,字典推导也可以增加 if 条件判断、多个 for 循环。
统计文本中字符出现的次数:
1 | \>>> my_text = ' i love you, i love sxt, i love gaoqi' |
集合推导式
集合推导式生成集合,和列表推导式的语法格式类似:
{表达式 for item in 可迭代对象 }
或者:{表达式 for item in 可迭代对象 if 条件判断}
>>> {x for x in range(1,100) if x%9==0} {99, 36, 72, 9, 45, 81, 18, 54, 90, 27, 63}
生成器推导式(生成元组)
很多同学可能会问:“都有推导式,元组有没有?”,能不能用小括号呢?
(x for x in range(1,100) if x%9==0)
<generator object
我们发现提示的是“一个生成器对象”。显然,元组是没有推导式的。
一个生成器只能运行一次。第一次迭代可以得到数据,第二次迭代发现数据已经没有了。
1 | >>> gnt = (x for x in range(1,100) if x%9==0) |
函数
Python 中函数分为如下几类: 1. 内置函数
我们前面使用的 str()、list()、len()等这些都是内置函数,我们可以拿来直接使用。
- 标准库函数
我们可以通过 import 语句导入库,然后使用其中定义的函数
- 第三方库函数
Python 社区也提供了很多高质量的库。下载安装这些库后,也是通过 import 语句导 入,然后可以使用这些第三方库的函数
- 用户自定义函数
用户自己定义的函数,显然也是开发中适应用户自身需求定义的函数。今天我们学习的 就是如何自定义函数。
文档字符串(函数的注释)
程序的可读性最重要,一般建议在函数体开始的部分附上函数定义说明,这就是“文档字符 串”,也有人成为“函数的注释”。我们通过三个单引号或者三个双引号来实现,中间可以
北京尚学堂·百战程序员 实战 系统 好教育
加入多行文字进行说明。
函数也是对象,内存底层分析
Python 中,“一切都是对象”。实际上,执行 def 定义函数后,系统就创建了相应的函数 对象。
变量的作用域(全局变量和局部变量)
变量起作用的范围称为变量的作用域,不同作用域内同名变量之间互不影响。变量分为:全局变量、局部变量。
全局变量:
\1. 在函数和类定义之外声明的变量。作用域为定义的模块,从定义位置开始直到模块 结束。
- 全局变量降低了函数的通用性和可读性。应尽量避免全局变量的使用。
- 全局变量一般做常量使用。
- 函数内要改变全局变量的值,使用 global 声明一下
局部变量:
\1. 在函数体中(包含形式参数)声明的变量。
\2. 局部变量的引用比全局变量快,优先考虑使用。
\3. 如果局部变量和全局变量同名,则在函数内隐藏全局变量,只使用同名的局部变量
1 | a = 100 #全局变量 |
另一种输出,因为local变global后没有了
1 | a = 100 #全局变量 |
局部变量和全局变量效率测试
局部变量的查询和访问速度比全局变量快,优先考虑使用,尤其是在循环的时候。 在特别强调效率的地方或者循环次数较多的地方,可以通过将全局变量转为局部变量提高运 行速度。
参数的传递
函数的参数传递本质上就是:从实参到形参的赋值操作。 Python 中“一切皆对象”, 所有的赋值操作都是“引用的赋值”。所以,Python 中参数的传递都是“引用传递”,不 是“值传递”。具体操作时分为两类:
\1. 对“可变对象”进行“写操作”,直接作用于原对象本身。
\2. 对“不可变对象”进行“写操作”,会产生一个新的“对象空间”,并用新的值填 充这块空间。(起到其他语言的“值传递”效果,但不是“值传递”)
可变对象有: 字典、列表、集合、自定义的对象等
不可变对象有: 数字、字符串、元组、function 等
浅拷贝和深拷贝
为了更深入的了解参数传递的底层原理,我们需要讲解一下“浅拷贝和深拷贝”。我们可以 使用内置函数:copy(浅拷贝)、deepcopy(深拷贝)。
浅拷贝:不拷贝子对象的内容,只是拷贝子对象的引用。 深拷贝:会连子对象的内存也全部拷贝一份,对子对象的修改不会影响源对象
1 | import copy |
1 | 结果 |
传递不可变对象包含的子对象是可变的情况
#传递不可变对象时。不可变对象里面包含的子对象是可变的。则 方法内修改了这个可变对象,源对象也发生了变化。
1 | a = (10,20,[5,6]) |
id相对于c中地址符&
*参数的几种类型 *
位置参数
函数调用时,实参默认按位置顺序传递,需要个数和形参匹配。按位置传递的参数,称为: “位置参数”。
默认值参数
我们可以为某些参数设置默认值,这样这些参数在传递时就是可选的。称为“默认值参数”。 默认值参数放到位置参数后面。def f1(a,b,c=10,d=20): #默认值参数必须位于普通位置参数后面
命名参数
我们也可以按照形参的名称传递参数,称为“命名参数”,也称“关键字参数”。f1(8,9,19) #位置参数 f1(c=10,a=20,b=30) #命名参数
强制命名参数
在带星号的“可变参数”后面增加新的参数,必须在调用的时候“强制命名参数”。
1 | def f1(*a,b,c): |
lambda 表达式和匿名函数
lambda 表达式可以用来声明匿名函数。lambda 函数是一种简单的、在同一行中定义函数 的方法。lambda 函数实际生成了一个函数对象。
lambda 表达式只允许包含一个表达式,不能包含复杂语句,该表达式的计算结果就是函数 的返回值。
lambda 表达式的基本语法如下:
lambda arg1,arg2,arg3… : <表达式>
arg1/arg2/arg3 为函数的参数。<表达式>相当于函数体。运算结果是:表达式的运算结果。
g = [lambda a:a2,lambda b:b3,lambda c:c*4]
eval()函数
功能:将字符串 str 当成有效的表达式来求值并返回计算结果。
语法: eval(source[, globals[, locals]]) -> value
参数:
source:一个 Python 表达式或函数 compile()返回的代码对象 globals:可选。必须是 dictionary locals:可选。任意映射对象
1 | dict1 = dict(a=100,b=200) |
递归函数
递归函数指的是:自己调用自己的函数,在函数体内部直接或间接的自己调用自己。递归类 似于大家中学数学学习过的“数学归纳法”。 每个递归函数必须包含两个部分:
\1. 终止条件
表示递归什么时候结束。一般用于返回值,不再调用自己。 2. 递归步骤
把第 n 步的值和第 n-1 步相关联。
递归函数由于会创建大量的函数对象、过量的消耗内存和运算能力。在处理大量数据时,谨 慎使用。
嵌套函数(内部函数)
嵌套函数: 在函数内部定义的函数!
一般在什么情况下使用嵌套函数?
- 封装 - 数据隐藏外部无法访问“嵌套函数”。
- 贯彻 DRY(Don’t Repeat Yourself) 原则嵌套函数,可以让我们在函数内部避免重复代码。
- 闭包
nonlocal 关键字
nonlocal 用来声明外层的局部变量。 global 用来声明全局变量。
1 | a=100 |
LEGB 规则
Python 在查找“名称”时,是按照 LEGB 规则查找的: Local–>Enclosed–>Global–>Built in
Local 指的就是函数或者类的方法内部
Enclosed 指的是嵌套函数(一个函数包裹另一个函数,闭包) Global 指的是模块中的全局变量
Built in 指的是 Python 为自己保留的特殊名称。
如果某个 name 映射在局部(local)命名空间中没有找到,接下来就会在闭包作用域 (enclosed)进行搜索,如果闭包作用域也没有找到,Python 就会到全局(global)命名空 间中进行查找,最后会在内建(built-in)命名空间搜索 (如果一个名称在所有命名空间 中都没有找到,就会产生一个 NameError)。