过python的入门笔记

笔记。

看的是廖雪峰的,入门级别的,快速过一遍用来当其他知识学习的前置。

和其他语言差不太多的部分就不记了。

Python基础

数据类型和变量

整数

对于很大的数,例如10000000000,很难数清楚0的个数。Python允许在数字中间以_分隔,因此,写成10_000_000_000和10000000000是完全一样的。十六进制数也可以写成0xa1b2_c3d4

python整数没有大小限制。

字符串

如果字符串里面有很多字符都需要转义,就需要加很多\,为了简化,Python还允许用r’’表示’’内部的字符串默认不转义,可以自己试试:

1
2
3
4
>>> print('\\\t\\')
\ \
>>> print(r'\\\t\\')
\\\t\\

空值

用None表示。

变量

看了下大部分都是动态赋值,有点像c++里的auto。

1
2
3
4
a = 123 # a是整数
print(a)
a = 'ABC' # a变为字符串
print(a)

也可以选择静态赋值,赋值的时候声明类型。

1
2
int a = 123 # a是整数类型变量
a = "ABC" # 错误:不能把字符串赋给整型变量

地板除

不知道c++有没有,反正在这里第一次看到。

1
2
>>> 10 // 3
3

字符串

对于单个字符的编码,Python提供了ord()函数获取字符的整数表示,chr()函数把编码转换为对应的字符:

1
2
3
4
5
6
7
8
>>> ord('A')
65
>>> ord('中')
20013
>>> chr(66)
'B'
>>> chr(25991)
'文'

由于Python的字符串类型是str,在内存中以Unicode表示,一个字符对应若干个字节。如果要在网络上传输,或者保存到磁盘上,就需要把str变为以字节为单位的bytes。

Python对bytes类型的数据用带b前缀的单引号或双引号表示

1
x = b'ABC'

有个encode函数,可以用来做编码转换

1
2
3
4
5
6
7
8
>>> 'ABC'.encode('ascii')
b'ABC'
>>> '中文'.encode('utf-8')
b'\xe4\xb8\xad\xe6\x96\x87'
>>> '中文'.encode('ascii')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-1: ordinal not in range(128)

纯英文的str可以用ASCII编码为bytes,内容是一样的,含有中文的str可以用UTF-8编码为bytes。含有中文的str无法用ASCII编码,因为中文编码的范围超过了ASCII编码的范围,Python会报错。

相反的,读取到bytes表示的字节流,就要用decode转换到字符

1
2
3
4
>>> b'ABC'.decode('ascii')
'ABC'
>>> b'\xe4\xb8\xad\xe6\x96\x87'.decode('utf-8')
'中文'

如果decode里有无法解码的部分就会报错,可以通过对decode传入参数来忽略错误字节。

1
2
>>> b'\xe4\xb8\xad\xff'.decode('utf-8', errors='ignore')
'中'

算字符长度的话用len,这一点倒是差不太多。

1
2
3
4
>>> len('ABC')
3
>>> len('中文')
2

python里的len可以算byte数,如果是字节流算的字节数。

1
2
3
4
5
6
>>> len(b'ABC')
3
>>> len(b'\xe4\xb8\xad\xe6\x96\x87')
6
>>> len('中文'.encode('utf-8'))
6

python代码开头写个这个来声明编码格式

1
2
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

字符串格式化

%

跟c差不太多,不过把’,’改成’%’了。

在Python中,采用的格式化方式和C语言是一致的,用%实现,举例如下:

1
2
3
4
>>> 'Hello, %s' % 'world'
'Hello, world'
>>> 'Hi, %s, you have $%d.' % ('Michael', 1000000)
'Hi, Michael, you have $1000000.

format

或者使用format也可以达到格式化的效果。

另一种格式化字符串的方法是使用字符串的format()方法,它会用传入的参数依次替换字符串内的占位符{0}、{1}……,不过这种方式写起来比%要麻烦得多:

1
2
>>> 'Hello, {0}, 成绩提升了 {1:.1f}%'.format('小明', 17.125)
'Hello, 小明, 成绩提升了 17.1%'

f-string

最后一种格式化字符串的方法是使用以f开头的字符串,称之为f-string,它和普通字符串不同之处在于,字符串如果包含{xxx},就会以对应的变量替换:

1
2
3
4
>>> r = 2.5
>>> s = 3.14 * r ** 2
>>> print(f'The area of a circle with radius {r} is {s:.2f}')
The area of a circle with radius 2.5 is 19.62

list和tuple

list

python内置的列表类型。

