前言

视频

我发现最近需要使用到python的时候越发变得频繁,由于初学的时间至今的确实有点长了,于是想着是时候在复习一下以前的知识点了。正所谓“温故而知新,可以为师”,确实再次看到教材的时候,才发现很多的知识点都已经忘记了。

为了方便后面需要再次复习的时候能够节约自己的学习时间成本,所以写的比较简略,当然也有一些自己常用的东西在里面。

第0章 环境

正所谓磨刀不误砍柴功,Windows安装python的环境太简单,此处忽略。

1.Linux环境

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
# 下载所需要的依赖软件
yum install wget zlib-devel bzip2-devel openssl-devel ncurses-devel sqlite-devel readline-devel tk-devel gcc make zlib zlib-devel libffi-devel -y

# 下载python
wget https://www.python.org/ftp/python/3.10.13/Python-3.10.13.tgz

# 解压安装包
tar -xvf Python-3.10.13.tgz

# 源码构建
cd Python-3.10.13
./configure --prefix=/usr/local/python3.10.13 # 指定安装路径

# 编译和安装
make && make install

# /usr/local/python3.10.13为编译好的路径
# /usr/local/python3.10.13/bin路径下的python3.10即为解释器

# 构建软链接
rm -f /usr/bin/python
ln -s /usr/local/python3.10.13/bin/python3.10 /usr/bin/python

# 修改yum的依赖
vim /usr/libexec/urlgrabber-ext-down
首行改为:#! /usr/bin/python2
vim /usr/bin/yum
首行改为:#! /usr/bin/python2

# 进入python
python

# 退出
exit()

2.pycharm使用

快捷键

1
2
3
4
5
ctrl + alt + s : 打开软件设置
ctrl + d : 复制当前行代码
shift + alt + 上/下 : 当前行代码上下移动
ctrl + shift + f10 : 运行代码
ctrl + d : 文件重命名

第一章 变量及简单的数据类型

0.常用的函数(遇到新的时候更新)

  • map()函数:
1
2
3
4
5
语法:
map(func,li)

func:指一个函数
li:指一个数据序列
  • help()函数:考试和比赛时候的开挂神器
1
help(int)

1.print()函数

1
2
3
print("hello world")

print("python",end=",")

2.标识符

  • 命名规则
    1. 数字,字母,下划线组成(不能数字开头)
    2. 不能使用关键字和函数名
  • 命名规范
    1. 变量名,方法名,模块名小写,多个单词_分割。my_name
    2. 常量大写。MY_AGE
    3. 类名大驼峰。MyName

3.type()返回数据类型

1
2
iCount = 10
print(type(iCount))

4.数据类型

字面量:在代码中,被写下来的的固定的值,称之为字面量

类型 描述
数字(Number) 支持整数(int)浮点数(float)复数(complex)布尔(bool) 整数(int),浮点数(float),复数(complex),如:4+3j,以j结尾表示复数,布尔(bool)
字符串(String) 描述文本的一种数据类型 字符串(string)由任意数量的字符组成
列表(List) 有序的可变序列 可有序记录一堆数据
元组(Tuple) 有序的不可变序列 可有序记录一堆不可变的数据集合
集合(Set) 无序不重复集合 可无序记录一堆不重复的数据集合
字典(Dictionary) 无序Key-Value集合 可无序记录一堆Key-Value型的Python数据集合

5.转义字符

1
2
3
4
5
6
7
8
\t	制表符(Tab)
\n 换行符
\' 转义成'
\" 转义成"
\\ 转义成\
\r 回到当前行的行首

\ 可以做续行符使用

6.大小写转换

1
2
3
4
5
6
7
8
9
皆返回新字符串对象

title() 首字母大写,其余小写
upper() 全部大写
lower() 全部小写


sStr = "hello world"
sStr.title()

7.去除空格

1
2
3
4
5
皆返回新字符串对象

strip() 去除全部空格
rstrip() 去除右边空格
lstrip() 去除左边空格

第二章 语法初步

1.缩进与pass

python遵循严格的缩进规则,指示代码之间的层级和包含关系。

pass:占位语句,不执行任何实际操作,保留代码形式上的完整性

2.操作符/运算符

1
2
3
4
5
6
7
1.复合运算符: +=,-=,*=,/=
2.整除与求模: //,%
3.比较运算符: >=,==,!=
4.逻辑运算符: and,or,not
5.求幂: **

优先级:算术 > 关系 > 逻辑 > 赋值

3.函数及函数的定义

  1. 函数使用

    1
    2
    3
    pow(2,8)		#2的8次方
    abs(-10) #绝对值
    round(2.5) #四舍五入
  2. 函数的定义

    1
    2
    3
    4
    5
    6
    7
    8
    def <函数名>(<形参>):
    <函数体>
    return <返回值列表>

    eg:
    def func(a,b,c):
    sum_abc = a + b + c
    return sum_abc

4.占位符

1
2
3
4
5
%d,%f,%s,%x(将以十六进制表示)

iNum1 = 10
fNum2 = 10.1
print("%10d,%10.2f"%(iNum1,fNum2))

5.进制

1
2
3
4
5
6
a = 0xff
b = 0b10111

bin(a) # 转二进制
oct(a) # 转八进制
hex(a) # 转十六进制

注意:转换成的是字符串

6.注释

1
2
3
4
1.#
2.'''hello world''' # 一般对:Python文件、类或方法进行解释

3."""hello world"""

7.获取用户输入

1
iNum_in = int(input("请输入一个正整数:"))

8.time模块

1
2
3
4
5
6
import time
a = time.time() # 获取当前时间,单位是秒。从1970年1月1日0时开始

import datetime
b = datetime.datetime.now()
print(b.year,b.month,b.day) # hour,minute,second

9.math模块

1
2
3
4
5
6
import math
from math import sqrt

print(math.floor(6.7)) # 向下取整
print(math.ceil(6.1)) # 向上取整
print(sqrt(9))

10.turtle模块

1
2
3
4
5
6
7
8
9
10
11
12
13
import turtle
t = turtle.pen()
turtle.forward(200)
turtle.backward(100)
turtle.circle(50) # 画圆
turtle.left(90)
turtle.right(180)
turtle.Screen().exitonclick() # 点击才退出


import turtle
t = turtle.Turtle() # 创建turtle对象
t.circle(50,180) # 画半圆

