笔记。
看的是廖雪峰的,入门级别的,快速过一遍用来当其他知识学习的前置。
和其他语言差不太多的部分就不记了。
Python基础
数据类型和变量
整数
对于很大的数,例如10000000000,很难数清楚0的个数。Python允许在数字中间以_分隔,因此,写成10_000_000_000和10000000000是完全一样的。十六进制数也可以写成0xa1b2_c3d4
python整数没有大小限制。
字符串
如果字符串里面有很多字符都需要转义,就需要加很多\,为了简化,Python还允许用r’’表示’’内部的字符串默认不转义,可以自己试试:
1 | print('\\\t\\') |
空值
用None表示。
变量
看了下大部分都是动态赋值,有点像c++里的auto。
1 | a = 123 # a是整数 |
也可以选择静态赋值,赋值的时候声明类型。
1 | int a = 123 # a是整数类型变量 |
地板除
不知道c++有没有,反正在这里第一次看到。
1 | 10 // 3 |
字符串
对于单个字符的编码,Python提供了ord()函数获取字符的整数表示,chr()函数把编码转换为对应的字符:
1 | ord('A') |
由于Python的字符串类型是str,在内存中以Unicode表示,一个字符对应若干个字节。如果要在网络上传输,或者保存到磁盘上,就需要把str变为以字节为单位的bytes。
Python对bytes类型的数据用带b前缀的单引号或双引号表示
1 | x = b'ABC' |
有个encode函数,可以用来做编码转换
1 | 'ABC'.encode('ascii') |
纯英文的str可以用ASCII编码为bytes,内容是一样的,含有中文的str可以用UTF-8编码为bytes。含有中文的str无法用ASCII编码,因为中文编码的范围超过了ASCII编码的范围,Python会报错。
相反的,读取到bytes表示的字节流,就要用decode转换到字符
1 | b'ABC'.decode('ascii') |
如果decode里有无法解码的部分就会报错,可以通过对decode传入参数来忽略错误字节。
1 | b'\xe4\xb8\xad\xff'.decode('utf-8', errors='ignore') |
算字符长度的话用len,这一点倒是差不太多。
1 | len('ABC') |
python里的len可以算byte数,如果是字节流算的字节数。
1 | len(b'ABC') |
python代码开头写个这个来声明编码格式
1 | #!/usr/bin/env python3 |
字符串格式化
%
跟c差不太多,不过把’,’改成’%’了。
在Python中,采用的格式化方式和C语言是一致的,用%实现,举例如下:
1 | 'Hello, %s' % 'world' |
format
或者使用format也可以达到格式化的效果。
另一种格式化字符串的方法是使用字符串的format()方法,它会用传入的参数依次替换字符串内的占位符{0}、{1}……,不过这种方式写起来比%要麻烦得多:
1 | 'Hello, {0}, 成绩提升了 {1:.1f}%'.format('小明', 17.125) |
f-string
最后一种格式化字符串的方法是使用以f开头的字符串,称之为f-string,它和普通字符串不同之处在于,字符串如果包含{xxx},就会以对应的变量替换:
1 | 2.5 r = |
list和tuple
list
python内置的列表类型。
1 | 'Michael', 'Bob', 'Tracy'] classmates = [ |
用len可以获取list的长度。
访问元素的方法跟数组差不多,直接用下标索引就行。
如果要取最后一个元素,除了计算索引位置外,还可以用-1做索引,直接获取最后一个元素:
1 | 1] classmates[- |
以此类推也可以倒数第二第三个元素
1 | 2] classmates[- |
list可变,用append追加。
1 | 'Adam') classmates.append( |
用insert插入
1 | 1, 'Jack') classmates.insert( |
用pop删除
指定位置就pop括号内带索引
1 | classmates.pop() |
替换元素直接赋值就ok。
list里的数据类型可以不同
也可以list里套list。
1 | 'Apple', 123, True] L = [ |
tuple
tuple跟list差不多,但是初始化之后不能修改。无append、insert和pop方法。
如果定义只有一个元素的tuple,要加一个逗号来消除歧义。
1 | 1,) t = ( |
tuple里面可以包含list,而这个list可以是可变的。tuple的不变指的是指向不变。
条件判断
用的不是else if,用的是elif
1 | age = 3 |
循环
同样有while和for
for:
1 | names = ['Michael', 'Bob', 'Tracy'] |
用range可以生成一个整数序列,n-1
1 | list(range(5)) |
while:
1 | sum = 0 |
dict和set
dict
类似于map,用键-值存储。
1 | 'Michael': 95, 'Bob': 75, 'Tracy': 85} d = { |
可以用in判断key是否存在
1 | 'Thomas' in d |
或者用get判断
key不存在返回None或者指定的值,这里指定的是-1.
1 | 'Thomas') d.get( |
删除用pop,跟list一样。
1 | 'Bob') d.pop( |
set
set和dict类似,也是一组key的集合,但不存储value。由于key不能重复,所以,在set中,没有重复的key。
要创建一个set,需要提供一个list作为输入集合:
1 | set([1, 2, 3]) s = |
重复元素会在set中自动过滤:
1 | set([1, 1, 2, 2, 3, 3]) s = |
用add添加(重复添加没有效果),remove删除:
1 | 4) s.add( |
函数
定义函数
1 | def my_abs(x): |
空函数
空函数用pass语句
1 | def nop(): |
pass也可以用在其他语句里
1 | if age >= 18: |
参数检查
python定义函数的时候没有定义参数类型,因此函数里要有对应的参数类型检查。
可以用isinstance()
1 | def my_abs(x): |
返回多个值
1 | import math |
返回的本质上是一个tuple,但是可以用多个变量接收,所以和go的也差不多。
函数的参数
默认参数
1 | def power(x, n=2): |
这里把n=2设为默认参数,如果传参是不传n对应的数据,则默认n=2。
这样,当我们调用power(5)时,相当于调用power(5, 2)。
也可以不按顺序提供部分默认参数。当不按顺序提供部分默认参数时,需要把参数名写上。比如调用enroll(‘Adam’, ‘M’, city=’Tianjin’),意思是,city参数用传进去的值,其他默认参数继续使用默认值。
默认参数必须指向不变对象
如果默认参数指向的是一个类似于list一样的可变参数,那么对应的默认参数指向不会因为多次调用而重置,每次调用默认参数都可能导致该参数发生改变,而影响后续调用。
例如
1 | def add_end(L=[]): |
多次调用默认会导致返回的L里头里是一堆’END’。
1 | add_end() |
要保持默认返回为空的话可以改为
1 | def add_end(L=None): |
当然合理利用的话这个机制没准有用。
可变参数
1 | def calc(*numbers): |
定义可变参数和定义一个list或tuple参数相比,仅仅在参数前面加了一个*号。在函数内部,参数numbers接收到的是一个tuple,因此,函数代码完全不变。但是,调用该函数时,可以传入任意个参数,包括0个参数:
1 | 1, 2) calc( |
如果已经有一个list或者tuple,要调用一个可变参数怎么办?可以这样做:
1 | 1, 2, 3] nums = [ |
关键字参数
可变参数允许你传入0个或任意个参数,这些可变参数在函数调用时自动组装为一个tuple。而关键字参数允许你传入0个或任意个含参数名的参数,这些关键字参数在函数内部自动组装为一个dict。请看示例:
1 | def person(name, age, **kw): |
函数person除了必选参数name和age外,还接受关键字参数kw。在调用该函数时,可以只传入必选参数或者任意个数的关键字参数:
1 | 'Michael', 30) person( |
这种写法可以确保接收到更多的参数。
当然也可以用现成的dict传参,如下:
1 | 'city': 'Beijing', 'job': 'Engineer'} extra = { |
命名关键字参数
用于限制关键字参数的名字。
如果要限制关键字参数的名字,就可以用命名关键字参数,例如,只接收city和job作为关键字参数。这种方式定义的函数如下
1 | def person(name, age, *, city, job): |
和关键字参数**kw不同,命名关键字参数需要一个特殊分隔符*,*后面的参数被视为命名关键字参数。
如果函数定义里已经有一个可变参数了,那么后面跟着的命名关键字参数就不需要特殊分隔符*了。
1 | def person(name, age, *args, city, job): |
命名关键字参数必须传入参数名,这和位置参数不同。如果没有传入参数名,调用将报错:
1 | 'Jack', 24, 'Beijing', 'Engineer') person( |
由于调用时缺少参数名city和job,Python解释器把前两个参数视为位置参数,后两个参数传给*args,但缺少命名关键字参数导致报错。
命名关键字参数可以和默认参数联合,用于缺省,如下:
1 | def person(name, age, *, city='Beijing', job): |
参数组合
在Python中定义函数,可以用必选参数、默认参数、可变参数、关键字参数和命名关键字参数,这5种参数都可以组合使用。但是请注意,参数定义的顺序必须是:必选参数、默认参数、可变参数、命名关键字参数和关键字参数
比如:
1 | def f1(a, b, c=0, *args, **kw): |
高级特性
切片
和GO的整体来说差不多。
取一个list或tuple的部分元素是非常常见的操作。比如,一个list如下,可以使用切片取前三个元素:
1 | 'Michael', 'Sarah', 'Tracy', 'Bob', 'Jack'] L = [ |
也支持倒数切片
1 | 2:] L[- |
字符串可以看作是一种list,所以也支持切片
1 | 'ABCDEFG'[:3] |
迭代
就是用for遍历。
没有下标的数据类型比如dict也可以用for遍历:
1 | 'a': 1, 'b': 2, 'c': 3} d = { |
由于不是采用list的方式存储的,所以迭代的顺序很可能会不一样的。
for循环其实可以同时使用两个甚至多个变量,比如dict的items()可以同时迭代key和value:
1 | 'x': 'A', 'y': 'B', 'z': 'C' } d = { |
用values()可以只迭代value。
那么,如何判断一个对象是可迭代对象呢?方法是通过collections.abc模块的Iterable类型判断
1 | from collections.abc import Iterable |
对于list也可以做到下标和value循环,但是要用到enumerate函数,如下:
1 | for i, value in enumerate(['A', 'B', 'C']): |
上面的for循环里,同时引用了两个变量,在Python里是很常见的,比如下面的代码:
1 | for x, y in [(1, 1), (2, 4), (3, 9)]: |
列表生成式
用来生成list。
之前说过了,如果要生成一个1-100的list,可以用range
1 | list(range(1,101)) |
但是如果要生成[1x1, 2x2, 3x3, …, 10x10],那range就不好使了,但可以用列表生成器
1 | for x in range(1, 11)] [x * x |
for循环里还可以加上if判断,筛选出仅偶数的平方
1 | for x in range(1, 11) if x % 2 == 0] [x * x |
也可以使用两层循环来生成全排列
1 | for m in 'ABC' for n in 'XYZ'] [m + n |
运用列表生成式,可以写出非常简洁的代码。例如,列出当前目录下的所有文件和目录名,可以通过一行代码实现:
1 | import os # 导入os模块,模块的概念后面讲到 |
列表生成式也可以使用两个变量来生成list:
1 | 'x': 'A', 'y': 'B', 'z': 'C' } d = { |
最后把一个list中所有的字符串变成小写:
1 | 'Hello', 'World', 'IBM', 'Apple'] L = [ |
感觉下来就是直接在[]里写生成式,然后用生成式给对应的list。