1
2
3
>>> classmates = ['Michael', 'Bob', 'Tracy']
>>> classmates
['Michael', 'Bob', 'Tracy']

用len可以获取list的长度。

访问元素的方法跟数组差不多,直接用下标索引就行。

如果要取最后一个元素,除了计算索引位置外,还可以用-1做索引,直接获取最后一个元素:

1
2
>>> classmates[-1]
'Tracy'

以此类推也可以倒数第二第三个元素

1
2
3
4
5
6
7
8
>>> classmates[-2]
'Bob'
>>> classmates[-3]
'Michael'
>>> classmates[-4]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
IndexError: list index out of range

list可变,用append追加。

1
2
3
>>> classmates.append('Adam')
>>> classmates
['Michael', 'Bob', 'Tracy', 'Adam']

用insert插入

1
2
3
>>> classmates.insert(1, 'Jack')
>>> classmates
['Michael', 'Jack', 'Bob', 'Tracy', 'Adam']

用pop删除
指定位置就pop括号内带索引

1
2
3
4
5
6
7
8
9
10
>>> classmates.pop()
'Adam'
>>> classmates
['Michael', 'Jack', 'Bob', 'Tracy']

>>> classmates.pop(1)
'Jack'
>>> classmates
['Michael', 'Bob', 'Tracy']

替换元素直接赋值就ok。

list里的数据类型可以不同
也可以list里套list。

1
2
3
4
5
>>> L = ['Apple', 123, True]

>>> s = ['python', 'java', ['asp', 'php'], 'scheme']
>>> len(s)
4

tuple

tuple跟list差不多,但是初始化之后不能修改。无append、insert和pop方法。

如果定义只有一个元素的tuple,要加一个逗号来消除歧义。

1
2
3
>>> t = (1,)
>>> t
(1,)

tuple里面可以包含list,而这个list可以是可变的。tuple的不变指的是指向不变。

条件判断

用的不是else if,用的是elif

1
2
3
4
5
6
7
age = 3
if age >= 18:
print('adult')
elif age >= 6:
print('teenager')
else:
print('kid')

循环

同样有while和for

for:

1
2
3
names = ['Michael', 'Bob', 'Tracy']
for name in names:
print(name)

用range可以生成一个整数序列,n-1

1
2
>>> list(range(5))
[0, 1, 2, 3, 4]

while:

1
2
3
4
5
6
sum = 0
n = 99
while n > 0:
sum = sum + n
n = n - 2
print(sum)

dict和set

dict

类似于map,用键-值存储。

1
2
3
>>> d = {'Michael': 95, 'Bob': 75, 'Tracy': 85}
>>> d['Michael']
95

可以用in判断key是否存在

1
2
>>> 'Thomas' in d
False

或者用get判断
key不存在返回None或者指定的值,这里指定的是-1.

1
2
3
>>> d.get('Thomas')
>>> d.get('Thomas', -1)
-1

删除用pop,跟list一样。

1
2
3
4
>>> d.pop('Bob')
75
>>> d
{'Michael': 95, 'Tracy': 85}

set

set和dict类似,也是一组key的集合,但不存储value。由于key不能重复,所以,在set中,没有重复的key。

要创建一个set,需要提供一个list作为输入集合:

1
2
3
>>> s = set([1, 2, 3])
>>> s
{1, 2, 3}

重复元素会在set中自动过滤:

1
2
3
>>> s = set([1, 1, 2, 2, 3, 3])
>>> s
{1, 2, 3}

用add添加(重复添加没有效果),remove删除:

1
2
3
4
5
6
7
8
9
10
>>> s.add(4)
>>> s
{1, 2, 3, 4}
>>> s.add(4)
>>> s
{1, 2, 3, 4}

>>> s.remove(4)
>>> s
{1, 2, 3}

函数

定义函数

1
2
3
4
5
def my_abs(x):
if x >= 0:
return x
else:
return -x

空函数

空函数用pass语句

1
2
def nop():
pass

pass也可以用在其他语句里

1
2
if age >= 18:
pass

参数检查

python定义函数的时候没有定义参数类型,因此函数里要有对应的参数类型检查。

可以用isinstance()

1
2
3
4
5
6
7
def my_abs(x):
if not isinstance(x, (int, float)):
raise TypeError('bad operand type')
if x >= 0:
return x
else:
return -x

返回多个值

1
2
3
4
5
6
import math

def move(x, y, step, angle=0):
nx = x + step * math.cos(angle)
ny = y - step * math.sin(angle)
return nx, ny

返回的本质上是一个tuple,但是可以用多个变量接收,所以和go的也差不多。

