根据廖雪峰大佬的教程提炼的精简版,用来在忘记某些东西的时候快速查找
本文基于python3.7
python基础
数据类型
- 整数:整数计算永远是精确的
- 浮点数:用科学计数法表示很大或很小的数,如1.23e9,或者12.3e8,0.000012可以写成1.2e-5,等等。
- 字符串:用单引号或双引号括起来的任意文本,比如’abc’,"xyz"等等。
如果字符串内部既包含’又包含"可以用转义字符\来标识:
'I\'m \"OK\"!'
//表示的字符串内容是:
I'm "OK"!
转义字符表
字符 | 含义 |
---|---|
\n | 换行 |
\t | 制表符 |
\ | \ |
用r’ ‘表示’ '内部的字符串不转义
>>> print('\\\t\\')
\ \
>>> print(r'\\\t\\')
\\\t\\
用’’’…’’'的格式表示多行内容
>>> print('''line1
... line2
... line3''')
line1
line2
line3
…是提示符 不是字符串中的一部分在python交互模式时,输完第一行跳到第二行时会自动出现…提示你接着输入
如果要保存为.py文件则在文件中输入:
print('''line1
line2
line3''')
示例:
- 布尔值:只有True或者False(注意大小写)
布尔值可以用and、or和not计算 - 空值:None。None不等于0,0是由意义的,而None是一个特殊的空值
- 变量:变量名只能是大小写字母、数字和_的组合,且不能用数字开头。
python中的=号是赋值语句,可以把任意数据类型赋给变量,同一个变量可以反复赋值,甚至可以是不同类型的变量。
变量本身类型不固定的语言称为动态语言,而静态语言在定义变量时必须指定变量类型。Java就是静态语言。
当我们写
a=‘ABC’
python解释器干了两件事:
- 在内存中创建了一个’ABC’字符串;
- 在内存中创建了一个名为a的变量,并把它指向’ABC’
如果此时令b=a,则把变量b指向变量a指向的数据
上图所示代码将会输出ABC,因为b指向的是’ABC‘,而a后面指向’XYZ’了,并不会影响b
- 常量:常量就是不能变的量,在python中用全部大写的变量名表示常量,如PI=3.14159265359,但其实PI仍然是一个变量,如果非要改变PI的值,是可以的。
在python中有两种除法
- “/”:它的计算结果永远是浮点数,就算是两个整数恰好可以整除,其结果也会表示为一个浮点数
- “//”:地板除,整数的地板除只取结果的整数部分,因此其结果永远是一个整数。
python中用"%"取余,整数取余的结果也永远是整数
字符串和编码
- 字符编码
ASCII编码是1个字节,而Unicode编码通常是2个字节。(一个字节8bit)
UTF-8编码是可变长编码,常用的英文字母是一个字节,汉字通常是3个字节,生僻字符编程4-6个字节
字符 | ASCII | Unicode | UTF-8 |
---|---|---|---|
A | 01000001 | 00000000 01000001 | 01000001 |
中 | x | 01001110 00101101 | 11100100 10111000 10101101 |
ASCII编码实际上可以被看成是UTF-8编码的一部分,大量只支持ASCII编码的历史遗留软件可以在UTF-8编码下继续工作。
在计算机内存中,统一使用Unicode编码,当需要保存到硬盘或者需要传输的时候,就转换为UTF-8编码。
- 字符串
python3中字符串是以Unicode编码的,因此字符串支持多种语言
- ord()函数获取字符的整数表示:
- chr()函数把编码转换为对应字符:
如果知道字符的整数编码,还可以用十六进制这么写:
由于Python的字符串类型是str,在内存中以Unicode表示,一个字符对应若干个字节。如果要在网络上传输,或者保存到磁盘上,就需要把str变为以字节为单位的bytes。
Python对bytes类型的数据用带b前缀的单引号或双引号表示: x=b’ABC’
'ABC’和b’ABC’的区别在于前者是字符串,而后者是bytes,bytes的每个字符都只占一个字节
encode()函数
以Unicode表示的str通过encode()方法可以编码为指定的bytes:
如果将中文用ascii编码,则python会报错,因为中文编码的范围超过了ascii编码的范围
在bytes中,无法显示为ascii字符的字节用\x##显示
decode()函数
把bytes变为str用decode()方法
如果bytes中有无法解码的字符,python会报错,如果只有一小部分无效的字节,可以用errors='ignore’忽略错误的字节:
len()函数
用len()函数计算str包含多少个字符
如果是bytes就计算字节数
为了让解释器在读取源代码时按UTF-8读取,通常在文件开头写上
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
第一行注释是为了告诉Linux/OS X系统,这是一个Python可执行程序,Windows系统会忽略这个注释;
第二行注释是为了告诉Python解释器,按照UTF-8编码读取源代码,否则,你在源代码中写的中文输出可能会有乱码。
申明了UTF-8编码并不意味着你的.py文件就是UTF-8编码的,必须并且要确保文本编辑器正在使用UTF-8 without BOM编码
- 格式化字符串
%运算符就是用来格式化字符串的。在字符串内部,%s表示用字符串替换,%d表示用整数替换,有几个%?占位符,后面就跟几个变量或者值,顺序要对应好。如果只有一个%?,括号可以省略。
常见占位符
占位符 | 替换内容 |
---|---|
%d | 整数 |
%f | 浮点数 |
%s | 字符串 |
%x | 十六进制整数 |
整数和浮点数还可以指定是否补0和整数与小数的位数
list
- list是一种有序集合,可以随时添加和删除元素
- 可以用len()获得list中元素的个数
- 用索引访问list中每个位置的元素(索引从0开始)
- 索引越界的话会报错
- 用-1做索引可以直接获取最后一个元素,以此类推-2可以获取倒数第二个等等,同样的这种方式也要注意不能越界
- 用append()函数追加元素到末尾
- 用insert()函数把元素插入到指定位置
- 删除末尾元素用pop()(删除指定位置用pop(i),i是索引)
- 要替换某个元素,直接赋值给对应索引位置如:
- list里面的元素类型可以不同
- list的元素可以是另一个list,如
要注意list的长度为4
s可以看作是一个二维数组,要得到’php’可以用s[2][1],类似的还有三维、四位等 - 如果list中一个元素都没有,那它就是空的,长度为0
tuple
tuple与list类似,但是tuple一旦初始化就不能修改
tuple没有insert()和append()等方法,只能使用索引查询元素
tuple更安全,如果有可能,能用tuple就用tuple
tuple的陷阱:如果定义只有一个元素的tuple,必须在元素后面加逗号,如:
这样是为了消除歧义,如果不加逗号,会以为是数学上的括号,因此会认为t这个变量表示的是1这个整数,由图可见python在显示只有一个元素的tuple时也会加上一个逗号,以免你产生误会
tuple中的元素不变指的是指向不变,如:
虽然表面上看起来变了,其实tuple指向的仍然还是那个list,只是list内的值变了而已,tuple的只想并没有改变
条件判断
用if语句实现条件判断
根据python的缩进原则,如果if为真则执行缩进的语句
注意if和else后面都有冒号
还可以使用elif
if判断条件可以简写:
只要x非零非空就判断为true
input返回的类型为str,如果直接把input传入的值和整数比较会报错,因此要用int()转化:
if语句的完整形式:
循环
-
for…in循环:
range(n)函数可以生成从0到n-1的整数序列
结果为10 -
while循环
只要条件满足就一直循环
-
break
break提前退出循环 -
continue
continue跳过当前这次循环
break和continue都必须配合if使用
dict
dict全称dictionary,在其他语言中称为map,使用键-值(key-value)存储,具有极快的查找速度
一个key只能对应一个value,所以,多次对一个key放入value,后面的值会把前面的值冲掉:
要避免key不存在的错误,有两种办法:
- 一是通过in判断key是否存在:
- 二是通过dict提供的get()方法,如果key不存在,可以返回None,或者自己指定的value:
返回None的时候Python的交互环境不显示结果。 - 要删除一个key,用pop(key)方法,对应的value也会从dict中删除:
list与dict的区别:
- dict查找速度快,不会随着元素的增加而变慢;list查找和插入的时间随着元素的增加而增加;
- dict需要占用大量内存,内存浪费多;list占用空间小,需要浪费内存少。
dict的key要是不可变对象
因为dict根据key值来计算value存储的位置,如果每次计算value得到的结果不同,那么dict内部就完全乱了。如list是可变值,那么它就不能作为key。
tuple也不可以做为key
- 不可变对象
对可变对象进行操作,它的内部是会发生变化的
而对不可变对象进行操作,它的内部不会变化
replace()函数创建了一个新的字符串,而变量a本身还是指向的’abc’这个字符串
set
set与dict类似,但是set不存储value,由于key不能重复,因此在set中没有重复的key
要创建一个set,要提供一个list作为输入集合:
重复元素在set中会被自动过滤
通过add(key)方法可以添加元素到set中
通过remove(key)方法可以删除元素
set无序且无重复元素,因此两个set可以做数学意义上的交集和并集:
差集:
set没有get()方法
函数
调用函数
函数 | 含义 | 参数 |
---|---|---|
abs() | 返回绝对值 | 只能有一个参数 |
max() | 返回最大值 | 可以接受任意个数参数 |
min() | 返回最小值 | 可以接受任意个数参数 |
int() | 把其他数据类型转换成整数 | 可以接受任意个数参数 |
hex() | 把其他进制的数转换成十六进制表示的字符串 | 一个参数 |
函数名其实是指向一个函数对象的引用,完全可以把函数名赋给一个变量,相当于给函数起了一个别名:
定义函数
用def语句定义一个函数
在Python交互环境中定义函数时,注意Python会出现…的提示。函数定义结束后需要按两次回车重新回到>>>提示符下
如果你已经把my_abs()的函数定义保存为abstest.py文件了,那么,可以在该文件的当前目录下启动Python解释器,用from abstest import my_abs来导入my_abs()函数,注意abstest是文件名(不含.py扩展名)
- 空函数
如果想定义一个什么都不做的空函数,可以用pass语句:
pass可以用来当作占位符,比如还没想好怎么写函数的代码,就可以先放一个pass让代码运行起来。pass也可以放在其他语句里当占位符用
如果参数个数不对,python解释器会检查出来,并抛出TypeError
但如果参数类型不对,python解释器无法检查出来,要再函数定义内做参数检查
用isinstance()检查参数类型
自定义函数可以返回多个值
其实这是一种假象,实际上返回的是一个tuple,然后按位置赋值给相应的变量
函数的参数
- 位置参数
上图中的x和n就是位置参数 - 默认参数
由于我们经常计算一个数的平方,所以可以给n一个默认参数
调用函数时,如果只输入一个参数,则计算它的平方,如果要计算三次方四次方等,则必须要传入两个参数
有几点需要注意的地方:
- 必选参数要放在前面,默认参数在后;
- 当函数有多个参数时,变化大的参数放前面,变化小的参数放后面,变化小的参数可以作为默认参数
使用默认参数的好处在于可以降低调用函数的难度
比如如果我们需要向一个表中输入一些信息,如果表中有一些项大部分数据是相同的,那么我们可以把它设为默认值,只有不相同时才传入不同的值,因此无论是简单调用还是复杂调用,函数都只要定义一个
需要注意的是如果调用函数的时候不按顺序提供默认参数,需要把参数名也写上,如:
如果需要传入的人年龄还是为6,但城市不为北京的话:
enroll(‘Adam’, ‘M’, city=‘Tianjin’)
默认参数必须指向不变对象!
要使上面的例子可行,可以用None这个不变对象来实现
这样的话不管使用默认参数调用多少次都不会有问题
- 可变参数
在函数内部,接收到的是一个tuple,在调用函数时可以传入任意个参数,包括0个参数
如果已有一个list或者tuple要调用可变参数,可以再list或tuple前加一个*号
- 关键字参数
关键字参数允许传入0个或任意个含参数名的参数,这些关键字参数再函数内部自动组装为一个dict
也可以组装一个dict然后把dict转化为关键字参数传进去
extra表示把extra这个dict的所有key-value用关键字参数传入到函数的kw参数,kw将获得一个dict,注意kw获得的dict是extra的一份拷贝,对kw的改动不会影响到函数外的extra。 - 命名关键字参数
如果要限制关键字参数的名字,则可以:
后面的参数被视为关键字参数,调用方式如下
如果函数定义中已经有了一个可变参数,后面跟着的命名关键字参数就不再需要一个特殊分隔符*了:
命名关键字必须传入参数名!
命名关键字可以有缺省值
则调用时可以不传入city。
- 参数组合
参数定义的顺序必须是:必选参数、默认参数、可变参数、命名关键字参数和关键字参数。
递归函数
在函数内部调用自身的函数称为递归函数
函数调用是通过栈(stack)这种数据结构实现的,每当进入一个函数调用,栈就会加一层栈帧,每当函数返回,栈就会减一层栈帧。由于栈的大小不是无限的,所以,递归调用的次数过多,会导致栈溢出。
解决递归调用栈溢出的方法是通过尾递归优化,事实上尾递归和循环的效果是一样的,所以,把循环看成是一种特殊的尾递归函数也是可以的。
尾递归是指,在函数返回的时候,调用自身本身,并且,return语句不能包含表达式。
尾递归调用时,如果做了优化,栈不会增长,因此,无论多少次调用也不会导致栈溢出。
遗憾的是,大多数编程语言没有针对尾递归做优化,Python解释器也没有做优化,所以,即使把上面的fact(n)函数改成尾递归方式,也会导致栈溢出。
高级特性
切片——Slice
L = [‘Michael’, ‘Sarah’, ‘Tracy’, ‘Bob’, ‘Jack’]
L[0:3]
[‘Michael’, ‘Sarah’, ‘Tracy’]
L[0:3]表示,从索引0开始取,直到索引3为止,但不包括索引3。即索引0,1,2,正好是3个元素。如果第一个索引是0,还可以省略。
python也支持倒数切片
L[-2:]
[‘Bob’, ‘Jack’]
L[-2:-1]
[‘Bob’]
还可以每n个数取一个
L = list(range(100))
L[:10:2]
[0, 2, 4, 6, 8]
L[:10:2]表示从前十个数中每2个数取一个
甚至什么都不写,只写[:]就可以原样复制一个list:
L[:]
[0, 1, 2, 3, …, 99]
tuple也是一种list,当它使用切片操作时,其结果仍然是一个tuple,字符串也是如此
迭代
许多语言的迭代是通过下标来完成的,而python不仅可以迭代有下标的对象,还可以迭代没有下标的对象,如dict:
因为dict的存储不是按照list的方式顺序排列,所以,迭代出的结果顺序很可能不一样。
默认情况下,dict迭代的是key。如果要迭代value,可以用for value in d.values(),如果要同时迭代key和value,可以用for k, v in d.items()。
字符串也可以迭代
通过collections模块的Iterable类型可以判断对象是否可迭代
Python内置的enumerate函数可以把一个list变成索引-元素对,这样就可以在for循环中同时迭代索引和元素本身:
列表生成式——List Comprehensions
生成[1x1, 2x2, 3x3, …, 10x10]:
for循环后面还可以加上if判断,这样我们就可以筛选出仅偶数的平方:
还可以使用两层循环,可以生成全排列:
列表生成式也可以使用两个变量来生成list:
把一个list中所有的字符串变成小写: