Python基础

Python基础学习搬运

很乱 !!! 复习时整理

一、关于Python

Python 简介

百度百科:Python

Python的创始人为荷兰人吉多·范罗苏姆 (Guido van Rossum)。1989年圣诞节期间,在阿姆斯特丹,Guido为了打发圣诞节的无趣,决心开发一个新的脚本解释程序,作为ABC 语言的一种继承。之所以选中Python(大蟒蛇的意思)作为该编程语言的名字,是取自英国20世纪70年代首播的电视喜剧《蒙提.派森的飞行马戏团》(Monty Python’s Flying Circus)。
ABC是由Guido参加设计的一种教学语言。就Guido本人看来,ABC 这种语言非常优美和强大,是专门为非专业程序员设计的。但是ABC语言并没有成功,究其原因,Guido 认为是其非开放造成的。Guido 决心在Python 中避免这一错误。同时,他还想实现在ABC 中闪现过但未曾实现的东西。
就这样,Python在Guido手中诞生了。可以说,Python是从ABC发展起来,主要受到了Modula-3(另一种相当优美且强大的语言,为小型团体所设计的)的影响。并且结合了Unix shell和C的习惯。

总结:Guido 无聊时开发出 Python! *_*

Python 特点

  • 解释型:无需编译
  • 面向对象
  • 自由度高

Python 安装

查看安装 Python 的版本信息
$ python --version python 2.7 (macOS 自带)
$ python3 --version Python 3.7.4 (安装的 py3k 版本)

Python 解释

  • 交互方式:终端输入python3 出现>>> 即进入交互模式,输入exit()退出或control + c(z)强制退出

  • 脚本方式:.py文件头部一般包含

    1
    2
    #!/usr/bin/env python3   # 告诉系统 Python 脚本可以执行
    # -*- coding: utf-8 -*- # 告诉 Python 按照 UTF-8 读取代码

    记得看权限chmod +x filename.pypython3 filename.py./filename.py

  • IDE工具(排叉、Anaconda)

二、语法基础

开始

  • Python 使用缩进表示代码块,其中约定俗成:4 个空格代替Tab

  • Python 对大小写敏感

  • 空行表示新代码开始

  • '" 完全相同

  • :语句结尾

  • \转义字符: \n换行、\t制表符,还可以使用\实现多行语句

  • 同一行使用多条语句:使用;间隔

  • 终端中:print( )多行字符串输入,输入('''开始,以''')结束

    1
    2
    3
    4
    5
    6
    7
    >>> print('''line1
    ... line2
    ... line3
    ... ''')
    line1
    line2
    line3

