825计划

时间

此计划于2021年7月10日实行,本计划的目的在于督促和标准化终身学习。

学习单

  • 马哲

  • 毛选

  • 数据结构(binary tree/graph)

  • 数据库

学习资源库

书籍

数字图书馆

数字图书馆Z-library

开源网课

一、北京大学

  1. github.com/lib-pku/libpku
  2. github.com/tongtzeho/PKUCourse

二、清华大学

  1. github.com/PKUanonym/REKCARC-TSC-UHT

三、浙江大学

  1. github.com/QSCTech/zju-icicles

四、中国科学技术大学

  1. github.com/USTC-Resource/USTC-Course
  2. ustc-resource.github.io/USTC-Course/

五、中山大学

  1. github.com/sysuexam/SYSU-Exam

六、B 站的公开课

  1. github.com/elder-frog/OpenCourseCatalog

Postgraduate

985

  1. 北京交通大学:数一、英一、(925)数据结构
  2. 中国农业大学(自划线院校):(821)数据结构
  3. 大连理工大学(自划线院校):数一、英一、(810)数据结构
  4. 北京化工大学:(842)数据结构
  5. 中央民族大学:(852)信号与系统或数据结构
  6. 中南大学(自划线院校):(943)数据结构

211

  1. 北京林业大学:(839)数据结构
  2. 中国传媒大学:数一、英一、(824)数据结构
  3. 中国矿业大学(北京):数一、英一、(854)数据结构
  4. 华北电力大学:(844)数据结构
  5. 大连海事大学:(808)数据结构
  6. 中国矿业大学:(874)数据结构
  7. 山东大学(自划线院校):(909)数据结构
  8. 中国石油大学(华东):(859)数据结构
  9. 暨南大学:(830)数据结构
  10. 西北大学:数一、英一、(851)数据结构

双非

  1. 北方工业大学:(861)数据结构
  2. 北京工商大学:(818)数据结构
  3. 北京电子科技学院:数一、英一、(831)数据结构
  4. 太原科技大学:(828)数据结构
  5. 中北大学:(811)数据结构
  6. 内蒙古科技大学:(810)数据结构
  7. 内蒙古工业大学:(808)数据结构
  8. 沈阳工业大学:(808)数据结构
  9. 辽宁工程技术大学:(810)数据结构 B
  10. 辽宁石油化工大学:(951)数据结构
  11. 沈阳化工大学:(817)算法与数据结构
  12. 大连工业大学:(822)数据结构
  13. 辽宁工业大学:(915)数据结构
  14. 大连民族大学:(802)数据结构与算法设计
  15. 长春理工大学:(809)数据结构
  16. 东北电力大学:(851)数据结构
  17. 长春工业大学:(809)数据结构
  18. 东北石油大学:(820)数据结构
  19. 黑龙江八一农垦大学:(923)数据结构
  20. 上海电力大学:(840)数据结构
  21. 上海应用技术大学:(816)数据结构与算法
  22. 上海工程技术大学:(809)数据结构
  23. 南京邮电大学:(811)数据结构
  24. 南京林业大学:(883)数据结构
  25. 南通大学:(822)数据结构
  26. 南京审计大学:(911)数据结构
  27. 陆军工程大学:(811)数据结构
  28. 南京信息工程大学:(853)数据结构与算法分析
  29. 宁波大学:(916)数据结构与算法分析
  30. 安徽工业大学:(861)数据结构
  31. 安徽理工大学:(841)数据结构
  32. 阜阳师范大学:(908)数据结构
  33. 安徽建筑大学:(909)数据结构
  34. 华侨大学:(826)数据结构
  35. 华东交通大学:(829)数据结构
  36. 东华理工大学:(811)数据结构(含C程序设计)
  37. 南昌航空大学:(961)数据结构(C语言版)
  38. 江西理工大学:(873)数据结构
  39. 青岛科技大学:(861)数据结构
  40. 济南大学:(846)数据结构
  41. 青岛理工大学:(813)数据结构
  42. 齐鲁工业大学:(872)数据结构
  43. 山东理工大学:(880)数据结构
  44. 山东中医药大学:(803)数据结构
  45. 山东师范大学:(915)数据结构B
  46. 曲阜师范大学:(870)数据结构与算法设计
  47. 临沂大学:(810)数据结构
  48. 青岛大学:(910)数据结构
  49. 烟台大学:(846)数据结构(李春葆主编)
  50. 华北水利水电大学:(967)数据结构
  51. 河南工业大学:(830)数据结构
  52. 河南科技大学:(825)数据结构
  53. 河南理工大学:(857)数据结构
  54. 武汉纺织大学:(848)数据结构
  55. 湖北大学:(811)数据结构
  56. 三峡大学:(836)数据结构
  57. 长沙理工大学:(850)数据结构
  58. 湖南工商大学:(808)数据结构
  59. 南华大学:(881)数据结构
  60. 广东财经大学:英一、(809)数据结构
  61. 广州大学:(914)数据结构
  62. 五邑大学:(810)数据结构
  63. 广东外语外贸大学:(846)数据结构
  64. 桂林电子科技大学:(823)数据结构
  65. 广西师范大学:(826)数据结构(含C程序设计)
  66. 海南师范大学:(918)数据结构
  67. 重庆理工大学:(812)数据结构
  68. 重庆邮电大学:(802)数据结构
  69. 四川轻化工大学:(816)数据结构与算法
  70. 西华大学:(823)数据结构
  71. 西华师范大学:(821)数据结构
  72. 昆明理工大学:(877)数据结构与算法分析
  73. 西安理工大学:(863)数据结构
  74. 西安科技大学:(824)数据结构与算法设计
  75. 陕西科技大学:(902)数据结构
  76. 西京学院:(861)数据结构
  77. 西安邮电大学:(826)数据结构
  78. 兰州理工大学:(892)数据结构
  79. 兰州交通大学:(828)数据结构
  80. 新疆大学:(824)数据结构
  81. 新疆师范大学:(864)数据结构

LeeCode

华为题库

1H字符串最后一个单词的长度

字符串

思路/考点

将输入的字符串通过split()转化成list,再通过index进行求解

描述

计算字符串最后一个单词的长度,单词以空格隔开,字符串长度小于5000。

输入描述:

输入一行,代表要计算的字符串,非空,长度小于5000。

输出描述:

输出一个整数,表示输入字符串最后一个单词的长度。

1
2
t = input().split(' ')
print(len(t[-1]))

2H计算某字母出现次数

字符串/哈希

思路/考点

考察count()函数,由于tuple\string\list在python具有相似的底层逻辑,因此count()既可以统计string中某字符串出出现的个数,又可以统计list中元素出现的个数

描述

写出一个程序,接受一个由字母、数字和空格组成的字符串,和一个字母,然后输出输入字符串中该字母的出现次数。不区分大小写,字符串长度小于500。

输入描述:

第一行输入一个由字母和数字以及空格组成的字符串,第二行输入一个字母。

输出描述:

输出输入字符串中含有该字符的个数。

1
2
3
4
5
str = input()
word = input()
str = str.lower()
word = word.lower()
print(str.count(word))

3H明明的随机数[0]

数组

描述

明明想在学校中请一些同学一起做一项问卷调查,为了实验的客观性,他先用计算机生成了N个1到1000之间的随机整数(N≤1000),对于其中重复的数字,只保留一个,把其余相同的数去掉,不同的数对应着不同的学生的学号。然后再把这些数从小到大排序,按照排好的顺序去找同学做调查。请你协助明明完成“去重”与“排序”的工作(同一个测试用例里可能会有多组数据(用于不同的调查),希望大家能正确处理)。

注:测试用例保证输入参数的正确性,答题者无需验证。测试用例不止一组。

当没有新的输入时,说明输入结束。

输入描述:

注意:输入可能有多组数据(用于不同的调查)。每组数据都包括多行,第一行先输入随机整数的个数N,接下来的N行再输入相应个数的整数。具体格式请看下面的”示例”。

输出描述:

返回多行,处理后的结果

1
2
3
4
5
6
7
8
9
10
11
12
13
import sys
while True:
line = sys.stdin.readline()
if not line:
break
n = int(line)
d = {}
for i in range(n):
d[int(sys.stdin.readline())]=1
l = list(d.keys())
l.sort()
for i in l:
print(i)

4H字符串分隔

字符串

思路/考点

注意slice的运用,同时对于需要对其的字符串使用ljust–左对齐–右填充

描述

•连续输入字符串,请按长度为8拆分每个字符串后输出到新的字符串数组;
•长度不是8整数倍的字符串请在后面补数字0,空字符串不处理。

输入描述

连续输入字符串(输入多次,每个字符串长度小于100)

输出描述

输出到长度为8的新字符串数组

1
2
3
4
5
6
7
8
9
while True:
try:
s = input()
while len(s) > 8:
print(s[:8])
s = s[8:]
print(s.ljust(8, '0'))
except:
break

使用ljust()、rjust()、center()

parameters:width(contains oneself), fillchar

是对齐方向ljust,左对齐右填充

5M进制转换

字符串

思路/考点

使用int(str, base=n)进行进制的直接转化,其中输入的str是在base的基础上的值,此操作将会返回一个int值(base=10)

同理bin(str, base=n) 将会返回一个int值(base=2)开头为0b

0b在c语言中表示二进制

描述

写出一个程序,接受一个十六进制的数,输出该数值的十进制表示。

输入描述:

输入一个十六进制的数值字符串。

输出描述:

输出该数值的十进制字符串。不同组的测试用例用\n隔开。

1
2
3
4
5
6
while True:
try:
temp = int(input(), base=16)
print(temp)
except:
break

6M质数因子

排序/递归

思路/考点

描述

功能:输入一个正整数,按照从小到大的顺序输出它的所有质因子(重复的也要列举)(如180的质因子为2 2 3 3 5 )

最后一个数后面也要有空格

输入描述:

输入一个long型整数

输出描述:

按照从小到大的顺序输出它的所有质数的因子,以空格隔开。最后一个数后面也要有空格。

1
2
3
4
5
6
7
8
9
10
11
def factor(n):
flag = True
for i in range(2, int(n**0.5+1)):
if n % i == 0:
print(i, end=" ")
flag = False
factor(int(n/i))
break
if flag:
print(n, end=" ")
factor(int(input()))

7E取近似值

数学/语法

思路/考点

  • int()函数如果接受的是个float数,他将只取整数位抛弃小数位
  • //也是只取整数位

写出一个程序,接受一个正浮点数值,输出该数值的近似整数值。如果小数点后数值大于等于5,向上取整;小于5,则向下取整。

1
2
num = int(float(input())+0.5)
print(num)

8M合并表记录

几何/哈希

思路/考点

考察dict的访问:

  • for i in dict–>access key
  • for key in dict.keys–>accsee key
  • for key, val in dict.item–>access key and val
  • dict[key]–>access key对应的val, 没有的话返回KeyError
  • dict.get(key, val(default:None))–>访问dict中key对应的值,没有的话返回val

有与dict中的key不可以直接修改,如果要修改key需要以下操作

第一种方法:将需要修改的键对应的值用dict.pop() 的方法提取出来,并重新赋值给新的键,即dict[新的键] = dict.pop(旧的键)。(字典dict的pop是删除某个键及其对应的值,返回的是该键对应的值)

1
2
dict={'a':1, 'b':2}
dict["c"] = dict.pop("a")

第二种方法:结合dict.pop() 和dict.update() 的方法。(字典dict的pop是删除某个键及其对应的值,返回的是该键对应的值)

1
2
dict={'a':1, 'b':2}
dict.update({'c':dict.pop("a")})

第三种方法:结合直接修改和del语句。先用直接修改的方式将旧键赋值给新的键,再用del语句删除原来的键名。

1
2
3
dict={'a':1, 'b':2}
dict['c']=dict['a']
del dict['a']

描述

数据表记录包含表索引和数值(int范围的正整数),请对表索引相同的记录进行合并,即将相同索引的数值进行求和运算,输出按照key值升序进行输出。

输入描述:

先输入键值对的个数
然后输入成对的index和value值,以空格隔开

输出描述:

输出合并后的键值对(多行)

1
2
3
4
5
6
7
8
9
num = int(input()) # 共输入几组key-val
dic = {}
for i in range(num):
line = input().split()
key = int(line[0])
value = int(line[1])
dic[key] = dic.get(key,0) + value
for key in sorted(dic):
print(key, dic.get(key))

9M提取不重复的整数

数组/位运算/哈希

描述

输入一个int型整数,按照从右向左的阅读顺序,返回一个不含重复数字的新的整数。

保证输入的整数最后一位不是0。

输入描述

输入一个int型整数

输出描述

按照从右向左的阅读顺序,返回一个不含重复数字的新的整数

1
2
3
4
5
6
7
8
temp = input()
ans = list(temp)[::-1]
anss = []
for item in ans:
if item not in anss:
anss.append(item)
print(''.join('%s' %i for i in anss))

:m:’’.join()将list中的元素转化成string,如果list中有number,则需要使用格式''.join('%s' %i for i in list)

10E字符个数统计

字符串/哈希

思路/考点

  • 如何记录已存在的值,如果当前值已经存在,则跳过该值的后续操作

描述

编写一个函数,计算字符串中含有的不同字符的个数。字符在ASCII码范围内(0~127,包括0和127),换行表示结束符,不算在字符里。不在范围内的不作统计。多个相同的字符只计算一次

例如,对于字符串abaca而言,有a、b、c三种不同的字符,因此输出3。

输入描述:

输入一行没有空格的字符串。

输出描述:

输出 输入字符串 中范围在(0~127,包括0和127)字符的种数。

1
2
3
4
5
6
7
8
9
10
11
12
while True:
try:
zifu = input()
a=''
for i in zifu:
if i in a and 0<ord(i)<127:
pass
else:
a+=i
print(len(a))
except:
break

11E数字颠倒

字符串

思路/考点

  • 考察对tuple/string/list使用slice操作[::-1]

输入一个整数,将这个整数以字符串的形式逆序输出

程序不考虑负数的情况,若数字含有0,则逆序形式也含有0,如输入为100,则输出为001

输入一个int整数

将这个整数以字符串的形式逆序输出

1
2
temp = input()[::-1]
print(temp)

12E字符串反转

字符串

思路/考点

接受一个只包含小写字母的字符串,然后输出该字符串反转后的字符串。(字符串长度不超过1000)

输入一行,为一个只包含小写字母的字符串。

输出该字符串反转后的字符串。

Essrntial:同11数字颠倒

13H句子逆序

数组

思路/考点

  • 控制输入,使用split()将输入的string转化成list,以方便后续操作
  • 控制输出,使用.join(),将list转化成string,其中sep=‘ ’

将一个英文语句以单词为单位逆序排放。例如“I am a boy”,逆序排放后为“boy a am I”
所有单词之间用一个空格隔开,语句中除了英文字母外,不再包含其他字符

输入一个英文语句,每个单词用空格隔开。保证输入只包含空格和字母。

得到逆序的句子

I am a boy

boy a am I

1
2
3
4
5
6
while True:
try:
temp = input().split()[::-1]
print(' '.join(temp))
except:
break

14M字符串排序

输入一定的字符串,将字符串按照字母优先级进行排序

1
2
3
4
5
6
7
8
9
words = []
num = int(input())
for i in range(num):
word = input()
words.append(word)
words.sort()
# 不能直接遍历words.sort(),否则返回None
for item in words:
print(item)

sort()即可进行数字排序也可以对单词按照字母优先级进行排序

15E求int数据在存贮中使用1的个数

位运算

思路/考点

  • 使用bin()将输入的base=10数据转化成binary数据(str)
  • 使用str的slice将0b(二进制开头)去除[2:]
  • 使用.count(‘item’) 返回str中‘item’出现的次数

描述

输入一个int型的正整数,计算出该int型数据在内存中存储时1的个数。

输入描述:

输入一个整数(int类型)

输出描述:

这个数转换成2进制后,输出1的个数

1
2
temp = bin(int(input()))
print(str(temp)[2:].count('1'))

16M购物单【:star::star::star:】

动态规划

思路/考点

描述

王强今天很开心,公司发给N元的年终奖。王强决定把年终奖用于购物,他把想买的物品分为两类:主件与附件,附件是从属于某个主件的,下表就是一些主件与附件的例子:

主件 附件
电脑 打印机,扫描仪
书柜 图书
书桌 台灯,文具
工作椅

如果要买归类为附件的物品,必须先买该附件所属的主件。每个主件可以有 0 个、 1 个或 2 个附件。附件不再有从属于自己的附件。王强想买的东西很多,为了不超出预算,他把每件物品规定了一个重要度,分为 5 等:用整数 1 ~ 5 表示,第 5 等最重要。他还从因特网上查到了每件物品的价格(都是 10 元的整数倍)。他希望在不超过 N 元(可以等于 N 元)的前提下,使每件物品的价格与重要度的乘积的总和最大。

设第 j 件物品的价格为 v[j] ,重要度为 w[j] ,共选中了 k 件物品,编号依次为 j 1 , j 2 ,……, j k ,则所求的总和为:

v[j 1 ]*w[j 1 ]+v[j 2 ]*w[j 2 ]+ … +v[j k ]*w[j k ] 。(其中 * 为乘号)

请你帮助王强设计一个满足要求的购物单。

输入描述:

输入的第 1 行,为两个正整数,用一个空格隔开:N m

(其中 N ( <32000 )表示总钱数, m ( <60 )为希望购买物品的个数。)

从第 2 行到第 m+1 行,第 j 行给出了编号为 j-1 的物品的基本数据,每行有 3 个非负整数 v p q

(其中 v 表示该物品的价格( v<10000 ), p 表示该物品的重要度( 1 **~** 5 ), q 表示该物品是主件还是附件。如果 q=0 ,表示该物品为主件,如果 q>0 ,表示该物品为附件, q 是所属主件的编号)

输出描述:

输出文件只有一个正整数,为不超过总钱数的物品的价格与重要度乘积的总和的最大值( <200000 )。

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
money, things = map(int, input().split())
money = money // 10 # money都是10的整数,整除后,减小循环次数
# 三列分别表示主件,附件1,附件2
weight = [[0] * 3 for _ in range(0, things + 1)] # 价格
value = [[0] * 3 for _ in range(0, things + 1)] # 价值=价格*重要度
result = [[0] * (money + 1) for _ in range(0, things + 1)]
for i in range(1, things + 1):
prices, degree, depend = map(int, input().split()) # 分别为价格,重要性,主附件
prices = prices // 10

if depend == 0: # 主件
weight[i][0] = prices
value[i][0] = prices * degree

elif weight[depend][1] == 0: # 附件
weight[depend][1] = prices # 第一个附件
value[depend][1] = prices * degree

else:
weight[depend][2] = prices # 第二个附件
value[depend][2] = prices * degree
# 遍历计算
for i in range(1, things + 1): # 每几件物品
for j in range(money, -1, -1):
if j >= weight[i][0]: # 当前价格j可以容下第i个主件时,比较(上一个物品对应价格的价值)与(第i个物品的价值 + 余额价格对应上个物品价值)
result[i][j] = max(result[i - 1][j], result[i - 1][j - weight[i][0]] + value[i][0])
# 在确定主件可容纳,并做相应计算之后,判断附件的容纳情况,如果主件都没有办法容纳,则附件必定不可容纳
if j >= (weight[i][0] + weight[i][1]):
# 当可以容下第i个主件和此主件的第1个附件时,此时需要在比大小时加入当前最优,保证添加附件的结果不会反而更小
# 比较(当前价格对应上一物品的价值)与(主键价值+附件1价值+上一物品在价格(j-主键价格-附件1价格)时对应的价值)
result[i][j] = max(result[i][j], result[i - 1][j], result[i - 1][j - weight[i][0] - weight[i][1]] + value[i][0] + value[i][1])

if j >= (weight[i][0] + weight[i][2]):
# 可以容下第i个主件和此主件的第2个附件,此时也需要在比大小时加入当前最优,保证添加附件的结果不会反而更小
# 比较(当前价格对应上一物品的价值)与(主键价值+附件2价值+上一物品在价格(j-主键价格-附件2价格)时对应的价值),
result[i][j] = max(result[i][j], result[i - 1][j], result[i - 1][j - weight[i][0] - weight[i][2]] + value[i][0] + value[i][2])
# 根据3件物品价格之和必然大于等于2件物品的规则,只有在能容纳主件和附件2时,才有判断全部容纳可能性的必要
if j >= (weight[i][0] + weight[i][1] + weight[i][2]):
# 当判断通过,则判断当前值与上物品计算当前价格价值与当前3物品情况价值中最大值,同时还要比较目前最优值
result[i][j] = max(result[i][j], result[i - 1][j], result[i - 1][j - weight[i][0] - weight[i][1] - weight[i][2]] + value[i][0] + value[i][1] + value[i][2])
else:
# 当前价格j不能容下第i个主件时,价值为上一个物品的对应价格的价值
result[i][j] = result[i - 1][j]
print(result[things][money] * 10)

17H坐标移动

思路/考点

使用isdigit判断输入的字符串是否为整数;使用slice[start:end)

开发一个坐标计算工具, A表示向左移动,D表示向右移动,W表示向上移动,S表示向下移动。从(0,0)点开始移动,从输入字符串里面读取一些坐标,并将最终输入结果输出到输出文件里面。