函数的参数

默认参数

1
2
3
4
5
6
def power(x, n=2):
s = 1
while n > 0:
n = n - 1
s = s * x
return s

这里把n=2设为默认参数,如果传参是不传n对应的数据,则默认n=2。

这样,当我们调用power(5)时,相当于调用power(5, 2)。

也可以不按顺序提供部分默认参数。当不按顺序提供部分默认参数时,需要把参数名写上。比如调用enroll(‘Adam’, ‘M’, city=’Tianjin’),意思是,city参数用传进去的值,其他默认参数继续使用默认值。

默认参数必须指向不变对象

如果默认参数指向的是一个类似于list一样的可变参数,那么对应的默认参数指向不会因为多次调用而重置,每次调用默认参数都可能导致该参数发生改变,而影响后续调用。

例如

1
2
3
def add_end(L=[]):
L.append('END')
return L

多次调用默认会导致返回的L里头里是一堆’END’。

1
2
3
4
>>> add_end()
['END', 'END']
>>> add_end()
['END', 'END', 'END']

要保持默认返回为空的话可以改为

1
2
3
4
5
def add_end(L=None):
if L is None:
L = []
L.append('END')
return L

当然合理利用的话这个机制没准有用。

可变参数

1
2
3
4
5
def calc(*numbers):
sum = 0
for n in numbers:
sum = sum + n * n
return sum

定义可变参数和定义一个list或tuple参数相比,仅仅在参数前面加了一个*号。在函数内部,参数numbers接收到的是一个tuple,因此,函数代码完全不变。但是,调用该函数时,可以传入任意个参数,包括0个参数:

1
2
3
4
>>> calc(1, 2)
5
>>> calc()
0

如果已经有一个list或者tuple,要调用一个可变参数怎么办?可以这样做:

1
2
3
>>> nums = [1, 2, 3]
>>> calc(*nums)
14

关键字参数

可变参数允许你传入0个或任意个参数,这些可变参数在函数调用时自动组装为一个tuple。而关键字参数允许你传入0个或任意个含参数名的参数,这些关键字参数在函数内部自动组装为一个dict。请看示例:

1
2
def person(name, age, **kw):
print('name:', name, 'age:', age, 'other:', kw)

函数person除了必选参数name和age外,还接受关键字参数kw。在调用该函数时,可以只传入必选参数或者任意个数的关键字参数:

1
2
3
4
5
6
7
>>> person('Michael', 30)
name: Michael age: 30 other: {}

>>> person('Bob', 35, city='Beijing')
name: Bob age: 35 other: {'city': 'Beijing'}
>>> person('Adam', 45, gender='M', job='Engineer')
name: Adam age: 45 other: {'gender': 'M', 'job': 'Engineer'}

这种写法可以确保接收到更多的参数。

当然也可以用现成的dict传参,如下:

1
2
3
>>> extra = {'city': 'Beijing', 'job': 'Engineer'}
>>> person('Jack', 24, **extra)
name: Jack age: 24 other: {'city': 'Beijing', 'job': 'Engineer'}

命名关键字参数

用于限制关键字参数的名字。

如果要限制关键字参数的名字,就可以用命名关键字参数,例如,只接收city和job作为关键字参数。这种方式定义的函数如下

1
2
def person(name, age, *, city, job):
print(name, age, city, job)

和关键字参数**kw不同,命名关键字参数需要一个特殊分隔符*,*后面的参数被视为命名关键字参数。

如果函数定义里已经有一个可变参数了,那么后面跟着的命名关键字参数就不需要特殊分隔符*了。

1
2
def person(name, age, *args, city, job):
print(name, age, args, city, job)

命名关键字参数必须传入参数名,这和位置参数不同。如果没有传入参数名,调用将报错:

1
2
3
4
>>> person('Jack', 24, 'Beijing', 'Engineer')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: person() missing 2 required keyword-only arguments: 'city' and 'job'

由于调用时缺少参数名city和job,Python解释器把前两个参数视为位置参数,后两个参数传给*args,但缺少命名关键字参数导致报错。

命名关键字参数可以和默认参数联合,用于缺省,如下:

1
2
def person(name, age, *, city='Beijing', job):
print(name, age, city, job)

参数组合

在Python中定义函数,可以用必选参数、默认参数、可变参数、关键字参数和命名关键字参数,这5种参数都可以组合使用。但是请注意,参数定义的顺序必须是:必选参数、默认参数、可变参数、命名关键字参数和关键字参数

比如:

1
2
3
4
5
def f1(a, b, c=0, *args, **kw):
print('a =', a, 'b =', b, 'c =', c, 'args =', args, 'kw =', kw)

def f2(a, b, c=0, *, d, **kw):
print('a =', a, 'b =', b, 'c =', c, 'd =', d, 'kw =', kw)

高级特性

切片

和GO的整体来说差不多。

取一个list或tuple的部分元素是非常常见的操作。比如,一个list如下,可以使用切片取前三个元素:

1
2
3
4
5
6
7
8
9
>>> L = ['Michael', 'Sarah', 'Tracy', 'Bob', 'Jack']

>>> L[0:3]
['Michael', 'Sarah', 'Tracy']

#0为前置可以省略,所以可以写成

>>> L[:3]
['Michael', 'Sarah', 'Tracy']

也支持倒数切片

1
2
3
4
>>> L[-2:]
['Bob', 'Jack']
>>> L[-2:-1] #取倒数第一个和倒数第二个元素
['Bob']

字符串可以看作是一种list,所以也支持切片

1
2
3
4
>>> 'ABCDEFG'[:3]
'ABC'
>>> 'ABCDEFG'[::2]
'ACEG'

迭代

就是用for遍历。

没有下标的数据类型比如dict也可以用for遍历:

1
2
3
4
5
6
7
>>> d = {'a': 1, 'b': 2, 'c': 3}
>>> for key in d:
... print(key)
...
a
c
b

由于不是采用list的方式存储的,所以迭代的顺序很可能会不一样的。

for循环其实可以同时使用两个甚至多个变量,比如dict的items()可以同时迭代key和value:

1
2
3
4
5
6
7
>>> d = {'x': 'A', 'y': 'B', 'z': 'C' }
>>> for k, v in d.items():
... print(k, '=', v)
...
y = B
x = A
z = C

用values()可以只迭代value。

那么,如何判断一个对象是可迭代对象呢?方法是通过collections.abc模块的Iterable类型判断

1
2
3
4
5
6
7
>>> from collections.abc import Iterable
>>> isinstance('abc', Iterable) # str是否可迭代
True
>>> isinstance([1,2,3], Iterable) # list是否可迭代
True
>>> isinstance(123, Iterable) # 整数是否可迭代
False

对于list也可以做到下标和value循环,但是要用到enumerate函数,如下:

1
2
3
4
5
6
>>> for i, value in enumerate(['A', 'B', 'C']):
... print(i, value)
...
0 A
1 B
2 C

上面的for循环里,同时引用了两个变量,在Python里是很常见的,比如下面的代码:

1
2
3
4
5
6
>>> for x, y in [(1, 1), (2, 4), (3, 9)]:
... print(x, y)
...
1 1
2 4
3 9

列表生成式

用来生成list。

之前说过了,如果要生成一个1-100的list,可以用range

1
>>> list(range(1,101))

但是如果要生成[1x1, 2x2, 3x3, …, 10x10],那range就不好使了,但可以用列表生成器

1
2
>>> [x * x for x in range(1, 11)]
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

for循环里还可以加上if判断,筛选出仅偶数的平方

1
2
>>> [x * x for x in range(1, 11) if x % 2 == 0]
[4, 16, 36, 64, 100]

也可以使用两层循环来生成全排列

1
2
>>> [m + n for m in 'ABC' for n in 'XYZ']
['AX', 'AY', 'AZ', 'BX', 'BY', 'BZ', 'CX', 'CY', 'CZ']

运用列表生成式,可以写出非常简洁的代码。例如,列出当前目录下的所有文件和目录名,可以通过一行代码实现:

1
2
3
>>> import os # 导入os模块,模块的概念后面讲到
>>> [d for d in os.listdir('.')] # os.listdir可以列出文件和目录
['.emacs.d', '.ssh', '.Trash', 'Adlm', 'Applications', 'Desktop', 'Documents', 'Downloads', 'Library', 'Movies', 'Music', 'Pictures', 'Public', 'VirtualBox VMs', 'Workspace', 'XCode']

列表生成式也可以使用两个变量来生成list:

1
2
3
>>> d = {'x': 'A', 'y': 'B', 'z': 'C' }
>>> [k + '=' + v for k, v in d.items()]
['y=B', 'x=A', 'z=C']

最后把一个list中所有的字符串变成小写:

1
2
3
>>> L = ['Hello', 'World', 'IBM', 'Apple']
>>> [s.lower() for s in L]
['hello', 'world', 'ibm', 'apple']

感觉下来就是直接在[]里写生成式,然后用生成式给对应的list。