注释

  • #:单行注释
  • '''"""包裹:多行注释

保留字

1
2
3
4
5
# 导入 keyword 模块
>>> import keyword
# 查看保留字
>>> keyword.kwlist
['False', 'None', 'True', 'and', 'as', 'assert', 'async', 'await', 'break', 'class', 'continue', 'def', 'del', 'elif', 'else', 'except', 'finally', 'for', 'from', 'global', 'if', 'import', 'in', 'is', 'lambda', 'nonlocal', 'not', 'or', 'pass', 'raise', 'return', 'try', 'while', 'with', 'yield']

数字 Number

Python3 支持 int、float、bool、complex(复数)

  • int 只有一种
  • type()可查看类型,type只能查看当前的类型
  • isinstance()判断类型,注意子类是一种父类类型

整数 int

整数只有int类型,表示长整型

1
2
3
4
5
i = 100
# Python 可以多个变量赋值
i = j = 100
# 多个对象指定赋值
i,j, k = 1, 2, 'Hello'

浮点数 float

1
float = 100.0

布尔值 bool

只有TrueFalse两个选项,大小写注意!!!其中TrueFalse的值还是为 1 和 0 ,可以和数值相加。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
>>> True and True
True
>>> True or False
True
>>> True and False
False
>>> 1 + 1 == 2 and 1 > 0
True
>>> 1 + 1 != 2 and 1 > 0
False
>>> not True
False
>>> not 1 > 2
True
>>> not 1 +1 = 2 #判断不是赋值!!!
File "<stdin>", line 1
SyntaxError: can't assign to operator
>>> not 1 +1 == 2
False

复数 complex

1 + 2j

空值 None

None为 Python 中的空值,不能理解为 0,因为 0 是有意义的值。

常量

通常用全部大写变量名表示常量,例如PI = 3.14159265359

数据类型转型

引用 runoob:https://www.runoob.com/python3/python3-data-type.html

函数 描述
int(x [,base]) 将x转换为一个整数
float(x) 将x转换到一个浮点数
complex(real [,imag]) 创建一个复数
str(x) 将对象 x 转换为字符串
repr(x) 将对象 x 转换为表达式字符串
eval(str) 用来计算在字符串中的有效Python表达式,并返回一个对象
tuple(s) 将序列 s 转换为一个元组
list(s) 将序列 s 转换为一个列表
set(s) 转换为可变集合
dict(d) 创建一个字典。d 必须是一个 (key, value)元组序列。
frozenset(s) 转换为不可变集合
chr(x) 将一个整数转换为一个字符
ord(x) 将一个字符转换为它的整数值
hex(x) 将一个整数转换为一个十六进制字符串
oct(x) 将一个整数转换为一个八进制字符串

数值运算

  • /表示精确除,为浮点

  • //只取整数部分

  • **表示指数运算

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    # 浮点除法
    >>> 10 / 3
    3.3333333333333335
    >>> 9 / 3
    3.0
    # 表示 取整除
    >>> 10 // 3
    3
    # 表示 2 的 5次方
    >>> 2 ** 5
    32

运算符优先级

运算符 描述
** 指数 (最高优先级)
~ + - 按位翻转, 一元加号和减号 (最后两个的方法名为 +@ 和 -@)
* / % // 乘,除,取模和取整除
+ - 加法减法
>> << 右移,左移运算符
& 位 ‘AND’
^ | 位运算符
<= < > >= 比较运算符
<> == != 等于运算符
= %= /= //= -= += = *= 赋值运算符
is is not 身份运算符
in not in 成员运算符
not and or 逻辑运算符

字符串 str

  • '"包裹 str 等效
  • 索引(从左到右)第一个位置为 0,第二个为 1
  • 索引(从右到左)第一个位置为 -1,第二个为 -2
  • 不能改变
1
2
3
4
5
6
7
8
9
# ord()转字符换为 UTF-8 十进制整数
>>> ord('a')
97
# chr()将 UTF-8 十进制整数转换为字符
>>> chr(97)
'a'
#16进制
>>> '\u4e2d\u6587'
'中文'

使用 ASCII 编码,为bytes字节流

1
2
3
4
>>> 'ABC'.encode('ascii')
b'ABC'
>>> b'ABC'.decode('ascii')
'ABC'

len( )求字符串长度

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

小黑板

  • 你说str 是不可变的对象?
1
2
3
4
5
6
7
>>> a = 'ABC'
# a.replace('A','a') = 'aBC'实际上是新的一个字符串
>>> a.replace('A','a')
'aBC'
# 可以看出,a 并没有改变
>>> a
'ABC'

格式化

类似 C 语言

1
2
>>> 'Hi, %s, you have $%d.' % ('Ashin', 100) 
'Hi, Ashin, you have $100.'
符号 表示
%d 整数
%f 浮点数
%s 字符串
%x 十六进制整数
1
2
3
4
5
>>> 'test %x ' % 10000000
'test 989680 '
##
>>> 'growth rate: %d %%' % 7
'growth rate: 7 %'
  • 有些时候,字符串里面的%是一个普通字符怎么办?这个时候就需要转义,用%%来表示一个%
1
2
3
4
5
>>> s1 = 72 #小明score1
>>> s2 = 85 #小明score2
>>> r = (s2-s1)/s1*100
>>> print('%s的成绩今年提升了%.2f%%。'%('小明',r))
小明的成绩今年提升了18.06%。

输入和输出

print

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
>>> print('i','love','ashin')
# 中间的`,`会变成空格输出
i love ashin
# 100 + 200 = ?
>>> print('100 + 200 =',100 + 200)
100 + 200 = 300
>>>
# 加上 end="" 实现不换行输出,或 end'str'显示不同字符
print(x, end="")
print(y, end="")
>>>
x y
'''
# 计算 n!
'''
n = int(input('n = '))
result = 1
for x in range(1, n + 1):
result *= x
# 注意下面的输出控制方式
print('%d! = %d' % (n, result))

input

1
2
3
4
5
6
>>> name = input()
Ashin
>>> name
'Ashin'
>>> print(name)
Ashin
1
2
3
4
5
6
7
s = input('birth: ')
input()返回的数据类型是 str,注意转型
birth = int(s)
if birth < 2000:
print('00前')
else:
print('00后')
  • 输入是Input,输出是Output,因此,我们把输入输出统称为Input/Output,或者简写为IO。

列表 list

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
>>> classmates = ['Ashin', 'Joy', 'Tom']
>>> classmates
['Ashin', 'Joy', 'Tom']
>>> len(classmates)
3
>>> classmates[0]
'Ashin'
>>> classmates[-1]
'Tom'
classmates[-2]
'Joy'
>>> classmates[3]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
IndexError: list index out of range
#越界 是 0-2
  • list 是一种有序的集合,其中的元素可修改
  • 元素写在方括号之间,并使用,分隔
  • 索引都是是从 [0] 开始!!!
  • 索引 [-1] 表示倒数第一个,以此类推
1
2
3
4
5
6
7
8
9
10
11
12
13
# append()追加,末尾
>>> classmates.append('Adam')
>>> classmates
['Ashin', 'Joy', 'Tom', 'Adam']
# insert()插入,可加入索引 insert(i,'name')
>>> classmates.insert(0,'Lisa')
>>> classmates
['Lisa', 'Ashin', 'Joy', 'Tom', 'Adam']
# pop()删除末尾,默认推出最后一个,加上索引 pop(i)
>>> classmates.pop()
'Adam'
>>> classmates
['Lisa', 'Ashin', 'Joy', 'Tom']

替换集合中某一个元素,可以直接复制

1
2
3
4
5
6
7
8
9
10
>>> classmates
['Lisa', 'Ashin', 'Joy', 'Tom']
# list 中包含的是基本 number 类型,字符串要用双引号或单引号包裹
>>> classmates[1] = Orange
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'Orange' is not defined
>>> classmates[1] = 'Orange'
>>> classmates
['Lisa', 'Orange', 'Joy', 'Tom']

list里面的元素的数据类型也可以不同,比如:

1
2
3
4
5
6
7
8
9
>>> L = ['Apple', 123, True,['Apple',123,False]]
>>> L
['Apple', 123, True, ['Apple', 123, False]]
>>> len(L)
4
>>> L[3][0]
'Apple'
>>> L[3][2]
False

元组 tuple

  • 元祖-原始的组,一旦元祖元素确定,就无法更改

  • >>> x = (1) 表示整数赋值,因为()·也表示数学计算,通常添加,j解决,如>>> m = (1,)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    >>> t = (1, 2)
    >>> t
    (1, 2)
    >>> t[1]
    2
    # tuple 无法更改
    >>> t[1] = 1
    Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
    TypeError: 'tuple' object does not support item assignment
    # 空元祖
    >>> s = ()
    >>> s
    ()
    >>> x = (1)
    >>> x
    1
    # 一个元素
    >>> m = (1,)
    >>> m
    (1,)
  • 下面改变的是元祖中的 list,但 tuple 并未变

    1
    2
    3
    4
    5
    6
    7
    >>> t = ('a', 'b', ['A', 'B'])
    >>> t
    ('a', 'b', ['A', 'B'])
    # 元祖的第三个元素为 list
    >>> t[2][0] = 'X'
    >>> t
    ('a', 'b', ['X', 'B'])

字典 dict

字典 全称 dictionary,在其他语言中也称为 map,使用键-值 key-value存储,查找速度快( hashable type )。

  • 例如使用 list ,当 list 很大时,查找就是从前往后,变得很慢。
1
2
>>> names = ['Mike', 'Tim', 'Jobs']
>>> score = [90, 100, 80]
  • 使用 dict 无论查询那个都会根据索引查找,所以很快

    1
    2
    3
    >>> d = {'Mike': 90, 'Tim': 100, 'Jobs': 80}
    >>> d["Jobs"]
    80
  • 字典的 key 只能对应一个 value,后面的值会冲掉前面的值

1
2
3
4
5
6
>>> d['haha'] = 100
>>> d['haha']
100
>>> d['haha'] = 101
>>> d['haha']
101
  • 判断字典的值是否存在,'haha'是否在d中存在 ( ‘haha’ in d )
1
2
3
4
5
6
7
8
9
10
11
12
13
14
>>> d['haha'] = 101
>>> d['haha']
101
# 元素 in dict-name
>>> 'haha' in d
True
# 使用 get()
>>> d.get('haha')
101
# 当无 value 时,自定义返回 'none',默认不显示
>>> d.get('hahaha','none')
'none'
>>> d.get('haha','none')
101
  • 要删除一个 key,用pop(key)方法,对应的 value 也会从 dict 中删除
1
2
3
4
5
6
7
8
>>> d = {'Mike': 90,'Tim':100,'Jobs':80}
>>> d
{'Mike': 90, 'Tim': 100, 'Jobs': 80}
# 删除 Mike
>>> d.pop('Mike')
90
>>> d
{'Tim': 100, 'Jobs': 80}
  • dict 特点(相比 list)

    1. 占用内存大
    2. 查找、插入速度快(hash 算法)
    3. key 为整数、字符串(在 Python 中不可变)

集合 set

  • set 跟 dict 相似,但是不存 value ,而且 key 唯一
  • 注意 set 跟 list 、dict 、tuple之间的区别
  • 无序不重复
  • 创建空集合,必须使用set()
1
2
3
4
5
6
7
8
9
10
11
12
13
# 使用 {元素1, 元素2, 元素...} 创建 set 
>>> s = {1, 2, 3}
>>> s
{1, 2, 3}
>>> type(s)
<class 'set'>
# 使用 set([]) 创建
>>> s = set([1, 2, 3])
>>> s
{1, 2, 3}
>>> s = set([1, 2, 3, 3, 3, 3])
>>> s
{1, 2, 3}
  • add( ) 添加 key
1
2
3
>>> s.add(4)
>>> s
{1, 2, 3, 4}
  • remove( ) 删除元素
1
2
3
>>> s.remove(2)
>>> s
{1, 3, 4}
  • set可以看成数学意义上的无序和无重复元素的集合,可以做集合运算
1
2
3
4
5
6
7
8
>>> s1 = set([1, 2, 3])
>>> s2 = set([1, 3, 5])
#进行 与 运算
>>> s1 & s2
{1, 3}
#进行 或 运算
>>> s1 | s2
{1, 2, 3, 5}

条件判断

1
2
3
4
5
6
7
8
if <条件判断1>:
<执行1>
elif <条件判断2>:
<执行2>
elif <条件判断3>:
<执行3>
else:
<执行4>
  • if 判断是从上至下
  • 注意 elif
  • 输入转型

循环

for…in… 循环

1
2
3
4
5
6
7
8
names = ['xiaomi','huawei','apple']
for name in names:
print(name)

>>>
xiaomi
huawei
apple
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
sum = 0
# s为变量
for s in [1,2,3,4,5,6,7,8,9,10]:
sum = sum + s
print(sum)
print(sum)
>>>
1
3
6
10
15
21
28
36
45
55
55
1
2
3
4
5
6
7
sum = 0
#range(101)表示产生1-100的整数序列
for x in range(101):
sum = sum + x
print(sum)
>>>
5050

while

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

break

  • 提前结束循环

continue

  • 结束本次循环,进入下次循环

三、函数基础

调用函数

查看函数使用帮助 help( function )

调用函数,直接使用function( )

函数别名 直接通过变量赋值形式 a = abs

1
2
3
>>> a = abs
>>> a(-100)
100
  • abs( )绝对值转换
  • int( ) float( ) str( ) 数值转型

定义函数

定义一个函数要使用def语句,依次写出函数名、括号、括号中的参数和:,然后,在缩进块中编写函数体,函数的返回值用return语句返回。

1
2
3
4
5
6
7
8
9
10
11
12
def 函数名(参数列表):
函数体
# --------------------
# my_abs()为函数名,x 为参数
def my_abs(x):
# 下面是函数体
if x >= 0:
return x
else:
return -x

print(my_abs(-99))

空函数

1
2
def nop():
pass
  • pass 的作用就是直接过

匿名函数

Python 使用 lambda 来创建匿名函数。

1
2
3
4
5
6
7
# 匿名函数
lambda [arg1 [,arg2,.....argn]]:expression
# 可写函数说明
sum = lambda arg1, arg2: arg1 + arg2
# 调用sum函数
print ("相加后的值为 : ", sum( 10, 20 ))
print ("相加后的值为 : ", sum( 20, 20 ))

函数参数

参数检查

TypeError 调用函数出错,抛出提示

1
2
3
4
5
6
7
8
9
10
11
12
def my_abs(x):
# isinstance(x,(int, float)) 进行整数、浮点数检查
if not isinstance(x, (int, float)):
# 如果不是 int float 就抛出 TypeError:bad operand type
raise TypeError('bad operand type')
if x >= 0:
return x
else:
return -x
my_abs('A')

>>> TypeError: bad operand type

返回多个值

  • Pyhotn 返回多个值时采用 tuple 方式

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    import math

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

    x, y = move(100, 100, 60, math.pi / 6)
    r = move(100, 100, 60, math.pi / 6)

    print(x, y)
    print(r)

    >>>
    151.96152422706632 70.0
    (151.96152422706632, 70.0)
  • return时,默认return None

默认参数

def func(参数1,参数2,参数3 = xx): 这样第三个参数默认为 xx 可以不用输入。

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

    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
6
7
8
9
10
11
# 多一个* 
def calc(*
):
sum = 0
for n in numbers:
sum = sum + n * n
return sum

>>> num = [1, 2, 3]
# calc(nums)相当于 calc(1, 2, 3)
>>> calc(nums)

关键字参数

1
2
3
4
5
6
7
# 参数装载为 tuple
>>> def person(name, age, **kw):
... print('name', name, 'age', age, 'other', kw)
...
>>> person('ashin', 18)
# 可变参数调用返回一个 dict
name ashin age 18 other {}
  • 如果限制关键字参数的名字,命名关键字参数需要一个特殊分隔符**后面的参数被视为命名关键字参数

  • *后面的参数如有默认值,则可以不写

  • 可通过 tuple 或 dict调用 func(*args, **kw)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    # * 后面的参数名字必须给出
    def person(name, age, *, city, job):
    print(name, age, city, job)

    >>> person('jack',24,beijing,engineering)
    # 参数NameError
    Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
    NameError: name 'beijing' is not defined

    >>> person('jack',24,city = 'beijing', job = 'engineering')
    jack 24 beijing engineering
    -----------------------------------------------------------
    # 默认了 city ,可以省略不写
    >>> def person(name, age, *, city='Beijing', job):
    ... print(name, age, city, job)
    >>> person('jack',18,job = 'Teacher')
    jack 18 Beijing Teacher

参数组合

1
2
3
4
5
6
>>> def f1(a, b, c=0, *args, **kw):
... print('a =', a, 'b =', b, 'c =', c, 'args =', args, 'kw =', kw)
>>> args = (1, 2, 3, 4)
>>> kw = {'x':100 , 'y': '##'}
>>> f1(*args, **kw)
a = 1 b = 2 c = 3 args = (4,) kw = {'x': 100, 'y': '##'}

递归函数

递归函数:一个函数在内部调用自身本身

1
2
3
4
5
# 阶乘Demo,fact()调用自身
def fact(n):
if n == 1:
return 1
return n * fact(n - 1)

四、高级特性

切片 slice

根据索引切片,只包含索引中间的元素

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
>>> L = ['apple', 'orange', 'berry', 'tomato']
>>> L
['apple', 'orange', 'berry', 'tomato']
# [-1::-1] 第一个 -1 代表从右到左,第二个元素(::中间的元素)为空,第三个元素 -1 表示步长为 1 # 且从右到左
>>> L[-1::-1]
['tomato', 'berry', 'orange', 'apple']
# 切[0:2] 就是0-1-2中间的元素
>>> L[0:2]
['apple', 'orange']
>>> L[0:4]
['apple', 'orange', 'berry', 'tomato']
>>> L[3:4]
['tomato']
>>> L[2:3]
['berry']
>>> L[-2]
'berry'
>>> L[-1]
'tomato'

关于索引位置可理解为下图

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# 生成一个 list[1-100]
>>> L = list(range(100))
>>> L
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99]
# 切片取前10
>>> L[:10]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
# 取后10
>>> L[-10:]
[90, 91, 92, 93, 94, 95, 96, 97, 98, 99]
# 取 11-19
>>> L[11:20]
[11, 12, 13, 14, 15, 16, 17, 18, 19]
# 取 11-20
>>> L[11:21]
[11, 12, 13, 14, 15, 16, 17, 18, 19, 20]
# 取 11-20 间隔 2 位
>>> L[10:21:2]
[10, 12, 14, 16, 18, 20]
>>> L[10:21:1]
[10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]
# 取 11-20 间隔 3 位
>>> L[10:21:3]
[10, 13, 16, 19]
# 取整个
>>> L[:]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99]
  • tuple 也是一种 list,区别是不可变

    1
    2
    >>> (0, 1, 2, 3, 4, 5)[:3]
    (0, 1, 2)
  • 字符串 也是 list

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

迭代 iteration

给定一个 list 或 tuple,我们可以通过for循环来遍历这个list或tuple,这种遍历我们称为迭代。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
>>> d = {'a': 1, 'b': 2, 'c': 3}
# 打印 key
>>> for key in d:
... print(key)
...
a
b
c
# 打印 value
>>> for value in d.values():
... print(value)
...
1
2
3
# 打印key
>>> for key in d.keys():
... print(key)
...
a
b
c
# 打印 item = key + value
>>> for k, v in d.items():
... print(k, v)
...
a 1
b 2
c 3
  • dict 的存储方式跟 list 不太一样,可能出来的顺序不一样

  • 字符串也是可迭代对象

1
2
3
4
5
6
>>> for ch in 'ABC':
... print(ch)
...
A
B
C
  • 判断是否可以实例化,通过 collections 模块的 Iterable 类型判断
1
2
3
4
5
6
7
8
>>> from collections import Iterable
>>> isinstance('abc',Iterable)
True
>>> isinstance('123',Iterable)
True
# 123 为整数
>>> isinstance(123,Iterable)
False
  • 把 list 变成 索引-元素 enumerate
1
2
3
4
5
6
>>> for i, value in enumerate(['A', 'B', 'C']):
... print(i, value)
...
0 A
1 B
2
  • 同时引用两个变量
1
2
3
4
5
>>> for x, y in  [(1, 1), (2, 2)]:
... print(x, y)
...
1 1
2 2

列表生成式 List Comprehensions

列表生成式即List Comprehensions,是Python内置的非常简单却强大的可以用来创建list的生成式。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# 创建一个 1-9 的 list 
>>> list(range(1, 10))
[1, 2, 3, 4, 5, 6, 7, 8, 9]

# 创建一个 1-10 的 list
>>> list(range(1, 11))
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

# 数组[1*1 2*2 3*3 ... 10*10] 写法一
>>> L = []
>>> for x in range(1, 11):
... L.append(x * x)
...
>>> L
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

# 数组[1*1 2*2 3*3 ... 10*10] 写法二
>>> [x * x for x in range(1, 11)]
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

# 数组[1*1 2*2 3*3 ... 10*10 且为偶数]
>>> [x * x for x in range(1, 11) if x % 2 == 0]
[4, 16, 36, 64, 100]

# 两个字符串相加
>>> [m + n for m in 'ABC' for n in 'XYZ']
['AX', 'AY', 'AZ', 'BX', 'BY', 'BZ', 'CX', 'CY', 'CZ']

# 查看当前目录下的文件和文件夹 ls
>>> import os
>>> [d for d in os.listdir('.')]
['.config', 'Music', '.DS_Store', 'VirtualBox VMs', '.CFUserTextEncoding', '.sogouinput', 'Pictures', '.pylint.d', '.zprofile', '.zsh_history', 'Desktop', 'Library', '.ShadowsocksX-NG', 'blog', '.bash_sessions', 'Public', '.ssh', 'Movies', 'Applications', '.Trash', '.npm', 'Documents', '.vscode', 'Downloads', '.python_history', '.gitconfig', '.bash_history', '.viminfo']

# lower() 大写换小写
>>> L = ['Hello', 'Ashin']
>>> [s.lower() for s in L]
['hello', 'ashin']

生成器 generator

能一边循环又一边计算,称为生成器:generator

  • 创建 generator
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# list 产生方法[]
>>> L = [x * x for x in range(1, 11)]
>>> L
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

# generator 产生方法()
>>> g = (x * x for x in range(11))
>>> g
<generator object <genexpr> at 0x102a068d

# 可使用 next() 打印
>>> next(g)
0
>>> next(g)
1
>>> next(g)
4
>>> next(g)
9
>>> next(g)
16
>>> next(g)
25
>>> next(g)
36
>>> next(g)
49
>>> next(g)
64
>>> next(g)
81
>>> next(g)
100

# 当没有元素时,出现 StopIteration
>>> next(g)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
  • 使用 for 迭代
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
>>> g = (x * x for  x in range(11))
>>> for n in g:
... print(n)
...
0
1
4
9
16
25
36
49
64
81
100
  • 斐波拉契
1
2
3
4
5
6
7
8
9
10
def  fib(max):
n, a, b = 0, 0, 1
while n < max:
print(b)
# a = b, b = a + b
a, b = b, a + b
n = n + 1
return 'done'

fib(10)

使用yield 创建 generator,遇到yield就会中断

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
>>> def odd():
... print('step 1')
... yield 1
... print('step 2')
... yield(3)
... print('step 3')
... yield(5)
...
>>> o = odd()
>>> next(o)
step 1
1
>>> next(o)
step 2
3
>>> next(o)
step 3
5
>>> next(o)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration

迭代器 iterator

1
2
3
4
5
6
7
8
9
>>> list=[1,2,3,4]
# 创建迭代器对象
>>> it = iter(list)
# 输出迭代器的下一个元素
>>> print (next(it))
1
>>> print (next(it))
2
>>>

可直接作用于for循环的数据类型有以下几种:

  • 集合数据类型,如listtupledictsetstr

  • generator,包括生成器和带yieldgenerator function

这些可以直接作用于for循环的对象统称为可迭代对象:Iterable,可以使用isinstance()判断一个对象是否是Iterable对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 可以被next()函数调用并不断返回下一个值的对象称为迭代器:Iterator
>>> from collections import Iterable
# 用 isinstance() 判断一个对象是否是Iterator对象
>>> isinstance([], Iterable)
True
>>> isinstance({}, Iterable)
True
>>> isinstance((), Iterable)
True
>>> isinstance('abc', Iterable)
True
>>> isinstance(100, Iterable)
False
>>> isinstance((x * x for x in range(1, 10)), Iterable)
True

小结

  • 能 for 循环的对象,都是 Iterable (能迭代)类型

  • 能 next( ) 的对象,都是 Iteraor (迭代器)类型

  • 集合数据类型如listdictstr等是Iterable但不是Iterator,不过可以通过iter()函数获得一个Iterator对象

1
2
3
4
5
6
7
8
9
10
11
>>> from collections import Iterator
>>> isinstance((x for x in range(10)), Iterator)
True
# [] {} () 不是 Iterator
>>> isinstance([], Iterator)
False
>>> isinstance('abc', Iterator)
False
# 通过 iter( ) 获得 Iterator
>>> isinstance(iter('abc'), Iterator)
True

五、函数式编程

高阶函数 Higher-order function

变量指向函数

1
2
3
4
5
6
7
8
>>> f = abs
>>> f(-1)
1
>>> abs
<built-in function abs>
# f() 和 abs() 一样,相当于给 abs 取别名 f
>>> f
<built-in function abs>

函数名也是变量

传入函数

一个函数就可以接收另一个函数作为参数,这种函数就称之为高阶函数

1
2
3
4
5
>>> def add(x, y, f):
... return f(x) + f(y)
...
>>> add(-5, 6, abs)
11

map

map(function, Iterable) map()函数接收两个参数,一个是函数,一个是Iterablemap将传入的函数依次作用到序列的每个元素,并把结果作为新的Iterator返回。

1
2
3
4
5
6
7
8
9
>>> def f(x):
... return x * x
...
>>>
# map(f, [])
>>> r = map(f, [1, 2 , 3, 4, 5, 6, 7, 8, 9])
# list(r) 将 r 计算并返还为序列,因为 r 是一个 Iterator
>>> list(r)
[1, 4, 9, 16, 25, 36, 49, 64, 81]

reduce

reduce 把一个函数作用在序列上,进行累f( ),如reduce(f, [x1, x2, x3, x4]) = f(f(f(x1, x2), x3), x4)

1
2
3
4
5
6
7
>>> from functools import reduce
>>> def add(x, y):
... return x + y
...
# 使用 reduce 进行累加
>>> reduce(add,[1, 3, 5, 7, 9])
25

filter

filter()函数用于过滤序列

1
2
3
4
5
>>> def is_odd(n):
... return n % 2 == 1
...
>>> list(filter(is_odd, [1, 2, 4, 5, 6, 9, 10, 15]))
[1, 5, 9, 15]
  • filter()函数返回的是一个Iterator,也就是一个惰性序列,所以要强迫filter()完成计算结果,需要用list()函数获得所有结果并返回list

sorted

sorted()函数就可以对list进行排序

1
2
3
4
5
>>> sorted([36, 5, -12, 9, -21])
[-21, -12, 5, 9, 36]
# 按绝对值大小排列
>>> sorted([36, 5, -12, 9, -21], key=abs)
[5, 9, -12, -21, 36]
1
2
3
4
5
6
7
8
9
>>> sorted(['bob', 'about', 'Zoo', 'Credit'])
# 按照 ASCII 大小排列
['Credit', 'Zoo', 'about', 'bob']
>>> sorted(['bob', 'about', 'Zoo', 'Credit'], key=str.lower)
# str.lower 变成小写再排
['about', 'bob', 'Credit', 'Zoo']
>>> sorted(['bob', 'about', 'Zoo', 'Credit'], key=str.lower, reverse=True)
# 倒序 reverse = True
['Zoo', 'Credit', 'bob', 'about']

返回函数

函数作为返回值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
>>> def lazy_sum(*args):
... def sum():
... ax = 0
... for n in args:
... ax = ax + n
... return ax
... return sum
...
>>> f = lazy_sum(1, 3, 5, 7, 9)
# lazy_sum( ) 返回一 sum 函数
>>> f
<function lazy_sum.<locals>.sum at 0x10171df80>
# f()
>>> f()
25
# 相同的传入参数
>>> f1 = lazy_sum(1, 3, 5, 7, 9)
>>> f2 = lazy_sum(1, 3, 5, 7, 9)
>>> f1()
25
>>> f2()
25
>>> f1 == f2
False
# f1 返回在0x1017210e0函数
>>> f1
<function lazy_sum.<locals>.sum at 0x1017210e0>
# f2 返回在0x101721050函数
>>> f2
<function lazy_sum.<locals>.sum at 0x101721050>

闭包

匿名函数

不需要显式地定义函数,直接传入匿名函数更方便

1
2
3
# 关键字 lambda 表示匿名函数,冒号前面的 x 表示函数参数
>>> list(map(lambda x: x * x, [1, 2, 3, 4, 5, 6, 7, 8, 9]))
[1, 4, 9, 16, 25, 36, 49, 64, 81]
  • 匿名函数限制,只能有一个表达式,不用写return,返回值就是该表达式的结果

  • 匿名函数作为返回值

    1
    2
    3
    4
    5
    6
    7
    8
    >>> def build(x, y):
    ... return lambda: x * x + y * y
    ...
    >>> build(5,5)
    <function build.<locals>.<lambda> at 0x101729170>
    >>> f = build(5,5)
    >>> f()
    50

装饰器 decorator

偏函数 Partial function

六、模块

  • 在 Python 中,一个.py文件就称之为一个模块Module
  • 按目录来组织模块的方法,称为包Package
  • \init__.py 包名下必要文件
  • import

使用模块

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# 下面这条语句可让 xxx.py 直接在Unix/Linux/Mac上运行
#!/usr/bin/env python3

# 采用标准的 utf-8 编码
# -*- coding: utf-8 -*-

# 模块文档解释
' a test module '

# 作者信息
__author__ = 'Michael Liao'

# 导入 sys 模块
import sys

def test():
args = sys.argv
if len(args)==1:
print('Hello, world!')
elif len(args)==2:
print('Hello, %s!' % args[1])
else:
print('Too many arguments!')


# if 测试可以让一个模块通过命令行运行时执行一些额外的代码,最常见的就是运行测试
# 如果我们想在模块被引入时,模块中的某一程序块不执行,我们可以用__name__属性来使该程序块仅在该模块自身运行时执行。
if __name__=='__main__':
test()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
sound/                          顶层包
__init__.py 初始化 sound 包
formats/ 文件格式转换子包
__init__.py
wavread.py
wavwrite.py
aiffread.py
aiffwrite.py
auread.py
auwrite.py
...
effects/ 声音效果子包
__init__.py
echo.py
surround.py
reverse.py
...
filters/ filters 子包
__init__.py
equalizer.py
vocoder.py
karaoke.py
...

作用域

  • public: abc

  • private: _abc

安装第三方模块

  • pip install + module.name
  • Anaonda

七、面向对象编程 OOP

面向对象编程——Object Oriented Programming,简称 OOP,是一种程序设计思想。OOP 把对象作为程序的基本单元,一个对象包含了数据操作数据的函数

类和实例

  • 类定义:通过class关键字 ,类名通常是大写,如Classname(object) object表示所有类都会继承的类
  • 创建实例:类名( )
  • __init__(self, , ) 注意前后是两个_ _ 绑定需要的属性
    • __init__(self, , )第一个参数是self 表示创建实例的本身,不需要传递参数,其他绑定的属性需要传递。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 定义类通过 class 关键字
class Student(object):
# 这里是初始化将要创建的实例的属性 __init__(self, , )
def __init__(self, name, score):
self.name = name
self.score = score

# 定义实例需要的分数
def print_score(self):
print('%s:%s' % (self.name, self.score))

# 创建实例 Classname()
tim = Student('Tim Fon', 59)
# 使用实例
tim.print_score()

>>>
Tim Fon:59
>>> tim
# tim 是指向 Student 的实例
<__main__.Student object at 0x1029aea58>
# 可给 tim(实例变量) 绑定一个 name 属性
>>> tim.name
'Tim Fon'

访问限制

  • 私有变量 private:在变量名前添加两个短杆__

继承

  • Class: Subclass, Baseclass,Superclass
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# 创建Animal类
class Animal(object):
def run(self):
print('Animal is running...')
# 创建 Dog 类并继承 Animal
class Dog(Animal):
pass

class Cat(Animal):
def run(self):
print('Cat is running...')

def eat(self):
print('Eating fish...')

dog = Dog()
# dog 继承 Animal 的 run()
dog.run()

# Cat 覆盖了父类 Animal 的方法 run()
cat = Cat()
cat.run()
cat.eat()

>>>
Animal is running...
Cat is running...
Eating fish...

多态

class 可以理解为数据类型,例如跟str, list, dict 没什么两样。

判断一个变量是否是某个类型可以用isinstance()判断

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
>>> a = list() # a是list类型
>>> b = Animal() # b是Animal类型
>>> c = Dog() # c是Dog类型
>>> isinstance(a, list)
True
>>> isinstance(b, Animal)
True
>>> isinstance(c, Dog)
True
# Dog 继承 Animal 因为 Dog 是 Animal 的一种
>>> isinstance(c, Animal)
True
# b 是 Animal 是 Dog 的父类,不能倒
>>> isinstance(b, Dog)
False
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
class Animal(object):
def run(self):
print('Animal is running...')
# 定义一个 run_twice()函数
def run_twice(animal):
animal.run()
animal.run()
# 定义一个 Cat 类
class Cat(Animal):
def run(self):
print('Cat is running...')

def eat(self):
print('Eating fish...')
# 定义一个 Tortoise 类
class Tortoise(Animal):
def run(self):
print('Tortoise is running slowly...')

cat = Cat()


run_twice(Animal())
run_twice(Cat())
run_twice(Tortoise())


>>>
Animal is running...
Animal is running...
Cat is running...
Cat is running...
# 继承了 Animal 的 run_twice()方法
Tortoise is running slowly...
Tortoise is running slowly...
  • 开闭原则

    • 对扩展开放:允许新增Animal
    • 对修改封闭:不需要修改依赖Animal类型的run_twice()函数
  • 继承不严格要求

获取对象信息

判断对象类型 type( )

1
2
3
4
5
6
7
8
9
10
11
12
13
# 判断 基本类型
>>> type(123)
<class 'int'>
>>> type('123')
<class 'str'>
>>> type(None)
<class 'NoneType'>
# 判断 函数
>>> type(abs)
<class 'builtin_function_or_method'>
# 判断类
>>> type(dog)
<class '__main__.Dog'>
  • type()函数返回对应的Class类型
1
2
3
4
5
6
7
8
9
10
>>> type(123)==type(456)
True
>>> type(123)==int
True
>>> type('abc')==type('123')
True
>>> type('abc')==str
True
>>> type('abc')==type(123)
False
  • 判断一个对象,使用types模块 或 isinstance()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 使用 types 模块
>>> import types
>>> def fn():
... pass
...
# 判断
>>> type(fn)==types.FunctionType
True
>>> type(abs)==types.BuiltinFunctionType
True
>>> type(lambda x: x)==types.LambdaType
True
>>> type((x for x in range(10)))==types.GeneratorType
True
  • 获得一个对象的所有属性和方法,使用dir()
1
2
3
4
5
6
7
8
9
# 返还 str 的所有属性和方法
>>> dir('asd')
['__add__', '__class__', '__contains__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getnewargs__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mod__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__rmod__', '__rmul__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'capitalize', 'casefold', 'center', 'count', 'encode', 'endswith', 'expandtabs', 'find', 'format', 'format_map', 'index', 'isalnum', 'isalpha', 'isascii', 'isdecimal', 'isdigit', 'isidentifier', 'islower', 'isnumeric', 'isprintable', 'isspace', 'istitle', 'isupper', 'join', 'ljust', 'lower', 'lstrip', 'maketrans', 'partition', 'replace', 'rfind', 'rindex', 'rjust', 'rpartition', 'rsplit', 'rstrip', 'split', 'splitlines', 'startswith', 'strip', 'swapcase', 'title', 'translate', 'upper', 'zfill']
# 类似__xxx__的属性和方法在Python中都是有特殊用途的
>>> len('asd')
3
# 普通方法
>>> 'asd'.upper()
'ASD'
  • getattr()
  • setattr()
  • hasattr()

注意

  • 实用属性 和 类属性 不要同名

使用 __slots__

Python允许在定义class的时候,定义一个特殊的__slots__变量,来限制该class实例能添加的属性:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
>>> class Student(object):
# 用tuple定义允许绑定的属性名称
... __slots__ = ('name', 'age')
...
>>> s = Student()
>>> s.name = 'Ashin'
>>> s.age = 18
# score 没有放在 slots 中,所以不能被绑定
>>> s.socore = 100
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'Student' object has no attribute 'socore'

# slots 对继承的子类无影响
>>> class GoodStudent(Student):
... pass
...
>>> g = GoodStudent()
>>> g.score = 100

使用 @property

多重继承

__str__

__getitem__

__iter__

__getattr__

__call__

定义枚举

通过Enum()实现

type( )

metaclass( )

八、错误、调试、测试

错误处理

try...except...finally

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# 将可能会出错的代码放置 try 中
try:
print('try...')
r = 10 / 0
print('result:', r)
# 如果执行错误 跳转 except 语句
except ZeroDivisionError as e:
print('except:', e)
# finally 在 except 后执行,若有 一定会被执行,可以不用 fanally 语句
finally:
print('finally...')
print('END')
>>>
try...
except: division by zero
finally...
END
------------------------
# 令 r = 10 / 2
try:
print('try...')
r = 10 / 2
print('result:', r)
except ZeroDivisionError as e:
print('except:', e)
# finally 在 except 后执行,except 不一定执行,若有 finally 一定会被执行,可以不用 fanally
finally:
print('finally...')
print('END')
>>>
try...
result: 5.0
finally...
END
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
 try:
print('try...')
r = 10 / int('2')
print('result:', r)
except ValueError as e:
print('ValueError:', e)
except ZeroDivisionError as e:
print('ZeroDivisionError:', e)
# 当没有出错时,else 后面的语句会自动执行
else:
print('no error!')
# finally 当然也要
finally:
print('finally...')
print('END')
>>>
try...
result: 5.0
no error!
finally...
END

调用栈

错误没被捕获,会一直向上抛,最后解释器捕获,并打印一个错误,然后退出。

记录错误

内置logging模块,可打印错误,并正常退出。

抛出错误

根据需要定义一个错误的 Class 使用raise抛出

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 定义一个错误类 FooError 继承 ValueError
class FooError(ValueError):
pass
def foo(x):
n = int(x)
if n==0:
# 使用 raise 抛出
raise FooError('invalid value: %s' % x)
return 10 / n
foo('0')
>>>
Traceback (most recent call last):
File "err_throw.py", line 11, in <module>
foo('0')
File "err_throw.py", line 8, in foo
raise FooError('invalid value: %s' % s)
__main__.FooError: invalid value: 0
  • rasie 是向上抛错误

调试

使用 print( ) 打印

将可能会出错的地方使用print( )打印出来

断言 assert

使用assert代替print

使用 logging

使用logging代替print

使用 pdb

输入python -m pdb filename.py进入调试

  • 输入 1 查看代码
  • 输入 n 单步执行
  • 输入 p + 变量名 查看变量
  • 输入 q 退出调试

使用pdb.set_trace( )

1
2
3
4
5
6
7
# 导入 pdb 
import pdb
s = '0'
n = int(s)
# 运行到这里会自动暂停,进入调试模式
pdb.set_trace()
print(10 / n)

使用 IDE

  • 排叉 PyCharm
  • VS Code (微软良心)

单元测试

TDD Test Driven Development 测试驱动开发

运行测试

1
2
if __name__ == '__main__':
unittest.main()
  • 加参数 python -m unittest mydict_test

setUp tearDown

文档测试

docktest

九、IO编程

IO: Input/Output 输入和输出

Stream 流

异步IO 同步IO

文件读写

1
2
3
4
5
6
# 使用 open() 打开
>>> f = open('/Users/ashin/Desktop/text.txt','r')
# read() 读写,read() 是一次性读写,注意文件的大小(内存够?)
>>> f.read()
# close() 关闭
>>> f.close

使用 with 语句,不必 try finally ,也不必使用 f.close( )

1
2
with open('/Users/ashin/Desktop/text.txt', 'r') as f:
print(f.read())

二进制文件

1
2
3
4
5
# binary 文件读取,rb 方式打开
>>> f = open('/Users/ashin/Desktop/test.png', 'rb')
>>> f.read()
# 十六进制显示
b'\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x0b@\x00\x00\x07\x08\x08\x06\x00\x00\x00\xaf\xe4@\x15\x00\x00\x0cMiCCPICC Profile\x00\x00H\x89\x95W\x07\\\x93G\x1b\xbfwd\x92\xb0\x02\x11\x90\x11\xf6\x12e\x13@F\x08+\x82\x80LATB\x12H\x181&\x04\x117R\xaa`\xdd"\njE\xab"\x16\xadV@\xeaD\xad\xb3(n\xeb(JQ\xa9\xd4b\x15\x17*\xdfe@\xad\xfd\xc6\xef{

字符编码

1
2
3
4
# 加入 encoding 参数 errors = 'ignore'
>>> f = open('/Users/Ashin/Desktop/text.txt','r',encoding = 'gbk',errors = 'ignore')
>>> f.read()
'浣犲ソ'

写文件

使用w模式(write 覆盖)

使用a模式(append 追加)

f.write('Hello Python!')

StringIO

StringIO 在内存中读写 str

1
2
3
4
5
6
7
8
9
>>> from io import StringIO
# 创建 StringIO
>>> f = StringIO()
# 写入
>>> f.write('Hello StrngIO')
13
# 使用 getvalue() 获得写入的 str
>>> print(f.getvalue())
Hello StrngIO
1
2
3
4
5
6
7
8
9
10
11
12
13
>>> from io import StringIO
# 使用 str 初始化 StringIO
>>> f = StringIO('Hello!\nHi!\nGoodbye!')
>>> while True:
... s = f.readline()
# '' 结尾
... if s == '':
... break
... print(s.strip())
...
Hello!
Hi!
Goodbye!

BytesIO

内存中二进制文件操作

1
2
3
4
5
6
>>> from io import BytesIO
>>> f = BytesIO()
>>> f.write('中文'.encode('utf-8'))
6
>>> print(f.getvalue())
b'\xe4\xb8\xad\xe6\x96\x87'
1
2
3
4
5
>>> from io import BytesIO
# 写入的是 utf-8 编码后的 BytesIO
>>> f = BytesIO(b'\xe4\xb8\xad\xe6\x96\x87')
>>> f.read()
b'\xe4\xb8\xad\xe6\x96\x87'

环境变量

s.environ 获取环境变量

操作文件和目录

1
2
3
4
5
6
7
8
9
10
# 查看当前绝对路径
>>> os.path.abspath('.')

# 路径表示
>>> os.path.join('/Users/ashin/Desktop/','creatdir')
'/Users/ashin/Desktop/creatdir'
# 创建目录
>>> os.mkdir('/Users/ashin/Desktop/creatdir')
# 删除目录
>>> os.rmdir('/Users/ashin/Desktop/creatdir')

os.path.split() 遍历文件

os.path.splitext() 获取文件后缀名

os.rename() 重命名

os.remove() 删除文件

检索文件 listdir( ) isfile( ) 和splitext( )

1
[x for x in os.listdir('.') if os.path.isfile(x) and os.path.splitext(x)[1]=='.py']

序列化

pickle.dumps( )

pickle.dump( )

pickle.load( )

JSON

JSON XML

JSON Python
object dict
array list
string str
number (int) int
number (real) float
true True
false False
null None

dumps() 返回的 str 为标准的 JSON

十、进程和线程

多线程

Unix/Linux中 fork( )调用 调用一次,返回两次,子进程返回 0 ,父进程返回 每个子进程的 ID

fork( )

getppid( )

1
2
3
4
5
6
7
8
9
10
11
import os
# 子进程调用getppid()到父进程的ID
print('Process (%s) start...' % os.getpid())
# Only works on Unix/Linux/Mac:
# fork()调用
pid = os.fork()
# 返回两次,子进程返回 0 ,父进程返回子进程的 ID
if pid == 0:
print('I am child process (%s) and my parent is %s.' % (os.getpid(), os.getppid()))
else:
print('I (%s) just created a child process (%s).' % (os.getpid(), pid))

multiprocessing模块 Windows多进程

start( )

join( ) 子进程结束后再继续运行

close( )

Pool 进程池 批量创建子进程

subprocess模块 控制其输入输出

注意:编码问题,Windows中文版 系统编码是GBK,而 Python 的编码我们一般默认为 UTF-8

进程间通信

Process Queue Pipes

ThreadLocal

import threading 再创建ThreadLocal对象

进程和线程

分布式进程

十一、正则表达式

  • 给定字符:精确匹配
  • \d匹配一个数字
  • \w匹配一个字母
  • .匹配任意一个字符
  • *匹配任意个字符
  • \s一个空格(或 Tab)
  • +至少一个字符
  • ?表示 0 个或 1 个字符
  • {n}表示 n 个字符
  • {n,m}表示 n-m 个字符
  • 使用\转义特殊字符

组合起来,例如:\d{3}\s+\d{3,8}

  • \d{3}表示匹配3个数字,例如'010'
  • \s+表示至少有一个空格,例如匹配' '' '
  • d{3,8}表示有3-8个数字

使用[] 表示范围

  • [0-9a-zA-Z\_]可以匹配一个数字、字母、下划线
  • [0-9a-zA-Z\_]+可以匹配至少一个数字、字母、下划线组成的字符串,比如aaa100_ss
  • [a-zA-Z\_][0-9a-zA-Z\_]*字母或下划线开头,后接任意个数字、字母
  • [a-zA-Z\_][0-9a-zA-Z\_]{0 ,19}字母或下划线开头, 后接1-19 个数字、字母(一个 + 后接最多 19 个)
  • A|B 匹配 A 或 B,(P|p)thon可以匹配 Python 和 python
  • ^表示行开头
  • ^\d表示以数字开头
  • $表示行结束
  • \d$表示以数字结束

re 模块

使用r不用考虑 Python 自身的转义

match( ) 匹配检查

切分字符串

1
2
3
4
5
6
7
8
9
10
11
# 按空格切分为数组
>>> 'a b c'.split(' ')
['a', 'b', '', '', 'c']
# 导入 re 模块
>>> import re
# 规范数组
>>> re.split(r'\s+', 'a b c')
['a', 'b', 'c']
# 规范数组 去掉 空格 英文逗号 英文分号
>>> re.split(r'[\s\,\;]+', 'a,b;; c d')
['a', 'b', 'c', 'd']

分组

使用()包裹

例如:^(\d{3})-(\d{3,8})$分别定义了两个组,可以直接从匹配的字符串中提取出区号和本地号码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
>>> m = re.match(r'^(\d{3})-(\d{3,8})$', '010-12345')
>>> m
<re.Match object; span=(0, 9), match='010-12345'>
# 分组查看
>>> m.groups()
('010', '12345')
# group(0)原始字符串为 0
>>> m.group(0)
'010-12345'
# 组(1)
>>> m.group(1)
'010'
# 组(2)
>>> m.group(2)
'12345'

贪婪匹配

1
2
3
4
5
6
# \d+ 会尽量多匹配后面的 0 (默认方式)
>>> re.match(r'^(\d+)(0*)$', '102300').groups()
('102300', '')
# \d+? 尽量少匹配后面的 0,加上 ? 为非贪婪匹配
>>> re.match(r'^(\d+?)(0*)$', '102300').groups()
('1023', '00')

编译

到一个正则重复很多次,可预编译正则表达式

十二、常用内建模块

“Batteries included” Python 内部集成很多模块

datetime

datetime 时间和日期处理的标准库。

  • 获取当前时间
1
2
3
4
5
6
7
8
9
# datetime 模块包含 datetime 类,如果是 import datetime,引用需要使用全名 datetime.datetime
>>> from datetime import datetime
# datetime.now() 返回当前日期和时间
>>> now = datetime.now()
>>> print(now)
2019-10-22 15:57:31.668157
#
>>> type(now)
<class 'datetime.datetime'>
  • 获取指定日期和时间
1
2
3
4
5
6
# 导入 datetime 类
>>> from datetime import datetime
# 指定时期
>>> dt = datetime(2015, 4, 19, 12, 20)
>>> print(dt)
2015-04-19 12:20:00

timestamp

在计算机当中,时间使用数字表示,把 1970 年 1 月 1 日 00:00:00 UTC+00:00 时区的时刻称为 epoch time,记为0(1970 年以前的时间 timestamp 为负数),当前时间就是相对于 epoch time 的秒数,称为timestamp。

1
2
3
timestamp = 0 = 1970-1-1 00:00:00 UTC+0:00
# beijing time
timestamp = 0 = 1970-1-1 08:00:00 UTC+8:00
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
>>> from datetime import datetime
>>> dt = datetime(2015, 4, 19, 12, 20)
>>> print(dt)
2015-04-19 12:20:00
# 把 datetime 转换为 timestamp
>>> dt.timestamp()
# 小数位表示毫秒级
1429417200.0
# 把 timestamp 转换为 datetime,使用 datetime 提供的 fromtimestamp() 方法
>>> tt =1429417200.0
>>> print(datetime.fromtimestamp(tt))
2015-04-19 12:20:00
# 转换为 UTC 时间,默认是系统时区
>>> print(datetime.utcfromtimestamp(tt))
2015-04-19 04:20:00

str 时间

1
2
3
4
5
6
7
8
9
10
# 使用 strptime() 将 str 转换为 datetime,无时区
>>> cday = datetime.strptime('2015-6-1 18:19:59', '%Y-%m-%d %H:%M:%S')
>>> print(cday)
2015-06-01 18:19:59

# 使用 strftime() 将 datetime() 转换为 str,可定义格式
# https://docs.python.org/3/library/datetime.html#strftime-strptime-behavior
>>> now = datetime.now()
>>> print(now.strftime('%a, %b %d %H:%M'))
Tue, Oct 22 16:23

datetime 加减

1
2
3
4
5
6
7
8
9
10
11
12
# 导入 timedelta 类
>>> from datetime import datetime, timedelta
>>> now = datetime.now()
>>> now
datetime.datetime(2019, 10, 22, 21, 16, 10, 149480)
# + 10小时
>>> now + timedelta(hours=10)
datetime.datetime(2019, 10, 23, 7, 16, 10, 149480)
>>> now - timedelta(days=1)
datetime.datetime(2019, 10, 21, 21, 16, 10, 149480)
>>> now + timedelta(days=2, hours=12)
datetime.datetime(2019, 10, 25, 9, 16, 10, 149480)

本地时间转换为UTC时间

1
2
3
4
5
6
7
8
9
10
11
# 导入 datetime, timedelta, timezone
>>> from datetime import datetime, timedelta, timezone
# 设置一个时区为 UTC+8:00
>>> tz_utc_8 = timezone(timedelta(hours=8))
>>> now = datetime.now()
>>> now
datetime.datetime(2019, 10, 22, 21, 20, 5, 973507)
# tzinfo时区属性 默认为None
>>> dt = now.replace(tzinfo=tz_utc_8)
>>> dt
datetime.datetime(2019, 10, 22, 21, 20, 5, 973507, tzinfo=datetime.timezone(datetime.timedelta(seconds=28800)))

时区转换

1
2
3
4
5
6
7
8
9
10
11
12
# utcnow()获取 UTC 时间,并设置为 UTC+0:00
>>> utc_dt = datetime.utcnow().replace(tzinfo=timezone.utc)
>>> print(utc_dt)
2019-10-22 13:28:05.587079+00:00
# astimezone()将转换时区为北京时间
>>> bj_dt = utc_dt.astimezone(timezone(timedelta(hours=8)))
>>> print(bj_dt)
2019-10-22 21:28:05.587079+08:00
# astimezone()将转换时区为东京时间
>>> tokyo_dt = utc_dt.astimezone(timezone(timedelta(hours=9)))
>>> print(tokyo_dt)
2019-10-22 22:28:05.587079+09:00

collections

collections是Python内建的一个集合模块,提供了许多有用的集合类。

namedtuple

namedtuple(‘名称’, [属性list])

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
>>> from collections import namedtuple
# namedtuple 自定义 tuple 对象,规定 tuple 个数
# namedtuple('名称', [属性list]):
>>> Point = namedtuple('Point', ['x', 'y'])
>>> p = Point(3,6)
>>> p.x
3
>>> p.y
6
# 可以验证 Point 就是 tuple 的一种子类
>>> isinstance(p, Point)
True
>>> isinstance(p, tuple)
True

# 当我们尝试传递更多的参数时
>>> pp = Point(3, 4, 5)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: __new__() takes 3 positional arguments but 4 were given
>>> pp = Point(3, 4, 5, 6)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: __new__() takes 3 positional arguments but 5 were given

deque

list 是线性存储,插入和删除效率低下,使用 deque 可实现高效的插入和删除(队列、栈 双向链表)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
>>> from collections import deque
>>> q = deque(['a', 'b', 'c'])
>>> q.append('x')
# 增加在左边
>>> q.appendleft('y')
>>> q
deque(['y', 'a', 'b', 'c', 'x'])
# popleft() 删除左边第一个
>>> q.popleft()
'y'
>>> q
deque(['a', 'b', 'c', 'x'])
# 删除右边
>>> q.pop()
'x'
>>> q
deque(['a', 'b', 'c'])

defaultdict

使用 defaultdict 构造一个默认值,当引用的 dict-Key不存在时,返回默认值

1
2
3
4
5
6
7
8
9
10
11
>>> from collections import defaultdict
# 构造默认返回值
>>> dd = defaultdict(lambda: 'N/A')
# dd['key1'] = 'abc'
>>> dd['key1'] = 'abc'
>>> dd['key1']
'abc'
>>> dd['key2']
'N/A'
>>> dd['key3']
'N/A'

OrderedDict

dict-Key 是无序的,可使用 OrderedDict 排序(OrderedDict 是按照插入的顺序 )FIFO 先进先出

1
2
3
4
5
6
>>> od = OrderedDict()
>>> od['z'] = 1
>>> od['x'] = 3
>>> od['y'] = 2
>>> list(od.keys())
['z', 'x', 'y']

ChainMap

###Counter

1
2
3
4
5
6
7
8
>>> from collections import Counter
>>> c = Counter()
# 统计 'programming' 中字符出现个数
>>> for ch in 'programming':
... c[ch] = c[ch] + 1
...
>>> c
Counter({'r': 2, 'g': 2, 'm': 2, 'p': 1, 'o': 1, 'a': 1, 'i': 1, 'n': 1})

base64

Base64 是用 64 个字符来表示任意二进制数据,二进制编码

原理:

1
2
#  base64 包含 26 个大写字母,26 个小写字母,10 个数字,+ 和 /
['A', 'B', 'C', ... 'a', 'b', 'c', ... '0', '1', ... '+', '/']

对二进制进行编码,每 3 个字节一组,一共为 3 x 8 = 24 bit,再划分为 4 组,每组 6 bit,n1, n2, n3, n4 通过查表得到对应的字符

若二进制不是 3 的倍数,用\x00字节在末尾补足后,再在编码的末尾加上1个或2个=号,表示补了多少字节,解码的时候,会自动去掉。

1
2
3
4
5
>>> import base64
>>> base64.b64encode(b'binary\00String')
b'YmluYXJ5AFN0cmluZw=='
>>> base64.b64decode(b'YmluYXJ5AFN0cmluZw==')
b'binary\x00String'
  • 在 URl Safe中,把字符+/分别变成-_ (+ / 不能直接作为参数)

  • 在 URL、Cookie 中,=会被自动去掉

hashlib

Python 的 hashlib 提供了常见的摘要算法,如 MD5,SHA1 等等。

摘要算法、哈希算法、散列算法:通过函数把任意长度的数据转换一个长度固定的数据串(通常 16 进制)

md5

MD5 是最常见的摘要算法,速度很快,生成结果是固定的 128 bit 字节,通常用一个 32 位的 16 进制字符串表示。

1
2
3
4
5
6
import hashlib
md5 = hashlib.md5()
md5.update('how to use md5 in python hashlib?'.encode('utf-8'))
print(md5.hexdigest())
>>>
d26a53750bc40b38b65a520292f69306

SHA1

SHA1的结果是 160 bit 字节,通常用一个 40 位的 16 进制字符串表示。比 SHA1 更安全的算法是 SHA256 和SHA512,不过越安全的算法不仅越慢,而且摘要长度更长。

1
2
3
4
5
6
7
import hashlib
sha1 = hashlib.sha1()
sha1.update('how to use sha1 in '.encode('utf-8'))
sha1.update('python hashlib?'.encode('utf-8'))
print(sha1.hexdigest())
>>>
2c76b57293ce30acef38d98f6046927161b46a44

HMAC

HMAC:Keyed-Hashing for Message Authentication 在计算哈希的过程中,常常把 Key 混入计算 md5(message + salt)

1
2
3
4
5
6
7
8
>>> import hmac
>>> message = b'Hello, world!'
>>> key = b'secret'
>>> h = hmac.new(key, message, digestmod='MD5')
>>> # 如果消息很长,可以多次调用h.update(msg)
>>> h.hexdigest()
# 与原来的 hash 长度一致
'fa4ee7d173f2d97ee79022d1a7355bcf'

itertools

itertools.count( ) 创建无线迭代器,停不下来

1
2
3
4
5
6
7
8
9
10
>>> import itertools
# count()
>>> natuals = itertools.count(1)
>>> for n in natuals:
... print(n)
...
1
2
3
...

cycle( ) 会把一个序列无限重复

1
2
3
4
5
6
7
8
9
10
11
12
>>> import itertools
>>> cs = itertools.cycle('ABC') # 注意字符串也是序列的一种
>>> for c in cs:
... print(c)
...
'A'
'B'
'C'
'A'
'B'
'C'
..

`repeat()负责把一个元素无限重复下去,不过如果提供第二个参数就可以限定重复次数:

1
2
3
4
5
6
7
>>> ns = itertools.repeat('A', 3)
>>> for n in ns:
... print(n)
...
A
A
A

takewhile()等函数根据条件判断来截取出一个有限的序列:

1
2
3
4
>>> natuals = itertools.count(1)
>>> ns = itertools.takewhile(lambda x: x <= 10, natuals)
>>> list(ns)
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

chain

chain( ) 吧一组的迭代对象串联起来,形成一个更大的迭代器

groupby

groupby( ) 找出重复元素

contextlib

urllib

Get

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# 导入 request 模块
from urllib import request
# 请求一个 URL,返回响应
with request.urlopen('https://ashin.wang') as f:
data = f.read()
print('Status:', f.status, f.reason)
for k, v in f.getheaders():
print('%s: %s' % (k, v))
print('Data:', data.decode('utf-8'))

>>>
Status: 200 OK
Server: GitHub.com
Content-Type: text/html; charset=utf-8
Strict-Transport-Security: max-age=31556952
Last-Modified: Tue, 22 Oct 2019 15:45:00 GMT
ETag: "5daf23fc-7a60"
Access-Control-Allow-Origin: *
Expires: Wed, 23 Oct 2019 03:23:02 GMT
Cache-Control: max-age=600
X-Proxy-Cache: MISS
X-GitHub-Request-Id: 266C:3E2C:D41CD:117E34:5DAFC53E
Content-Length: 31328
Accept-Ranges: bytes
Date: Wed, 23 Oct 2019 03:13:02 GMT
Via: 1.1 varnish
Age: 0
Connection: close
X-Served-By: cache-hkg17923-HKG
X-Cache: MISS
X-Cache-Hits: 0
X-Timer: S1571800382.364482,VS0,VE226
Vary: Accept-Encoding
X-Fastly-Request-ID: 07b0570da3a1cb2a1764bef1f3c77013b1192f3b
Data: <!DOCTYPE html><html lang="zh-CN">
...

Post

User-Agent 模拟浏览器

Handler

通过 ProxyHandlder 访问

XML

HTML Parser