输入:

合法坐标为A(或者D或者W或者S) + 数字(两位以内)

坐标之间以;分隔。

非法坐标点需要进行丢弃。如AA10; A1A; $%$; YAD; 等。

下面是一个简单的例子 如:

A10;S20;W10;D30;X;A1A;B10A11;;A10;

处理过程:

起点(0,0)

+ A10 = (-10,0)

+ S20 = (-10,-20)

+ W10 = (-10,-10)

+ D30 = (20,-10)

+ x = 无效

+ A1A = 无效

+ B10A11 = 无效

+ 一个空 不影响

+ A10 = (10,-10)

结果 (10, -10)

注意请处理多组输入输出

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
while True:
try:
s = input().split(';')
x, y = 0, 0
for c in s:
if not c:
continue
elif c[0] == 'A' and c[1:].isdigit():
x -= int(c[1:])
elif c[0] == 'S' and c[1:].isdigit():
y -= int(c[1:])
elif c[0] == 'W' and c[1:].isdigit():
y += int(c[1:])
elif c[0] == 'D' and c[1:].isdigit():
x += int(c[1:])
print(str(x)+','+str(y))
except:
break

18H识别有效的IP地址和掩码并进行分类统计

字符串/查找

思路/考点

请解析IP地址和对应的掩码,进行分类识别。要求按照A/B/C/D/E类地址归类,不合法的地址和掩码单独归类。

所有的IP地址划分为 A,B,C,D,E五类

A类地址1.0.0.0~126.255.255.255;

B类地址128.0.0.0~191.255.255.255;

C类地址192.0.0.0~223.255.255.255;

D类地址224.0.0.0~239.255.255.255;

E类地址240.0.0.0~255.255.255.255

私网IP范围是:

10.0.0.0~10.255.255.255

172.16.0.0~172.31.255.255

192.168.0.0~192.168.255.255

子网掩码为二进制下前面是连续的1,然后全是0。(例如:255.255.255.32就是一个非法的掩码)

注意二进制下全是1或者全是0均为非法

注意:

\1. 类似于【0...】和【127..*.*】的IP地址不属于上述输入的任意一类,也不属于不合法ip地址,计数时可以忽略

\2. 私有IP地址和A,B,C,D,E类地址是不冲突的

输入描述:

多行字符串。每行一个IP地址和掩码,用~隔开。

输出描述:

统计A、B、C、D、E、错误IP地址或错误掩码、私有IP的个数,之间以空格隔开。

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
ans = [0] * 7

while True:
try:
m = input().split("~")
m0 = m[0].split(".") # ip 地址
m1 = m[1].split(".") # 子网掩码

# 检查是否有“空”的格式错误, 例如192..0.2
flag = False
for i in m0 + m1:
if i.isdigit() is False:
ans[5] += 1 # type 5
flag = True
break
if flag is True:
continue

# 求子网掩码的二进制格式 = m2
m2 = ""
for i in m1:
temp = "{0:b}".format(int(i)) # 二进制转换
m2 += '0' * (8 - len(temp)) + temp # 补全8bit
# 检查子网掩码格式
count = 0
for i in range(len(m2) - 1):
if m2[i] != m2[i + 1]:
count += 1
if count != 1: # ==1 说明格式正确
ans[5] += 1 # type 5
count = 0
continue

# 统计
if 1 <= int(m0[0]) <= 126:
ans[0] += 1
elif 128 <= int(m0[0]) <= 191:
ans[1] += 1
elif 192 <= int(m0[0]) <= 223:
ans[2] += 1
elif 224 <= int(m0[0]) <= 239:
ans[3] += 1
elif 240 <= int(m0[0]) <= 255:
ans[4] += 1

if int(m0[0]) == 10:
ans[6] += 1
elif int(m0[0]) == 172 and 16 <= int(m0[1]) <= 31:
ans[6] += 1
elif int(m0[0]) == 192 and int(m0[1]) == 168:
ans[6] += 1
except:
break

for i in range(len(ans) - 1):
print(ans[i], end=" ")
print(ans[-1], end="")

19H简单错误记录

字符串

思路/考点

描述

开发一个简单错误记录功能小模块,能够记录出错的代码所在的文件名称和行号。

处理:

1、 记录最多8条错误记录,循环记录,最后只用输出最后出现的八条错误记录。对相同的错误记录只记录一条,但是错误计数增加。最后一个斜杠后面的带后缀名的部分(保留最后16位)和行号完全匹配的记录才做算是”相同“的错误记录。

2、 超过16个字符的文件名称,只记录文件的最后有效16个字符;

3、 输入的文件可能带路径,记录文件名称不能带路径。

4、循环记录时,只以第一次出现的顺序为准,后面重复的不会更新它的出现时间,仍以第一次为准

输入描述:

每组只包含一个测试用例。一个测试用例包含一行或多行字符串。每行包括带路径文件名称,行号,以空格隔开。

输出描述:

将所有的记录统计并将结果输出,格式:文件名 代码行数 数目,一个空格隔开,如:

示例1

输入:

1
2
3
4
5
6
7
8
9
10
D:\zwtymj\xccb\ljj\cqzlyaszjvlsjmkwoqijggmybr 645
E:\je\rzuwnjvnuz 633
C:\km\tgjwpb\gy\atl 637
F:\weioj\hadd\connsh\rwyfvzsopsuiqjnr 647
E:\ns\mfwj\wqkoki\eez 648
D:\cfmwafhhgeyawnool 649
E:\czt\opwip\osnll\c 637
G:\nt\f 633
F:\fop\ywzqaop 631
F:\yay\jc\ywzqaop 631

输出:

1
2
3
4
5
6
7
8
rzuwnjvnuz 633 1
atl 637 1
rwyfvzsopsuiqjnr 647 1
eez 648 1
fmwafhhgeyawnool 649 1
c 637 1
f 633 1
ywzqaop 631 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
# 初始全纪录list
nv = []
# 初始去除重复list
check = []
while True:
try:
word, n = input().split() # 处理数据,先以空格区分
word = word.split("\\") # 再以\区分
word = word[-1] # 提取最后一项
if len(word) > 16: # 规范为最大十六位
word = word[(len(word) - 16)::] # 只要最后十六位
str1 = word + " " + n # 由于要求行数和后缀完全匹配才算,直接重新合并判断这一整条就好了不用分开
nv.append(str1) # 输入list里全纪录
if str1 not in check: # 模拟set功能,由于set会乱序在这不能用
check.append(str1) # 当为新的时候添加
except:
break
# 当错误记录大于8项时(去除重复),只用最后八项设n=8
if len(check) > 8:
n = 8
else:
# 小于8项时
n = len(check)
for i in range(n):
# 由于可能小于八项无法直接-8+i起始,换成(总长度-n+i)逐个提取
str2 = check[(len(check)-n+i)] + " " + str(nv.count(check[(len(check)-n+i)]))
print(str2)

20H密码验证合格程序

字符串/数组

思路/考点

[如何检测字符串的类型是本题中的关键]

string.isxxx()检测

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
isdecimal():判断给定字符串是否全为数字

isalpha():判断给定的字符串是否全为字母

isalnum():判断给定的字符串是否只含有数字与字母

isupper():判断给定的字符串是否全为大写

islower():判断给定的字符串是否全为小写

istitle():判断给定的字符串是否符合title()

isspace():判断给定的字符串是否为空白符(空格、换行、制表符)

isprintable():判断给定的字符串是否为可打印字符(只有空格可以,换行、制表符都不可以)

isidentifier():判断给定的字符串是否符合命名规则(只能是字母或下划线开头、不能包含除数字、字母和下划线
以外的任意字符。)

[使用slice[i:i+3]]来控制子串,同时使用string.count([:])>1–>False控制子串是否有长度大于3且相等的情况]

密码要求:

1.长度超过8位

2.包括大小写字母.数字.其它符号,以上四种至少三种

3.不能有相同长度大于2的子串重复

输入

一组或多组长度超过2的字符串。每组占一行

输出

如果符合要求输出:OK,否则输出NG

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
while True:
try:
line = input()
a = 0
b = 0
c = 0
d = 0
flag = True
for i in line:
if i.isdigit():
a = 1
elif i.islower():
b = 1
elif i.isupper():
c = 1
else:
d = 1
for j in range(len(line) - 3):
if line.count(line[j:j + 3]) > 1:
flag = False
if len(line) > 8 and (a + b + c + d) >= 3 and flag:
print("OK")
else:
print("NG")
except:
break

21M简单密码

字符串

思路/考点

【考察】

  1. dict.get(key, defalut=Value)–>不存在return None
  2. ord与chr
  3. 字符串检测.isdigit()/is.upper()/.islower

描述

密码是我们生活中非常重要的东东,我们的那么一点不能说的秘密就全靠它了。哇哈哈. 接下来渊子要在密码之上再加一套密码,虽然简单但也安全。

假设渊子原来一个BBS上的密码为zvbo9441987,为了方便记忆,他通过一种算法把这个密码变换成YUANzhi1987,这个密码是他的名字和出生年份,怎么忘都忘不了,而且可以明目张胆地放在显眼的地方而不被别人知道真正的密码。

他是这么变换的,大家都知道手机上的字母: 1–1, abc–2, def–3, ghi–4, jkl–5, mno–6, pqrs–7, tuv–8 wxyz–9, 0–0,就这么简单,渊子把密码中出现的小写字母都变成对应的数字,数字和其他的符号都不做变换,

声明:密码中没有空格,而密码中出现的大写字母则变成小写之后往后移一位,如:X,先变成小写,再往后移一位,不就是y了嘛,简单吧。记住,z往后移是a哦。

输入描述

输入包括多个测试数据。输入是一个明文,密码长度不超过100个字符,输入直到文件结尾

输出描述:

输出渊子真正的密文

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
temp = input()
result = ''
mapping = {'abc': 2, 'def': 3, 'ghi': 4,
'jkl': 5, 'mno': 6, 'pqrs': 7,
'tuv': 8, 'wxyz': 9}

for i in range(len(temp)):
if temp[i].isdigit():
result += temp[i]

elif temp[i].islower():
for key in mapping.keys():
if temp[i] in key:
result += str(mapping.get(key))

elif temp[i].isupper():
temp_char_num = ord(temp[i])
if temp_char_num == 90:
temp_char_num = 64
result += chr(temp_char_num+1).lower()
else:
result += temp[i]
print(result)

22E汽水瓶

数学/模拟

思路/考点

【连续输入整数】

1
2
3
4
5
while True:
try:
temp = int(input())
except:
break

从本质上讲为n//2–>return value

描述

有这样一道智力题:“某商店规定:三个空汽水瓶可以换一瓶汽水。小张手上有十个空汽水瓶,她最多可以换多少瓶汽水喝?”答案是5瓶,方法如下:先用9个空瓶子换3瓶汽水,喝掉3瓶满的,喝完以后4个空瓶子,用3个再换一瓶,喝掉这瓶满的,这时候剩2个空瓶子。然后你让老板先借给你一瓶汽水,喝掉这瓶满的,喝完以后用3个空瓶子换一瓶满的还给老板。如果小张手上有n个空汽水瓶,最多可以换多少瓶汽水喝?

输入描述:

输入文件最多包含10组测试数据,每个数据占一行,仅包含一个正整数n(1<=n<=100),表示小张手上的空汽水瓶数。n=0表示输入结束,你的程序不应当处理这一行。

输出描述

对于每组测试数据,输出一行,表示最多可以喝的汽水瓶数。如果一瓶也喝不到,输出0。

1
2
3
4
5
6
7
8
9
while True:
try:
temp = int(input())
n = 0
if temp > 1:
temp //= 2
print(temp)
except:
break

23H删除字符串中出现次数最少的字符

字符串

思路/考点

【方向点】

  1. 如何找到出现次数最小的元素
  2. 如何再字符串中剔除1中的元素
  3. string.replace(ele, ‘target’)用于替换字符串中的元素,其中可以用于删除/更改操作

【method1】创建一个unicode(list)记录每个ele出现的次数,如果ele出现的次数不等于min,则跳过result

描述

实现删除字符串中出现次数最少的字符,若多个字符出现次数一样,则都删除。输出删除这些单词后的字符串,字符串中其它字符保持原来的顺序。

注意每个输入文件有多组输入,即多个字符串用回车隔开

输入描述

字符串只包含小写英文字母, 不考虑非法输入,输入的字符串长度小于等于20个字节。

输出描述

删除字符串中出现次数最少的字符后的字符串。

方法一:使用unicode[list]来存贮每个ele的出现次数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# method1
while True:
try:
temp = input()
unicode = []
result = ''
for i in range(len(temp)):
unicode.append(temp.count(temp[i]))
for i in range(len(temp)):
if min(unicode) != unicode[i]:
result += temp[i]
print(result)
except:
break

方法二:使用hash table来存贮每个ele出现的次数

1
2
a = min(dict.values)
return dict中最小的value
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
while True:
try:
temp = input()
unicode = {}
for char in temp:
unicode[char] = unicode.get(char, 0) + 1

min_val = min(unicode.values())

i = 0
for key in unicode:
if unicode[key] == min_val:
i += 1
temp = temp.replace(key, '')
print(temp.strip())
except:
break

24H合唱队【:star::star::star:未解决】

动态规划/队列

思路/考点

描述

计算最少出列多少位同学,使得剩下的同学排成合唱队形

说明:

N位同学站成一排,音乐老师要请其中的(N-K)位同学出列,使得剩下的K位同学排成合唱队形。
合唱队形是指这样的一种队形:设K位同学从左到右依次编号为1,2…,K,他们的身高分别为T1,T2,…,TK, 则他们的身高满足存在i(1<=i<=K)使得T1<T2<……<Ti-1<Ti+1>……>TK。

你的任务是,已知所有N位同学的身高,计算最少需要几位同学出列,可以使得剩下的同学排成合唱队形。

注意:不允许改变队列元素的先后顺序 不要求最高同学左右人数必须相等

请注意处理多组输入输出!

备注:

1<=N<=3000

输入描述

有多组用例,每组都包含两行数据,第一行是同学的总数N,第二行是N位同学的身高,以空格隔开

输出描述

最少需要几位同学出列

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import bisect
def max_order(lists):
list_num = []
list_max = []
for i in lists:
local = bisect.bisect_left(list_num, i)
if local == len(list_num):
list_num.append(i)
list_max.append(local+1)
else:
list_num[local] = i
list_max.append(local+1)
return list_max
while True:
try:
people_num = int(input())
height_list = list(map(int, input().split()))
result_1 = max_order(height_list)
result_2 = max_order(height_list[::-1])[::-1]
print(people_num - max(map(sum, zip(result_1, result_2))) + 1)
except BaseException as er:
#print("fault line is", er.__traceback__.tb_lineno)
break

25H数据分类处理【未解决】

排序

思路/考点

输入描述

一组输入整数序列I和一组规则整数序列R,I和R序列的第一个整数为序列的个数(个数不包含第一个整数);整数范围为0~0xFFFFFFFF,序列个数不限

输出描述

从R依次中取出R,对I进行处理,找到满足条件的I:

I整数对应的数字需要连续包含R对应的数字。比如R为23,I为231,那么I包含了R,条件满足 。

按R从小到大的顺序:

(1)先输出R

(2)再输出满足条件的I的个数;

(3)然后输出满足条件的I在I序列中的位置索引(从0开始);

(4)最后再输出I。

附加条件:

(1)R需要从小到大排序。相同的R只需要输出索引小的以及满足条件的I,索引大的需要过滤掉

(2)如果没有满足条件的I,对应的R不用输出

(3)最后需要在输出序列的第一个整数位置记录后续整数序列的个数(不包含“个数”本身)

序列I:15,123,456,786,453,46,7,5,3,665,453456,745,456,786,453,123(第一个15表明后续有15个整数)

序列R:5,6,3,6,3,0(第一个5表明后续有5个整数)

输出:30, 3,6,0,123,3,453,7,3,9,453456,13,453,14,123,6,7,1,456,2,786,4,46,8,665,9,453456,11,456,12,786

说明:

30—-后续有30个整数

3—-从小到大排序,第一个R为0,但没有满足条件的I,不输出0,而下一个R是3

6— 存在6个包含3的I

0— 123所在的原序号为0

123— 123包含3,满足条件

