Python简介与基础
Python控制结构
组合数据类型
综述
组合数据类型更能够将多个同类或不同类型组织起来,通过单一的表示使数据更有序、更容易。根据数据之间的关系,组合数据类型可以分为3类:序列类型、集合类型和映射类型。
- 序列类型是一个元素向量,元素之间的存在先后关系,通过序号访问,元素之间不排他。
- 集合类型是一个元素类型,元素之间无序,相同元素在集合中唯一存在。
- 映射类型是键值数据项的组合,每个元素是一个键值对,没有顺序,键唯一存在。
附:序列类型和映射类型是一类数据类型的总称,不是具体类型。
序列类型
序列类型通用函数
序列类型(元组,字符串,列表都可以使用以下操作)
操作符 | 运算 |
---|---|
x (not) in s |
是否在内,输出正误 |
s + t |
连接st,不是按索引求和 |
s * t (t * s) |
重复 |
s[i] |
索引 |
s[i:j:k] |
切片 |
len(s) |
求长 |
min/max(s) |
求最值(str比ASCII) |
sum(s) |
求和 |
s.index(x) |
x第一次出现的索引 |
s.count(x) |
x出现总数 |
sorted(iter, key, reverse) |
返回新的排序后序列类型 |
reversed() |
返回倒序后迭代器,建议再套个list() |
enumerate(A[, start=0]) |
返回迭代器,套list() 后形成索引与值的元组列表 |
a = [1,2,3,4] b=a[::-1]
这是切片,切片是得到了一个新的列表,属于浅拷贝,对a修改不影响b,反过来也是。
字符串
字符串是不可变类型,所以字符串方法不改变原字符串,而是生成新副本。
函数和处理方法 | 描述 |
---|---|
str(x) |
返回任意类型对应字符串形式 |
chr(x) |
十六进制/十进制Unicode转单字符 |
ord(x) |
单字符转Unicode,得到十进制数 |
hex(x) |
返回整数x对应16进制数小写形式字符串 |
oct(x) |
返回整数x对应8进制数小写形式字符串 |
bin(x) |
返回整数x对应2进制数小写形式字符串 |
str.lower() |
返回副本,全小写 |
str.upper() |
返回副本,全大写 |
str.split(sep=None) |
返回列表,str根据sep分隔,默认空格 |
str.count(s) |
数数 |
str.replace(old, new) |
返回副本,old都被改为new |
str.center(width, fillchar) |
宽度不够就输出全部,居中,fillchar默认空格(可修改为单字符) |
str.strip(chars) |
去掉str两侧chars列出的字符 |
str.join(iter) |
将iter(主体,一种组合数据类型)变量的 每一个元素后增加一个str字符串(客体) |
a=' '.join(['1','2','3']) #a = '1 2 3'
a=' '.join([1,2,3]) #出错
数值类型转换
#int(x)
#str类int/int/float => int
int('12') #输出12,支持str类int
int(12.34) #输出12,支持float
int('101',2) #输出5,进制转换必须str类int
int('12.34') #出错
int('12g') #出错
int(12g) #出错
int(101,2) #出错
int('101.5',2)#出错
#float(x)
#int/str类 => float
float(12)
float('12')
float(12.0)
float('12.0') #都输出12.0
#str(x)
#int/float => str类
str(10.01) #输出'10.01'
str(1010+101) #输出'1111'
str('1010'+'101') #输出'1010101'
列表
是有序序列,但内部可以更改,列表无长度限制,元素类型可以不同,不需要预先定义长度。
列表创建方法如下:
a=[1010,'1010',[1010,'1010']]
a=list('由字符串生成一个字一个元素')
a=list() #空列表
由于类型可变
就有
ls[i]=k
在原列表修改某个元素
ls[i:j:k]=lt
在原列表一部分改为新列表
ls=[i for i in range(6)]
ls[1:5:2]=['a','b'] #如果个数不对会出错
print(ls) #输出[0, 'a', 2, 'b', 4, 5, 6]
del [i]/[i:j:k]
删除一部分
当然也有len(ls)
ls.index(x)
ls.count(x)
x (not) in ls
+/*
min/max(x)
list(x)
等方法操作
list(x)
将变量x转化为列表类型,可以是字符串、集合也可以是字典(但是形成的是键的列表)
列表的特殊函数与操作
由于列表可变,几乎所有方法不需要ls=ls.fn(x)
,因为原列表已经被改变。
如果写成l(或者是另外的a变量)=ls.fn(x)
,只会得到None
直接ls.fn(x)
才是对的。
函数和处理方法 | 描述 |
---|---|
ls.append(x) |
在最后加元素 |
ls.insert(i, x) |
在第i位置加元素x |
ls.clear() |
清空 |
ls.pop(i) |
取出并返回第i位置元素并删除,默认最后一个元素 |
ls.remove(x) |
删除第一次出现的元素x,不返回 |
ls.reverse() |
元素反转(类比序列方法reversed() ) |
ls.sort(reverse=False, key=None) |
列表排序(类比序列方法sorted() ) |
str.split(sep=None) |
返回列表,str根据sep分隔,默认空格 |
str.count(s) |
数数 |
ls.copy() |
例外,生成新列表,浅拷贝 |
元组
不可更改的序列类型(但是元组中可变的元素仍然是可以进行修改的,做字典的键的元组不能含可变的列表、字典、集合),元组可以实现的操作都可以由列表完成,如果不需要修改数据可以把列表换成元组类型,python对元组处理效率高。
元组创建方法如下:
a=(1, 2, 3)
a=1, 2, 3
a=(1, )
a=1,
a=tuple()
a=()
#以下错误,括号只能放一个序列类型
a=tuple(1, 2, 3)
#以下正确
a=tuple([1, 2, 3]) #a=[1, 2, 3]
a=tuple({2:3, 4:5}) #a=[2, 4]
当函数返回多个值,多个返回值以元组类型返回,实际上是返回一个数据类型。
def f(x):
return x, x+1, x+2
print(type(f(1))) #输出<calss 'tuple'>
集合
集合中的元素不可重复,元素类型只能是不可变数据类型(整数,浮点数,字符串,元组等),不能是列表,字典,集合。
集合可以进行数据去重操作。
集合元素间不能比较和排序。
空集合用set()表示,对比{}表示空字典
操作符 | 运算 |
---|---|
S-T |
差集 |
S&T |
交集 |
S|T |
并集 |
S^T |
对称差分 |
可以使用S-=T
进行增强操作,运算的同时更新S集合
集合类型常用的函数或方法
函数或方法 | 描述 |
---|---|
S.add(x) |
把x加到S中(类比列表L.append(x) ) |
S.remove(x) |
移除x,不存在会产生KeyError异常 |
S.discard(x) |
移除x,不存在也不报错(与上对比) |
S.clear() |
移除所有数据但不去除集合本身 |
S.pop() |
随即返回并删除这个元素来更新S,如果s为set()会产生异常 |
其他也有len(S)
x (not) in S
S.copy()
(浅拷贝)等
字典
不可变组合类型、基本数据类型可以作为键,元组在各个元素不可变时可做键,列表、集合和其他字典不可做键。
对字典for i in d
遍历时,i是字典的每一个键
字典函数和方法
操作符 | 运算 |
---|---|
d.keys() |
返回所有键 |
d.values() |
返回所有值 |
d.items() |
返回所有键值对 |
d.get(a key, default=) |
键存在返回相应值, 否则返回默认 增加新键值对 |
d.pop(a key, default=) |
键存在返回相应值 并删除键值对 否则返回默认 |
d.popitem() |
随机取出键值对,以元组返回并删除键值对 |
d.clear() |
成为空字典 |
del d[x] |
删除以x为键的键值对 |
注意:keys,values,items方法返回的是特殊的内部数据类型,想更好的使用返回数据最好利用list()
转化成列表,键值对以元组类型储存在列表里。
字典与序列类型类似的通用操作
in
判断一个键是否在字典中
len(d)
字典键值对数量
min/max(b)
返回最小最大的索引值(前提是字典中各个索引元素可以比较)
字典的索引
print(d[x]) #输出x对应的键
d[x] = 1 #修改字典键值对,也可以增加新键值对
字典创建方法如下:
a={'1':2, '3':4}
a=dict()
a={}
#错误方法
a={(1,2), (3,4)} #创建的是含两个元组的集合
#正确方法可使用dict(),这个括号里只能有一个大类,大类里有小类,每个小类两个元素(长为2)
a=dict(([1,2], [3,4]))
a=dict([((1,2),2), ((3,),4)])
深拷贝与浅拷贝
python中,变量与变量的值占用不同的内存。变量占用的内存,并非直接存储数值,而存储的是值在内存中的地址。
ls = ['中国','矿业','大学']
ls1 = ls #ls1 = ls 实际上是ls1指向了ls指向的地址块,两者指向的是同一块地址区域,不是拷贝
print(ls1) #输出ls = ['中国','矿业','大学']
ls.append('憨憨') #对ls进行元素操作时,也是对ls1造成影响
print(ls1) #输出['中国', '矿业', '大学', '憨憨']
ls2 = ls[:] #利用切片,在内存中新建了一个列表,ls2指向这个新的列表,浅拷贝
print(ls2) #输出ls = ['中国','矿业','大学', '憨憨']
ls.append('憨包') #对ls的元素进行任何操作,都不会影响ls2的内容
print(ls2) #输出ls = ['中国','矿业','大学', '憨憨']
a = 1 b= a
这不是拷贝,赋值是多个变量指向同一个内存地址,内存的任一元素改变,所有变量都受影响
a = [1,2,3,4] b=a[:]
这是切片,切片是得到了一个新的列表,属于浅拷贝
浅拷贝x.copy()
对只有一层元素关系的列表进行测试:
ls = ['python','c','cpp']
ls1 = ls.copy()
print(id(ls),id(ls1)) #输出 2551633171528 2551633985416,可见二者指向的内存地址不同
ls.append('php')
ls1.append('java')
print(ls) #输出 ['python', 'c', 'cpp', 'php'],对ls的append操作没有影响ls1
print(ls1) #输出 ['python', 'c', 'cpp', 'java'],对ls1的append操作没有影响ls
从以上结果可得:在copy完之后,新的列表指向新的内存地址,分别对列表的最后一位进行appen操作,结果互不影响。
对具有二层的元素的列表进行测试:
##对ls的第二个元素进行append操作
ls = ['德玛',['费欧娜','戴安娜'],'赵信']
ls1 = ls.copy()
print(ls1) #输出['德玛', ['费欧娜', '戴安娜'], '赵信']
ls[1].append('莫甘娜') #copy完成后对ls的第二个元素进行append操作
print(ls1) #输出'德玛', ['费欧娜', '戴安娜', '莫甘娜'], '赵信'],ls1发生了改变
print(id(ls[1]),id(ls1[1])) #查看两者的ID,2202073060424 2202073060424
##对ls1的第二个元素进行操作
ls = ['德玛',['费欧娜','戴安娜'],'赵信']
ls1 = ls.copy()
print(ls) #输出['德玛', ['费欧娜', '戴安娜'], '赵信']
ls1[1].append('莫甘娜') #copy完成后对ls1的第二个元素进行append操作
print(ls) #输出'德玛', ['费欧娜', '戴安娜', '莫甘娜'], '赵信'],ls发生了改变
print(id(ls[1]),id(ls1[1])) #输出 2303581443144 2303581443144
从上边输出的结果来看,copy完成之后,分别对ls和ls1的中间元素进行append操作,对方的元素也会发生相应的改变。
结论:浅拷⻉, 只会拷⻉第⼀层内容,只拷贝第二层关系的内存地址,而第⼆层的内容不会拷贝,所以被称为浅拷贝。
深拷贝x.deepcopy()
深拷贝使用之前一定要先加载copy模块
import copy
new_ls = copy.deepcopy(ls)
实例:
#对ls进行二层操作
import copy
ls = ['德玛',['费欧娜','戴安娜'],'赵信']
ls1 = copy.deepcopy(ls)
ls[1].append('莫甘娜')
print(ls) #输出['德玛', ['费欧娜', '戴安娜', '莫甘娜'], '赵信']
print(ls1) #输出['德玛', ['费欧娜', '戴安娜'], '赵信']
#对ls1进行二层操作
import copy
ls = ['德玛',['费欧娜','戴安娜'],'赵信']
ls1 = copy.deepcopy(ls)
ls1[1].append('莫甘娜')
print(ls) #输出['德玛', ['费欧娜', '戴安娜'], '赵信']
print(ls1) #输出['德玛', ['费欧娜', '戴安娜', '莫甘娜'], '赵信']
由以上结果可得:深拷贝是完完全全的复制,是创建一个完完全全的新的对象,新旧对象之间不会相互影响。不可变数据类型新旧列表指向同一个空间,可变类型新列表创建了新的空间。
另附:
a = [1, 2]
a[1] = a #对a列表的第二个元素进行修改成自己
print(a[1]) #输出为[1, [...]]
print(a) #输出为[1, [...]]
print(id(a),id(a[1])) #输出内存地址1191853053000 1191853053000 a与a[1]的内存地址一样
print(type(a[1])) ##输出<class 'list'>
分析: a[1] 原来是int类型,在对a[1]进行修改操作时,会先把原来的指向剪断,因为赋值a,指向了自己,此时出现了数据上的死循环。
函数
参数
参数及其传递
#向函数传递信息
def greet_user(username): #username是形参
print("hello," + username + "!" )
greet_user('python') #'python'是实参
在函数名中定义一个形参,函数调用时,给函数名中的形参传递实参。
传递实参的方法有:
- 位置实参(按照基于实参的顺序关联到形参,有顺序)
- 关键字参数(类似按照键值对,按照名字进行关联,无顺序问题)
- 默认值参数(给形参进行固定值作为默认值,如果没有提供实参对默认值进行覆盖处理,则代入默认数值后运行函数)
注意: - 两种传参方式可以混合使用,但使用时关键字参数必须位于位置参数之后
可变参数*args与**kwargs
def f(*args, **kwargs):
print('args = ', args)
print('kwargs = ', kwargs)
print()
if __name__ == '__main__':
f(1,2,3,4)
f(a=1,b=2,c=3)
f(1,2,3,4, a=1,b=2,c=3)
f('a', 1, None, a=1, b='2', c=3)
输出结果是
args = (1, 2, 3, 4)
kwargs = {}
args = ()
kwargs = {'a': 1, 'c': 3, 'b': 2}
args = (1, 2, 3, 4)
kwargs = {'a': 1, 'c': 3, 'b': 2}
args = ('a', 1, None)
kwargs = {'a': 1, 'c': 3, 'b': '2'}
这是python中的两种可变参数。
*args
表示任何多个无名参数,它是一个tuple
**kwargs
表示关键字参数,它是一个dict
- 同时使用
*args
和**kwargs
时,*args
参数必须在**kwargs
前,像f(a=1, b='2', c=3, a', 1, None)
这样调用的话,会提示语法错误SyntaxError: non-keyword arg after keyword arg
等效的函数调用
函数调用时,无论是使用位置参数还是关键字参数、默认值参数,得到的结果都是一样的。
#一个名为harry的小狗
def describe_pet(pet_name,animal_type = 'dog'):
函数省略
describe_pet(pet_name="harry")
describe_pet("harry")
两次使用describe_pet()
函数的结果一致
全局变量和局部变量
- 这是两种不同的变量
- 局部变量是函数内部的占位符,与全局变量可能重名但是不同
- 函数运算结束后,局部变量被释放,不再进行储存
- 可以使用
global
保留字,从而在函数内部使用全局变量
- 局部变量为组合数据类型且未创建,等同于全局变量
ls=['x', 'y'] #真实创建了一个ls全局变量列表
def f(n):
ls.append(n) #ls是列表类型,没有真实创建,等同于全局变量
return #加不加return都是一样的
f('z') #全局变量被修改
print(ls)
以上输出为['x', 'y', 'z']
ls=['x', 'y'] #真实创建了一个ls全局变量列表
def f(n):
ls = [] #ls是列表类型,真实创建,在这里ls是局部变量
ls.append(n)
return #加不加return都是一样的
f('z')
print(ls)
以上输出为['x', 'y']
规则总结
- 基本数据类型,无论是否重名,局部变量与全局变量不同
- 可以通过
global
保留字在函数内部声明全局变量 - 组合数据类型,如果局部变量未真实创建,则是全局变量
另附:
return
在不带参数的情况下(或者没有写),默认返回None