第三章 列表(list [])

  1. 列表的定义:列表是容纳一系列元素的容器。
  2. 列表的索引:0n-1;-1-n.

1.list的操作

1
2
3
4
5
6
7
8
一、元素的增加
a.append("hello")
a.insert(2,"male")

二、元素的删除
del a[2]
a.pop(2) # 默认弹出最后一个元素
a.remove(47)

2.名字绑定

1
2
a = [1,2,3]
b = a # a和b指向的是同一个列表对象

3.list排序

1
2
3
4
5
6
7
8
a = [2,1,3,5,4]
a.sort() # 递增排序
a.sort(reverse=True) # 递减排序
a.sort(key=len) # 按照长度进行排序,key的参数可以是自定义函数

b = sorted(a) #对a进行排序,并且返回一个新的列表,也有reverse,key参数

a.reverse() # 将原列表顺序颠倒

4.range()函数

1
2
3
4
5
生成[,)区间的整数
range(x)
range(x,y)

range(x,y,z) # [x,y)步长为z的整数数列

5.list统计

1
2
3
4
a = [1,2,3]
max(a)
min(a)
sum(a)

6.list快速生成

1
2
3
4
5
6
7
8
9
10
11
1.
a = [x**3+100 for x in range(1,11)]

2.
a = [[0]*8]*10 #注意:名字绑定

3.
a = [[r*c for c in range(8)] for r in range(10)] # 10 * 8的二维列表

4.
a = [x*x for x in range(10) if x % 3 == 0]

7.list运算

1
2
3
4
+	两个列表拼接
* 重复n次

innot in

8.list成员函数

1
2
3
4
5
6
7
a.count("b")
a.clear()
a.extend([1,2,3]) # 将列表拼接到a列表后,与+不同的是,+会返回新列表
a.index(47)
a.remove(47)
a.copy() #复制列表并返回
id() #获取地址

9.list切片

1
2
3
4
5
a = [x for x in range(10)]
a[1:10]
a[:10]
a[0:]
a[0::2]

第四章 数据类型及名字绑定

1.元组(tuple ())——只读的列表

1
2
3
用法与用途与列表完全相同
被创建后,其值可以获取但不能修改。所以不能使用remove(),sort()等
(4,) #用于告知解释器,这是由一个元素构成的元组,而不是括号括起来的整数4

2.集合(set {})

集合必须满足以下3点:

  1. 不重复
  2. 无序
  3. 元素为可哈希类型(一般是只读的)
  4. 不可以使用下标索引

set创建

1
2
3
4
5
6
7
1.
a = {1,2,3}
a = set([1,2,3])
a = set("hello") # h,e,l,o

2.
a = set([]) #创建空集合,否则为空字典

set的基本操作

1
2
3
4
5
6
a = {1,2,3}
a.add(20)
a.remove(1)
a.pop() #随机
len(),min(),max(),sum()
in,not in

set运算

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
s1 = {1,2,3}
s2 = {1,2}

1.子集与超集
s2任意元素是s1元素,则s2是s1子集。s1是s2超集。
s2.issubset(s1)
s1.issuperset(s2)

2.交集(&),并集(|),差集(-),补集(^)
# 交集
s1 & s2
s1.intersection(s2)
# 并集
s1 | s2
s1.union(s2)
# 差集
s1 - s2
s1.difference(s2)
# 补集
s1 ^ s2
s1.symmetric_difference(s2)

3.字节串(bytes)

背景:与硬件或其他计算机通信时,需要将其他数据类型转换成明确的二进制形式。

bytes是只读的“字节串”类型。

  • bytes的定义
1
2
3
4
5
6
7
8
buffer = b"abcde"
print(buffer,type(buffer),len(buffer))

buffer = b"\x11\xff\x77" # 表示3个字节构成的字节串为十六进制(0x)
print('%x'%(buffer[1]))

buffer = bytes(i+0x10 for i in range(11))
print(buffer)
  • bytes的转换
1
2
3
4
5
6
7
8
9
10
x = 65534
bufferLittle = x.to_bytes(2,"little") # int的to_bytes()成员函数,转换为指定的字节长度
print(bufferLittle)

bufferLittle = x.to_bytes(2,"big") # little endian:高位字节存高地址
print(bufferLittle)


y = int.from_bytes(b"\xfe\xff","little") # int的from_bytes()的成员函数将bytes重新打包成int对象
print(y)

4.字节数组(bytearray)

与bytes功能类似,可修改。

1
2
3
4
5
6
buffer = bytearray(0x00 for x in range(10))
print(buffer)

buffer = bytearray(b"abc")
buffer[1] = ord("B")
print(buffer)

5.位运算

按位与(&)、或(|)、位移(>>,<<)、取反(~)、异或(^)

6.名字绑定

“垃圾回收”机制

解释器内部有专门的引用计数来指示当前对象有多少个名字绑定。当对象引用为0后,python解释器会在恰当的时候从内存中销毁这个对象。

注意:python解释器考虑到执行速度的优化,倾向于将名字尽可能绑定在系统已有的对象上,而不是创建对象。

is和==

is(id是否相同)

==(对象的值是否相同)

只读/可修改数据类型

  • 只读数据类型(可忽略名字绑定)

    int,float,str,bool,bytes,tuple

  • 可修改数据类型(有名字绑定)

    list,bytearray,set

    解决:使用copy() 浅拷贝和deepcopy() 深拷贝(deepcopy()需要导入copy模块)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11

    import copy
    a = [1,2,[3,4],5]
    b = a.copy()
    c = copy.deepcopy(a)
    print(id(a),id(b),id(c))

    a[2][0] = 2
    print(a)
    print(b)
    print(c)

7.其他

  1. 集合不是序列类型
  2. ord()返回参数字符的Unicode编码
  3. chr()将Unicode编码转换成字符

第五章 条件,循环和其他

1.序列解包

1
2
3
4
5
6
7
8
9
a,b,c = 1,2,3
a,b = b,c # 两个数进行交换
num = 1,2,3 # 打包成元组
a,b,c = num # 元组序列解包
a,b,c = "789" # 字符串序列解包

a,*b = "123456" # b是列表
*a,b = 1,2,3,4,5,6 # a是列表
print(a,b)

2.链式赋值

1
a = b = 2