1
2
3
4
5
6
instance
>>input
15 123 456 786 453 46 7 5 3 665 453456 745 456 786 453 123
5 6 3 6 3 0
>>output
30 3 6 0 123 3 453 7 3 9 453456 13 453 14 123 6 7 1 456 2 786 4 46 8 665 9 453456 11 456 12 786
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
try:
while True:
l1=input().split()[1:]
l2=list(map(int,input().split()))[1:]
l2=list(set(l2))
l2.sort()
res=[]
l2=list(map(str,l2))
for i in range(len(l2)):
ans =[]
for j in range(len(l1)):
if l2[i] in l1[j]:
ans.append(str(j))
ans.append(l1[j])
if ans:
res.append(l2[i])
res.append(str(len(ans)//2))
res +=ans
ss = str(len(res))+' '+' '.join(res)
print(ss)
except:
pass

26H字符串排序

字符串

思路/考点

  • 首先讲string中的element的alpha与number和others分离,这里采用.isdigit()
  • 对alpha进行排序操作,再输入顺序的前提下对A/a这种相同元素的字母进行排序这里使用:sorted(iteration, key=lambda x:x.lower())
  • 由于result中的值,是除了alpha之外的字符串,当检测到当前result为空(‘ ’)说明此处应该填上排好序的alpha,因此使用if not x:进行控制

编写一个程序,将输入字符串中的字符按如下规则排序。

规则 1 :英文字母从 A 到 Z 排列,不区分大小写。

如,输入: Type 输出: epTy

规则 2 :同一个英文字母的大小写同时存在时,按照输入顺序排列。

如,输入: BabA 输出: aABb

规则 3 :非英文字母的其它字符保持原来的位置。

如,输入: By?e 输出: Be?y

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
while True:
try:
s = input()
result = [''] * len(s)
temp = []
for i, v in enumerate(s):
# 如果不是字母
if not v.isalpha():
result[i] = v
else:
# 是字母,将其存放
temp.append(v)
# 对temp进行排序
temp.sort(key=lambda c: c.lower())
for i, v in enumerate(result):
if not v:
result[i] = temp[0]
temp.pop(0)
print(''.join(result))
except:
break

27H查找兄弟单词

查找

思路/考点

描述

定义一个单词的“兄弟单词”为:交换该单词字母顺序(注:可以交换任意次),而不添加、删除、修改原有的字母就能生成的单词。

兄弟单词要求和原来的单词不同。例如:ab和ba是兄弟单词。ab和ab则不是兄弟单词。

现在给定你n个单词,另外再给你一个单词str,让你寻找str的兄弟单词里,按字典序排列后的第k个单词是什么?

注意:字典中可能有重复单词。本题含有多组输入数据。

输入描述

先输入单词的个数n,再输入n个单词。 再输入一个单词,为待查找的单词x 最后输入数字k

输出描述

输出查找到x的兄弟单词的个数m 然后输出查找到的按照字典顺序排序后的第k个兄弟单词,没有符合第k个的话则不用输出。

28M按照输入顺序排序[:star:]

题目

输入一串英文字符(含大小写), 将英文字符在ascending的基础上对于A,a等按照输入顺序排序

1
2
3
4
5
6
7
8
9
10
11
12
a = input().strip()
temp = list(a)
print(temp)
print(''.join(sorted(temp, key=lambda x: x.lower())))

# key step
sorted(temp, key=lambda x: x.lower())

>>
addAeefERDAqa
['a', 'd', 'd', 'A', 'e', 'e', 'f', 'E', 'R', 'D', 'A', 'q', 'a']
aAAaddDeeEfqR

Leecode

1E.两数之和

给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。

你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。

你可以按任意顺序返回答案。

思路:

  1. 使用双循环暴力求解,缺点是时间复杂度会很高
  2. 使用哈希表,记录上一个值和对应的index
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
def solution1(alist, target):
if len(alist) == 0:
return []
for index, val in enumerate(alist):
for i in range(index+1, len(alist)):
if alist[i] + val == target:
return [index, i]
time = 3340ms
memory = 14.9MB

def solution2(alist, target):
hashmap = {}
for index, val in enumerate(alist):
if target - val in hashmap:
return hashmap[target-val], index
hashmap[val] = index
time = 40ms
memory = 15.8MB

2M.两数相加

给你两个 非空 的链表,表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的,并且每个节点只能存储 一位 数字。

请你将两个数相加,并以相同形式返回一个表示和的链表。

你可以假设除了数字 0 之外,这两个数都不会以 0 开头。

img

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
def solution(l1, l2):
length1, length2 = 0, 0
p = l1
while p:
length1 += 1
p = p.next
p = l2
while p:
length2 += 1
p = p.next
if length1 < length2:
l1, l2 = l2, l1

p1, p2 = l1, l2
c = 0
while p2:
p1.val += p2.val
p1 = p1.next
p2 = p2.next
p1 = l1
while p1:
p1.val += c
c = 0
if p1.val > 9:
p1.val -= 10
c = 1
if not p1.next and c:
p1.next = ListNode(1)
break
p1 = p1.next
return l1
>>
Time = 50ms
Memory = 14.9M

3M求最长的回文子串

思路/考点

子序列!==子集

求子序列的问题可以理解成求解index的问题,通过控制[start:end)切片操作来实现字符串子序列的提取

chars=‘acbcd’的所有子字符串为:

{‘’, ‘b’, ‘cb’, ‘cbc’, ‘acbc’, ‘a’, ‘c’, ‘acb’, ‘bc’, ‘ac’}

找出长度最大的回文子串:

比如a,aba

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
chars = "acbcd"
b = set()
length = len(chars)
for j in range(length):
for i in range(length):
if i+j < length:
b.add(chars[i:i+j])
print(b)

temp = []
for ele in b:
if ele == ele[::-1] and ele != '':
temp.append(ele)
print(temp)
>>
{'', 'b', 'cb', 'cbc', 'acbc', 'a', 'c', 'acb', 'bc', 'ac'}
['b', 'cbc', 'a', 'c']

4H最小路径[:star:DP/回溯]

从上到下找到最短路径(数宇之和最小),只能往下层走,同时只能走向相邻的节点,例如图中第一排2只能走向第二排的3、7﹔第一排的5可以走向第二排的1、7、3

1 8 5 2
4 1 7 3
3 6 2 9

批评与自我批评

目的

为了保持初心,对在生活、工作等社会实践过程中出现的各种问题进行及时的反馈和解决,要求每周或双周进行一次批评与自我批评,在于及时发现问题并解决问题,同时勉励自己,对犯的各种错误进行反思总结,切勿搞形式主义,可慢总结,不可形式总结。

2021/11/7

  • 凯德项目复盘
  1. 分清阶段性矛盾和主要矛盾的关系, 矛盾处在变化中
  2. 在分工时,明确分工的意义和目的,进行必要的阶段性对接
  3. 对于合作项目,复盘和总结远大于追责,首要考虑的是如何让解决问题,但并不是不承担责任,必要的自我批评同样不可缺少
  4. 自力更生为主,争取外援为辅
  • 事物永远处在不断变化中,认识不到变化,或拒绝承认变化,抱着可怜的相对静止的本位主义分析和认同是最大的愚蠢,同时也是对自喻辩证唯物主义者的最大亵渎
  • 分析问题时,不要简单得将原因归于某个人或某个个体,将对问题得分析转移到对个人的无休止得批判
  • 团结-批评-团结, 一时头脑发热将对问题的批判转移为对个体的能力、品行甚至行为生活习惯的批判,是纯粹的空头理论家,是不会将理论用于实际的典型,是早已被批评数次的本本主义者
  • 在矛盾中发现,在矛盾中舍去

2021/10/10

  • 现阶段在消费理念上须保持一下几点
  1. 自我提升类消费包括不限于:学习|身体锻炼|节省时间为首要消费
  2. 生活消费为次要消费,不做不必要的节省
  3. 避免不必要的娱乐消费但不禁止必要的放松消费
  • 要和左中右都交朋友,了解他们的意识形态并加以批判性的吸收

2021/9/25

  • 增加英语日常学习

2021/9/21

批评

  • 在交流的过成功拒绝主观大家长式的交流方式, 在知识分享和辩论上尤其注意
  • 对事物要辩论的看待, 即要有积极的方面又要有批判的方面, 不能因为批判的一面超过积极就完全摒弃放弃分析

增加

  • 增加每周反馈, 对比批评的执行效果并反馈结果和后序的执行方案

2021/9/12

批评

  • 知识应该是用于分享和交流的, 而不是让你自觉高人一等, 在分享知识的时候丢到摆架子的心态
  • 遇到不同意见, 仔细分析冲突的点, 抓住主要矛盾, 拒绝主观臆想的经验主义

2021/9/3

批评:

  • 在动手做之前必须明确各个部分的需求,必须清楚了解每一步所需要的参数,不要轻易动手

2021/8/30

批评:

  • 在实践中对于什么是主要矛盾,什么是次要矛盾理解不够,不能从实际一分为二的解决矛盾的两重性,老实妄想“一下”解决矛盾
  • 对于主要矛盾和次要矛盾的处理关系认识不足,在实际生活中不是只要主要矛盾不要次要矛盾,而是两手抓但要注意侧重点不同,所要付的精力也要视情况而不同
  • 经济基础决定上层建筑,经济基础存在对抗性矛盾,必然不可能在上层建筑上达成这样或那样的一致性, 所谓永远的一致绝不可能,不能留有一丝的幻想,只有片面的短暂的一致
  • 明确当前主要矛盾,并对此定制一系列的计划,以起督促作用
  • 在立场问题上绝不能抱有一丝的妥协性和调和性,对抗性矛盾除非在经济基础或外因对内因的作用下可以转换外,其余一切皆是自欺的幻想
  • 面对批评质疑,首先展开自我批评的,对待一切同志,除极少数不愿沟通、不愿交流、自以为是且立场极端之外均采用“批评-团结-批评”的公式

2021/8/8

批评:

  • 阅读时间大量降低
  • 最近一周的状态极差
  • 很少思考遇到的问题

我的世界观

目录

艾思奇

  • 辩证唯物主义 历史唯物主义艾思奇

卞历南

  • 卞历南 制度变迁的逻辑 中国现代国营企业制度之形成

达尔文

  • 达尔文 人类的由来
  • 达尔文 人类和动物的表情
  • 达尔文 物种起源

大卫·哈维

  • 大卫·哈维 美《资本论》 第一卷
  • 大卫·哈维 美《资本论》 第二卷
  • 大卫·哈维 新自由主义简史
  • 大卫哈维 资本主义社会的17个矛盾

费约翰

  • 费约翰 唤醒中国

哥白尼

  • 哥白尼 天体运行论

共产党宣言(1963 1997)

  • 共产党宣言1963
  • 共产党宣言1997

赫胥黎

  • 天演论

康德

  • 康德三大批判合集(注释版)
  • 实用人类学
  • 永久和平论
  • 自然科学的形而上学基础

雷蒙阿隆

  • 阶级斗争工业社会新讲

李达

  • 李达 唯物辩证法大纲

梁漱溟

  • 梁漱溟 中国文化要义
  • 梁漱溟 乡村建设理论

列宁

  • 列宁 国家与革命
  • 列宁选集第一卷1972
  • 列宁选集第二卷1972
  • 列宁选集第三卷1972
  • 列宁选集第四卷1972

卢卡奇

  • 卢卡奇 历史和阶级意识-马克思主义辩证法研究

卢梭

  • 社会契约论2003

鲁迅

  • 鲁迅 4卷 三闲集、二心集、南腔北调集

罗伯特欧文

  • 罗伯特欧文 选集一
  • 罗伯特欧文 选集二
  • 罗伯特欧文 选集三

马克思

  • 辩证唯物主义和历史唯物主义
  • 马恩列斯 论共产主义社会
  • 马克思:法兰西内战
  • 马克思:哥达纲领批判
  • 马克思《哲学的贫困》研究读本

毛泽东

  • 毛泽东选集1
  • 毛泽东选集2
  • 毛泽东选集3
  • 毛泽东选集4
  • 毛泽东选集5
  • 毛泽东选集6
  • 毛泽东选集7
  • 毛泽东 早期文稿1912.6-1920.11
  • 毛泽东 读书笔记解析
  • 毛泽东 读书生活
  • 毛泽东 社会主义政治经济学批注和谈话 上
  • 毛泽东 社会主义政治经济学批注和谈话 下
  • 毛泽东 苏联解密档案选
  • 毛泽东评点二十四史
  • 毛泽东思想1913-1943
  • 毛泽东思想1943-1949
  • 毛泽东思想1949-1957
  • 毛泽东思想1958-1960
  • 毛泽东思想1961-1968
  • 中央编辑组 社会主义政治经济学
  • 毛泽东语录

孟德斯鸠

  • 论法的精神(上册)1961
  • 论法的精神(下册)1963

摩尔根

  • 古代社会1971

黄宗智

  • 农村阶级斗争1998

蒲鲁东

  • 什么是所有权

戚本禹回忆录

  • 戚本禹回忆录 上
  • 戚本禹回忆录 下

三联书屋

  • 资本主义文化矛盾

斯大林

  • 斯大林 苏联社会主义经济问题

斯诺

  • 西行漫记

维特根斯坦

  • 维特根斯坦 逻辑哲学论

希特勒

  • 我的奋斗 2004

密洛凡·德热拉斯

  • 新阶级(1963 1981)

亚当密斯

  • 国富论2005 亚当·斯密
  • 国民财富的性质和原因的研究 1972

约翰穆勒

  • 功利主义
  • 政治经济学原理 上
  • 政治经济学原理 下
  • 论自由([英]约翰·密尔)

哲学小词典

  • 哲学小辞典

中国社会科学院

  • 历史虚无主义的批判

资本论(1982 2004 2009)

  • 资本论卷一
  • 资本论卷二
  • 资本论卷三

资治通鉴

  • 资治通鉴全译本 柏杨

佚名

  • 白俄罗斯的阶级斗争系列

Python-BuiltIn

[Class魔法]

–iter–

–str/repr/format–

string/represent

其实这正是反应了两者的区别,如果简单理解,这两个函数都是将一个实例转成字符串。但是不同的是,两者的使用场景不同,其中__str__更加侧重展示。所以当我们print输出给用户或者使用str函数进行类型转化的时候,Python都会默认优先调用__str__函数。而__repr__更侧重于这个实例的报告,除了实例当中的内容之外,我们往往还会附上它的类相关的信息,因为这些内容是给开发者看的。所以当我们在交互式窗口输出的时候,它会优先调用__repr__。

1
2
3
4
5
def __str__(self):
return 'x: %s, y: %s' % (self.x, self.y)

def __repr__(self):
return 'x: %s, y: %s' % (self.x, self.y)

到这里还没有结束,在有些场景当中,对于同一个对象我们可能有多种输出的格式。比如点,在有些场景下我们可能希望输出(x, y),有时候我们又希望输出x: 3, y: 4,可能还有些场景当中,我们希望输出<x, y>。

我们针对这么多场景,如果各自实现不同的接口会非常麻烦。这个时候利用__format__当中的这个参数,就可以大大简化这个过程,我们来看代码:

.format()配合__format__使用,减少api的设置,同时可是设置不同的输出格式

__str__:侧重展示

__repr__:侧重报告,可以展示类的相关信息给开发者

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
# dictionary
formats = {
'normal': 'x: {p.x}, y: {p.y}',
'point': '({p.x}, {p.y})',
'prot': '<{p.x}, {p.y}>'
}

class point:
def __init__(self, x, y):
self.x = x
self.y = y

def __str__(self):
return 'x: %s, y: %s' % (self.x, self.y)

def __format__(self, code):
# use dictionary[key].format(message==>/dictionary_varable/attribuate)
return formats[code].format(p=self)

p = point(3, 4)
# call-->{:key}.format(instance)
print('the point is {:normal}'.format(p))
print('the point is {:point}'.format(p))
print('the point is {:prot}'.format(p))
>>
the point is x: 3, y: 4
the point is (3, 4)
the point is <3, 4>

–contains–

1
2
3
4
5
6
7
8
9
10
11
12
13
Class A:
def __init__(self):
self.item = 10

def __contains__(self, item):
if item is self.item:
return True
else:
return False
a = A()
print(a.__contains__(5))
>>
False

[查看信息]

加密Class属性

使用__getattribute__进行加密

为了避免从实列中使用dir()或者__dict__access属性,可以使用__getattribute__:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Student:

def __init__(self):
self.__name = 'LIU'
self.__score = 96
self.age = 17

def __getattribute__(self, item):
# prevent all methods to access attributes
return None

temp = Student()
print(temp.age)
print(temp.__name)
print(temp.__dict__)
print(dir(temp))
>>
None
None
None
[]
  • 使用__getattr__只能对private attribute 和不存在的属性有效,当访问私有属性和不存在的属性时,return __getattr__里的内容

使用__getitem_ _进行单个key加密

access attributes use [key'string']

instance[‘attr_1’]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Student:

def __init__(self):
self.__name = 'LIU'
self.__score = 96
self.age = 17

def __getitem__(self, key):
if key in ['__name', '__score', 'age']:
return 'the class has the attributes.'
else:
return 'no access'

temp = Student()
print(temp['age'])
print(temp['ee'])
>>
the class has the attribute.
no access

访问Class属性

访问属性的方法常见的有四种

1
2
3
4
5
__getattribute__() # default 优先级最高
instance.attribute
__getattr__()
__getitem__()
.

查看class属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Student:

def __init__(self):
self.__name = 'LIU' # private attribute
self.__score = 96
self.age = 17

temp = Student()
print(temp.age)
print(dir(temp))
print(temp.__dict__)
>>
17
['_Student__name', '_Student__score', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'age']
----------------
{'_Student__name': 'LIU', '_Student__score': 96, 'age': 17}

–len–

Python对象含有的xxxx__这样定义的属性和方法,都有特殊用途,比如__len()。

在Python中,如果你调用len()函数试图获取一个对象的长度,实际上,在len()函数内部,它自动去调用该对象的__len__()方法。

–dict–

1
2
3
4
5
6
7
8
instance.__dict__()
==>list
return all attributes(contains private attribute)

dir(instance)
==>dictionary
return all operations and attributes

1
__dict__ <==difference==> dic()

__dict__与dir()的区别:

  1. dir()是一个函数,返回的是list;
  2. __dict__是一个字典,键为属性名,值为属性值;
  3. dir()用来寻找一个对象的所有属性,包括__dict__中的属性,__dict__是dir()的子集;
  4. 并不是所有对象都拥有__dict__属性。许多内建类型就[没有__dict__属性,如list,此时就需要用dir()来列出对象的所有属性。

__dict__是用来存储对象属性的一个字典,其键为属性名,值为属性的值。

注意:

  1. 实例的__dict__仅存储与该实例相关的实例属性,

    正是因为实例的__dict__属性,每个实例的实例属性才会互不影响。

  2. 类的__dict__存储所有实例共享的变量和函数(类属性,方法等),类的__dict__并不包含其父类的属性。

结论:

dir()函数会自动寻找一个对象的所有属性,包括__dict__中的属性。

__dict__是dir()的子集,dir()包含__dict__中的属性。

dir()是Python提供的一个API函数,dir()函数会自动寻找一个对象的所有属性(包括从父类中继承的属性)。

一个实例的__dict__属性仅仅是那个实例的实例属性的集合,并不包含该实例的所有有效属性。所以如果想获取一个对象所有有效属性,应使用dir()。


dir()

dir() 函数不带参数时,返回当前范围内的变量、方法和定义的类型列表;带参数时,返回参数的属性、方法列表。如果参数包含方法dir(),该方法将被调用。如果参数不包含dir(),该方法将最大限度地收集参数信息。

Get与Attr

如果在类中定义了getitem()方法,那么他的实例对象(假设为P)就可以这样P[key]取值。当实例对象做P[key]运算时,就会调用类中的getitem()方法。

1
2
3
4
5
6
7
__getitem__(self,key):返回键对应的值。

__setitem__(self,key,value):设置给定键的值

__delitem__(self,key):删除给定键对应的元素。

__len__():返回元素的数量

python的一切数据都是对象,包括函数、基本数据类型、自定义数据类型等等,这其中最复杂的就是对象内部存储的数据结构(引用),包括类属性、数据描述符、实例属性及非数据描述符,不仅它们的优先级不一样,而且它们的回调函数也存在很大的差异,这也是本文需要阐述的地方。

如果以前有过Javascript的编程经验,初上Python肯定会对“.”运算符与“[]”之间的差异难以理解,它们不仅不能替换,而且完全不相关,如下:

1
2
3
4
5
6
7
mem = {'username': 'yiifaa'}
# 无法通过.运算符进行计算
mem.username
# 声明String
name = str('yiifaa')
# 无法使用“[]”运算符
name['upper']

1. 为实例添加“[]”运算符支持

这也是“_ getattribute_”与“_ getitem_”的最大差异,示例如下:
_ getattribute_”只适用于所有的“.”运算符;
_ getitem_”只适用于所有的“[]”运算符;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Employee(object):

def __init__(self, username, age):
self.username = username
self.age = age

def __getattribute__(self, attr):
return super(Employee, self).__getattribute__(attr)

def __getitem__(self, attr):
return super(Employee, self).__getattribute__(attr)

em = Employee('yiifaa', 32)
print em.username
# 现在支持“[]”运算符
print em['username']

通过实现“_ getitem_”回调接口,现在Employee可以支持“[]”运算符。

  1. 避免语法错误错误

在对象属性的调用中,如果没有调用了不存在的属性,则Python解释器会报错,如下:

1
2
3
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'str' object has no attribute 'length'

通过覆盖实现“_ getattr_”回调接口可以解决此问题,如下:

1
2
3
4
5
6
#   直接返回空对象,将此方法添加到类Employee的声明中
def __getattr__(self, attr):
return None

# 现在调用不存在的属性也不会报错
print em.company

那“_ getattribute_”与“_ getattr_”的最大差异在于:

  1. 无论调用对象的什么属性,包括不存在的属性,都会首先调用“_ getattribute_”方法;
  2. 只有找不到对象的属性时,才会调用“_ getattr_”方法;

3. 将对象作为数据描述符

这就是“_ get_”的作用了,将整个对象都作为数据描述符,但是请记住,要想_ get_作为数据描述符,那么此对象只能作为类属性,作为实例属性则无效,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Dept(object):

def __init__(self, name):
self.name = name

# target是拥有此属性的对象
def __get__(self, target, type=None):
# 默认返回self与obj都可以
return 'Dept'

class Company(object):
# 一定要作为类属性,作为实例属性无效
dept = Dept('organ')

# 现在的测试结果
x = Company()
# 返回True
print type(x.dept) == str

4. 获取对象属性数据的三种方法

对象的所有属性都存储在“_ dict_”中(启用了“_ slots_”除外),所以访问对象的属性数据有如下三种方法:

1
2
3
print yiifaa.name
print yiifaa.__dict__['name']
print getattr(yiifaa, 'name')

结论

每个以“__ 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
37
38
39
40
41
42
43
44
45
class Examp(object):
def __init__(self, username, age):
self.username = username
self.age = age

# 1. “_ getattribute_”只适用于所有的“.”运算符;
def __getattribute__(self, item):
print('---getattribute----')
return super(Examp, self).__getattribute__(item)

# 2. “_ getitem_”只适用于所有的“[]”运算符;
def __getitem__(self, item):
print('======getitem======')
return super(Examp, self).__getattribute__(item)

# 3. 解决了访问不存在的属性报错问题, 找不到属性时会被调用:
def __getattr__(self, item):
return None

# 4. ““__get__””作为数据描述符,那么此对象只能作为类属性,作为实例属性则无效
def __get__(self, instance, owner):
# instance 访问此描述符的对象若通过类访问则为None ower 指所有者的类
# print(id(instance), id(owner),id(Obj1))
print('____get____')
return self



em = Examp('xiao66', 32)
# print(em.username) # 输出 ---getattribute---- xiao66
# print(em['username']) # 输出 ======getitem====== xiao66
# print(em.length) # 输出 ---getattribute---- None

# 获取对象属性的三种方法
# print(em.username)
# print(em.__dict__['username'])
# print(getattr(em, 'username'))

# __get__ 作用
class Obj1(object):
#一定要作为类属性,作为实例属性无效
attr = Examp('xiao66',12)

x = Obj1()
print(x.attr)

总结:
“__getattribute__”与“__getattr__”的最大差异在于:
1.无论调用对象的什么属性,包括不存在的属性,都会首先调用“__getattribute__”方法;
2.只有找不到对象的属性时,才会调用“__getattr__”方法;

仅希望以某种特殊的方式使用少数属性,而其他属性则按照普通属性操作。这些普通属性不会触发任何特殊代码,也不会因为遍历方法代码而浪费时间。在这些情况下,您可以对属性使用描述符
每次访问descriptor(即实现了__get__的类),都会先经过__get__函数。

需要注意的是,当使用类访问不存在的变量是,不会经过__getattr__函数。而descriptor不存在此问题,只是把instance标识为none而已

[快捷操作]

sorted/sort

  • sort排序,可以对iteration属性的数据结构进行操作
  • list\dict等时最常见的操作形式
  • .sort(key=lambda var: expression)是常见的组合形式

对列表进行排序

1
2
3
4
5
6
a = [1, 5, 3, 6, 2]
print(sorted(a))
>>
[1, 2, 3, 5, 6]
sorted(iteration) --> return a new iteration
iteration.sort() --> return None --> operation on old original data

对list中的iteration进行排序,以tuple为例[list nested tuple]

假如a是一个由元组构成的列表,我们需要用到参数key,也就是关键词,同时使用lambda;

在下实例中x表示列表中的一个元素(tuple),在这里,表示一个元组,x只是临时起的一个名字,你可以使用任意的名字;

x[0]表示元组里的第一个元素,当然第二个元素就是x[1];所以这句命令的意思就是按照列表中第一个元素排序

1
2
3
4
5
6
# sort the element in the list based on the first element(tuple_0, tuple_1)
# 以tuple中的第一个元素进行排序
a = [('b', 4), ('a', 0), ('c', 2), ('d', 3)]
print(sorted(a, key=lambda x: x[0]))

>>[('a', 0), ('b', 4), ('c', 2), ('d', 3)]
1
2
3
4
5
# 以tuple中的第二个元素进行排序
a = [('b', 4), ('a', 0), ('c', 2), ('d', 3)]
print(sorted(a, key=lambda x: x[1]))

>>[('a', 0), ('c', 2), ('d', 3), ('b', 4)]

可以使用sort进行倒序排序sorted()相当于[::-1]

1
2
3
4
5
6
7
a = [1, 5, 3, 6, 2]
print(sorted(a, reverse=True))
print(sorted(a)[::-1])

>>
[6, 5, 3, 2, 1]
[6, 5, 3, 2, 1]

map()【映射】

–【函数,参数值】

接受迭代函数和迭代对象(list,tuple,dictionary)

返回迭代对象(return 物理地址)

可以根据输入的迭代参数对return的值进行可视化展示譬如:

1
2
3
4
5
6
7
8
9
x = list(range(1, 11))
y = list(range(1, 11))

print(map(lambda x, y: x * y, x, y), end='\n\n')
print(list(map(lambda x, y: x * y, x, y)))

>>
<map object at 0x00000205E4273F48>
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

通俗的讲是接受函数和对应的参数(可迭代的),并返回最小参数长度所对应的函数的运算值的物理存储地址

1
map(function, iterable, ...)

它需要两个必须的参数:

  • function - 针对每一个迭代调用的函数
  • iterable - 支持迭代的一个或者多个对象。在 Python 中大部分内建对象,例如 lists, dictionaries, 和 tuples 都是可迭代的。

在 Python 3 中,map()返回一个与传入可迭代对象大小一样的 map 对象。在 Python 2中,这个函数返回一个列表 list。

其中function可以使用lambda关键词替换

你可以将任意多的可迭代对象传递给map()函数。回调函数接受的必填输入参数的数量,必须和可迭代对象的数量一致。

下面的例子显示如何在两个列表上执行元素级别的操作:

1
2
3
4
5
6
7
8
9
10
11
def multiply(x, y):
return x * y

a = [1, 4, 6]
b = [2, 3, 5]

result = map(multiply, a, b)

print(list(result))
>>
[2, 12, 30]

同样的代码,使用 lambda 函数,会像这样:

1
2
3
4
5
6
a = [1, 4, 6]
b = [2, 3, 5]

result = map(lambda x, y: x*y, a, b)

print(list(result))

当提供多个可迭代对象时,返回对象的数量大小和最短的迭代对象的数量一致。

让我们看看一个例子,当可迭代对象的长度不一致时:

1
2
3
4
5
6
a = [1, 4, 6]
b = [2, 3, 5, 7, 8]

result = map(lambda x, y: x*y, a, b)

print(list(result))

超过的元素 (7 和 8 )被忽略了。

1
[2, 12, 30]

三、总结

Python 的 map()函数作用于一个可迭代对象,使用一个函数,并且将函数应用于这个可迭代对象的每一个元素。

lambda【匿名函数】

–【变量:表达式】

本质上讲为匿名函数,接受变量和表达式

使用语法:

1
lambda argument_list:expersion
  • argument_list:变量列表
  • expression:表达式

lambda 常常和map()函数结合使用

get()

字典中的get操作

return: 返回字典中key对应value值,如果没有就return None, 而get函数可以将没有的key进行操作

dict.get(key, default=None)

  • key – 字典中要查找的键。
  • default – 如果指定键的值不存在时,返回该默认值。

返回指定键的值,如果键不在字典中返回默认值 None 或者设置的默认值。

split()

return list

Python split() 通过指定分隔符对字符串进行切片,如果参数 num 有指定值,则分隔 num+1 个子字符串

string.split(sep=’’, maxsplit=num)

  • sep–>分隔符,默认为所有的空字符,包括空格、换行(\n)、制表符(\t)等默认情况下不显示sep,直接输入需要splitstr即可。
  • maxsplit– >分割次数。默认为 -1, 即分隔所有。
1
2
3
4
5
temp = input().split(sep='**', maxsplit=-1)
print(temp)
>>
1 2 3 ** 4
['1 2 3 ', ' 4']

print()

print(objectes, sep=’’, end=’\n’, flush=False, file=sys.stdout)

  • objects:需要输出的对象
  • sep:以什么间隔default:space
  • end:以什么结尾default:\n
  • file:是要写入的对象,其中sys.stdout为python的标准输出
  • flush:输出是否被缓存通常决定于 file,但如果 flush 关键字参数为 True,流会被强制刷新
1
2
3
4
5
6
7
import time
print('Loading', end='')
# 以end=''结尾防止Loading后面的"."换行

for i in range(20):
print('.', end='', flush=True)
time.sleep(0.5)

img

ljust/center

对于需要填充的字符串一种快捷操作

  • ljust(width, char)–>左对齐–>右填充

  • rjust(width, char)–>右对齐–>左tianchong

  • center(width, char)–>中间对齐–>两边填充

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    str = '  123  '
    print(len(str))
    print(str.ljust(10, '*'))
    print(str.rjust(10, '*'))
    print(str.center(10, '*'))
    >>
    7
    123 ***
    *** 123
    * 123 **

‘’.join()

将list中的element转化成string,其中”char”中的符号为间隔填充符,但是如果list中只有数字,将不能直接转化,需要遍历列表,将列表中的内容转化成字符串:

1
2
3
4
5
6
7
8
alist = [1, 2, 3]
# True
print(''.join('%s' %i for i in alist))

# False
print(''.join(alist))
>>
TypeError: sequence item 3: expected str instance, int found

int()

int()可以快速的实现进制的转换

int(num, base)

1
2
3
4
5
print(int(num, base=n))
# 在base进制下的num表示成10j

num=110, base=2
>>6

eval()

eval() 函数用来执行一个字符串表达式,并返回表达式的值。

以下是 eval() 方法的语法:

1
eval(expression[, globals[, locals]])

参数

  • expression – 表达式。
  • globals – 变量作用域,全局命名空间,如果被提供,则必须是一个字典对象。
  • locals – 变量作用域,局部命名空间,如果被提供,可以是任何映射对象。

返回值

返回表达式计算结果。

1
2
3
4
5
6
7
8
9
10
>>>x = 7
>>> eval( '3 * x' )
21
>>> eval('pow(2,2)')
4
>>> eval('2 + 2')
4
>>> n=81
>>> eval("n + 4")
85

bin()

bin() 返回一个整数 int 或者长整数 long int 的二进制表示。

1
2
3
4
print(bin(10))
>>
0b1010
# 0b 指二进制标识,在c语言中用0b标识二进制

.format()

Python2.6 开始,新增了一种格式化字符串的函数 **str.format()**,它增强了字符串格式化的功能。

基本语法是通过 {}: 来代替以前的 %

format 函数可以接受不限个参数,位置可以不按顺序。

1
2
3
4
5
6
7
8
9
10
11
>>>"{} {}".format("hello", "world")    
# 不设置指定位置,按默认顺序
'hello world'

>>> "{0} {1}".format("hello", "world")
# 设置指定位置
'hello world'

>>> "{1} {0} {1}".format("hello", "world")
# 设置指定位置
'world hello world'

[sys]

.stdin.readline()

.stdin.read()

1
2
3
line = sys.stdin.readline()     # 读取一行(包括换行符)
char = sys.stdin.read(1) # 读取一个字节
lines = sys.stdin.read() # 读取到文件尾

Python中常用到的两种标准化输入方式:分别sys.stdin和input,两者使用方式大致相同,但是总的来说sys.stdin使用方式更加多样化一些,下面就例子说明两者之间的使用差别。

  • python3中使用sys.stdin.readline()可以实现标准输入,其中默认输入的格式是字符串,如果是int,float类型则需要强制转换。

[特殊方法]

.isxxx()[string]

is判断函数为一种判断函数,根据规定字符串判断是否符合结果返回True或者False

主要判断如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
isdecimal():判断给定字符串是否全为数字

isalpha():判断给定的字符串是否全为字母

isalnum():判断给定的字符串是否只含有数字与字母

isupper():判断给定的字符串是否全为大写

islower():判断给定的字符串是否全为小写

istitle():判断给定的字符串是否符合title()

isspace():判断给定的字符串是否为空白符(空格、换行、制表符)

isprintable():判断给定的字符串是否为可打印字符(只有空格可以,换行、制表符都不可以)

isidentifier():判断给定的字符串是否符合命名规则(只能是字母或下划线开头、不能包含除数字、字母和下划线
以外的任意字符。)

count()

可以统计在list\tuple\string中某个element\string出现的次数

  • list.count(element)
  • string.count(string, strat, end)
参数 描述
value 必需。字符串。要检索的字符串。
start 可选。整数。开始检索的位置。默认是 0。
end 可选。整数。结束检索的位置。默认是字符串的结尾。

strip()

Python strip() 方法用于移除字符串头尾指定的字符(默认为空格或换行符)或字符序列。

注意:该方法只能删除开头或是结尾的字符,不能删除中间部分的字符。

1
2
3
4
5
6
7
8
str1 = "00000003210Runoob01230000000"; 
print(str1.strip( '0' )) # 去除首尾字符 0

str2 = " Runoob " # 去除首尾空格
print(str2.strip())
>>
3210Runoob0123
Runoob

一、默认用法:去除空格

  • str.strip() : 去除字符串两边的空格
  • str.lstrip() : 去除字符串左边的空格
  • str.rstrip() : 去除字符串右边的空格

注:此处的空格包含’\n’, ‘\r’, ‘\t’, ‘ ‘

1
2
3
4
5
6
7
8
a=' abc de a  1'
print(a.strip())
print(a.lstrip())
print(a.rstrip())
>>
abc de a 1
abc de a 1
abc de a 1

二、去除指定字符

  • str.strip(‘do’) :去除字符串两端指定的字符
  • str.lstrip(‘do’) :用于去除左边指定的字符
  • str.rstrip(‘do’) :用于去除右边指定的字符

三个函数都可以传入一个参数(这里以’do’为例),指定要去除的首尾字符,编译器会去除两端所有相应的字符,直到没有匹配的字符。

指定的字符表示的一种组合,例如'do'表示'dd','do','od','oo','ddd','ooo'等

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
dodo = "say hello say boy saaayaaas"  

print(dodo.strip('say') )
print(dodo.strip('yas') )
#当传入的参数中加入空格时
print(dodo.strip('say ') )

print(dodo.lstrip('say') )
print(dodo.rstrip('say') )

hello say boy
hello say boy
hello say bo
hello say boy saaayaaas
say hello say boy

处理异常

1
2
3
4
5
6
7
8
try:
可能产生异常的代码块
except [ (Error1, Error2, ... ) /[as e] ]:
处理异常的代码块1
except [ (Error3, Error4, ... ) /[as e] ]:
处理异常的代码块2
except [Exception]:
处理其它异常

该格式中,[] 括起来的部分可以使用,也可以省略。其中各部分的含义如下:

  • (Error1, Error2,…) 、(Error3, Error4,…):其中,Error1、Error2、Error3 和 Error4 都是具体的异常类型。显然,一个 except 块可以同时处理多种异常。
  • [as e]:作为可选参数,表示给异常类型起一个别名 e,这样做的好处是方便在 except 块中调用异常类型(后续会用到)。
  • [Exception]:作为可选参数,可以代指程序可能发生的所有异常情况,其通常用在最后一个 except 块。

try except 语句的执行流程如下:

  1. 首先执行 try 中的代码块,如果执行过程中出现异常,系统会自动生成一个异常类型,并将该异常提交给 Python 解释器,此过程称为捕获异常。
  2. 当 Python 解释器收到异常对象时,会寻找能处理该异常对象的 except 块,如果找到合适的 except 块,则把该异常对象交给该 except 块处理,这个过程被称为处理异常。如果 Python 解释器找不到处理异常的 except 块,则程序运行终止,Python 解释器也将退出。

通过前面的学习,我们已经可以捕获程序中可能发生的异常,并对其进行处理。但是,由于一个 except 可以同时处理多个异常,那么我们如何知道当前处理的到底是哪种异常呢?

其实,每种异常类型都提供了如下几个属性和方法,通过调用它们,就可以获取当前处理异常类型的相关信息:

  • args:返回异常的错误编号和描述字符串;
  • str(e):返回异常信息,但不包括异常信息的类型;
  • repr(e):返回较全的异常信息,包括异常信息的类型。

举个例子:

1
2
3
4
5
6
7
8
9
10
11
12
try:
1/0
except Exception as e:
# 访问异常的错误编号和详细信息
print(e.args)
print(str(e))
print(repr(e))

>>
('division by zero',)
division by zero
ZeroDivisionError('division by zero',)

输出结果为:

(‘division by zero’,)
division by zero
ZeroDivisionError(‘division by zero’,)

isdigit()

str.isdigit()

  • 不需要任何参数
  • 返回True False
  • 检测字符串是否只有数字组成

ord()–chr()

ord():返回对应的ASCII值

1
2
print(ord('A'))
>>65

chr():返回对应的 Unicode 数值

1
2
print(chr(65))
>>A

slots

1
__slots__
  1. 节省memory。在上面的例子里,如果我们看看barslotted_bar就看到,slotted_bar并没有__dict__bar却含有__dict____slots__正是通过避免定义动态的数据结构__dict__来实现对memory的节省,大约能节省30%的memory
  2. .access attributes更快。事实上,在CPython的实现里,__slots__是一个静态数据结构(static),里面存的是value references,比__dict__更快

此处要注意一点,正因为__slots__是static的,定义了__slots__之后,你将不能创造新的attribute

如果你想获得__slots__的好处并且可以创造新的attribute,你可以将__dict__作为__slots__中的一个element(见下面code):

什么情况下该使用__slots__?

当你事先知道class的attributes的时候,建议使用slots来节省memory以及获得更快的attribute access。

注意不应当把防止创造__slots__之外的新属性作为使用__slots__的原因,可以使用decorators以及getters,setters来实现attributes control。

使用__slots__的特殊注意事项

  1. 当inherit from a slotted class,那么子类自动变成slotted并且获得parent class的slots;子类可以定义新的elements加入到inherited slots里。slots里面的每一个element,只能在inheritance里面定义一次(否则就是redundant)
  2. 在multiple inheritance(比如mixedins)里,如果两个parents定义了不同的nonempty __slots__,那么python会报错。这个时候,就进一步factor out 母类的slots。

if–name–==’–main–’:

1
if __name__ == '__main__':

当你在写 .py 的时候

一般是以两种方式存在的

  • 1、作为脚本直接运行

  • 2、作为模块给别人导入

的代码在被执行的时候,Python 解释器会先去读取你的 Python 代码然后定义一些全局的内置变量,而我们常常写的这个 –name–就是其中的变量之一,如果判断出 –name– 的值是 –main–就说明这里是程序入口,而非被别的 py 文件 import

所以你的 .py 可以作为自己的脚本运行,在 –main– 中做一些测试,或者本身程序的运行,当然你也可以使用你的 .py 作为模块给别人使用,给别人提供一些便利,为了不让别人一导入你的模块就直接运行整个脚本那么使用 **if –name– == ‘–main–’**其中的代码就不会在被 import 时被执行。

yield<==>send==>next

首先,如果你还没有对yield有个初步分认识,那么你先把yield看做“return”,这个是直观的,它首先是个return,普通的return是什么意思,就是在程序中返回某个值,返回之后程序就不再往下运行了。看做return之后再把它看做一个是生成器(generator)的一部分(带yield的函数才是真正的迭代器),好了,如果你对这些不明白的话,那先把yield看做return,然后直接看下面的程序,你就会明白yield的全部意思了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
def foo():
print("starting...")
while True:
res = yield 4
print("res:",res)
g = foo()
print(next(g))
print("*"*20)
print(next(g))
>>
starting...
4
********************
res: None
4

我直接解释代码运行顺序,相当于代码单步调试:

  1. 程序开始执行以后,因为foo函数中有yield关键字,所以foo函数并不会真的执行,而是先得到一个生成器g(相当于一个对象)

  2. 直到调用next方法,foo函数正式开始执行,先执行foo函数中的print方法,然后进入while循环

  3. 程序遇到yield关键字,然后把yield想想成return,return了一个4之后,程序停止,并没有执行赋值给res操作,此时next(g)语句执行完成,所以输出的前两行(第一个是while上面的print的结果,第二个是return出的结果)是执行print(next(g))的结果,

  4. 程序执行print(“*”20),输出20个

  5. 又开始执行下面的print(next(g)),这个时候和上面那个差不多,不过不同的是,这个时候是从刚才那个next程序停止的地方开始执行的,也就是要执行res的赋值操作,这时候要注意,这个时候赋值操作的右边是没有值的(因为刚才那个是return出去了,并没有给赋值操作的左边传参数),所以这个时候res赋值是None,所以接着下面的输出就是res:None,

  6. 程序会继续在while里执行,又一次碰到yield,这个时候同样return 出4,然后程序停止,print函数输出的4就是这次return出的4.

到这里你可能就明白yield和return的关系和区别了,带yield的函数是一个生成器,而不是一个函数了,这个生成器有一个函数就是next函数,next就相当于“下一步”生成哪个数,这一次的next开始的地方是接着上一次的next停止的地方执行的,所以调用next的时候,生成器并不会从foo函数的开始执行,只是接着上一步停止的地方开始,然后遇到yield后,return出要生成的数,此步就结束。


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
def foo():
print("starting...")
while True:
res = yield 4
print("res:",res)

g = foo()
print(next(g))
print("*"*20)
print(g.send(7))
>>
starting...
4
********************
res: 7
4

先大致说一下send函数的概念:此时你应该注意到上面那个的紫色的字,还有上面那个res的值为什么是None,这个变成了7,到底为什么,这是因为,send是发送一个参数给res的,因为上面讲到,return的时候,并没有把4赋值给res,下次执行的时候只好继续执行赋值操作,只好赋值为None了,而如果用send的话,开始执行的时候,先接着上一次(return 4之后)执行,先把7赋值给了res,然后执行next的作用,遇见下一回的yield,return出结果后结束。

  1. 程序执行g.send(7),程序会从yield关键字那一行继续向下运行,send会把7这个值赋值给res变量

  2. 由于send方法中包含next()方法,所以程序会继续向下运行执行print方法,然后再次进入while循环

  3. 程序执行再次遇到yield关键字,yield会返回后面的值后,程序再次暂停,直到再次调用next方法或send方法。

简要理解:yield就是 return 返回一个值,并且记住这个返回的位置,下次迭代就从这个位置后开始。

[关键字]

not

not用于bool值的运算,常见的用法如下:

  1. not与逻辑判断句if连用。
  2. 判断元素是否在列表或者字典中

特殊的:

1
2
3
4
5
6
for i, v in enumerate(result):
if not v:
result[i] = temp[0]
temp.pop(0)
return ''.join(result)

【:star:】:对于习惯于使用if not x这种写法的pythoner,必须清楚x等于None, False, 空字符串””, 0, 空列表[], 空字典{}, 空元组()时对你的判断没有影响才行。 而对于if x is not Noneif not x is None写法,很明显前者更清晰,而后者有可能使读者误解为if (not x) is None,因此推荐前者,同时这也是谷歌推荐的风格 结论: if x is not None是最好的写法,清晰,不会出现错误,以后坚持使用这种写法。

【:star:】 使用if not x这种写法的前提是:必须清楚x等于None, False, 空字符串””, 0, 空列表[], 空字典{}, 空元组()时对你的判断没有影响才行。

在python中 None, False, 空字符串"", 0, 空列表[], 空字典{}, 空元组()都相当于False

global

Python变量的作用域一共有4种,分别是:

  • L (Local) 局部作用域
  • E (Enclosing) 闭包函数外的函数中
  • G (Global) 全局作用域
  • B (Built-in) 内建作用域 以 L –> E –> G –>B 的规则查找,即:在局部找不到,便会去局部外的局部找(例如闭包),再找不到就会去全局找,再者去内建中找。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# instance 1
x = 5 # global
def fun_a():
x = 4 # local
print(x)

def fun_b():
x = 3
print(x)

print(x)
fun_a()
fun_b()
>>\n
5 4 3
1
2
3
4
5
6
7
8
9
10
# instance 2
def fun_a():
x = 5 # enclosing
print(x)
return fun_b

b = fun_a()
b()
>>
5
1
2
3
4
5
6
7
8
9
10
11
# instance 3
def fun_a():
x = 5 # enclosing
def fun_b():
x = 4 # local
print(x)
return fun_b
b = fun_a()
b()
>>
4
1
2
3
4
5
6
7
8
9
10
11
# instance 4
x = 5 # global
def fun_a():
print(x)

def fun_b():
print(x)
fun_a()
fun_b()
>>\n
5 4

结论:

实例一: 即使变量同名,优先引用的是局部变量。

实例二 & 实例三: 闭包的情况,如果内部函数没有局部变量,则会优先引用闭包的环境变量。

实例四: 引用全局变量。


对于全局变量来说,既然能函数体内直接引用,并且程序没有报错,那为什么还要用 global 关键字?

现在在重新声明一个 func_c 函数,并且对这个 x 进行加 1 的操作。你觉得会打印什么结果?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# instance 5
x = 5 # global -->同名且有修改-->认为def中的变量为局部变量
def fun_a():
print(x)

def fun_b():
print(x)

def fun_c():
x += 1 # 由于内部函数有引用外部函数的同名变量或者全局变量,并且对这个变量有修改的时候,此时 Python 会认为它是一个局部变量,而函数中并没有 x 的定义和赋值,所以报错。
print(x)
fun_a()
fun_b()
fun_c()
>>\n
5 5
UnboundLocalError: local variable 'x' referenced before assignment

可以看到已经报错了 ,UnboundLocalError,错误代码第 9 行 x = x+1。

在这个例子中设置的 x=5 属于全局变量,而在函数内部中没有对 x 的定义。

根据 Python 访问局部变量和全局变量的规则:当搜索一个变量的时候,Python 先从局部作用域开始搜索,如果在局部作用域没有找到那个变量,那样 Python 就会像上面的案例中介绍的作用域范围逐层寻找。

最终在全局变量中找这个变量,如果找不到则抛出 UnboundLocalError 异常。

但你会想,明明已经在全局变量中找到同名变量了,怎么还是报错?

因为内部函数有引用外部函数的同名变量或者全局变量,并且对这个变量有修改的时候,此时 Python 会认为它是一个局部变量,而函数中并没有 x 的定义和赋值,所以报错。


global 关键字为解决此问题而生,在函数 func_c中,显示地告诉解释器 x 为全局变量,然后会在函数外面寻找 x 的定义,执行完 x = x + 1 后,x 依然是全局变量。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# instance 6
x = 5
def fun_a():
print(x)

def fun_b():
print(x)

def fun_c():
global x # 定义def中的变量为全局变量,即先Enclosing->local-->//global//-//->buinltin
x += 1
print(x)

fun_a()
fun_b()
fun_c()
>>\n
5 5 6

yield

简要理解:yield就是 return 返回一个值,并且记住这个返回的位置,下次迭代就从这个位置后开始

  • 它首先是个return:在程序中返回某个值,返回之后程序就不再往下运行了。
  • 之后是生成器(generator)的一部分(带yield的函数才是真正的迭代器)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
def foo():
while True:
res = yield 4
print("res:",res)
g = foo()
print(next(g)) --|1|
print("*"*20)
print(next(g))

>>
4
********************
res: None
4

1.程序开始执行以后,因为foo函数中有yield关键字,所以foo函数并不会真的执行,而是先得到一个生成器g(相当于一个对象)

2.直到调用next方法,foo函数正式开始执行,进入while循环

3.程序遇到yield关键字,然后把yield想想成return,return了一个4之后,程序停止,并没有执行赋值给res操作,此时next(g)语句执行完成,所以输出4(return出的结果)是执行print(next(g))的结果(–|1|)

4.程序执行print(“*”20),输出20个

5.又开始执行下面的print(next(g)),这个时候和上面那个差不多,不过不同的是,这个时候是从刚才那个next程序停止的地方开始执行的,也就是要执行res的赋值操作,这时候要注意,这个时候赋值操作的右边是没有值的(因为刚才那个是return出去了,并没有给赋值操作的左边传参数),所以这个时候res赋值是None,所以接着下面的输出就是res:None

6.程序会继续在while里执行,又一次碰到yield,这个时候同样return 出4,然后程序停止,print函数输出的4就是这次return出的4.

带yield的函数是一个生成器,而不是一个函数了这个生成器有一个函数就是next函数,next就相当于“下一步”生成哪个数,这一次的next开始的地方是接着上一次的next停止的地方执行的,所以调用next的时候,生成器并不会从foo函数的开始执行,只是接着上一步停止的地方开始,然后遇到yield后,return出要生成的数,此步就结束。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
def foo():
while True:
res = yield 4
print("res:",res)
g = foo()
print(next(g))
print("*"*20)
print(g.send(7))

4
********************
res: 7
4

raise

raise 关键字用于引发异常。

您可以定义要引发的错误类型以及要向用户打印的文本。

1
2
3
4
5
6
7
8
x = "hello"

if not type(x) is int:
raise TypeError("Only integers are allowed")
>>
Traceback (most recent call last):
raise TypeError("Only integers are allowed")
TypeError: Only integers are allowed

Framework-Thinking

Backtracking Algorithm

算法介绍

实际场景中,解决问题的出发点有多种,为了确认这些出发点是否真的可以解决问题,我们需要逐一测试,找出 1 个甚至所有可行的解决方案,必要时还需要从众多可行方案中找出一个最优的方案。这种情况下,就可以优先考虑回溯算法。

回溯是一种可查找部分甚至全部解决方案的算法,它能够对所有的可行性方案进行测试,一旦判定某个方案无效,则以回溯的方式继续测试其它的方案,直至测试完所有的方案。

回溯可以简单地理解为“回退”或者“原路返回”,我们以一个实例来理解回溯的含义。如下图所示,找出从 A 到 K 的路径:

图 1 回溯的含义
显然,从 A 到 K 的路径为 A-E-J-K,但如果要编程实现查找可行的路径,需要一一测试从 A 出发的所有路径,整个查找的过程为:

  • 从 A 出发,有 2 条路径可以选择,先选择 A-B;从 B 出发,又有 2 条路径,先选择 B-C;
  • 到达 C 后,发现无法达到 K,所以放弃此路线,回退至 B,继续测试其他路径(第一次回溯);
  • 继续探查 B-D,发现也无法到达 K,回退到 B,继续探测其他路径(第二次回溯);
  • 除 B-C 和 B-D 外,没有其他路径可供选择,回退至 A,探测其他路径(第三次回溯);
  • 除了 A-B 外,还可以选择 A-E,测试过程同测试 A-B 部分的方式完全相同。

下图给您描绘了测试所有路径所经历的过程:


通过不断的回溯,找到解决问题的所有可行方案,这样的算法就称为回溯算法。

算法运用

回溯算法经常以递归的方式实现,该算法常用于解决以下 3 类问题:

  1. 决策问题:从众多选择中找到一个可行的解决方案;
  2. 优化问题:从众多选择中找到一个最佳的解决方案;
  3. 枚举问题:找出能解决问题的所有方案。

经典的适合用回溯算法解决的问题有 N皇后问题迷宫问题等,我们会在后续章节给大家做详细的讲解。

Divide and conquer algorithm

用空间换时间

算法介绍

实际开发中,我们经常会使用一些巧妙的算法解决问题,分治算法就是其中之一。

分治算法是指采用分而治之的思想,将一个复杂的问题划分成很多相互独立的小问题,通过逐个解决这些小问题,使整个问题得到解决。

使用分治算法解决问题,大致经历 3 个阶段:

  1. 分:将整个问题划分为很多相互独立的小问题(子问题),很多小问题还可以进行更细致地划分,直至这些问题不可再分;
  2. 治:逐个解决每个子问题,整个过程通常采用递归的方式实现;
  3. 合并:合并所有子问题的解决方案,得到整个问题的解决方案。

算法特点

分治算法擅长解决一些规模较大的问题,直接解决此类问题的难度较大,通过将大问题“分而治之”,可以简化问题的难度。此外,由于分解得到的诸多小问题之间是相互独立的,所以可以采用并发执行的方式,提高问题的解决效率。

分治算法的缺陷也很明显,该算法常常采用递归的方式实现,整个过程需要消耗大量的系统资源,严重时还会导致程序崩溃。

算法运用

Greedy Algorithm

  • 计算性价比
  • 根据性价比进行排序(包括weight、value)等
  • 使用while循环控制每次取局部最优解

算法介绍

贪心算法又称贪婪算法,是所有算法中最简单、最易实现的一种算法。

一个问题通常对应有多种算法,每种算法由多个步骤组成。贪心算法解决问题的核心思想是:算法的每一步都选择当前场景中最优的解决方案(又称局部最优解)。

举个例子,假设我们有面值分别为 1、2、5、10 的硬币,要求用尽可能少的硬币拼凑出的总面值为 18。如果采用贪心算法解决此问题,则解决方案为:

  1. 选择一个面值为 10 的硬币,可最大程度解决问题,剩余需要拼凑的面值数为 8;
  2. 选择一个面值为 5 的硬币,可最大程度解决问题,剩余需要拼凑的面值数为 3;
  3. 选择一个面值为 2 的硬币,可最大程度解决问题,剩余需要拼凑的面值数为 1;
  4. 选择一个面值为 1 的硬币。

贪心算法每一步都选择的是当前所允许的最大面值的硬币(即局部最优解),整个解决方案也是最优的。

注意,贪心算法并不是在任何场景中都可以得出最优的解决方案。比如,有面值分别为 1、7、10 的硬币,要求用尽可能少的硬币拼凑出的总面值为 15。如果采用贪心算法,则解决方案为:

  1. 选择一个面值为 10 的硬币,剩余面值为 5;
  2. 选择一个面值为 1 的硬币,剩余面值为 4;
  3. 选择一个面值为 1 的硬币,剩余面值为 3;
  4. 选择一个面值为 1 的硬币,剩余面值为 2;
  5. 选择一个面值为 1 的硬币,剩余面值为 1;
  6. 选择一个面值为 1 的硬币。

贪心算法使用了“10+1+1+1+1+1”共 6 枚硬币,但实际上,最优的解决方案只需要“7+7+1”共 3 枚硬币。所以,贪心算法追求的是局部最优解,它并不关心设计的整个解决方案是否最优。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
target = 18
price = [1, 2, 5, 10]

price.sort(reverse=True)

i = 0
count = 1
while target > 0:
if price[i] <= target:
target -= price[i]
print('the %d take:%d' % (count, price[i]))
count += 1
else:
i += 1

算法运用

贪心算法可用于解决很多实际问题,例如:

Dynamic Programming

算法思想

  1. 画表,明确dp[i]与dp[i-1]代表的是什么

  2. 初始化dp[0]=[0]…

  3. 考虑状态转移方程

    一般为dp[i] = max/min(dp[i-1], fun(dp[i-1], dp_temp[i]))

即是否考虑此物品的收益

​ 4.完善选此物品和不选此物品的方程即【3】中的方程式

算法介绍

和分治算法类似,动态规划算法也是先将问题分解成多个规模更小、更容易解决的小问题,通过解决这些小问题,从而找到解决整个问题的方法。不同之处在于,分治算法分解的每个小问题都是相互独立的,而动态规划算法分解的小问题之间往往存在关联,比如要想解决问题 A 和问题 B,必须先解决问题 C。

讲解贪心算法时,我们举过一个例子:假设有 3 种面值分别为 1、7 和 10 的硬币,要求用尽可能少的硬币拼凑出的总面值为 15。贪心算法的解决方案为共需要 6 枚硬币(10+1+1+1+1+1),但如果采用动态规划算法,可以找到更优的解决方案,只需要 3 枚硬币( 7+7+1)。

动态规划算法的解题思路是:用 f(n) 表示凑齐面值为 n 所需要的最少的硬币数量,则面值 15 的拼凑方案有以下 3 种:

  • f(15) = f(14) +1:选择一枚面值为 1 的硬币,f(14) 表示拼凑出面值 14 所需要的最少的硬币数量;
  • f(15) = f(8) + 1:选择一枚面值为 7 的硬币,f(8) 表示拼凑出面值 8 所需要的最少的硬币数量;
  • f(15) = f(5) + 1:选择一枚面值为10 的硬币,f(5) 表示拼凑出面值 5 所需要的最少的硬币数量。

由此,我们将求 f(15) 的值,转换成为了求 min( f(14) , f(8) , f(5) ) 的值(min() 表示取三者中的最小值)。在此基础上,f(14)、f(8)、f(5) 还可以分解为更小的问题:

  • f(5) = f(4) + 1;
  • f(8) = f(7) + 1 = f(1) +1;
  • f(14) = f(13)+1 = f(7) + 1 = f(4) +1。

通过不断地分解,问题的规模会不断地减小,直至分解为 f(0) 或 f(1) 这类很简单的子问题。也就是说,我们只需要求得 f(0) 和f(1) 的值,所有小问题都可以得到解决(如表 1 所示)。

子问题 分解方案 最佳方案 硬币数量
f(0) 不可再分 不选择任何硬币 0
f(1) f(0) + 1 “1” 1
f(2) f(1) + 1 “1+1” 2
f(3) f(2) + 1 “1+1+1” 3
f(4) f(3) + 1 “1+1+1+1” 4
f(5) f(4) + 1 “1+1+1+1+1” 5
f(6) f(5) + 1 “1+1+1+1+1+1” 6
f(7) f(6) + 1 , f(0) + 1 “7” 1
f(8) f(7) + 1 , f(1) + 1 “7+1” 2
f(9) f(8) + 1 , f(2) + 1 “7+1+1” 3
f(10) f(9) + 1 , f(3) + 1 , f(0) + 1 “10” 1
f(11) f(10) + 1 , f(4) + 1 , f(1) + 1 “10+1” 2
f(12) f(11) + 1 , f(5) + 1 , f(2) + 1 “10+1+1” 3
f(13) f(12) + 1 , f(6) + 1 , f(3) + 1 “10+1+1+1” 4
f(14) f(13) + 1 , f(7) + 1 , f(4) + 1 “7+7” 2
f(15) f(14) + 1 , f(8) + 1 , f(5) + 1 “7+7+1” 3

表 1 中借助 f(0) 和 f(1) 的值,依次推导出了 f(2)~f(15) 的值。因此,我们可以很容易得出“拼凑总面值为 15 只需要 7+7+1 共 3 个硬币”的最佳解决方案,这样解决问题的方法就称为动态规划算法。

算法运用

如下给您列举了几个适合采用动态规划算法解决的经典案例:


经典算法题


N皇后问题[回溯+递归]

问题介绍

在国际象棋中,皇后是整个棋盘上实力最强的一种棋子,可以直走、横走和斜着走(沿 45 ° 角移动),可以攻击行走路线上的任何棋子。

N 皇后问题就源自国际象棋,它研究的是:如何将 N 个皇后摆放在 N*N 的棋盘中,使它们不能相互攻击。也就是说,棋盘上各个皇后无论直走、横走还是斜着走,都无法相互攻击。

举个例子,如下在 4*4 的棋盘中摆放了 4 个皇后,它们都用 Q 来表示。

问题思路

显然要想使各个皇后不相互攻击,应保证它们不位于同一行(或者同一列)。借助回溯算法,我们可以逐一测试出每一行皇后所在的位置,最终得出 N 皇后问题的解决方案。

代码实现

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
count = 0    #统计解决方案的个数
q = [0]*20 #记录各个皇后的放置位置,最终放置 20 个皇后
#输出 N 皇后问题的解决方案
def display(n):
global count
count = count + 1
print("输出第%d个解:" % (count))
for i in range(1 , n + 1):
for j in range(1 , n + 1):
if q[i] != j:
print("x",end=" ");
else:
print("Q",end=" ");
print()
print()
#检验第k行的第j列是否可以摆放皇后
def isSafe(k , j):
for i in range(1 , k):
#如果有其它皇后位置同一列上,或者位于该位置的斜线位置上,则该位置无法使用
if q[i] == j or abs(i - k) == abs(q[i] - j):
return False
return True
#放置皇后到棋盘上
def n_queens(k , n):
if k > n: #递归的出口
display(n)
else:
for j in range(1 , n + 1): #试探第k行的每一列,找到符合要求的列
if isSafe(k , j):
q[k] = j
n_queens(k + 1 , n); #在确认第 k 行皇后位置的前提下,继续测试下一行皇后的位置
print("请输入皇后个数:")
n = int(input());
n_queens(1,n)
print("共有 %d 种不同的排列" % (count))

迷宫问题[回溯+递归]

问题介绍

假设有一个 N*N 的正方形迷宫(如图 1 所示),老鼠必须从起始点移动到终点,它可以向上、向下、向左、向右移动,但仅限于在白色区域内移动。

回溯算法可以很好地解决类似的迷宫问题,该算法能够从起点开始,逐一测试所有的移动路线,并最终找到能够达到终点的移动路线。

问题思路

通过使用回溯不断“试错”,直到找出最优路径

假设迷宫中可行走的白色区域用 1 表示, 不能行走的黑色区域用 0 表示,图 1 的迷宫可以用如下的矩阵表示:

1 0 1 1 1
1 1 1 0 1
1 0 0 1 1
1 0 0 1 0
1 0 0 1 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
52
53
54
55
56
57
# 指定地图的行数和列数
ROW = 5
COL = 5
# 初始化地图
maze = [['1', '0', '1', '1', '1'],
['1', '1', '1', '0', '1'],
['1', '0', '0', '1', '1'],
['1', '0', '0', '1', '0'],
['1', '0', '0', '1', '1']]
# 假设当前迷宫中没有起点到终点的路线
find = False


# 回溯算法查找可行路线
def maze_puzzle(maze, row, col, outrow, outcol):
global find
maze[row][col] = 'Y'
if row == outrow and col == outcol:
find = True
print("成功走出迷宫,路线图为:")
printmaze(maze)
if canMove(maze, row-1, col):
maze_puzzle(maze, row - 1, col, outrow, outcol)
# 如果程序不结束,表明此路不通,恢复该区域的标记
maze[row - 1][col] = '1'
if canMove(maze, row, col - 1):
maze_puzzle(maze, row, col - 1, outrow, outcol)
# 如果程序不结束,表明此路不通,恢复该区域的标记
maze[row][col - 1] = '1'
# 尝试向下移动
if canMove(maze, row + 1, col):
maze_puzzle(maze, row + 1, col, outrow, outcol)
# 如果程序不结束,表明此路不通,恢复该区域的标记
maze[row + 1][col] = '1'
# 尝试向右移动
if canMove(maze, row, col + 1):
maze_puzzle(maze, row, col + 1, outrow, outcol)
# 如果程序不结束,表明此路不通,恢复该区域的标记
maze[row][col + 1] = '1'


# 判断指定路线是否可以行走
def canMove(maze, row, col):
return 0 <= row <= ROW - 1 and 0 <= col <= COL - 1 and maze[row][col] != '0' and maze[row][col] != 'Y'


# 输出行走路线
def printmaze(maze):
for i in range(0, ROW):
for j in range(0, COL):
print(maze[i][j], end=" ")
print(end='\n')


maze_puzzle(maze, 0, 0, ROW-1, COL-1)
if find is False:
print("未找到可行路线")

部分背包问题[贪婪算法]

问题介绍

背包问题指的是在背包所能承受的重量范围内,将哪些物品装入背包可以获得最大的收益?

举个例子,一个小偷正在商店行窃,他随身携带的背包可以装一定重量的商品,商店中摆放着不同重量和价值的商品,小偷应该如何选择才能获得最大的收益?这样的问题就属于背包问题。

根据所选物品是否可再分(比如选择某物品的 1/2),背包问题可分为以下两种:

  • 0-1 背包问题:挑选物品时,单个物品要么放弃,要么全部装入背包,不存在“装入 1/2 物品”的情况;
  • 部分背包问题:挑选物品时,单个物品可再分,例如将物品的 1/2 装入背包。

本节给您讲解的是如何使用贪心算法解决部分背包问题。

问题思路

接下来以“小偷在商店行窃,商店的商品都可再分”为例,给您讲解如何解决部分背包问题。

为了方便讲解,我们做以下假设:

  • 背包最多可以承受的商品重量为 W(后续简称承重);
  • 商店共有 n 件商品,它们都是可再分的,其中第 i 件商品的总重量为 Wi,总收益为 Pi。

显然,要想获得最大的收益,小偷需要做到以下 2 点:

  • 充分利用背包的承重,背包装的商品越多,获得收益就越大;
  • 确保选中的 W 重量商品中,单位重量下获得的收益最大。

结合以上 2 点,我们可以采用贪心算法来解决这个问题,具体的解决方案是:计算每个商品的收益率(即 Pi/Wi),优先选择收益率最大的商品,直至选择的商品总重量恰好为 W。

假设背包的承重为 20,商店中共有 3 种商品,它们的重量和收益分别为:

  • 商品 1:重量 10,收益 60,单位重量的收益为 6;
  • 商品 2:重量 20,收益 100,单位重量的收益为 5;
  • 商品 3:重量 30,收益 120,单位重量的收益为 4。

通过比较收益率,以上 3 种商品装入背包的顺序依次为:商品 1 > 商品 2 > 商品 3。受到背包承重的限制,商品 1 可以全部装入背包,而商品 2 只能装一半(10 斤),商品 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
32
33
34
35
W = 50
w = [10, 30, 20]
p = [60, 100, 120]
value = 0
result = [0] * len(w)

v = []
for i in range(len(w)):
v.append(p[i]/w[i])

for j in range(len(w)):
for i in range(j+1, len(w)):
if v[j] < v[i]:
v[j], v[i] = v[i], v[j]
w[j], w[i] = w[i], w[j]
p[j], p[i] = p[i], p[j]

i = 0
while W > 0:
temp = min(W, w[i])
result[i] = temp / w[i]
i += 1
W -= temp

for i in range(len(result)):
if result[i] == 1:
print('weight:%f, total value:%f-->100%%' % (w[i], p[i]))
value += p[i]
elif result[i] == 0:
print('weight:%f, total value:%f-->0%%' % (w[i], p[i]))
else:
print('weight:%f, total value:%f-->%f' % (w[i], p[i], result[i]*100))
value += p[i]*result[i]

print('the total value:%f' % value)

01背包问题[动态规划]

问题介绍

仍以小偷在商店行窃为例,他随身携带的背包可以装一定重量的商品,商店中摆放着不同重量和价值的商品,小偷如何选择能获得最大的收益?在 01背包问题中,每种商品都是不可再分的,小偷在选择商品时,要么装入整个商品,要么放弃装该商品,不存在“装某商品的 1/2”的情况。

贪心算法仅适用于解决部分背包问题,不再适合解决 01背包问题。举个例子,假设商店摆放着以下几种商品:

  • 商品 1 :重量为 6,收益为 6;
  • 商品 2 :重量为 2,收益为 4;
  • 商品 3 :重量为 3,收益为 4;
  • 商品 4 :重量为 5,收益为 3。

假设背包可容纳商品的最大重量为 7,如果采用贪心算法,首先会选取收益最大的商品 1,此时背包的剩余承重为 1,无法再装下其他商品,因此最终获得的收益为 6。实际上还有一种更好的解决方案,即选择装入商品 2 和商品 3,总重量为 5(<7),总收益为 4 + 4 = 8。

显然,贪心算法不再适用于解决 01背包问题,解决此问题可以使用动态规划算法

问题思路

假设背包可以承受的最大重量为 11,商店有 5 种商品,它们的重量和价值分别为:

wi 表示第 i 种商品的重量,vi 表示第 i 种商品的价值
w1 = 1,v1 = 1
w2 = 2,v2 = 6
w3 = 5,v3 = 18
w4 = 6,v4 = 22
w5 = 7,v5 = 28

采用动态规划算法解决此问题的具体过程是:

  1. 从背包所能承受的最大重量为 1 开始,依次推导出各个承重条件下所能获得的最大收益。

建立如下这张表格,逐一分析出各个载重量下每一个商品装与不装对收益值的影响,从而获得各个载重量对应的最大收益值。

背包载重量 0 1 2 3 4 5 6 7 8 9 10 11
w1 = 1,v1 = 1 0
w2 = 2,v2 = 6 0
w3 = 5,v3 = 18 0
w4 = 6,v4 = 22 0
w5 = 7,v5 = 28 0

当背包载重量为 0 时,无法装入各个商品,因此收益一直为 0。

  1. 首先考虑商品 (w1 , v1),当背包载重量分别为 1、2、…、11 时,都可以装入此商品,且和不装该商品相比(背包是空的),装该商品的收益更大,因此对上表进行更新:
背包载重量 0 1 2 3 4 5 6 7 8 9 10 11
w1 = 1,v1 = 1 0 1 1 1 1 1 1 1 1 1 1 1
w2 = 2,v2 = 6 0
w3 = 5,v3 = 18 0
w4 = 6,v4 = 22 0
w5 = 7,v5 = 28 0
  1. 再分析商品 (w2,v2):
  • 载重量为 1 时:w2>1,无法装入该商品,最大收益仍为 1。

  • 载重量为 2 时:背包可以装入此商品,装入此商品对应的总收益可以用 6+f(0) 表示,其中 f(0) 指的是载重量为 0 时装 (w1,v1) 对应的最佳收益。从上表可知,f(0) = 0,因此 6+f(0)=6,比先前只装 (w1 , v1) 的收益大。

  • 载重量为 3 时:背包可以装入此商品,装入此商品对应的总收益可以用 6+f(1) 表示,f(1) 指的是载重量为 1 时装 (w1 , v1) 对应的最佳收益。从上表可知,f(1) = 1,因此 6+f(1) = 7,比先前只装 (w1 , v1) 的收益大。

  • 载重量为 4~11:背包可以装入此商品,且装此商品的收益总比不装时的大,总收益都为 7。

    继续对表格进行更新:

背包载重量 0 1 2 3 4 5 6 7 8 9 10 11
w1 = 1,v1 = 1 0 1 1 1 1 1 1 1 1 1 1 1
w2 = 2,v2 = 6 0 1 6 7 7 7 7 7 7 7 7 7
w3 = 5,v3 = 18 0
w4 = 6,v4 = 22 0
w5 = 7,v5 = 28 0
  1. 再分析商品 (w3 , v3):
  • 载重量为 1 时:w3>1,无法装入该商品,最大收益仍为 1。
  • 载重量为 2 时:w3>1,无法装入该商品,最大收益仍为 6。
  • 载重量为 3~4 时:w3>1,无法装入该商品,最大收益仍为 7。
  • 载重量为 5:背包可以装入此商品,且装此商品的总收益可以用 18+f(0) 表示,f(0) 指的是载重量为 0 时装 (w2 , v2) 对应的最佳收益。从上表可知,f(0) = 0,因此 18+f(0) = 18,比先前统计的收益值更高。
  • 载重量为 6:背包可以装入此商品,且装此商品的总收益可以用 18+f(1) 表示,f(1) 指的是载重量为 1 时装 (w2 , v2) 对应的最佳收益。从上表可知,f(1) = 1,因此 18+f(1) = 19,比先前统计的收益值更高。
  • 载重量为 7:背包可以装入此商品,且装此商品的总收益可以用 18+f(2) 表示,f(2) 指的是载重量为 2 时装 (w2 , v2) 对应的最佳收益。从上表可知,f(2) = 6,因此 18+f(2) = 24,比先前统计的收益值更高。
  • 根据此规律可以依次求得载重量分别为 8、9、…、11 时,对应的总收益为 25。

继续对表格进行更新:

背包载重量 0 1 2 3 4 5 6 7 8 9 10 11
w1 = 1,v1 = 1 0 1 1 1 1 1 1 1 1 1 1 1
w2 = 2,v2 = 6 0 1 6 7 7 7 7 7 7 7 7 7
w3 = 5,v3 = 18 0 1 6 7 7 18 19 24 25 25 25 25
w4 = 6,v4 = 22 0
w5 = 7,v5 = 28 0
  1. 借助 (w3 , v3) 的收益数据,我们可以继续推导出不同载重量对应的 (w4 , v4) 商品的收益值;同理借助 (w4 , v4) 的收益数据,可以推导出不同载重量对应的 (w5 , v5) 商品的收益值。推导结果如下所示:
背包载重量 0 1 2 3 4 5 6 7 8 9 10 11
w1 = 1,v1 = 1 0 1 1 1 1 1 1 1 1 1 1 1
w2 = 2,v2 = 6 0 1 6 7 7 7 7 7 7 7 7 7
w3 = 5,v3 = 18 0 1 6 7 7 18 19 24 25 25 25 25
w4 = 6,v4 = 22 0 1 6 7 7 18 22 24 28 29 29 40
w5 = 7,v5 = 28 0 1 6 7 7 18 22 28 29 34 35 40

因此借助动态规划算法,当背包的载重量为 11 时,获得的最大收益为 40。

代码实现

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
N = 5       # 设定商品的种类
W = 11 # 设定背包的最大载重量
w = [0, 1, 2, 5, 6, 7] # 商品的载重,不使用 w[0]
v = [0, 1, 6, 18, 22, 28] # 商品的价值,不使用 v[0]
# 二维列表,记录统计数据
result = [[0]*(W+1) for _ in range(len(w))]
# 动态规划算法解决01背包问题

# 逐个遍历每个商品
for i in range(1, N+1):
# 求出从 1 到 W 各个载重对应的最大收益
for j in range(1, W+1):
# 如果背包载重小于商品总重量,则该商品无法放入背包,收益不变
if j < w[i]:
result[i][j] = result[i-1][j]
else:
# 比较装入该商品和不装该商品,哪种情况获得的收益更大,记录最大收益值
result[i][j] = max(result[i-1][j], v[i]+result[i-1][j-w[i]])
print(result)
print("背包可容纳重量为 %d,最大收益为 %d" % (W, result[N][W]))

# 追溯选中的商品
n = N
bagw = W
# 逐个商品进行判断
print("所选商品为:")
while n > 0:
# 如果在指定载重量下,该商品对应的收益和上一个商品对应的收益相同,则表明未选中
if result[n][bagw] == result[n-1][bagw]:
n = n - 1
else:
# 输出被选用商品的重量和价值
print("(%d,%d) " % (w[n], v[n]))
# 删除被选用商品的载重量,以便继续遍历
bagw = bagw - w[n]
n = n - 1

简化后的code

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
N = 5
W = 11
w = [0, 1, 2, 5, 6, 7]
v = [0, 1, 6, 18, 22, 28]

result = [[0]*(W+1) for _ in range(N+1)]

for i in range(1, N+1):
for j in range(1, W+1):
if j < w[i]:
result[i][j] = result[i-1][j]
else:
result[i][j] = max(result[i-1][j], v[i]+result[i-1][j-w[i]])

print('bag`s weight:%d, total value:%d' % (W, result[N][W]))

print('The selected products are:')
n = N
bagw = W
while n > 0:
if result[n][bagw] == result[n-1][bagw]:
n -= 1
else:
print('({}, {})'.format(w[n], v[n]))
bagw -= w[n]
n -= 1

List

  1. 一般情况下使用index进行列表的切分比使用[:]–>[start:end)的时间复杂度更低,效率也更高O(1)<–>O(n)
  2. 对于具有特殊意义的index一般追踪起来比较困难,我们选择直接再terminal中return对应的information而不是return index

String

Set

Dictionary

Tuple

Linked List

使用指针(pointer)

Python-Underlying-Structure

Python底层结构

三元表达式

条件为真时的结果 if 判段的条件 else 条件为假时的结果

1
2
3
4
x = 4
y = 99 if x > 3 else 999
# if = True execute front
# else = True execute else-clause

基础知识

[Python里面如何生成随机数?]

random模块
**随机整数:random.randint(a,b)**:返回随机整数x,a<=x<=b
random.randrange(start,stop,[,step]):返回一个范围在(start,stop,step)之间的随机整数,不包括结束值。
**随机实数:random.random( ):**返回0到1之间的浮点数
random.uniform(a,b):返回指定范围内的浮点数。

[python复制一个对象]

Python里面如何拷贝一个对象?(赋值,浅拷贝,深拷贝的区别)
答:赋值(=),就是创建了对象的一个新的引用,修改其中任意一个变量都会影响到另一个。
浅拷贝:创建一个新的对象,但它包含的是对原始对象中包含项的引用(如果用引用的方式修改其中一个对象,另外一个也会修改改变){1,完全切片方法;2,工厂函数,如list();3,copy模块的copy()函数}
深拷贝:创建一个新的对象,并且递归的复制它所包含的对象(修改其中一个,另外一个不会改变){copy模块的deep.deepcopy()函数}

[is与==的区别]

在 Python 中,”==” 和 “is” 的区别可类比这个例子 ,**==是相等性比较,比较的是两个对象中的值是否相等,is是一致性比较,比较的是两个对象的内存空间地址是否相同。**

[列表由于是可以增删的(可变化)因此不能做为hashtable的key]

[python不支持的数据类型是什么?]

python不支持的数据类型是char 、byte类型。Python没有char或byte类型来保存单一字符或 8 比特整数。你可以使用长度为 1 的字符串表示字符或 8 比特整数。

python的数据类型有:

数字(int)、浮点(float)、字符串(str),列表(list)、元组(tuple)、字典(dict)、集合(set)、

byte 是字节数据类型 ,是有符号型的,可以表示-128—127 的数;

char 是字符数据类型 ,是无符号型的,可以表示一个整数,不能表示负数。

字典排序

【1.按照keys进行排序】

1
2
3
4
5
6
7
dct = {'a': 1, 'e': 8, 'c': 10, 'd': 4}
for key in sorted(dct.keys()):
value = dct.get(key)
print(key, value, sep=':', end=' ')

>>
a:1 c:10 d:4 e:8

【2.按照values进行排序】

1
2
3
4
5
6
7
8
9
10
dct = {'a': 1, 'e': 8, 'c': 10, 'd': 4}
# 使用items获得key-value,再使用sorted中的key=与lambda的匿名函数控制排序方式
temp = sorted(dct.items(), key=lambda item: item[1])
print(dict(temp))

>>
item[1]:按照value排序
-->{'a': 1, 'd': 4, 'e': 8, 'c': 10}
item[0]:按照key排序
-->{'a': 1, 'c': 10, 'd': 4, 'e': 8}

【3.降序排列,可以在sorted()中设置reverse】

【4.集合无法进行排序】

【0.注意】

1
2
3
4
5
a = set()
>> type(a) --> set

b = {}
>> type(b) --> dict

Boolean运算优先级

同一优先级默认从左往右计算。

  1. 优先级是 not > and > or
  2. not如果x是假的,则“非假”为真,否则x是真的,则非真为假
  3. and: 找到并返回第一个False(假)或最后一个True(真)
  4. or: 找到并返回第一个True(真)或最后一个False(假)

迭代器|生成器|迭代对象

生成器Generator

一种特殊的迭代器与迭代器定义一致

他与迭代器的编程方式不一样

1
2
3
4
5
6
>>> L = [x * x for x in range(10)]
>>> L
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
>>> g = (x * x for x in range(10))
>>> g
<generator object <genexpr> at 0x1022ef630>

迭代器定义

1
2
3
1.在class中定义__iter__和__next__方法
2.__iter__中的方法返回本身即:self
3.__next__中的方法需返下一个值,并且使用raise StopIteration抛出exception

判断是否是迭代对象或者迭代器使用:

1
2
3
4
5
6
7
8
9
10
from collections.abc import Iterator, Iterable

isinstance(obj, Iterable)
判断是否为可迭代对象/迭代器对象
依据是否有__iter__并return Iterator(迭代器)

isinstance(obj, Iterator)
判断是否为迭代器
依据是__iter__、__next__ 并且在__next__中
raise StopIteration

instance:

1
2
3
4
5
6
7
8
9
10
11
from collections.abc import Iterator, Iterable

v1 = [11, 22 ,33] # 只有__iter__;return迭代器
print(isinstance(v1, Iterator))
>> False
# 判断是否是迭代器,判断依据是__iter__和__next__

v2 = v1.__iter__() # __iter__ return iterator
print(isinstance(v2, Iterator))
>> False
# 因为可迭代对象

可迭代对象内部有__iter__方法,retun一个迭代器

当terminal中输出的是generator时,可以使用:

1
2
3
4
>>list(generator)

>>for i in generator:
print(i)

查看迭代器的内容,而非输出其memory地址

浅拷贝/深拷贝

copy解决的是容器中可迭代的值:应用于可变类型对象

要想切断浅拷贝中第二层的值,可以通过重新引用来实现

1
2
c[3] = [0, 1, 2, 3]
# c中的第四个数据将引用新的值,并与之前=赋的值不再有联系

首先需要了解下几个概念

  • 变量:是一个系统表的元素,拥有指向对象的连接空间
  • 对象:被分配的一块内存,存储其所代表的值
  • 引用:是自动形成的从变量到对象的指针
  • 类型:属于对象,而非变量
  • 不可变对象:一旦创建就不可修改的对象,包括字符串、元组、数值类型

(该对象所指向的内存中的值不能被改变。当改变某个变量时候,由于其所指的值不能被改变,相当于把原来的值复制一份后再改变,这会开辟一个新的地址,变量再指向这个新的地址。)

  • 可变对象:可以修改的对象,包括列表、字典、集合

(该对象所指向的内存中的值可以被改变。变量(准确的说是引用)改变后,实际上是其所指的值直接发生改变,并没有发生复制行为,也没有开辟新的地址,通俗点说就是原地改变。)

当我们写:

1
a = 'python'

Python解释器干的事情:

\1. 创建变量a

\2. 创建一个对象(分配一块内存),来存储值 ‘python’

\3. 将变量与对象,通过指针连接起来,从变量到对象的连接称之为引用(变量引用对象)


1.赋值: 只是复制了新对象的引用,不会开辟新的内存空间。

并不会产生一个独立的对象单独存在,只是将原有的数据块打上一个新标签,所以当其中一个标签被改变的时候,数据块就会发生变化,另一个标签也会随之改变。

2.浅拷贝: 创建新对象,其内容是原对象的引用。

浅拷贝有三种形式: 切片操作,工厂函数,copy模块中的copy函数。

如: lst = [1,2,[3,4]]

切片操作:lst1 = lst[:] 或者 lst1 = [each for each in lst]

工厂函数:lst1 = list(lst)

copy函数:lst1 = copy.copy(lst)

浅拷贝之所以称为浅拷贝,是它仅仅只拷贝了一层,拷贝了最外围的对象本身,内部的元素都只是拷贝了一个引用而已,在lst中有一个嵌套的list[3,4],如果我们修改了它,情况就不一样了。

浅复制要分两种情况进行讨论:

1)当浅复制的值是不可变对象(字符串、元组、数值类型)时和“赋值”的情况一样,对象的id值(id()函数用于获取对象的内存地址)与浅复制原来的值相同。

2)当浅复制的值是可变对象(列表、字典、集合)时会产生一个“不是那么独立的对象”存在。有两种情况:

第一种情况:复制的对象中无复杂子对象,原来值的改变并不会影响浅复制的值,同时浅复制的值改变也并不会影响原来的值。原来值的id值与浅复制原来的值不同。

第二种情况:复制的对象中有复杂子对象(例如列表中的一个子元素是一个列表),如果不改变其中复杂子对象,浅复制的值改变并不会影响原来的值。 但是改变原来的值中的复杂子对象的值会影响浅复制的值。

3.深拷贝:和浅拷贝对应,深拷贝拷贝了对象的所有元素,包括多层嵌套的元素。深拷贝出来的对象是一个全新的对象,不再与原来的对象有任何关联。

所以改变原有被复制对象不会对已经复制出来的新对象产生影响。

只有一种形式,copy模块中的deepcopy函数

对于不可变对象的深浅拷贝

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import copy
a=(1,2,3)

print("=====赋值=====")
b=a
print(a)
print(b)
print(id(a))
print(id(b))

print("=====浅拷贝=====")
b=copy.copy(a)
print(a)
print(b)
print(id(a))
print(id(b))

print("=====深拷贝=====")
b=copy.deepcopy(a)
print(a)
print(b)
print(id(a))
print(id(b))

结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
=====赋值=====
(1, 2, 3)
(1, 2, 3)
43481128
43481128
=====浅拷贝=====
(1, 2, 3)
(1, 2, 3)
43481128
43481128
=====深拷贝=====
(1, 2, 3)
(1, 2, 3)
43481128
43481128

不可变对象类型,没有被拷贝的说法,即便是用深拷贝,查看id的话也是一样的,如果对其重新赋值,也只是新创建一个对象,替换掉旧的而已。

一句话就是,不可变类型,不管是深拷贝还是浅拷贝,地址值和拷贝后的值都是一样的。

对于可变对象深浅拷贝

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import copy
a=[1,2,3]

print("=====赋值=====")
b=a
print(a)
print(b)
print(id(a))
print(id(b))

print("=====浅拷贝=====")
b=copy.copy(a)
print(a)
print(b)
print(id(a))
print(id(b))

print("=====深拷贝=====")
b=copy.deepcopy(a)
print(a)
print(b)
print(id(a))
print(id(b))

结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
=====赋值=====
[1, 2, 3]
[1, 2, 3]
37235144
37235144
=====浅拷贝=====
[1, 2, 3]
[1, 2, 3]
37235144
37191432
=====深拷贝=====
[1, 2, 3]
[1, 2, 3]
37235144
37210184

赋值: 值相等,地址相等

copy浅拷贝:值相等,地址不相等

deepcopy深拷贝:值相等,地址不相等

对于可变对象深浅拷贝(外层改变元素)

1
2
3
4
5
6
7
8
9
10
11
12
import copy
l=[1,2,3,[4, 5]]

l1=l #赋值
l2=copy.copy(l) #浅拷贝
l3=copy.deepcopy(l) #深拷贝
l.append(6)

print(l)
print(l1)
print(l2)
print(l3)

结果:

1
2
3
4
[1, 2, 3, [4, 5], 6]     #l添加一个元素6
[1, 2, 3, [4, 5], 6] #l1跟着添加一个元素6
[1, 2, 3, [4, 5]] #l2保持不变
[1, 2, 3, [4, 5]] #l3保持不变

对于可变对象深浅拷贝(内层改变元素)

1
2
3
4
5
6
7
8
9
10
11
12
import copy
l=[1,2,3,[4, 5]]

l1=l #赋值
l2=copy.copy(l) #浅拷贝
l3=copy.deepcopy(l) #深拷贝
l[3].append(6)

print(l)
print(l1)
print(l2)
print(l3)

结果:

1
2
3
4
[1, 2, 3, [4, 5, 6]]      #l[3]添加一个元素6
[1, 2, 3, [4, 5, 6]] #l1跟着添加一个元素6
[1, 2, 3, [4, 5, 6]] #l2跟着添加一个元素6
[1, 2, 3, [4, 5]] #l3保持不变

1.外层添加元素时,浅拷贝不会随原列表变化而变化;内层添加元素时,浅拷贝才会变化。
2.无论原列表如何变化,深拷贝都保持不变。
3.赋值对象随着原列表一起变化。

几何[set]

Python set 集合最常用的操作是向集合中添加、删除元素,以及集合之间做交集、并集、差集等运算,本节将一一讲解这些操作的具体实现。

向 set 集合中添加元素

set 集合中添加元素,可以使用 set 类型提供的 add() 方法实现,该方法的语法格式为:

setname.add(element)

其中,setname 表示要添加元素的集合,element 表示要添加的元素内容。

需要注意的是,使用 add() 方法添加的元素,只能是数字、字符串、元组或者布尔类型(True 和 False)值,不能添加列表、字典、集合这类可变的数据,否则 Python 解释器会报 TypeError 错误。例如:

1
a = {1,2,3}a.add((1,2))print(a)a.add([1,2])print(a)

运行结果为:

{(1, 2), 1, 2, 3}
Traceback (most recent call last):
File “C:\Users\mengma\Desktop\1.py”, line 4, in
a.add([1,2])
TypeError: unhashable type: ‘list’

从set集合中删除元素

删除现有 set 集合中的指定元素,可以使用 remove() 方法,该方法的语法格式如下:

setname.remove(element)

使用此方法删除集合中元素,需要注意的是,如果被删除元素本就不包含在集合中,则此方法会抛出 KeyError 错误,例如:

1
a = {1,2,3}a.remove(1)print(a)a.remove(1)print(a)

运行结果为:

{2, 3}
Traceback (most recent call last):
File “C:\Users\mengma\Desktop\1.py”, line 4, in
a.remove(1)
KeyError: 1

上面程序中,由于集合中的元素 1 已被删除,因此当再次尝试使用 remove() 方法删除时,会引发 KeyError 错误。

如果我们不想在删除失败时令解释器提示 KeyError 错误,还可以使用 discard() 方法,此方法和 remove() 方法的用法完全相同,唯一的区别就是,当删除集合中元素失败时,此方法不会抛出任何错误。

例如:

1
a = {1,2,3}a.remove(1)print(a)a.discard(1)print(a)

运行结果为:

{2, 3}
{2, 3}

Python set集合做交集、并集、差集运算

集合最常做的操作就是进行交集、并集、差集以及对称差集运算,首先有必要给大家普及一下各个运算的含义。

img
图 1 中,有 2 个集合,分别为 set1={1,2,3} 和 set2={3,4,5},它们既有相同的元素,也有不同的元素。以这两个集合为例,分别做不同运算的结果如表 1 所示。

运算操作 Python运算符 含义 例子
交集 & 取两集合公共的元素 >>> set1 & set2 {3}
并集 | 取两集合全部的元素 >>> set1 | set2 {1,2,3,4,5}
差集 - 取一个集合中另一集合没有的元素 >>> set1 - set2 {1,2} >>> set2 - set1 {4,5}
对称差集 ^ 取集合 A 和 B 中不属于 A&B 的元素 >>> set1 ^ set2 {1,2,4,5}

字典[dict]

字典中的.pop()是指删除key-val并抛出val

字典的访问:

  • dic.get(key, default):访问key,如果没有key返回None,有key返回对应的value
  • dic[key]:通过key访问value
  • for key in dic.keys():访问所有的keys
  • for value in dic.values():访问所有的values
  • for key, value in dic.items():访问所有的键值对

  • for i in dict–>access key
  • for key in dict.keys–>accsee key
  • for key, val in dict.item–>access key and val
  • dict[key]–>access key对应的val, 没有的话返回KeyError
  • dict.get(key, val(default:None))–>访问dict中key对应的值,没有的话返回val

有与dict中的key不可以直接修改,如果要修改key需要以下操作

第一种方法:将需要修改的键对应的值用dict.pop() 的方法提取出来,并重新赋值给新的键,即dict[新的键] = dict.pop(旧的键)。(字典dict的pop是删除某个键及其对应的值,返回的是该键对应的值)

1
2
dict={'a':1, 'b':2}
dict["c"] = dict.pop("a")

第二种方法:结合dict.pop() 和dict.update() 的方法。(字典dict的pop是删除某个键及其对应的值,返回的是该键对应的值)

1
2
dict={'a':1, 'b':2}
dict.update({'c':dict.pop("a")})

第三种方法:结合直接修改和del语句。先用直接修改的方式将旧键赋值给新的键,再用del语句删除原来的键名。

1
2
3
dict={'a':1, 'b':2}
dict['c']=dict['a']
del dict['a']

Operators

//:rounding down ==> 7//2 = 3.5 —> 3

[start:end]==>contains start except end

[1, 2, 3, 4, 5] ==[2:]==>[3, 4, 5]

[1, 2, 3, 4, 5] ==[:2]==>[1, 2]

[1, 2, 3, 4, 5] ==[1:3]==>[2, 3]

切片操作[start:end]==>[start, end)

[a::b]

这个是python的slice notation的特殊用法。
b = a[i:j] ==>[i, j)==>new list

  • default(i)=0
  • default(j)=len(alist)

[i:j:s]==>[i, j) step=s

如果s<0

  • i缺省时,默认为-1;
  • j缺省时,默认为-len(a)-1

所以a[::-1]相当于 a[-1:-len(a)-1:-1],也就是从最后一个元素到第一个元素复制一遍。

常见的譬如[::-1]==>将list的所有元素反转

1
2
3
4
5
6
7
b1 = [1, 2, 3, 4][::-1]
b2 = [1, 2, 3, 4][::-2]
print(b1)
print(b2)
>>
[4, 3, 2, 1]
[4, 2]

randint(start, end)==>contains start and end==>[start, end]

example==>randint(1, 3)==>1, 2, 3

测试code性能

Performance

1.timeit

timeit只输出被测试代码的总运行时间

1
2
3
4
5
6
7
8
9
10
11
import timeit

def fun():
for i in range(1000):
a = i * i

cost_time = timeit.timeit('fun()', 'from __main__ import fun', number=1000)
print(cost_time)

>>
0.0074794

2.profile[函数时间]

profile:纯Python实现的性能测试模块,接口和cProfile一样。

1
2
3
4
5
6
7
import profile

def fun():
for i in range(1000):
a = i * i

profile.run('fun()')
  • ncall:函数运行次数
  • tottime: 函数的总的运行时间,减去函数中调用子函数的运行时间

第一个percall:percall = tottime / nclall

  • cumtime:函数及其所有子函数调整的运行时间,也就是函数开始调用到结束的时间。

第二个percall:percall = cumtime / nclall

名称 含义
ncalls 函数被调用次数
tottime 函数(除去子函数)总运行时间
percall 平均时间,等于tottime/ncalls
cumtime 函数(包括子函数)总运行时间
percall 平均时间,等于cumtime/ncalls
filename 文件:行号(函数)


3.cProfiler[函数时间]

profile:c语言实现的性能测试模块,接口和profile一样。

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

def fun():
a = 0
b = 0
for i in range(10000):
a = a + i * i

for i in range(3):
b += 1
time.sleep(0.1)

return a + b


cProfile.run('fun()')

result:

profiler 和 cProfiler 都只能检测整个函数的运行时间,要想知道每行代码运行所损耗的时间需要line_profiler或者memory_profiler(不仅可以得到每行代码运行的时间还可以统计每行代码所占用的内存大小)


4.line_profiler[分段时间]

pip install line_profiler

安装之后kernprof.py会加到环境变量中。

line_profiler可以统计每行代码的执行次数和执行时间等,时间单位为微妙。

该模块常通过装饰器来使用。在安装好该模块后,编写需要测试的python程序,并在需要测试的函数上方加入@profile,保存为*.py文件。在命令行使用kernprof -l -v *.py,在输出打印出程序结果之后会打印性能分析。

输出结果的含义:

1
2
3
4
5
6
7
8
9
10
11
12
from line_profiler import LineProfiler

def do_stuff(numbers):
s = sum(numbers)
l = [numbers[i] / 43 for i in range(len(numbers))]
m = ['hello' + str(numbers[i]) for i in range(len(numbers))]

numbers = [i for i in range(1000)]

lp = LineProfiler(do_stuff)
lp.runcall(do_stuff, numbers)
lp.print_stats()
名称 意义
Timer unit 计时器单位,微秒
Total time 测试代码总运行时间
File 测试代码文件名
Hits 每行代码运行次数
Time 每行代码运行时间
Per Hit 每行代码运行一次的时间
% Time 每行代码运行时间的百分比
Line Contents 每行代码

  • Total Time:测试代码的总运行时间
  • Hits:表示每行代码运行的次数
  • Time:每行代码运行的总时间
  • Per Hits:每行代码运行一次的时间
  • % Time:每行代码运行时间的百分比

5.memory_profiler:[内存]

memory_profiler工具可以统计每行代码占用的内存大小。

测试代码:

使用方法同line_profiler模块,借助装饰器调用。在需要分析的函数上方加@profile。在命令行使用python -m memory_profiler *.py运行代码。
输出结果中:
Mem usage:内存使用情况
Increment:每行代码运行后内存增减情况

使用:

  1. 在需要测试的函数加上@profile装饰
  2. 执行命令: python -m memory_profiler C:Python34 est.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 将调试文件独立出xx.py
# 在pycharm 的 terminal windows中使用
# code: python -m memory_profiler memory_profiler
from memory_profiler import profile

@profile
def test1():
c = []
a = [1, 2, 3] * (2 ** 20)
b = [1] * (2 ** 20)
c.extend(a)
c.extend(b)
del b
del c

if __name__ == "__main__":
test1()

>>cmd ->python version 3.7
>>//terminal//
<python -m memory_profiler memory_profiler>

  • Mem usage 为当前总内存
  • Increment 为增加的内存

总结起来使用非常时间

  • 方法前加上 @profile 主键
  • 使用 python -m memory_profiler 来运行,不过直接用 python 运行也可以

通过上面这段代码我们可以发现,del 语句只是将变量删除,并不能减少内存的消耗。

参数使用

  • precision 显示小数点后的位数

默认显示的内存单位为 MiB,小数点后显示一位,如果某行代码占用内存比较小,就可能显示不出来,此时可以通过调整小数点后的位数实现。

1
2
3
4
5
6
7
8
9
from memory_profiler import profile


@profile(precision=4)
def test3():
a = [1, 2] * (2 ** 10)

if __name__ == "__main__":
test3()
  • stream 将结果输出到流中

每次运行都要打印内存情况,势必会影响程序输出效果,我们可以将结果通过流输出到文件中

1
2
3
4
5
6
7
8
from memory_profiler import profile

@profile(precision=4, @profile(precision=4, stream=open('/tmp/memory_profiler.log','w+')))
def test3():
a = [1, 2] * (2 ** 10)

if __name__ == "__main__":
test3()

6.objgraph:[待完善]

objgraph是一个实用模块,可以列出当前内存中存在的对象,可用于定位内存泄露。

objgraph需要安装:

pip install objgraph


Time

计算Python的某个程序,或者是代码块运行的时间一般有三种方法。

1.datetime

1
2
3
4
5
6
7
import datetime
start = datetime.datetime.now()
run_function():
# do something

end = datetime.datetime.now()
print (end-start)

2.time–time

1
2
3
4
5
6
import time
start = time.time()
run_function()
end = time.time()

print str(end-start)

3.time-clock(version<3.8)

1
2
3
4
5
6
import time
start = time.clock()
run_function()
end = time.clock()

print str(end-start)

通过对以上方法的比较我们发现,方法二的精度比较高。方法一基本上是性能最差的。这个其实是和系统有关系的。一般我们推荐使用方法二和方法三。我的系统是Ubuntu,也就是Linux系统,方法二返回的是UTC时间。 在很多系统中time.time()的精度都是非常低的,包括windows。

python 的标准库手册推荐在任何情况下尽量使用time.clock().但是这个函数在windows下返回的是真实时间(wall time)

方法一和方法二都包含了其他程序使用CPU的时间。方法三只计算了程序运行CPU的时间。

方法二和方法三都返回的是浮点数
究竟是使用 time.clock() 精度高,还是使用 time.time() 精度更高,要视乎所在的平台来决定。总概来讲,在 Unix 系统中,建议使用 **time.time()**,在 Windows 系统中,建议使用 **time.clock()**。

我们要实现跨平台的精度性,我们可以使用timeit 来代替time

1
2
3
4
5
6
import timeit

start = timeit.default_timer()
do_func()
end = timeit.default_timer()
print str(end-start)

时间复杂度

随着问题规模的增加,T(n)变化最大的数量级称为时间复杂度O(n)

字符编码

Python2中默认的编码格式是 ASCII 格式,程序文件中如果包含中文字符(包括注释部分)需要在文件开头加上 # -*- coding: UTF-8 -*- 或者 #coding=utf-8 就行了

Python3默认支持Unicode

保留字(关键字)

30个关键字

1
2
3
4
5
6
7
8
9
10
and exec    not
assert finally or
break for pass
class from print
continue global raise
def if return
del import try
elif in while
else is with
except lambda yield

注释

1
2
3
4
5
6
7
8
9
10
11
# 单行注释

'''
多行注释
多行注释
'''

"""
多行注释
多行注释
"""

变量赋值

Python是动态语言,所以在定义变量的时候不需要申明类型,直接使用即可。
Python会根据值判断类型。

1
2
3
name = "Zeta" # 字符串变量
age = 38 # 整数
income = 1.23 # 浮点数

多变量赋值

1
2
a,b = 1,2 # a=1; b=2
c = d = 3 # c=3; d=3

标准数据类型

Python 的标准数据类型有:

  • Boolean(布尔值)
  • Number(数字)
  • String(字符串)
  • List(列表)
  • Tuple(元组)
  • Set(集合)
  • Dictionary(字典)(hash map)—遇到hash冲突时,采用的是开放寻址法而不是链接法

Python标准数据结构有:

有序结构,可以使用index下标进行检索数据,并且序列中的元素可以重复

  • List
  • Tuple
  • String

无序结构,不可以用index下标进行检索,序列中允许有重复元素

  • Set
  • Dictionary

数据类型转换

Python类型转换非常简单,用类型名作为函数名即可。

1
2
3
4
5
6
int(n)            # 将数字n转换为一个整数
float(n) # 将数字n转换到一个浮点数
str(o) # 将对象 obj 转换为字符串
tuple(s) # 将序列 s 转换为一个元组
list(s) # 将序列 s 转换为一个列表
set(s) # 将序列 s 转换为一个集合

另外,Python中可以直接转换数字字符串和数字:

1
2
3
s = "123"
i = 456
print(int(s), str(i))

条件语句

Python传统的判断语句如下

1
2
3
4
if name == 'zeta':          # 判断变量是否为 zeta 
print('Welcome boss') # 并输出欢迎信息
else:
print('Hi, ' + name)

Python不支持三元表达式,但是可以用一种类似的替代办法

1
2
3
title = "boss"
name = "zeta" if title == "boss" else "chow"
print(name)

逻辑与用 and ,逻辑或用 or

循环语句

Python中有whilefor两种循环,都可以使用break跳出循环和continue立即进入下一轮循环,另外,Python的循环语句还可以用else执行循环全部完毕后的代码,break跳出后不会执行else的代码

while 条件循环,

1
2
3
4
5
6
7
8
9
count = 0
while (count < 9):
print('The count is:', count)
count = count + 1
if count == 5:
break # 可以比较以下break和不break的区别
pass
else:
print('loop over')

for 遍历循环,循环遍历所有序列对象的子项

1
2
3
4
5
6
7
8
names = ['zeta', 'chow',  'world']
for n in names:
print('Hello, ' + n)
if n == 'world':
break
pass
else:
print('Good night!')

for循环中也可以用else,(注释掉代码中的break试试看。)

函数

def关键字定义函数,并且在Python中,作为脚本语言,调用函数必须在定义函数之后。

1
2
3
def foo(name):
print("hello, "+name)
passfoo("zeta")

默认参数 Python定义函数参数时,可以设置默认值,调用时如果没有传递该参数,函数内将使用默认值,默认值参数必须放在无默认值参数后面。

1
2
3
4
def foo(name="zeta"):
print("hello, "+name)
pass
foo()

关键字参数 一般函数传递参数时,必须按照参数定于的顺序传递,但是Python中,允许使用关键字参数,这样通过指定参数明,可以不按照函数定义参数的顺序传递参数。

1
2
3
4
def foo(age, name="zeta"):
print("hello, "+name+"; age="+str(age))
pass
foo(name="chow", age=18)

不定长参数,Python支持不定长参数,用*定义参数名,调用时多个参数将作为一个元祖传递到函数内

1
2
3
4
5
def foo(*names):
for n in names:
print("hello, "+n)
pass
foo("zeta", "chow", "world")

return 返回函数结果。

模块

  • 模块是一个.py文件
  • 模块在第一次被导入时执行
  • 一个下划线定义保护级变量和函数,两个下划线定义私有变量和函数
  • 导入模块习惯性在脚本顶部,但是不强制

导入包

在Python中,使用import导入模块。

1
#!/usr/bin/python# -*- coding: UTF-8 -*- # 导入模块import support support.print_func(“Runoob”)

还可以使用from import导入模块指定部分

1
from modname import name1[, name2[, ... nameN]]

为导入的包设置别名用 as关键字

1
import datetime as dt

错误和异常

Python中用经典的 try/except 捕获异常

1
2
3
4
try:<语句>    #运行别的代码
except <异常名称>:<语句>
#except <异常名称>,<数据>:<语句>
#如果引发了指定名称的异常,获得附加的数据

还提供了 elsefinally

如果没发生异常的执行else语句块,finally块的代码无论是否捕获异常都会执行

Python内建了很全面的异常类型名称,同时能自定义异常类型

面向对象

Python完全支持面向对象的。

多线程

  1. 使用thread模块中的start_new_thread()函数
  2. 使用threading模块创建线程

总结

Python和Go分别在动态语言和静态语言中都是最易学易用的编程语言之一。

它们并不存在取代关系,而是各自在其领域发挥自己的作用。

Python的语法简单直观,除了程序员爱不释手外也非常适合于其他领域从业者使用。

Go兼具语法简单和运行高效的有点,在多线程处理方面很优秀,非常适合已经掌握一定编程基础和一门主流语言的同学学习,不过,Go是不支持面向对象的,对于大多数支持面向对象语言的使用者在学习Go语言的时候,需要谨记并且转换编程思路。

我的工作实践

面试

Python开发工程师

  • python基础
  • 数据结构与算法分析
  • linux系统
  • Mysql数据库
  • python Django框架
  • git命令

自我介绍

我是柳宇豪,是辽宁工程技术大学,信息与计算机科学专业的一名应届本科生。我将从四个方面介绍我自己

一、竞赛方面:我参加过许多数学建模竞赛,曾获MCM/ICM国二、MathorCup省三等;

二、校园经历方面:曾担任过礼仪队队长、舞蹈队队长以及女生部部长和学生会副主席,成功举办了女生月活动和最美面容活动,

三、学习方面:绩点专业排名前十,曾获校优秀学生二等奖学金和三等奖学金以及三号学生等多项荣誉称号

四、兴趣爱好方面:在整个大学生活中,兴趣爱好可以分两个阶段,第一阶段为大一大二时期,十分喜欢辩论演讲,曾在院赛多次获得最佳辩手称号,也荣获校第十四届辩论赛冠军队伍等荣誉称号;大三大四时期喜欢读马哲和毛选等书籍,完善了我的世界观和方法论,并创建阅读社区星火社

总的来说,在生活中注重效率,喜欢思考,具有一定的组织能力以上是自我介绍的全部内容。

反问

  1. 对于我应聘的这份工作来说,您认为需要的最重要的能力是什么?(体现了你的态度)
  2. 您能说一下这个岗位在公司的的发展前景和晋升机制吗?(体现了你的发展规划与上进心)
  3. 您能描述一下,您心中这个职位的理想候选人是什么样的吗?(体现了你的思考)
  4. 你点评下我次面试的大体情况

0面试

1试用期

2实习期

3入职

4离职


0公司制度

1合同

2违法与用法

3节假日

4公司文化


离职

1.试用期提前三天申请离职就好。(双方灵活选择,节约彼此时间。新员工上手工作也不多,交接方便。)

2.正式员工离职提前一个月申请。(部分岗位员工突然离职,但Ta手上的工作不能停,需要下一个人交接,或者找下一个人还需要时间,还需要带。So,立法考虑设立一个月)(一般的公司不会卡你时间,交接也简单,不排除有硬生生卡人一个月的公司,劳动者心理要做好准备)

5.辞退除工资外,还应该有赔偿金(double)。

13.离职最好要拿一份离职证明。


违法用法

违法

3.公司单方面降薪不合法。

4.不买社保,违法。公司缴纳大部分。(工资是工资,五险一金,六险一金,是社会福利保障。社保国家强制,一金是福利(已经是默认福利,正规公司一般情况应该有),和那些定期团聚、出国游、开工红包、生日红包、节假日福利一样,不同公司不一样)

14.一点要看清自己的工作职位和职责,不符合录用,经常会扯这里。(好在,法律规定,用人单位提供不符合录用证据,用人单位提供不出来,违法解除)

15.合同里经常会有一条“领导安排的其他任务”或者“根据工作需要,可以岗位调整,安排其他事情”,就是针对14,设立的。

43.试用期被辞退。同理,关注14点。看清楚自己岗位和职责内容(一开始你就要清楚知道胜任标准,效率完成工作内容)。用人单位辞退,对方要出示你确实不能胜任的证据,而不是说一句话,你不能胜任工作你可以走了。

19.涉嫌歧视、人身攻击、肢体冲突可以举报、报警。

34.用人单位忌讳,吸毒人员。还有一些受刑事处罚人员。我们也很难,搞不好就是歧视,只能从入职诚实信用入手。

45.不要帮公司做违法的事情。之前遇到一个年轻小伙(女朋友卖婚房委托律师,哭着说从小到大成绩优秀,被抓了家人都不相信)帮公司开发一个理财软件(诈骗软件),一起进去了。

46.用人单位不交社保,找Ta交。协商后单位仍旧不给缴,去用人单位所在地人力资源社会保障行政部门投诉(打电话就可以)(市长热线也可以),由社会保险费征收机构责令其限期缴纳或者补足。如果用人单位漏报或少报社会保险基数的,则可以向所在地社保经办机构稽核科投诉反映。

47.用人单位未在一个月内按规定缴纳的,劳动者可以解除劳动合同,可同时要求用人单位依法支付经济补偿。

65.解除劳动合同VS终止劳动合同通知

用法

31.劳动仲裁不要钱,财政保障。程序很简单,自己都可以操作,你合情合理合法很容易赢。

32.劳动争议诉讼10元。(仲裁是前置程序)(一般小问题,仲裁就可以了,不用后面诉讼)

48.市长热线12345

公共法律服务热线12348


就职

6.大学生临近毕业(还有几个月毕业),没有拿到毕业证,可以签订劳动合同。

7.一直不签劳动合同,违法。入职就应该签。(一个月后,该担心的应该是公司吧)

中华人民共和国劳动合同法实施条例

第五条自用工之日起一个月内,经用人单位书面通知后,劳动者不与用人单位订立书面劳动合同的,用人单位应当书面通知劳动者终止劳动关系,无需向劳动者支付经济补偿,但是应当依法向劳动者支付其实际工作时间的劳动报酬。

第六条用人单位自用工之日起超过一个月不满一年未与劳动者订立书面劳动合同的,应当依照劳动合同法第八十二条的规定向劳动者每月支付两倍的工资,并与劳动者补订书面劳动合同;劳动者不与用人单位订立书面劳动合同的,用人单位应当书面通知劳动者终止劳动关系,并依照劳动合同法第四十七条的规定支付经济补偿。

前款规定的用人单位向劳动者每月支付两倍工资的起算时间为用工之日起满一个月的次日,截止时间为补订书面劳动合同的前一日。

10.入职身份证复印件上,注明,仅供入职使用。(所有使用身份证都可以注明)

17.部分工作,该有防护的应该有防护工具(口罩等等)。

18.简历,入职表不要乱填乱写,诚实信用原则。(一般合同有一条,作假不录取,已录取无条件辞退)


实习期

12.试用期工资不低于正式工资80%。(国家单位,国企试用期一般100%)

20.试岗。法律规定试用期,就是为了双方考察选择。只是部分企业自己又设置了一个3-7天试岗,多数还表明还没有工资。这么🐶公司还是不要去了。

52.面试前去“天眼查”“企查查”查一下,顺便看看有没有公司和前员工的劳动纠纷,看看公司态度。

56.实习生。这个问题复杂,不过不是特别行业,法律、医学等,已经毕业的娃,还当什么实习生(试用期实习生,只是称呼吧?)。(大学生寒暑期实习,肯定叫实习生)(例如法律行业是那个证,要实习至少一年,也可以叫实习生,实习律师)(还有临近毕业,文中第6,有的公司说不能和你签合同,你还没毕业证,先做实习生。)


合同

8.劳动合同一式两份,一定要保管好自己那一份。(不然纠纷的时候你劳动仲裁提交材料都没有)

9.一般情况,不要给公司押东西(各类证件:毕业证、上一家离职证明等等),仅现场审核,然后自己带回去。『有押护照的,那就是大佬级别了』

11.合同一年,一般就一个月试用期。偶尔两个月(🐶公司)。

21.三年劳动合同,不超过6个月。三个月不满一年,1个月试用期。一年以上不满三年,试用期不超过2个月。『这个其实是约定,一个半月都可以。合法合规就好』

67.有的公司三个月或六个月签一次,特别🐶。连续不间断签,最好不要去。

68.还有些公司老板觉得自己开了一个公司,Ta就是王,Ta就是法。签一年Ta就要试用期三个月。连国家法律都不敬畏,有纠纷了有的扯。

68.还有的公司,告诉你是他们关联/下属企业,为了人员管理/避税等等理由,合同上签的另外的公司。谨慎一点吧,首先看看那个公司注册下来了没有。

工资

16.工资构成。假设工资8k。然后合同约定4k基本工资,1k交通补贴,1k吃饭补贴,2k技术津贴。他们告诉你还是8k,看上去也是。风险就在于以后很多东西(保险,请假等等,甚至劳动纠纷)看基本工资(基数)。

22.工资不能低于当地最低工资标准。

33.上班一周有没有工资?有。

38.工资讨论。一般公司会设置这个,不准讨论。最好遵守规定。(违反公司制度辞退风险)实在忍不住,不要留证据,披个马甲,好吗。

49.到手工资。可以问一句,工资到手多少。顺便推算,社保交几档。(一般情况,国家单位一、二档)其他,危。

61.招聘软件上部分公司,工资跨度很大。一般情况看最低。面试前,也可以问问,岗位预算到底多少。

公司制度

绩效

这是大家关注的吧。公司单方随意改动违法。员工手册,我们一般不会设置绩效,因为这就是“递刀”。公司动员工工资,或者激励努力劳动,都是动这一款。一般是附件,改动也方便,不同部门也不一样。但是这些都属于规则制度一部分,也不能朝令夕改,要程序合法、大家同意(有时候职工代表已经代表你了😏)。(但是选职工代表你们有流程,要参与哦)

24.迟到、请假等所有的公司规章制度,要合理合法合程序,应该经过公司民主程序制定,投票(全体员工或者职工代表),规章制度公示(✔),报请工会(不一定)。规章制度员工必须知悉(✔)或同意签字留底等等。(签字留底表明知悉,是最周全的)『一般情况,职工代表是老板的人😂,老板对结果不my,换下一个职工代表😏』

PS:不合理不合法的东西,在公司站得住脚,上了法庭站不住脚。(所以,很多时候,要员工同意并签字,例:奋斗者协议)

什么是合理。假设某工厂记件,一天最快的10个,最慢的5个,平均6个。规定基本工资说5个就达标,7个良好,8个优秀。如果现在新厂长改变制度,说7个达标,10个良好,12个优秀。(有人能达到12吗,没有,除非技术变革)

不合理VS明显不合理

(提一句996/007,上面例子,我们确定最高10个。但是在实际生活中,法定时间是八个小时,007/996工作强度能不能接受,员工又“自愿接受”,变数太多。又加上员工自己签的“奋斗者协议”,自愿并同意。正规公司按时按量给你发加班工资,你不能阻止别人奋斗吧)

⚡我发现一个问题,法院曾认可某一个公司“奋斗者协议”但不代表其他公司可以无限制、不顾员工身体,强制xxx。如果被发现强迫劳动也是违反《劳动法》《劳动合同法》等的,建议没有“南山必胜客”那样的法务团队的公司,不要乱搞,随便加班“强迫劳动”。⚡

25.公司内部规章制度必须遵守相应法律法规,例如《劳动法》《劳动合同法》等等。

请假

要符合公司规定流程。具体情况具体分析。紧急情况,要有电话录音或信息记录等等证据留底。此处风险,容易被记旷工。

内卷

奋斗者协议,这个嘛,法院认可过。不过具体问题具体分析。加班加班工资还要应该有的。一般公司,没有腾讯级别的法务部,不要瞎搞。这不是代表官方认可这个,只是个例。自愿和“被自愿”难道不好分清楚吗?

保密协议

40.保密协议。应该保密就要保密。

竞业限制

41.竞业限制。慎重。如果你只有这个安身立命的本事,不要签。竞业限制签了,约定期限内,应该有补偿金。

注意事项

44.公司群不要“胡言乱语”,私下群不要“胡言乱语”,还是那句话,同事告密你就死了,匿名,马甲,不香吗。(最近的官方公众号推算案子,说领导“彩旗飘飘”“xxx”等等,被告密,被违法辞退,二审赔了10万。折腾好麻烦)

50.服装。有的🐶公司,上班统一发(定制)服装,离职出现服装费(1-2-3千)。

64.玩手机,抗辩理由很多。看工作群、和客户联系、父母急事。不太知道具体公司具体岗位情况。我们这行是结果导向,没有这一说。

培训进修

26.培训。刚进去让你贷款培训公司有问题。公司为了劳动者更好劳动,适应岗位,一般情况,免费培训。

27.进修。进修后要在用人单位工作服务几年。注意看清楚违约金多少。

工伤

28.工伤保险,包括上下班途中。(一)在合理时间内往返于工作地与住所地、经常居住地、单位宿舍的合理路线的上下班途中;(二)在合理时间内往返于工作地与配偶、父母、子女居住地的合理路线的上下班途中;(三)从事属于日常工作生活所需要的活动,且在合理时间和合理路线的上下班途中;(四)在合理时间内其他合理路线的上下班途中。

29.外出做工作上的事情出事,工伤。

PS:非本人原因的交通事故,工伤。就是公司认定工伤,肇事者还要赔。

换岗

35.劳动者患病或非因工受伤,确实不太适合现在的岗位。不是立马辞退。立法者考虑到这样的情况,首先,是给你换一个岗位,(给你一个挽救的机会)。(例如,工作是前台,嗓子??,不太适合前台沟通职责,可以换去后面行政。)

骚扰

36.怀孕。让你3-5-7年内不能怀孕,违背公序良俗。

36.正式员工怀孕时,不能被辞退,也不能降薪。

37.女性遇到性骚扰,要学会保留证据,一击即中。

38.试用期怀孕。《劳动合同法》第39条:“劳动者在试用期间被证明不符合录用条件,用人单位可以与劳动者解除劳动合同。也就是说,悬。

派遣

42.派遣。国家那种编外派遣,还差不多。其他的,仁者见仁。

三方协议

51.三方协议,是学校为没有出校门、没有接受社会“关爱”,提供的保护你的好东西。

查看公司

53.法定代表人还是有风险的。例:京D换人

54.几个月没发工资了?跑路吧。

55.股权激励,有些是饼有些是真的,看公司发展。

节假日

57.调休。先看最近的五一,这个话题,略。(为什么,有人看不懂五一梗,so,还是略)(谁说普通调休,我说企业三倍工资调休)

58.法定节假日放假,带薪年假,病假(患病or非因工负伤,必须要给医疗期)有钱。婚假(女20岁、男22岁)3天,有工资。

59.产假(98天,产前休假15天。难产+15,多胞胎+15)。怀孕流产也有假,不满4个月,15-30天;满4个月,42天。晚育,奖励30天,配偶10天护理假。(劳动时间内,还有一个小时哺乳时间。多胞胎,多一个,加一个小时。)

63.离开工位。看离开时长。一般情况去洗手间,去接水喝水,不能制止我吧。如果是其他事情,较长时间,建议请假或者告知自己去干什么多少分钟回。(主要是急事现场找不到人,领导就火大,火大就惨了)不同公司具体规章不一样,严重情况且多次可能列为旷工。例如:一月旷工N次,就拜拜。

公司文化

66.公司文化呢,要不是保护人权的区别于同行或其他公司的东西,还有就是拼搏(狼性)、奋斗、快乐、幸福等精神,包容、女性关怀、年轻员工等一些关爱措施。(卖身,那是不可能的)

举例:我们这行还是有年轻律师起薪几个万元俱乐部;还有些给年轻律师配行政秘书的;还有给年轻妈妈设置专门的空间喂奶或小孩玩乐区域。等等。(健身房?那是让你更好的工作😏)

Infectious Disease Model

概念介绍

S (Susceptible)易感者指缺乏免疫能力健康人,与感染者接触后容易受到感染;

E (Exposed)暴露者 指接触过感染者但暂无传染性的人,可用于存在潜伏期的传染病;

I (Infectious)患病者指有传染性的病人,可以传播给 S,将其变为 E 或 I ;

R (Recovered)康复者指病愈后具有免疫力的人,如是终身免疫性传染病,则不可被重新变为 S 、E 或 I ,如果免疫期有限,就可以重新变为 S 类,进而被感染。

各个变量之间并不是相互独立,而是可以相互转化。

The various variables are not independent of each other, but can be transformed into each other.

COVID19 3.7-3.8

模型一:SI-Model

不进行人为的控制

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
import scipy.integrate as spi
import numpy as np
import matplotlib.pyplot as plt

# N为人群总数
N = 10000
# β为传染率系数
beta = 0.25
# gamma为恢复率系数
gamma = 0
# I_0为感染者的初始人数
I_0 = 1
# S_0为易感者的初始人数
S_0 = N - I_0
# T为传播时间
T = 150

# INI为初始状态下的数组
INI = (S_0,I_0)


def funcSI(inivalue,_):
Y = np.zeros(2)
X = inivalue
# 易感个体变化
Y[0] = - (beta * X[0] * X[1]) / N + gamma * X[1]
# 感染个体变化
Y[1] = (beta * X[0] * X[1]) / N - gamma * X[1]
return Y

T_range = np.arange(0,T + 1)

RES = spi.odeint(funcSI,INI,T_range)

plt.plot(RES[:,0],color = 'darkblue',label = 'Susceptible',marker = '.')
plt.plot(RES[:,1],color = 'red',label = 'Infection',marker = '.')
plt.title('SI Model')
plt.legend()
plt.xlabel('Day')
plt.ylabel('Number')
plt.show()

模型二:SIS-Model

易感染者–>感染者—TIME—>易感染者

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
import scipy.integrate as spi
import numpy as np
import matplotlib.pyplot as plt

# N为人群总数
N = 10000
# β为传染率系数
beta = 0.25
# gamma为恢复率系数
gamma = 0.05
# I_0为感染者的初始人数
I_0 = 1
# S_0为易感者的初始人数
S_0 = N - I_0
# T为传播时间
T = 150

# INI为初始状态下的数组
INI = (S_0,I_0)


def funcSIS(inivalue,_):
Y = np.zeros(2)
X = inivalue
# 易感个体变化
Y[0] = - (beta * X[0]) / N * X[1] + gamma * X[1]
# 感染个体变化
Y[1] = (beta * X[0] * X[1]) / N - gamma * X[1]
return Y


T_range = np.arange(0,T + 1)

RES = spi.odeint(funcSIS,INI,T_range)

plt.plot(RES[:,0],color = 'darkblue',label = 'Susceptible',marker = '.')
plt.plot(RES[:,1],color = 'red',label = 'Infection',marker = '.')
plt.title('SIS Model')
plt.legend()
plt.xlabel('Day')
plt.ylabel('Number')
plt.show()

模型三:SIR-Model

易感染者–>感染者—time—>康复者(隔离or抗体)—-no–>易感染者

拐点:infected峰值的时间

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
import scipy.integrate as spi
import numpy as np
import matplotlib.pyplot as plt

# N为人群总数
N = 10000
# β为传染率系数
beta = 0.25
# gamma为恢复率系数
gamma = 0.05
# I_0为感染者的初始人数
I_0 = 1
# R_0为治愈者的初始人数
R_0 = 0
# S_0为易感者的初始人数
S_0 = N - I_0 - R_0
# T为传播时间
T = 150

# INI为初始状态下的数组
INI = (S_0,I_0,R_0)


def funcSIR(inivalue,_):
Y = np.zeros(3)
X = inivalue
# 易感个体变化
Y[0] = - (beta * X[0] * X[1]) / N
# 感染个体变化
Y[1] = (beta * X[0] * X[1]) / N - gamma * X[1]
# 治愈个体变化
Y[2] = gamma * X[1]
return Y


T_range = np.arange(0,T + 1)

RES = spi.odeint(funcSIR,INI,T_range)

plt.plot(RES[:,0],color = 'darkblue',label = 'Susceptible',marker = '.')
plt.plot(RES[:,1],color = 'red',label = 'Infection',marker = '.')
plt.plot(RES[:,2],color = 'green',label = 'Recovery',marker = '.')
plt.title('SIR Model')
plt.legend()
plt.xlabel('Day')
plt.ylabel('Number')
plt.show()

模型四:SIRS-Model

数学建模常用算法——传染病模型(四)SIRS模型 - 知乎 (zhihu.com)

适用于有易感者、患病者和康复者三类人群,康复者只有暂时性的免疫力,单位时间后变为易感者,有可能再次被感染而患病。

模型假设

易感者与患病者有效接触即被感染,变为患病者,可被治愈再次变为易感者,有短暂免疫力,无潜伏期。

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
import scipy.integrate as spi
import numpy as np
import matplotlib.pyplot as plt

# N为人群总数
N = 10000
# β为传染率系数
beta = 0.25
# gamma为恢复率系数
gamma = 0.05
# Ts为抗体持续时间
Ts = 7
# I_0为感染者的初始人数
I_0 = 1
# R_0为治愈者的初始人数
R_0 = 0
# S_0为易感者的初始人数
S_0 = N - I_0 - R_0
# T为传播时间
T = 150

# INI为初始状态下的数组
INI = (S_0,I_0,R_0)


def funcSIRS(inivalue,_):
Y = np.zeros(3)
X = inivalue
# 易感个体变化
Y[0] = - (beta * X[0] * X[1]) / N + X[2] / Ts
# 感染个体变化
Y[1] = (beta * X[0] * X[1]) / N - gamma * X[1]
# 治愈个体变化
Y[2] = gamma * X[1] - X[2] / Ts
return Y


T_range = np.arange(0,T + 1)

RES = spi.odeint(funcSIRS,INI,T_range)

plt.plot(RES[:,0],color = 'darkblue',label = 'Susceptible',marker = '.')
plt.plot(RES[:,1],color = 'red',label = 'Infection',marker = '.')
plt.plot(RES[:,2],color = 'green',label = 'Recovery',marker = '.')
plt.title('SIRS Model')
plt.legend()
plt.xlabel('Day')
plt.ylabel('Number')
plt.show()

模型五:SEIR-Model

暴露者:处于潜伏期的人,有一定概率变成感染者

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
import scipy.integrate as spi
import numpy as np
import matplotlib.pyplot as plt

# N为人群总数
N = 10000
# β为传染率系数
beta = 0.6
# gamma为恢复率系数
gamma = 0.1
# Te为疾病潜伏期
Te = 14
# I_0为感染者的初始人数
I_0 = 1
# E_0为潜伏者的初始人数
E_0 = 0
# R_0为治愈者的初始人数
R_0 = 0
# S_0为易感者的初始人数
S_0 = N - I_0 - E_0 - R_0
# T为传播时间
T = 150

# INI为初始状态下的数组
INI = (S_0,E_0,I_0,R_0)


def funcSEIR(inivalue,_):
Y = np.zeros(4)
X = inivalue
# 易感个体变化
Y[0] = - (beta * X[0] * X[2]) / N
# 潜伏个体变化
Y[1] = (beta * X[0] * X[2]) / N - X[1] / Te
# 感染个体变化
Y[2] = X[1] / Te - gamma * X[2]
# 治愈个体变化
Y[3] = gamma * X[2]
return Y


T_range = np.arange(0,T + 1)

RES = spi.odeint(funcSEIR,INI,T_range)

plt.plot(RES[:,0],color = 'darkblue',label = 'Susceptible',marker = '.')
plt.plot(RES[:,1],color = 'orange',label = 'Exposed',marker = '.')
plt.plot(RES[:,2],color = 'red',label = 'Infection',marker = '.')
plt.plot(RES[:,3],color = 'green',label = 'Recovery',marker = '.')

plt.title('SEIR Model')
plt.legend()
plt.xlabel('Day')
plt.ylabel('Number')
plt.show()

模型六:SEIRS-Model

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
import scipy.integrate as spi
import numpy as np
import matplotlib.pyplot as plt

# N为人群总数
N = 10000
# β为传染率系数
beta = 0.6
# gamma为恢复率系数
gamma = 0.1
# Ts为抗体持续时间
Ts = 7
# Te为疾病潜伏期
Te = 14
# I_0为感染者的初始人数
I_0 = 1
# E_0为潜伏者的初始人数
E_0 = 0
# R_0为治愈者的初始人数
R_0 = 0
# S_0为易感者的初始人数
S_0 = N - I_0 - E_0 - R_0
# T为传播时间
T = 150

# INI为初始状态下的数组
INI = (S_0,E_0,I_0,R_0)


def funcSEIRS(inivalue,_):
Y = np.zeros(4)
X = inivalue
# 易感个体变化
Y[0] = - (beta * X[0] * X[2]) / N + X[3] / Ts
# 潜伏个体变化
Y[1] = (beta * X[0] * X[2]) / N - X[1] / Te
# 感染个体变化
Y[2] = X[1] / Te - gamma * X[2]
# 治愈个体变化
Y[3] = gamma * X[2] - X[3] / Ts
return Y


T_range = np.arange(0,T + 1)

RES = spi.odeint(funcSEIRS,INI,T_range)

plt.plot(RES[:,0],color = 'darkblue',label = 'Susceptible',marker = '.')
plt.plot(RES[:,1],color = 'orange',label = 'Exposed',marker = '.')
plt.plot(RES[:,2],color = 'red',label = 'Infection',marker = '.')
plt.plot(RES[:,3],color = 'green',label = 'Recovery',marker = '.')

plt.title('SEIRS Model')
plt.legend()
plt.xlabel('Day')
plt.ylabel('Number')
plt.show()