3.条件

  1. 非零即真,非空即真

  2. if

  3. if-else

  4. if-elif-else

  5. startswith()和endswith()是str的成员函数,可以判断是否是以参数字符串开头/结尾。

    1
    2
    3
    a = "123"
    print(a.startswith("12"))
    print(a.endswith("23"))
  6. 条件语句

    1
    2
    3
    num_1 = 1
    num_2 = 2 if num_1==1 else 3
    print(num_2)
  7. bool运算的短路

4.Python之禅与断言

断言(assert)

可以通过断言进行检查函数内部,若assert后的逻辑判断不成立,程序报错并停止执行。

1
2
3
4
5
6
7
8
score = [21,22,23,24,25,101] # 101数据会出错

def compAverageScore(score):
assert len(score) > 0
assert max(score) <= 100
assert min(score) >= 0
return sum(score) / len(score)
print(compAverageScore(score))

Python之禅

1
import this

5.循环

  1. for循环
  2. while循环
  3. break跳出当层循环
  4. continue结束当次迭代
  5. 循环else子句:仅当循环体内部的break未被调用时,else子句才会被执行
1
2
3
4
5
6
7
8
9
10
11
a = [1,2,3]
for i in a:
print(i)

while len(a) > 3:
print(True)

for i in range(len(a)):
print(a[i])
else:
print("hello")

6.序列缝包与循环解包

  • zip():可以将两个序列缝合并返回一个由元组构成的序列

    注:可以缝合任意序列,当最短序列用完时,停止缝合

1
2
3
4
number = [1,2,3,4]
name = ["王","张","李","孙"]
zip_number_name = zip(number,name)
print(list(zip_number_name))
  • enumerate():将序列元素与元素的下标缝合成一个元组
1
2
3
name = ["王","张","李","孙"]
for idx,name in enumerate(name):
print(idx,name)
  • reversed():接收一个序列,返回一个可迭代对象与原序列相反
1
2
3
name = ["王","张","李","孙"]
for i in reversed(name):
print(i)
  • del
1
del y # 删除名字y以及y与对象的绑定
  • exec():

    接收一个字符串参数,当作代码执行(提供动态执行代码的能力)

    第二个参数:指定一个名字空间(namespace)或作用域(scope)

1
2
3
4
5
scopeTemp = {}
scopeTemp["x"] = 30
scopeTemp["y"] = 20
exec("sum = x + y",scopeTemp)
print(scopeTemp["sum"])
  • eval():接收一个字符串,和名字空间

    与exec()区别:eval()有返回值,exec()没有返回值

1
2
a = eval("1 + 2 + 3")
print(a)

第六章 字典(dict)

键值对(key value pair)。

字典:能够高效地自动完成键到值的映射。字典同集合一样,不可以使用下标索引

1.创建

  1. 字典以一对大括号包裹,键值对以“,”分隔,键值用“:”分隔。

  2. 添加新的键值对:字典名[键] = 值

  3. 获取给定键的值:字典名[键]

  4. 字典中的键值对没有先后顺序(字典不属于序列类型)

  5. 在键值对中:

    可以是任意数据类型

    必须是可哈希数据类型(通常可哈希数据类型都是只读类型)

    • list,dict,set不是可哈希数据类型
    • int,float,str,tuple等是可哈希数据类型

2.dict()函数

1
2
3
4
5
6
items = [("id",123),("name","王"),("age",26)]
dorothy = dict(items)
print(dorothy)

dor = dict(name="张",age=26)
print(dor)

3.基本操作

1
2
3
4
5
len()
dro["key"] #获取值,若没有则报错
dro["key"] = value # 若无key键则创建,若有则更新value
del dro["key"] #删除键值对,若无则报错
"age" in dro # 判断age是否是dro的键

注:字典可嵌套

1
2
3
4
5
6
7
8
9
10
a = {
"key" :{
"ren":{
"001":{
"name":"王"
}
}
}
}
print(a["key"]["ren"]["001"]["name"])

4.成员函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
dor_1 = {'id': 123, 'name': '王', 'age': 26}
dor_2 = {'id': 123, 'name': '王', 'age': 26}

dor_1.clear() # 将字典内容全部清空
dorCopy = dor_1.copy() # 与列表相似

d1 = dict.fromkeys(["id","name","age"],"NoValue")
d2 = {}.fromkeys(["id","name","age"],"NoValue")
"""
两者运行结果:
{'id': 'NoValue', 'name': 'NoValue', 'age': 'NoValue'}
{'id': 'NoValue', 'name': 'NoValue', 'age': 'NoValue'}
"""

dor_1= {'id': 123, 'name': '王', 'age': 26}
value = d1.get("id","Nokey") # 返回键id的值,若无id键,则返回第二个参数值,若无第二个参数,则返回"N/A"

num = d1.pop("id") # 删除键为id的键值对
d1.popitem() # 随机删除

dor_1.update(dor_2) # 将dor_2更新到dor_1中,没有的键值对会加入,有的键值对值更新
  • 字典的遍历
1
2
3
4
5
6
7
8
9
dor_1 = {'id': 123, 'name': '王', 'age': 26}
for k in dor_1.keys(): # 返回一个dict_keys的特殊对象集合
print(k)

for v in dor_1.values(): # 返回一个dict_values的特殊集合
print(v)

for k,v in dor_1.items(): # 返回一个dict_items的特殊数据类型
print(k,v)

5.小练习

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
"""
问题描述:统计给出的任意字符串序列出现的次数
asdfasdf
输出格式:
a:2
s:2
d:2
f:2
"""
sList = "asdfasdf"
dictList = {}
for i in sList:
dictList[i] = dictList.get(i,0) + 1
for k,v in dictList.items():
print(str(k) + ":" + str(v))

6.容器的通用功能

通用for循环 遍历容器(字典是遍历key)
max 容器内最大元素
min() 容器内最小元素
len() 容器元素个数
list() 转换为列表
tuple() 转换为元组
str() 转换为字符串
set() 转换为集合
sorted(序列, [reverse=True]) 排序,reverse=True表示降序得到一个排好序的列表

第七章 函数与抽象

函数:是组织好的,可重复使用的,用来实现特定功能的代码段。

1.函数的定义

  • 函数调用:调用并执行函数
  • 可执行性:callable()函数用于鉴别是否是函数
1
callable(max)
  • 函数文档

    属性命中的双下划线表明这是一个特殊属性

1
2
3
4
5
def a(x=1):
"这是一个函数" #这是独立的字符串,称为函数文档,这个属性是__doc__
print("hello")
a()
help(a)

2.函数的参数

  • 默认值参数:参位于参数列表尾部

  • 非只读类型参数:会修改函数外部值

  • 关键字参数:位置传参与关键字传参

1
2
3
def fun(a=1,b=2):
print(a+b)
fun(b=4,a=1)
  • 任意数量的参数
    1. 这些实参被组织成一个元组
    2. 一个函数中,带*号的参数只有一个,且只能放最后(自动合并为一个元组)
1
2
3
4
5
6
一个“*”号的参数不会接收关键字参数,两个“*”可以,且只接受关键字参数
def myPrint(title,*contents):
print(title)
for x in contents:
print("\t",x)
myPrint("Read-only data types","int","float","str")
  • 分配参数:调用时给实参加上“*”
1
2
3
4
5
6
7
def myPrint(title,*contents):
print(title)
for x in contents:
print("\t",x)

a = ["Read-only data types","int","float","str"]
myPrint(*a) #此处为分配参数,会变成元组传递,加**实际传递的是字典
  • 函数做参数
1
2
3
4
5
6
def compute(x,y):
return x+y
def fun_to_compute(fun_com):
result = fun_com(1,2)
print(result)
fun_to_compute(compute)
  • 匿名函数
1
2
fun = lambda x,y:x+y # 只能写一行代码
print(fun(1,2))

3.作用域

  • 函数的作用域:先局部变量,再全局变量
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
'''
将函数内定义的变量声明为全局变量:global y
'''

def func():
x = 1
global y # 告诉解释器,这是全局变量
z[0] = 1000
print(x)
print(y)
print(z)

if __name__ == '__main__':
x = 2
y = 3
z = [1, 2, 3]
func()
print(x)
print(y)
print(z)


"""
运行结果:
1
3
[1000, 2, 3]
2
3
[1000, 2, 3]
"""
  • globals()函数:返回一个包含全局变量的字典

    globals()[“x”]:访问以x为键的值

1
2
3
4
5
6
7
8
9
def func():
x = 1
print("local x:",x)
print("global x:",globals()["x"])

if __name__ == '__main__':
x = 2
func()

  • 递归(汉诺塔)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#递归算法
#1.n-1个从A通过C移动到B
#2.第n个盘子从A移动到C
#3.将n-1个从B通过A移动到C

s=0
num = 3#有几个圆盘
def han(n,a,b,c):
if n>0:
han(n-1,a,c,b)
global s
s+=1
print(a + "->" + c,s)
han(n-1,b,a,c)
han (num,"A","B","c")

4.小练习

写一个进度条

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
"\r	是回到当前行的行首"
progressBefore = 0
def printProgressBar(percent,prefix=""):
global progressBefore
if percent-progressBefore < 0.001:
return
progressBefore = percent
percentStr = ("{0:.1f}").format(percent * 30)
filledLength = int(30 * percent)
bar = "*" * filledLength + " " * (30 - filledLength)
print("\r%s |%s|%s%% "%(prefix,bar,percentStr),end="")

if __name__ == '__main__':
import time
for i in range(100):
printProgressBar((i+1)/1000,prefix="Progress:")
time.sleep(0.01)

第八章 面向对象

  • 特征:封装,继承,多态

1.面向对象程序设计(Object Oriented Programming)

  1. 类(class):和数据类型(data type)是同义词。如:“人”这一个类
  2. 对象(object):在OO中,变量(variable),对象,实例(instance)三个术语概念大致相同。如:一个具体的人,小明
  3. 属性(attribute):也称数据成员(data member)。如:姓名,性别等
  4. 方法(method):也称成员函数。如:说话,吃饭
  5. 消息(message):执行一个对象的方法,也称向这个对象发送了一个消息。
  6. 对象 = 属性 + 方法

2.创建类

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
from enum import Enum
class Gender(Enum):
male = 0
female = 1

class Person: # 用以指示类定义的过程的开始

"user defined class: Person." # 类的描述性文档:Person.__doc__

def __init__(self,idNo="N/A",name="N/A"): # __init__()是构造函数,
#构造函数的作用:实例化一个对象的同时初始化这个对象,即创建或实例化对象时,该函数会被自动执行,确保对象经过恰当的实例化
self.sName = name
self.gender = Gender.male
self.sId = idNo
self.iWeight = 0

def speak(self):
print("Person::speak():")
print("I am", self.sName + ",", "Nice to meet you here.")

def eat(self, weight):
self.iWeight += weight
print("I just eat", weight, "gram's food.")

def description(self):
assert self.gender in (Gender.female,Gender.male)
s = "ID:%s\tName:%s\n"%(self.sId,self.sName)
t = "Gender:%s\tBody Weight:%d"%("Male" if self.gender == Gender.male else "Female",self.iWeight)
return s+t
  • self参数:基本上是必须的(P125)
  • 定义类的成员函数:第一个参数是self
  • 在成员函数内部:应用对象的形式为self.属性名
  • 构造函数也是成员函数,由解释器自动调用。

3.枚举类型

1
2
3
4
from enum import Enum
class Gender(Enum):
male = 0
female = 1

继承Enum类型,可以用Gender.male来表示男性

4.创建对象

1
2
3
4
5
dora = Person("001","Dora chen")	# 理论上只有当构造函数执行完毕之后,对象才真正存在
dora.gender = Gender.female
dora.iWeight = 100
dora.eat(320)
print(dora.description())

注:同一类型的多个对象,其属性是相互独立的,但方法却是共享的。

5.封装、接口与实现

  • 复用(reuse):利用他人代码的方式(from math import *)
  • 接口(interface):告诉我们功能和使用方法
  • 实现(implementation):不需要了解的部分(如功能具体的实现算法等)
  • 封装(encapsuation):实现一个复杂系统并将其细节隐藏起来,只向使用者提供一个简洁易用的接口的工作模式。
  • 组合(composition):将某些自定义类型的对象作为对象属性/构成部分的方法,也是一种代码复用的手段。

6.继承

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
from Person import Person, Gender   # Person类定义在同一目录下的另一个文件中,通过from...import把Person类和Gender类引入进来

class Employee(Person): # Employee是Person的子类
def __init__(self,emplNo,idNo,name):
super(Employee,self).__init__(idNo,name) # 执行父类Person的构造函数
#Person.__init__(self,idNo,name)更佳
# super(Employee,self)用于获取Employee的父类对象
self.sEmployeeNo = emplNo
self.sJobTitle = ""
self.sDepartment = ""
self.iWeekSalary = 0

def work(self):
print("I am a", self.sJobTitle + ", I am working with my partners in department:",self.sDepartment)
def speak(self):
print("Employee::speak():")
super().speak() #等价于Person.speak(self)
print("I am happy to work for you.")
def description(self):
assert self.gender in (Gender.female,Gender.male)
return "调用decription方法"
  • 函数重载(override):子类重新实现父类的同名函数的方式。调用父类可以使用(super().speak() #等价于Person.speak(self))
术语 同义术语
父类(parent class) 超类(super class),基类(base class)
子类(sub class) 扩展类(derived class),继承类(inherited class)

7.多态、抽象类

8.继承链

1
2
3
4
5
6
# 继承链有关的方法和属性
issubclass(D,A) #确定D是否是A的子类
D.__base__ #返回D类的基类
isinstance(d,B) #判断d对象是否是B类的实例
d.__class__ #返回对象d的类型

9.实现的隐藏

  • 在属性和方法前面加上两个”_“,可以将其私有化

10.万物皆对象

  • OO的五大特性
    1. 万物皆对象
    2. 对象皆有类型
    3. 程序就是由一堆对象构成,对象间通过发送消息协同工作
    4. 每个对象均有自己的存储空间,并由其他对象来构成
    5. 相同类型的对象可以接收相同类型的消息
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
def foo(x):
"Function Foo."
return x*x

print(type(foo),foo)
print(foo.__doc__)
print("foo.dir():",foo.__dir__()) # 列出对象的全部成员
func = foo
print(func(2))
class Dummy:
"Class Dummy."
def say(self):
print("Dummy.")
print(type(Dummy), Dummy)
print(Dummy.__doc__)
Dummy2 = Dummy
d = Dummy2()
d.say()

11.类的对象属性和方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Tomato:
objectCount = 0

def __init__(self):
self.objectCount += 1
Tomato.objectCount += 1
t1 = Tomato()
t2 = Tomato()
print(Tomato.objectCount)
print(t1.objectCount)

"""
运行结果:
2
1
"""

12.多重继承

不建议使用,会出现“并发症”。推荐看前言中的视频

13.特殊/魔法方法

  • 特殊方法(special method):函数名前后各两个下划线,长具备特殊用途。
  • 析构函数(destructer):
1
__del__() #正好与构造函数相反

14.property属性

property是一种特殊的属性,工作起来像属性,实际上是方法

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
class Rect:
def __init__(self):
self.width = 0
self.height = 0
self.area = self.width*self.height

def setSize(self,size):
self.width, self.height = size#从size元组解包
self.area = self.width * self.height
print("setSize(): I worked like a common property, but actually, I am a method.")
def getSize(self):
print("getSize(): I am a method too.")
return self.width,self.height
size = property(getSize,setSize)

r0 = Rect()
r0.size = 120,20#120,20被当作一个元组传递给setSize()函数
r1 = Rect()
r1.size = 13,30
print("Rect:", r0.size, "Area =",r0.area)
print("Rect:", r1.size, "Area =",r1.area)

'''
1.通过property()"函数"我们创造了一个size属性,并指定了这个属性的读取函数和设置函数
2.对size属性的赋值会执行setSize()方法,对size取值则会执行getSize()方法
3.size属性是虚拟的,它本身并不存储任何值。数据存储在对象的width,height,area属性中
'''

15.其他函数

1
2
3
hasattr()函数用于检查对象是否拥有指定名称的属性,返回布尔型
setattr()函数给对象设定属性及其值。
getattr()函数则用于获取对象指定名称的属性的值,如果属性不存在,返回第三个参数给定的默认值。

16.类型的注解

  1. 变量的类型注解

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    语法:
    变量: 类型
    a:int = 10
    b:float = 20.0

    class Student:
    pass
    stu:Student = Student()

    li:list[int] = [1,2,3,4]
    li2:list[int,float] = [1,2.1]
  2. 函数(方法)的类型注解

    1
    2
    3
    4
    5
    6
    方法的注解:
    def fun(a:int,b:int):
    pass

    def fun2(a:int,b:int) -> int:
    pass
  3. Union注解

    1
    2
    from typing import union
    li:list[union[str,int,float]] = [1,1,1.2,8.2,"hello"]

第九章 字符串进阶

1.浅尝辄止

字符串的字典映射替换:

  • format()函数
1
2
sText = "name:{},age:{age},sex:{sex}".format("小明",age=20,sex="male") #无名实参替换,有名实参替换。{},{sex}前者是匿名替换字段,后者有指定名称
print(sText)
  • format_map()函数
1
2
3
dro = {"name":"小明","age":20}
s = "name:{name},age:{age}"
print(s.format_map(dro))

2.替代字段

1
2
3
4
5
6
7
score = 97
print(f"score={score:10.2f}")
'''
{score:10.2f} 为替代字段
score 为替代字段名
:后面部分 格式说明符
'''
说明符 含义
b 整数以二进制展现。
c 整数按Unicode码转成对应符号,请试:”{num:c}”.format(num=0x1f60a)
d 整数以十进制展现,如果格式说明符被省略,整数默认为十进制
o 整数以八进制展现(字母小o,不是数字0)
x 整数以十六进制展现且使用小写字母
X 整数以十六进制展现但使用大写字母
e 整数/浮点数使用科学计数法展现,使用e表示指数
E 整数/浮点数使用科学计数法展现,使用E表示指数
f 整数/浮点数以定点小数展现,比如3.14,特殊值nan和inf,用小写表示。nan表示非数-not a number, inf表示正无穷,inf表示负无穷。
F 同f,但对于特殊值NAN和INF,使用大写表示
g 系统自行选择使用定点小数或者科学计数法表示整数/浮点数
G 同g,但用大写字符表示指数及特殊值
n 同g, 但插入数字分隔符
s 字符串以字符串原始形式展现
% 整数/浮点数以百分比展示

3.宽度、精度及分结符

1
2
3
4
5
6
7
8
9
10
11
{pi:10.2f},说明定点小数宽度10,精确到小数点第2
{num:10},说明整数宽度10,打印结果中明显看到1024前的空格。
{:.5},取了字符串的前5个字符。
{:,},十进制整数中使用逗号作为千位分节符。

{pi:010.2f},宽度10,精度210前的0表示补0
{pi:<10.2f},<表示左对齐。
{pi:^10.2f},^表示居中。
{pi:@>10.2f},>表示右对齐,使用特殊字符@来填充。
{0:+10.2f},使用第0个参数,+号表示要显示正号。
{1:=+10.2f},使用第1个参数,=号表示通过在符号位和数之间插入填充字符(空格)来满足10位宽度。

4.其他函数

  • center()函数:接受两个参数,第一个为目标宽度,第二个为填充字符,默认为空格。

    相关函数有ljust()-右边补填充字符,rjust()-左端补填充字符,zfill()-左端补0。

  • find()函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
s.find("to",0,len(s))试图在s中找"to"第一次出现的位置下标,如果找不到,返回-1。参数23指明了搜索的开始下标和结束下标(不含结束下标),参数2,3可省略。

rfind() - 找字符串中指定的子串的最后一次出现的位置下标;

index() - 找指定子串的第一次出现的位置下标,找不到引发异常;

rindex() - 找最后一个子串的索引,找不到引发异常;

count() - 统计子串出现次数;

startswidth() - 是否以子串开头;

endswidth() - 是否以子串结尾。

  • join()函数
1
2
s.join(seq)中,s为指定的间隔字符串,seq可以是元组或者列表,但其中的元素必须是字符串

  • replace()函数
1
2
sText.replace(old,new,count=-1)将字符串内的指定子串全部替换成另一个指定子串,不会导致sText被修改,函数会返回一个新的字符串作为结果。

  • split()函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
split()函数的作用与join()函数相反,把一个字符串用指定的间隔符拆开成多个字符串,并放入一个列表中返回。

相关函数:

partition(sep)-使用子串划分字符串,返回一个元组,包括三个字符串:sep前的部分,sep,sep后的部分;
seq=“stringsthbig”
s=seq.partition(“sth”)
s的结果为('string', 'sth', 'big')

rpartition(sep)-同partition(sep),搜索方向为右向左;

rsplit()-同split(),方向右向左;

splitlines()-分拆字符串为多行。

translate()-借助于转换表实现字符串内单字符的快速替换;

is系列函数,用于判断字符串是否具备某种特性,比如仅包含数字,全是大写之类:
isspace(), isdigit(),isupper(),islower(),isdecimal(),
isidentifier(),isnumeric(),isprintable(),istitle()。

第十章 文件读写

1.基本操作

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
f = open("datafile.txt","w")
f.write("This is a file which is writable in text mode.\n")
f.write("'w' means open file in write mode.\n")
f.close()
f = open("datafile.txt","r")
sLine1 = f.readline()
sLine2 = f.readline()
print(sLine1,sLine2)
f.close()

"""
open(file,mode,encoding='utf-8')函数用于打开一个文件,其中,file参数用于指定操作文件名,该文件名既可以是绝对路径,比如"d:\\Python\\Test\\test.dat",也可以是相对路径,比如"data\\test.dat"

close(f)用于关闭已打开的文件f,好处有:
1.减少操作系统I/O资源占用;
2.避免因缓存/意外错误导致文件没有成功写入甚至损坏。

如果不想关闭文件,同时又希望确保将缓存中的数据写入文件,可以执行f.flush()函数。

f.read()函数从文件读出全部内容并以字符串形式(文本模式时)返回
f.read(5)则从文件读出5个字符
f.readline()则从文件读出一行
f.readlines()读取全部行,得到列表
f.writeline()则负责向文件写入一行
f.write()用于文件写入数据
直接调用f.write,内容并未真正写入文件,而是会积攒在程序的内存中,称之为缓冲区
当调用f.flush的时候,内容会真正写入文件
with open() as f 通过with open语法打开文件,可以自动关闭
"""

mode参数:

文件模式 说明
“r” 读模式(默认)
“w” 写模式
“x” 独占写模式,这意味着不允许其它应用程序在该文件关闭前使用该文件; x来源于英文exclusive-独占。
“a” 附加模式 - append
“b” 二进制模式,与其它模式配用
“t” 文本模式(默认)
“+” 读写模式,必须与r,w,a等配合使用
1
2
3
4
5
6
open()函数省略了文件模式参数时,默认为"rt“

以 "w"或"w+"模式打开文件时,如果文件不存在,会自动新建一个

"wb"以写模式打开一个二进制文件,"rb"则以读模式打开一个二进制文件。如果又想读,又想写,可以使用"wb+"或者"rb+"。

2.随机存取

文件可以看作是一个流(stream)。在流中,每一个元素(对于二进制文件而言,以字节为单位关)都有地址,这个地址称为偏移量(offset).

系统允许跳到某个特定偏移量读取或写入。写入时,该偏移量开始的文件原内容会被覆盖。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import io
f = open('randomaccess.txt',"w+")
sText = "".join((str(x) for x in range(30)))
f.write("汉" + sText)
f.seek(6)
f.write("OFFSET_6_")
print("Current file position after seek and write:", f.tell())
f.close()
f = open('randomaccess.txt',"r")
print("Content:")
print(f.read())
f.close()

"""
seek(offset,whence=io.SEEK_SET)函数:
whence表明偏移量的参照物:io.SEEK_SET(值为0) - 相对于文件开头偏移,此时偏移量当为正数;
io.SEEK_END (值为2) - 相对于文件尾偏移,此时偏移量当为负数;
io.SEEK_CUR (值为1) - 相对于当前读写位置偏移,此时偏移量可为正数或负数。
当文件以文本模式工作时,seek()无法相对文件尾或者当前读写位置偏移。

"""

3.标准输入,输出及错误流

和UNIX操作系统的概念很像

1
2
3
4
5
6
7
sys.stdin : 标准输入,对应于键盘/控制台输入,使用input()函数读取操作者输入时,数据即可认为“读取”自sys.stdin

sys.stdout : 标准输出,对应于显示器/控制台输出,使用print()函数打印信息时,可视作数据被"写入"至sys.stdout

sys.stderr : 标准错误,程序产生错误时,报错信息则被输出给sys.stderr,默认情况下,sys.stderr也对应显示器/控制台输出


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
import sys
f = open('input.txt','w')
f.write("Alex\n")
f.write("27\n")
f.close()
fIn = open('input.txt',"r")
sys.stdin = fIn
fOut = open('output.txt',"w")
sys.stdout = fOut
fError = open('error.txt','w')
sys.stderr = fError
sName = input("What's your name?")
iAge = int(input("How old are you?"))
print("Hi,", sName.title(), "You are {} year's old.".format(iAge))
fIn.close()
fOut.close()
raise Exception("ERROR INFO")
fError.close()

"""
将sys.stdin, sys.stdout, sys.stderr重定向:
sys.stdin = fIn

sys.stdout = fOut

sys.stderr = fError

"""

4.文件内容迭代

1
2
3
4
5
6
7
8
9
10
with open('title.txt') as f:
while True:
char = f.read(1)
if not char:
break
process(char) # 某种字符串处理
"""
read(1)每次从文件中读取一个字符,当读到文件尾时,返回空。按照非空即真,空即是假的原则,到达文件尾时,break语句将执行,循环结束,文件被自动关闭。

"""
1
2
3
4
5
6
7
8
9
10
# 按行迭代:
with open('title.txt') as f:
for x in f.readlines():
process(x)


with open('title.txt') as f:
for line in f:
print(line)

5.json文件格式

json的全称为JavaScript Object Notation ,是一种轻量级的跨语言、跨平台数据交换格式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import json
dora = {"name":"Dora CHEN", "no":"2018173", "age":26,"married":False,
"scores":[{"C++":76},{"Data Structure":99.5},{"Home Econoics":62}]}

#print(json.dumps(dora))
with open("dora.json","w") as f:
json.dump(dora,f)
with open("dora.json") as f:
doraLoaded = json.load(f)
for key,value in doraLoaded.items():
print(key,":", value)


"""
json模块和dump()函数把一个嵌套的字典以及内部列表,甚至列表内部的字典序列化并存储至一个文本文件: dora.json。

"""
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import json
dora = {"name":"Dora CHEN", "no":"2018173", "age":26,"married":False,
"scores":[{"C++":76},{"Data Structure":99.5},{"Home Econoics":62}]}
#print(json.dumps(dora))
with open("dora.json","w") as f:
json.dump(dora,f)
with open("dora.json") as f:
doraLoaded = json.load(f)
for key,value in doraLoaded.items():
print(key,":", value)

"""
借助于load()函数,从dora.json文件复原了一个doraLoaded字典

"""

第十一章 异常处理及单元测试

1.遇到过的异常

异常类 说明
ValueError 值与期望的不符
IndentationError 代码缩进错误
IndexError 序列索引不存在
AssertionError 断言失败
NameError 名字不存在
KeyError 映射(比如字典)中的键不存在
AttributeError 属性错误(对象无指定名字的属性)
TypeError 类型出错
SyntaxError 代码语法错误
OSError 操作系统未能执行指定任务
ZeroDivisionError 除0错误

异常发现后的处理:

  1. 捕获并处理异常,尝试将程序从异常中拯救出来,继续正常运行。
  2. 捕获并处理异常,至少做一些必要的紧急操作,避免严重后果的发生。
  3. 捕获并处理异常,将异常信息保存在错误日志中,以便程序员查找错误发生的原因。

2.异常处理

try-except-else-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
35
36
37
38
39
40
41
42
def divide(a, b):
return a/b

while True:
sFirst = input("First number:")
sSecond = input("Second number:")
if sFirst == "q" or sSecond == "q":
break
try:
iFirst = int(sFirst)
iSecond = int(sSecond)
fResult = divide(iFirst,iSecond)
except (ZeroDivisionError) as e:
print("You can not divide by 0:",e)
except (ValueError,TypeError) as e:
print("Illegal value been inputted:",e)
print(type(e))
except (Exception) as e:
print("An exception found, I do not know how to process it.")
raise
else:
print( sFirst, "/", sSecond, "=", fResult)
finally:
print("'Finally' will be executed what ever happens.")


"""
1.首先,解释器将会执行try子句内的代码
2.try子句的执行没有发生异常,在try子句执行完毕后将执行else子句,然后再执行finally语句
3.如果try子句的执行发生了异常,将从前往后逐一检查except子句括号里所包括的异常类型,当实际发生的异常属于该except子句括号内的异常类型时,该except子句将会被执行。最后,finally子句也会被执行。
4.如果捕获的异常在当前情境下处理不了,也可以接着向外抛:raise即为该用途。如果直接raise,抛出的是原有异常。

try:
iFirst = int(sFirst)
iSecond = int(sSecond)
fResult = divide(iFirst,iSecond)
except:
print("You can not divide by 0:",e)
except (NameError, ZeroDivisionError):
print('ZeroDivision错误...')

"""
  • 异常是具有传递性的
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
'''
当函数func01中发生异常, 并且没有捕获处理这个异常的时候, 异常
会传递到函数func02, 当func02也没有捕获处理这个异常的时候
main函数会捕获这个异常, 这就是异常的传递性.
'''

# 定义一个出现异常的方法
def func1():
print("func1 开始执行")
num = 1 / 0 # 肯定有异常,除以0的异常
print("func1 结束执行")

# 定义一个无异常的方法,调用上面的方法
def func2():
print("func2 开始执行")
func1()
print("func2 结束执行")

# 定义一个方法,调用上面的方法
def main():
try:
func2()
except Exception as e:
print(f"出现异常了,异常的信息是:{e}")

main()

3.警告

1
2
3
4
5
6
7
8
9
10
11
12
from warnings import warn
def divide(a,b):
fResult = a / b
if fResult < 0.0000001:
warn("The result is very close to Zero!")
return fResult
print(divide(0.1, 10000000000))
print("Something else.")

"""
warn()函数发出了警告,并把警告打印至屏幕,但程序的执行不会因为该警告而停止:print("Something else.")在警告之后继续执行了。
"""

第十二章 python的模块和包

1.模块

  • 模块

    Python 模块(Module),是一个 Python 文件,以 .py 结尾. 模块能定义函数,类和变量,模块里也能包含可执行的代码.

    1
    2
    3
    4
    5
    import 模块名
    from 模块名 import 类、变量、方法等
    from 模块名 import *
    import 模块名 as 别名
    from 模块名 import 功能名 as 别名
  • 自定义模块

    每个Python文件都可以作为一个模块,模块的名字就是文件的名字.

    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
    1.
    问题:
    def test(a, b):
    print(a + b)
    test(1, 1)
    无论是当前文件,还是其他已经导入了该模块的文件,在运行的时候都会自动执行`test`函数的调用

    解决:
    def test(a, b):
    print(a + b)
    # 只在当前文件中调用该函数,其他导入的文件内不符合该条件,则不执行test函数调用
    if __name__ == '__main__':
    test (1, 1)

    2.
    当导入多个模块的时候,且模块内有同名功能. 当调用这个同名功能的时候,调用到的是后面导入的模块的功能
    from my_module1 import test
    from my_module2 import test
    test(1, 2) # 调用的是my_module2

    3.__all__
    如果一个模块文件中有`__all__`变量,当使用`from xxx import *`导入时,只能导入这个列表中的元素
    # __all__变量:__all__=["test_a"]
    from my_module1 import *
    test_a(1, 2)

2.包

  • 什么是包

    1
    2
    3
    从物理上看,包就是一个文件夹,在该文件夹下包含了一个 __init__.py 文件,该文件夹可用于包含多个模块文件
    从逻辑上看,包的本质依然是模块
    注意:新建包后,包内部会自动创建`__init__.py`文件,这个文件控制着包的导入行为
  • 导入包

    1
    2
    3
    4
    5
    6
    7
    8
    9
    1.方式一
    import 包名.模块名
    包名.模块名.目标

    2.方式二
    注意:必须在`__init__.py`文件中添加`__all__ = []`,控制允许导入的模块列表
    from 包名 import *
    模块名.目标

  • 安装第三方包

    1
    pip install -i https://pypi.tuna.tsinghua.edu.cn/simple 包名称

第十三章 python高阶技巧

1.闭包

在函数嵌套的前提下,内部函数使用了外部函数的变量,并且外部函数返回了内部函数,把这个使用外部函数变量的内部函数称为闭包。

  • 优点:

    1. 无需定义全局变量即可实现通过函数,持续的访问、修改某个值
    2. 闭包使用的变量的所用于在函数内,难以被错误的调用修改
  • 缺点:

    1. 由于内部函数持续引用外部函数的值,所以会导致这一部分内存空间不被释放,一直占用内存
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
# 1.简单闭包
def outer(logo):

def inner(msg):
print(f"<{logo}>{msg}<{logo}>")

return inner


fn1 = outer("黑马程序员")
fn1("大家好")

fn2 = outer("传智教育")
fn2("大家好")


# 2.使用nonlocal关键字修改外部函数的值
def outer(num1):
def inner(num2):
nonlocal num1
num1 += num2
print(num1)

return inner

fn = outer(10)
fn(10)
fn(10)
fn(10)
fn(10)


# 3.使用闭包实现ATM小案例
def account_create(initial_amount=0):

def atm(num, deposit=True):
nonlocal initial_amount
if deposit:
initial_amount += num
print(f"存款:+{num}, 账户余额:{initial_amount}")
else:
initial_amount -= num
print(f"取款:-{num}, 账户余额:{initial_amount}")

return atm

atm = account_create()

atm(100)
atm(200)
atm(100, deposit=False)

2.装饰器

装饰器:也是一种闭包, 其功能就是在不破坏目标函数原有的代码和功能的前提下,为目标函数增加新功能。

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.装饰器的一般写法(闭包)
def outer(func):
def inner():
print("我睡觉了")
func()
print("我起床了")

return inner

def sleep():
import random
import time
print("睡眠中......")
time.sleep(random.randint(1, 5))

fn = outer(sleep)
fn()



# 2.装饰器的快捷写法(语法糖)
# 常用写法
def outer(func):
def inner():
print("我睡觉了")
func()
print("我起床了")

return inner

@outer
def sleep():
import random
import time
print("睡眠中......")
time.sleep(random.randint(1, 5))
sleep()

3.设计模式

设计模式是一种编程套路,可以极大的方便程序的开发。最常见、最经典的设计模式,就是我们所学习的面向对象了。

除了面向对象外,在编程中也有很多既定的套路可以方便开发,称之为设计模式:

  • 单例、工厂模式

  • 建造者、责任链、状态、备忘录、解释器、访问者、观察者、中介、模板、代理模式

1.单例模式

单例模式(Singleton Pattern)是一种常用的软件设计模式,该模式的主要目的是确保某一个类只有一个实例存在。在整个系统中,某个类只能出现一个实例时,单例对象就能派上用场。

定义:

  • 保证一个类只有一个实例,并提供一个访问它的全局访问点
  • 适用场景:当一个类只能有一个实例,而客户可以从一个众所周知的访问点访问它时。

优点:

  • 节省内存
  • 节省创建对象的开销
1
2
3
4
5
6
# 创建一个str_tools_py.py文件

class StrTools:
pass

str_tool = StrTools()
1
2
3
4
5
6
7
8
9
# 创建一个test.py文件

from str_tools_py import str_tool

s1 = str_tool
s2 = str_tool

print(id(s1))
print(id(s2))

2.工厂模式

当需要大量创建一个类的实例的时候, 可以使用工厂模式。即从原生的使用类的构造去创建对象的形式迁移到,基于工厂提供的方法去创建对象的形式。

优点:

  • 大批量创建对象的时候有统一的入口,易于代码维护
  • 当发生修改,仅修改工厂类的创建方法即可
  • 符合现实世界的模式,即由工厂来制作产品(对象)
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
class Person:
pass

class Worker(Person):
pass

class Student(Person):
pass

class Teacher(Person):
pass


class PersonFactory:
def get_person(self, p_type):
if p_type == 'w':
return Worker()
elif p_type == 's':
return Student()
else:
return Teacher()


pf = PersonFactory()
worker = pf.get_person('w')
stu = pf.get_person('s')
teacher = pf.get_person('t')

后记

写到这里,基本上是把python的语法笔记写完了。最开始,我还以为就能够2天写完,没想到重新学了一遍收获颇多,大大小小花了两周多的时间。就例如OO程序设计,在平常用来学习写的代码,几乎是用不到的,这也以为着遗忘也是必然的。所以,希望这一片文档能够,快速帮我复习python的语法。

当然,对于教材后面的Numpy等知识点,还是去看前言中的视频。当然等有需要的时候,在去写数据分析与可视化的笔记。