写在前面的话
代码中的# > 表示的是输出结果
输入
- 使用input()函数
- 用法
str = input('请输入内容:')
print(str)
#>> 此时控制台上打印的一定是你输入的字符串
- 注意
- input函数输出的均是字符串,如需要接收其他类型的变量,就需要使用强制类型转换。
- 例如:将字符串转换为数字类型的变量
num = input('请输入数字:')
print(type(num))
# >> <class 'str'>
#此时可以看见,就算输入数字类型的变量,num接收的值依然是一个字符串类型
num = int(num) #强制转换为int类型
print(type(num))
# >> <class 'int'>
#此时控制台返回的是num变量的类型,可以看到,类型已经是int了
输出
- 使用print()函数
- 用法
#常用用法
print('Hello World!')
# 自定义用法
print(value,sep = ' ',end = '\n')
value表示需要打印的值
sep表示打印多个值之间的分隔符,默认是一个空格
end表示打印结束后的分隔符,默认是\n
变量、函数、类命名规则
- 可以使用数字、字母、下划线
- 不可以用数字开头
- 不可以使用关键字和保留字
- 推荐驼峰命名法
- 类名使用大驼峰,如MyFirst
- 变量、函数使用小驼峰,如myFirst
- 也可使用posix写法,如my_first
常用操作
- type()函数,查看当前变量类型
- id()函数,查看当前变量地址
- help()函数,查找帮助文档
变量进行赋值
- 单独赋值
a = 5
b = 'hh'
- 一起赋值
a , b = 5 , 'hh'
- 两个变量之间的互换
a , b = 5 , 10
#如果遇到JAVA程序员,会这样写
int temp = a
a = b
b = temp
#但是在python中,只需要进行简单交换即可
a , b = b , a
#通过这种方式就完成了两个变量值的交换
变量的类型
数字型(number)
- 整数(int)
- 二进制
- 八进制
- 十进制
- 十六进制
- 浮点小数(float)
- 一般采用的是科学计数法,以e表示10,e上面为次方
- 布尔值(boolean)
- True,可用数字表示,除了0以外的数字都为True
- False,可用数字0表示
- 复数(complex)
- 一般不会涉及到
字符串型(string)
- 用单引号,双引号或者是三引号进行表示
a = 'Jack'
b = "Jack"
c = "Bob,'haha',Jack"
print(c)
#其结果为Bob,'haha',Jack
- 格式化的两种方式
-
利用%
%s : 格式化字符串
%d : 格式化整数
-
n = '我是XX'
print('你好,%s'%n)
- 利用format()函数进行格式化
n = '我是XX'
print('你好,{0}'.format(n))
#还可以这么使用
age = 18
name = 'Bob'
print('我是{0},我今年{1}岁,你也{1}岁'.format(name,age))
- 字符串的转义
用"\"表示
最简单的一种方法就是字符串前面加上"r",表示的是原始字符:字符串中的所有转义字符都不进行转义
-
使用转义字符
\\ : 表示\
\n : 表示换行
\t : 表示制表符
\r : 表示回车
```
print(r'\\')
#打印出的为\\
print('\\\\')
#这样也可以打印出\\
```
-
常用函数
capitalize() : 将字符串的第一个字母大写,其他小写
strip() : 去掉字符串两边的空格
replace() : 替换字符串
lower() : 全部小写
upper() : 全部大写
encode() : 编码
decode() : 解码
s1 = ' abc ' s2 = 'aBc' s1.strip() # > abc s2.capitalize() # > Abc s2.lower() # > abc s2.upper() # > ABC s2.encode('utf-8') # > b'aBc' b'aBc'.decode('utf-8') # > aBc s2.replace('B','*') # > a*c
列表(list)
-
创建列表的方式
list = [1,2,3,4,5]
-
列表属于一个可迭代对象,可用于迭代
-
使用for对列表中的元素进行迭代
for item in list: print(item) #此时会输出 # > 1 # > 2 # > 3 # > 4 # > 5 #item表示的是列表中的元素
-
使用下角标进行操作,但是一般不会这么使用
for i in range(len(list)): print(list[i]) #len()函数表示测量某个迭代序列的长度,返回int #range()函数用于组成从0数字到给定值的序列
-
-
可使用成员判断符 in
print(1 in list) # > True
-
可使用 * ,表示一个列表重复N次,并组成一个新的列表
print(list * 5) # > [1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3, 4, 5]
-
可使用 + ,表示两个列表相加,组成一个新列表
print(list + [6.7,8]) # > [1, 2, 3, 4, 5, 6,7,8]
-
常用函数
del: 删除列表
len(): 长度
max()、min():最大、最小值
list() : 将可迭代的序列转化为list
append() : 在列表末尾添加元素
index() : 返回列表中某个元素的引索值
remove() : 移除列表中的某个元素
pop() :移除列表中引索值为XX的元素
insert() : 向列表中引索值为XX的位置添加一个元素
reverse() : 反向列表元素
copy() : 复制一份列表到新的列表中
list = [1,2,3,4,5] del list # > 删除list len(list) # > 5 max(list) # > 5 min(list) # > 1 list.append(6) # > [1,2,3,4,5,6] list.index(1) # > 0 list.remove(6) # > [1,2,3,4,5] list.pop(0) # > [2, 3, 4, 5] list.insert(0,7) # > [7, 1, 2, 3, 4, 5] list.reverse() # > [5, 4, 3, 2, 1] l = list.copy # > [1,2,3,4,5]
-
列表生成式
可操作列表中的单个元素,并迭代整个列表组成一个新列表
-
使用方法:list2 = [i for i in list1 if ....]
list1 = [1,2,3,4,5,6,7,8,9] #下面我们使用列表生成式挑选偶数组成一个新的列表 list2 = [i for i in list1 if i % 2 == 0] # > [2, 4, 6, 8] # i for i in list1 表示遍历list1列表中的元素 # if i % 2 == 0 表示挑选出所有能整除2的元素
-
列表生成式的嵌套
list1 = [1,2] list2 = [3,4] list3 = [x+y for x in list1 for y in list2] # > [4, 5, 5, 6] #可以看出,新的列表的结果是list1中的每个元素加上list2中每个元素的结果 #组成是[list1[0]+list2[0],list1[0]+list2[1],list1[1]+list2[0],list1[1]+list2[1]]
元组(tuple)
-
创建元组的方式
tuple = (1,2,3)
元组具有list中的部分属性,但只可以进行读取操作,不能进行写入操作
-
可以将一个list列表转化为一个元组tuple
t = tuple(list)
字典(dict)
-
字典的创建方式
dict = {'jack':24,'bob':18} #或者是 d = dict(jack = 24,bob = 18)
字典是一组键——值对应序列,它是一个无序的对象。
-
遍历字典
-
使用item()方法遍历
for key,value in dict.items(): print(key,value) #jack 24 #bob 18
-
直接遍历
for i in dict: print(i,dict[i]) #jack 24 #bob 18
-
注意,一旦使用dict[key]查找value值,当key的值不存在时,就会报错,推荐使用get()方法
-
常用方法
get() : 查找对应键的值,当键不存在时不会报错
keys() : 将字典所有key组成一个可迭代的序列
values() : 将字典所有value组成一个可迭代的序列
items() : 将字典的每组key和value组成一个元组,并将所有组组成一个序列,序列的类型是dict_items
fromkeys() : 将一个列表作为key值传入,并指定默认的value值
dict.get('jack') # > 24 dict.keys() # > dict_keys(['jack', 'bob']) dict.values() # > dict_values([24, 18]) dict.items() # > dict_items([('jack', 24), ('bob', 18)]) list = ['jack', 'bob'] d = dict.fromkeys(list , 18) # > {'jack': 18, 'bob': 18}
集合(set)
-
定义方式
list = [1,2,3,4,4] s = set(list) # > {1, 2, 3, 4} #或者是 s = {1,2,3,4}
注意:不要与dict字典搞混淆,字典也是{}定义的,但是却是由key-value组成的一对
set集合中数据也是无序的,且不包含重复元素,必须是可哈希数据
-
常用函数
add() : 添加单个数据
update : 添加可迭代数据
remove() : 移除集合中某个值,一旦值不存在,便会报错
discard() : 移除集合中某个值,就算值不存在,也不会报错
clear() : 清空集合数据
intersection() : 求交集
difference() : 求差集
union() : 求并集
issubset() : 检查一个集合是否是另一个集合的子集
issuperset() : 检查一个集合是否是另一集合的超集
s = {1,2,3,4,5} s1 = {4,5,6,7,8} list = [5,6,7,8,9] s.add(6) # > {1,2,3,4,5,6} s.update() # > {1, 2, 3, 4, 5, 6, 7, 8, 9} s.remove(5) # > {1,2,3,4} s.discard(5) # > {1,2,3,4} s.clear() s.intersection(s1) # > {4, 5} s.difference(s1) # > {1, 2, 3} s.union(s1) # > {1, 2, 3, 4, 5, 6, 7, 8}
运算符
算数运算符
-
加法运算符 +
-
在数字中表示两个数字相加
print(2+5) # > 7
-
在字符串中表示两个字符串相连
print('hello'+'world') # > helloworld
-
在列表中表示两个列表首尾相连
l1 = [1,2,3] l2 = [3,4,5] l = l1 + l2 # > [1,2,3,3,4,5]
-
-
减法运算符 -
print(5 - 3) # > 2
-
乘法运算符 *
-
当对象为数字型时,执行乘法运算
print(5 * 2) # > 10
-
当对象为非数字型时,打印N次,并组成一个新对象
print('a'*3) # > aaa print([1,2,3] * 2) # > [1, 2, 3, 1, 2, 3]
-
-
除法运算符 /
print(6 / 2) # > 3
-
取余运算符 %
- 计算两个数字的余数
print(3 % 2) # > 1
- 计算两个数字的余数
-
整除运算符 //
-
计算两个数整除的数字,不会保留小数点
print( 3 // 2 ) # > 1
-
-
幂运算符 **
-
次方运算
print(2 ** 3) # > 8
-
比较运算符
注意:返回的都为布尔值类型
-
等于 ==
print(1 == 1) # > True
-
不等于 !=
print(1 != 1) # > False
-
小于 <
print(1 < 1) # > False
-
小于等于 <=
print(1 <= 1) # > True
-
大于 >
print(1 > 1) # > False
-
大于等于 >=
print(1 >= 1) # > True
赋值运算符
- 等于 =
逻辑运算符
- and 与
print(True and False) # > False
- or 或
print(True and False) # > True
- not 非
s = 'abc' print('d' not in s) # > True
成员运算符
注意:成员运算符需要判断的对象是一个可以迭代的类型
-
in 在里面
print('a' in 'abc') # > True
-
not in 不在里面
print('a' not in 'abc') # > False
身份运算符
- 注意,身份运算符是比较两个对象内存地址是否相同
- is 是
- not is 不是
运算符的优先级
- 永远记住,括号里面的最先开始运算,具有最高优先级
程序结构
顺序结构
- 按照代码编写的方式逐行进行
分支、判断结构
- if...else语句
a = 10 b = 5 if a > b: print(a,'>',b) else: print(a,'<',b) # > 10 > 5
- if..elif..else语句
- 注意,以上代码并不严谨,缺少相等的时候
a = 10 b = 10 if a > b: print(a,'>',b) elif: print(a,'=',b) else: print(a,'<',b) # > 10 = 10
循环结构
- 使用for进行循环(知道具体循环次数的时候)
s = 'abcdefg' for i in s: print(i) # > a # > b # > c # > d # > e # > f # > g
- 使用while进行循环(不知道具体循环次数)
- while
n = 0 while n < 10: print(n) n = n + 1 # > 0 # > 1 # > 2 # > 3 # > 4 # > 5 # > 6 # > 7 # > 8 # > 9
- while
- break 跳出整个循环
- 当需要跳出整个循环的时候可以用break
n = 1 while n < 10: print(n) n = n + 1 if n == 3: break # > 1 # > 2
- 当需要跳出整个循环的时候可以用break
- continue 结束本次循环
- 当某些条件时,跳过本次循环
n = 0 while n < 10: n = n + 1 if n == 3: continue print(n) # > 1 # > 2 # > 4 # > 5 # > 6 # > 7 # > 8 # > 9 # > 10
- 当某些条件时,跳过本次循环
函数
定义函数
-
使用def进行定义,后面跟着方法名和()
def func(): print('hello')
调用,传入函数
- 调用函数时,用函数名加括号
#调用函数 func() # > hello
- 传参时,只用函数名
#如tk框架中绑定按钮事件 button = Button(text = '按钮' ,command = func) #如使用help函数 print(help(print)) # > Help on built-in function print in module builtins: # > print(...) # > print(value, ..., sep=' ', end='\n', file=sys.stdout, flush=False) # > Prints the values to a stream, or to sys.stdout by default. # > Optional keyword arguments: # > file: a file-like object (stream); defaults to the current sys.stdout. # > sep: string inserted between values, default a space. # > end: string appended after the last value, default a newline. # > flush: whether to forcibly flush the stream. # > None
函数的参数
-
位置参数
- 根据定义函数时参数的位置,自动进行参数传递,此时,必须严格按照参数的顺序进行传参
def func(name,age): print('my name is {0},I am {1} years old.'.format(name,age)) func('jack',24) # > my name is jack,I am 24 years old. # 此时传递参数的顺序必须是name,age,否则参数就会传递不正确,严重时,会报错
- 根据定义函数时参数的位置,自动进行参数传递,此时,必须严格按照参数的顺序进行传参
-
关键字参数
- 根据指定的关键字进行传递参数,传参的顺序是否正确,不会影响函数的参数传递
def func(name,age): print('my name is {0},I am {1} years old.'.format(name,age)) func(age = 24,name = 'jack') # > my name is jack,I am 24 years old.
- 根据指定的关键字进行传递参数,传参的顺序是否正确,不会影响函数的参数传递
-
默认参数
- 在定义函数时,就传递默认值,被传递默认值的参数如果不进行再次传参,就会使用默认值
def func(name,age = 0): print('my name is {0},I am {1} years old.'.format(name,age)) func('jack') # > my name is jack,I am 0 years old. func('jacl',24) # > my name is jack,I am 24 years old.
- 在定义函数时,就传递默认值,被传递默认值的参数如果不进行再次传参,就会使用默认值
可变参数
-
解包:是将可迭代的对象变为单个元素
-
明确一个概念,可迭代的对象可以进行解包操作,而且可以不使用解包的操作符号
list = [1,2,3] a , b , c = list # > 1 # > 2 # > 3 #可以看出,对于一个知道长度的可迭代对象我们可以用赋值的方式将其解包
-
使用*对可迭代的对象进行解包
#但如果对于一个我们不知道长度的列表如何进行解包呢? #这就是*的用法 list = [1,2,3,4] print(*list) # > 1 2 3 4 #可以看出,此时列表list已经被用*解包为单个元素
-
使用**对字典进行解包
#首先我们使用*对dict解包,看能得到什么? dict = {'jack':24 , 'bob':18} print(*dict) # > jack bob # 结论:使用*进行解包时,我们只是得到了字典的key值 # 那我们试试**吧 print(**dict) # > TypeError: 'jack' is an invalid keyword argument for this function # 百度后得知print()函数不支持打印**对象 # 那就试试打印出key对应的值 print('{jack},{bob}'.format(**dict)) # > 24,18 #参考网上资料,**dict解包之后应该是这样的 **dict = Jack = 24 , bob = 18 #现在你想到了什么?
-
-
压包,将几个不同的可迭代对象的单个元素按顺序用元组的形式结合成一个列表
- 使用zip()函数进行压包
list1 = [1,2,3] list2 = ['a','b','c'] list3 = ['one','two','three'] for i in zip(list1,list2,list3): print(i) # > (1, 'a', 'one') # > (2, 'b', 'two') # > (3, 'c', 'three') # 可以看出这里对每个列表的第一元素、第二个元素、第三个元素,都进行了组合,使之成为了了一个新的元组 a = zip(list1 ,list2 ,list3 ) print(list(a)) # > [(1, 'a', 'one'), (2, 'b', 'two'), (3, 'c', 'three')] #可以看出,将三个序列压包后,每个序列中下标相同的元素,重新组成了一个元组,而这些元组都构成了一个列表
- 使用zip()函数进行压包
-
可变参数函数
- 了解了*解包之后我们就可以明白函数的可变参数是怎么回事了
- 定义一个可变参数的函数
#注意:这里args可以是任何变量,但为了程序的可读性,我们需要遵循传统,写上args,让其他人一看就知道这里应该是可变参数 def func(*args): print(args) func('jack','hello',28,41) # >('jack', 'hello', 28, 41) #可以看出我们不论写多少参数,函数就会接收多少个参数,看到这里你也许会说,这并没有什么用啊? #是的,但是你想想如果我们需要将一个列表的元素都传入函数中去的时候要怎么办? #总不能用list[0],list[1]...一个一个进行传参吧? list = ['name','age','sex','birth'] func(*list) # > ('name', 'age', 'sex', 'birth') #可以看出,*list确实是将单个元素传入了函数中
- 可变参数的关键字函数
#刚才对字典解包后得出**dict = Jack = 24 , bob = 18 #仔细一想,如果把**dict当成参数,这不就是一个关键字参数嘛
- 定义一个可变参数的关键字函数
def func(**kwargs): print('我的名字是{0},我今年{1},我是{2},我的生日是{3}'.format(kwargs.get('name'),kwargs.get('age'),kwargs.get('sex'),kwargs.get('birth'))) func(name = 'jack',age = 24 ,sex = 'male',birth = '1980-10-10') # > 我的名字是jack,我今年24,我是male,我的生日是1980-10-10 #定义一个字典传入函数中 dict = {'name':'jack','age':24} func(**dict) # > 我的名字是jack,我今年24,我是None,我的生日是None # 可以看出,即使有些参数是不填的函数也可以正常运行,这是不可变参数函数所不能达到的效果
-
参数混用
- 需要遵照函数(位置函数,关键字函数,可变参数函数,可变参关键字函数)的顺序进行
def func(name , age = 24 , *args , **kwargs): pass
- 需要遵照函数(位置函数,关键字函数,可变参数函数,可变参关键字函数)的顺序进行
函数的返回值
- 所有函数都有返回值,表示函数调用已经结束
- 定义函数返回值,使用return
def func(a): return a+10 b = func(1) # > 11
- 即使没有定义return,解释器也会自动加上 return None
函数文档
当使用help()函数时,显示的帮助文档
也可以使用doc调用文档
当你在不知道怎么使用函数或者是类时,都可以调用帮助文档,里面会给你详细说明各种用法
-
自定义函数帮助文档,在开头加上''' '''即可,如果使用的ide,会自动给你加上其他参数
def func(x,y): ''' 这个函数什么也不做 :param x: 传参x :param y: 传参y :return: None ''' pass print(help(func)) # > Help on function func in module __main__: # > func(x, y) # > 这个函数什么也不做 # > :param x: 传参x # > :param y: 传参y # > :return: None # > None print(func.__doc__) # > 这个函数什么也不做 # > :param x: 传参x # > :param y: 传参y # > :return: None
高阶函数
指该函数的参数中传入另一个函数
-
map()函数,映射操作,将传入的函数作用于可迭代对象的每个元素上,返回一个可以迭代的map对象
list = [1,2,3,4] def func(x): return x+1 a = map(func,list) for i in a: print(i) # > 2 # > 3 # > 4 # > 5
-
reduece()函数,归并操作,将列表的两个值经过函数计算后与列表中的第三个值再次进行相同计算,直到列表元素完成循环。
-
简单的用公式表达就是这样,f(f(f(x1,x2),x3),x4)....
#计算列表中所有元素的乘积 from functools import reduce def func(x,y): return x*y list = [2,3,4] print(reduce(func,list)) # > 24
-
-
filter()函数,过滤操作,用于筛选列表中的元素
#筛选出列表中的偶数 def func(x): if x % 2 == 0: return True else: return False list = [2,3,4] for i in filter(func,list): print(i) # > 2 # > 4
-
sorted()函数,排序操作,可以使用自定义的函数进行排序
-
默认排序
list = [5,4,3,9,8] l = sorted(list) print(l) # > [3, 4, 5, 8, 9]
-
关键字排序
关键字, key = 函数名
反向排序,传入reverse = True
#将列表的元素按绝对值大小进行排序并倒序 list = [5,-11,6,9,1] l = sorted(list, key=abs,reverse = True) print(l) # > [-11, 9, 6, 5, 1]
-
-
zip()函数,压缩操作,将多个传入的列表每个下标相同的元素组成一个新的元组,再将这些元组组成一个可迭代的zip对象,返回zip对象
- 注意 : 如果传入列表的长度不一致,那么zip操作只会取最短的那个列表的长度,组成zip对象
l1 = [1,2,3,4] l2 = ['a','b','c','d'] l3 = ['one','two','three','four'] l = zip(l1,l2,l3) for i in l: print(i) # > (1, 'a', 'one') # > (2, 'b', 'two') # > (3, 'c', 'three') # > (4, 'd', 'four') # 如果用列表生成式会怎么样? list = [i for i in l] print(list) # > [(1, 'a', 'one'), (2, 'b', 'two'), (3, 'c', 'three'), (4, 'd', 'four')] #长度不一致时 l4 = ['jack','bob'] l5 = zip(l1,l4) list = [i for i in l5] print(list) # > [(1, 'jack'), (2, 'bob')]
-
enumerate()函数,将列表中每个引索值和其对应的元素组成一个新的元组,再将这些元组组成一个enumerate对象,返回enumerate对象
l = ['a','b','c','d'] em = enumerate(l) list = [i for i in em] print(list) # > [(0, 'a'), (1, 'b'), (2, 'c'), (3, 'd')]
返回函数
-
将函数作为返回结果
def hello(): def say(): print('hello') return say f = hello() print(f) # > <function hello.<locals>.say at 0x00000233A6EF8A60> f() # > hello
-
闭包,当一个函数定义在另一个函数的内部时,使用外部函数的变量或者参数,当这个内部函数被当做返回值时,外部函数的所有参数和变量都保存在返回的函数中时,就叫做闭包
def hello(n): def say(): print(n) return say f = hello('hello') f() # > hello
-
注意:在返回函数中不能引用循环变量,如果确实需要引用循环变量时,需要在内部函数外面再包裹一层函数,该函数以参数的形式接受循环变量,内部函数就可以安全使用循环变量
def func(): list = [] for i in range(1,4): def funb(): return i * i list.append(funb) return list list = func() print(list) # > [<function func.<locals>.funb at 0x000001F1CE5A8A60>, <function func.<locals>.funb at 0x000001F1CE5A8AE8>, <function func.<locals>.funb at 0x000001F1CE5A8B70>] for i in list: print(i()) # > 9 # > 9 # > 9 #此时可以看出,循环变量并没有正确的引入返回函数中去. #正确的写法应该是使用funb将funa包裹,然后通过funb 返回fana 的返回值 def func(): def funb(i): def funa(): return i * i return funa list = [] for i in range(1,4): list.append(funb(i)) return list list = func() print(list) # > [<function func.<locals>.funb.<locals>.funa at 0x000001E2944C8AE8>, <function func.<locals>.funb.<locals>.funa at 0x000001E2944C8B70>, <function func.<locals>.funb.<locals>.funa at 0x000001E2944C8BF8>] for i in list: print(i()) # > 1 # > 4 # > 9
匿名函数
- lambda,用于自定义一个函数,此函数没有名称,比如可以向高级函数中传递这样一个匿名函数,而不必去定义它
#将每个元素的前面加上hello
list = ['jack','bob','luce','alice']
l = map(lambda x:'hello'+x,list)
for i in l:
print(i)
# > hellojack
# > hellobob
# > helloluce
# > helloalice
偏函数
偏函数,将一个函数中的参数固定住,然后创建一个新的函数,这就是偏函数
定义偏函数:
偏函数名 = functools.partial(原函数,固定值)
#假设,我们有'jack','bob','luce','alice',这4个人,他们的年龄都是24岁
# 此时有一个函数say,是为了介绍自己,需要传入name和age两个参数
# 希望在不改变say函数的情况下,简化操作,此时我们就可以使用偏函数了
import functools
list = ['jack','bob','luce','alice']
def say(name,age):
print('My name is {0},age is {1}'.format(name,age))
say24 = functools.partial(say,age = 24)
for person in list:
say24(person)
# > My name is jack,age is 24
# > My name is bob,age is 24
# > My name is luce,age is 24
# > My name is alice,age is 24
递归函数
- 函数内部自己调用自己的情况叫做递归函数
#遍历一个盘符下的所有文件(包括子文件夹、文件)
import os
def getFile(path):
try:
filelist = os.listdir(path) #得到该文件夹下的所有文件
for file in filelist:
file = os.path.join(path,file) #将文件名和路径结合起来
if os.path.isdir(file):
getFile(file) #在这里如果判断一个文件是文件夹,那么就会再次调用自己
else:
print(file)
except:
print('出错,跳过')
getFile(r'd:/')
装饰器
对原函数进行扩展功能但是不改变原函数
使用@+装饰器名进行装饰,具有固定格式
-
使用@装饰时,执行函数时,相当于执行行了装饰器函数(原函数)
#首先我们思考一下,接收一个无参数的函数,并且扩展功能后返回这个函数 import time def printTime(f): print('Time: ', time.strftime('%Y-%m-%d %H:%M:%S', time.localtime())) return f def hello(): print('hello') print(printTime(hello)()) # > Time: 2018-08-09 08:45:25 # > hello # > None # 这时,我们可以看到定义了一个printTime函数,并且执行打印时间后返回原函数,这可以说是一个最简单的装饰器了。 # 为什么会返回一个None?我认为是hello函数后系统自己添加了一个return None后导致的,我们将hello函数手动添加return 5后试试,结果返回的是5 # 如果我们需要接收一个带参数的函数呢? import time def printTime(f): def wrapper(*args,**kwargs): print('Time: ',time.strftime('%Y-%m-%d %H:%M:%S',time.localtime())) return f(*args,**kwargs) return wrapper @printTime def hello(): print('hello') hello() # > Time: 2018-08-08 20:21:26 # > hello # printTime函数接收一个函数,返回一个wrapper函数 # wrapper函数接收可变参数,然后将这些参数赋值给printTime接收到的函数,最后再返回这个函数。
高级特性
切片 slice
切片操作是对一个可迭代的对象进行切割,并组成一个新的迭代对象
方式:
切片对象[开始引索:结束引索:步长]
注意:切片操作中包含开始引索,不包含结束引索,如果开始引索是0,可以省略,如果步长是1,也可以省略
开始引索也可以是负数,当为负数时,则从最后开始计算
list = [1,2,3,4,5,6,7,8,9,10]
#挑选出所有奇数
print(list[:9:2])
# > [1, 3, 5, 7, 9]
#取前4个数
print(list[:4])
# > [1, 2, 3, 4]
#取后2个数
print(list[-2:])
# > [9, 10]
#取3~7
print(list[2:7])
# > [3, 4, 5, 6, 7]
迭代
- 使用for进行迭代
列表生成式
- 见list列表中
generator生成器
- 存疑
类
思想
- OO面向对象
- OOA面向对象的分析
- OOD面向对象的设计
- OOI面向对象的实现
- OOP面向对象的编程
类和对象
-
类
类是一个抽象的名词,是共性事物
类有成员属性,是表明事物的特征,可以被读取,写入,删除
类有成员函数,表明事物的功能或者动作
-
对象
对象是一个具体的名词,是一个具体事物
对象是类的实例化
说明:我们都是人,这就是类;但我们有男人,也有女人,每个人高矮胖瘦都不一样,我们每个人就是对象,是类的实例化
-
类和对象的对成员访问顺序
首先访问对象中的成员
对象没有成员的会访问类中的成员
类中没有成员会访问父类中的成员
依次向上递推
定义类
用
class 类名(继承类):
定义类中只允许出现成员属性和成员方法
定义类之后,会在内存中专门划分一块地方储存类和类的成员;当实例化类之后,继承的成员会指向内存中的这块地方
class Person(object):
#定义属性
name = 'NoName'
age = 0
#定义函数
def say(self):
print('say')
def eat(self):
print('eat')
def sleep():
print('sleep')
-
self
self不是一个关键字,可以是任何一个变量代替,表示的是当前对象本身,当调用方法时,对象会将自己传入到方法中去
-
有self的函数称为非绑定方法,可以通过对象进行访问
-
使用实例化对象访问
p = Person() p.say() # > say
-
使用
类().方法
访问Person().say() # > say
-
使用
类.方法(类())
访问Person.say(Person()) # > say
实质是都是需要实例化一个对象,并传入非绑定方法中去
-
-
无self的函数称为绑定方法,只能通过类进行访问
p = Person() p.sleep # > TypeError: sleep() takes 0 positional arguments but 1 was given #可以看出,绑定方法不可以用对象进行访问,因为方法中没有定义传入对象本身的参数,所以出现了sleep函数不需要参数,但传入了一个参数,传入的参数就是对象本身 Person.sleep() # > sleep
- 如果一定要使用对象访问,可用
对象.__class__.方法
访问p.__class__.sleep() # > sleep
- 如果一定要使用对象访问,可用
-
super
super也不是一个关键字,但他是一个类
作用是获取MRO列表中的第一个类
可以使用
类.__mro__
获取类的MRO列表在单继承中,表示父类
在多继承中,代表了了MRO列表中的第一个元素代表的类
面向对象
-
封装,对类的成员访问进行限制
-
public 公开
- 公开成员,都可以进行访问
-
protected 受保护
受保护成员,只能在类、子类、对象中进行访问
定义:使用
_成员
进行定义
class Person(): _name = 'NoName' _age = 24 class Student(Person): pass s = Student() print(s._name,s._age) # > NoName 24 #可以看出,子类和子类的对象访问都是没有问题的
-
private 私有
私有成员,只能在当前类中进行访问
注意:子类不继承私有成员
定义:使用
__成员
进行定义
class Person(): __name = 'NoName' __age = 24 class Student(Person): pass p = Person() print(p.__name, p.__age) # > AttributeError: 'Person' object has no attribute '__name' s = Student() print(s.__name, s.__age) # > AttributeError: 'Student' object has no attribute '__name' #可以看出,子类并没有继承父类中的私有属性,而且对象也不允许使用私有成员
-
-
继承
-
类成员扩充,当子类继承了父类时,就拥有了父类除私有成员以外的所有其他成员,这相当于将子类成员指向父类成员的地址,同时,还可以继续添加相应的属性和函数
class Person(): def talk(self): print('talk') class Student(Person): def study(self): print('study') s = Student() s.talk() # > talk s.study() # > study
-
子类扩充父类函数方法
-
使用self,其中self指的是子类本身
class Person(): def talk(self): print('talk') class Student(Person): def talk(self): Person.talk(self) print('扩充函数') s = Student() s.talk() # > talk # > 扩充函数
-
使用super,指的是父类
class Person(): def talk(self): print('talk') class Student(Person): def talk(self): super().talk() print('扩充函数') s = Student() s.talk() # > talk # > 扩充函数
-
-
MRO钻石继承\菱形继承(继承顺序)
子类永远在父类前面
根据括号内继承的顺序存放
继承的多个类中,有共同的父类,子类只会继承括号中第一个父类的父类
-
单继承
单继承隐患少,但不利于扩展功能
子类如果没有构造函数,则会查找父类的构造函数,如果都没有,那么就一直向上查找。按照MRO顺序
class Person(): def talk(self): print('talk') class Student(Person): def talk(self): super().talk() print('asdasd') print(Student.mro()) # > [<class '__main__.Student'>, <class '__main__.Person'>, <class 'object'>] #可以看出,Student类的MRO顺序为Student、Person、object
-
多继承
扩展类的功能方便但隐患多
继承方式按照MRO顺序
class Animal(): def sleep(self): print('sleep') class Person(): def talk(self): print('talk') class Student(Person,Animal): pass s = Student() s.talk() s.sleep() # > talk # > sleep print(Student.mro()) #[<class '__main__.Student'>, <class '__main__.Person'>, <class '__main__.Animal'>, <class 'object'>] #可以看出继承顺序遵照MRO顺序
-
Mixin设计模式
我认为主要是为了避免继承关系的混乱,为了让代码可以进行更改的维护,已经实现类中方便的功能增减
Mixin类中必须定义单一功能
子类不继承Mixin类,只是缺少某项功能,但不影响子类的正常使用
Mxin功能不依赖子类进行实现
class TalkMixin(): def talk(self): print('talk') class EatMixin(): def eat(self): print('eat') class SleepMixin(): def sleep(self): print('sleep') class Person(TalkMixin,EatMixin,SleepMixin): pass p = Person() p.talk() p.eat() p.sleep() # >talk # >eat # >sleep
-
-
多态
同一对象在不同状态的不同功能
注意:在Python中没有对应的语法
类的实例化
- 方法:
变量 = 类名()
访问类
检查类中的所有成员,使用
类名.__dict__
访问类的成员属性,使用
类名.属性
访问类的成员函数,使用
类名.函数名()
类的函数
-
魔法函数(不需要自己进行调用,但满足某些特定条件时,会自动调用)
-
__init__
构造方法在实例化类的时候可以向其中传递参数
在将类实例化对象时,会自动调用
此函数也可以进行扩充
class Person(): def __init__(self,name,age): self.name = name self.age = age def say(self): print('My name is {0},I am {1} years old.'.format(self.name,self.age)) p = Person('jack',24) p.say() # > My name is jack,I am 24 years old. p.name = 'bob' p.age = 18 p.say() # > My name is bob,I am 18 years old.
-
__call__
方法- 将对象当做函数执行时调用
class Person(): def __init__(self,name,age): self.name = name self.age = age def __call__(self, *args, **kwargs): print('为什么要执行我?') p = Person('jack',24) p() # > 为什么要执行我?
-
__str__
方法- 将对象打印时,输出的方法
class Person(): def __str__(self): return '打印了一个Person类的实例' p = Person() print(p) # > 打印了一个Person类的实例
-
-
实例方法
也就是非绑定方法,只有当类实例化后,才可以使用的方法
详情参照self-非绑定方法
-
静态方法
采用@staticmethod装饰
里面没有self参数
-
注意,这里和绑定方法有区别
绑定方法,只能通过
类名.方法名()
调用,如果采用对象调用就会报错静态方法,既可以通过类来调用,也可以通过对象调用
class Person(): @staticmethod def say(): print('say') p = Person() Person.say() # > say p.say() # > say
-
类方法
采用@classmethod装饰
不需要实例化就可以使用
方法中传入的是cls,也就是类对象
class Person(): @classmethod def say(cls): print('say') p = Person() Person.say() p.say()
-
总结:无论是实例方法、静态方法、类方法都可以使用对象进行调用,但是实例方法必须实例化后才可以使用;静态方法、类方法可以直接用类名进行调用
-
注意:静态方法不是绑定方法
-
property
- 主要是将对函数的访问装饰起来变为对属性的访问
#例如,我们需要定义一个Person类,但是需要对Person类中传入的几个属性进行检查,那么这时候property就派上用场了 class Person(): _name = 'Noname' _age = 0 #此时,我们想对age的值进行检查,不允许小于0 #我们就可以定义一个property的装饰器,将函数伪装为变量进行访问
-
调用Property装饰器
@property def age(self): print('调用get') return self._name @age.setter def age(self,value): print('调用set') if value > 0: self._age = value else: print('不允许负值') @age.deleter def age(self): print('调用del') del (self._age) p = Person() p.age = -15 # > 调用get # > 不允许负值
-
调用property方法
属性 = property( fget, fset, fdel, doc)
def get_age(self): print('调用get_age') return self._age def set_age(self,value): print('调用set_age') if value > 0: self._age = value else: print('不允许是负值') def del_age(self): print('调用del_age') del self._age age = property(get_age,set_age,del_age) p = Person() p.age = -15 # > 调用get # > 不允许是负值
-
类的描述符
只要包含了
__get__
、__set__
、__del__
三个方法中的一个就是描述符描述符的作用,对类的属性进行限制操作
-
__get__(self,instance,owner)
方法和__set__(self, instance, value)
方法class Dscrip(): def __init__(self,property_name): self.property_name = property_name def __get__(self, instance, owner): print('调用了get方法') print('self:%s'%self) print('*'*20) print('instance:%s'%instance) print('*' * 20) print('owner:%s'%owner) print('-'*20) def __set__(self, instance, value): print('调用了set方法') print('self:%s' % self) print('*' * 20) print('instance:%s' % instance) print('*' * 20) print('value:%s'%value) print('-' * 20) class Person(): name = 'Noname' age = Dscrip('age') def __init__(self,name,age,sorce): self.name = name self.age = age self.sorce =sorce p = Person('jack',24,60) p.age print('p:%s'%p) # > 调用了set方法 # > self:<__main__.Dscrip object at 0x000001F71AD78EF0> # > ******************** # > instance:<__main__.Person object at 0x000001F71AD78F60> # > ******************** # > value:24 # > -------------------- # > 调用了get方法 # > self:<__main__.Dscrip object at 0x000001F71AD78EF0> # > ******************** # > instance:<__main__.Person object at 0x000001F71AD78F60> # > ******************** # > owner:<class '__main__.Person'> # > -------------------- # > p:<__main__.Person object at 0x000001F71AD78F60> #在这里我们我们打印了get、set方法中的self、instance、owner的值 #可以看出self 指的是一个Dscrip类,在Person类中,指的就是age变量 #instance 和我们打印对象p的地址一致,instance就是指的对象p #owner 是一个名为Person的类 #value 就是我们传入对象p中age的值
-
总结:
在get和set方法中,self指的就是我们定义的类描述符的实例对象,也就是类中的属性
在get和set方法中,instance指的是包含了该实例对象的实例化类对象
在set方法中,value就是传入该类对象的值
-
使用类的描述符进行多个类属性检查
#上面学习了如何使用property对属性进行检查 #现在如果我想要将Person类中新定义一个sorce,并且也进行检查,此时如果使用property就会显得异常繁杂 #这时候就可以使用类的描述符了 class Desc(): def __init__(self, propertyname): self.propertyname = propertyname def __get__(self, instance, owner): if instance is None: return self return instance.__dict__[self.propertyname] def __set__(self, instance, value): if value < 0: print('不允许是负值') else: instance.__dict__[self.propertyname] = value def __del__(self): del self.propertyname class Person(): age = Desc('age') sorce = Desc('sorce') def __init__(self, name, age, sorce): self.name = name self.age = age self.sorce = sorce p = Person('jack',18,60) p.sorce = -5 # > 不允许是负值 #这样就检查了两个属性age、sorce,从而避免了写多个property
-
类的常用函数
__dict__
: 获取类和对象中的所有成员属性hasattr
: 检测某类中是否有某属性issubclass
: 检测某类是否是另一个类的子类isinstance
: 检测某个对象是否是一个类的实例getattr
: 获取某个对象的某个属性值__getattribute__
:获取某个对象的某个属性值setattr
: 设置某个对象的某个属性值delattr
: 删除某个对象的某个属性-
__slots__
: 在类的开头定义一个slots的元组,实例化对象后,对象只能添加元组中定义的属性如果子类中没有定义slots,那么子类不存在slots限制
如果子类中定义了slots,那么子类中slots的限制是子类和父类元组相加的限制
属性和函数的动态绑定
-
对类的实例对象进行动态绑定
-
动态绑定属性,使用
对象.属性 = 值
绑定class Person(): pass p = Person() p.name = 'jack'
-
动态绑定函数,使用
对象.方法名 = types.MethodType(方法名,对象)
绑定import types class Person(): pass def say(): print('say') p = Person() p.say = types.MethodType(say, p)
注意:此时绑定的方法只对绑定的该实例对象有效,如果重新定义一个对象,此方法就失效了
-
-
对类进行动态绑定
-
类.方法名 = 方法名
绑定class Person(): pass def say(): print('say') Person.say = say Person.say()
-
类名.方法名 = types.MethodType(方法,类名)
绑定- 用法参照对象绑定
-
抽象类
定义抽象类是为了让其子类必须实现抽象类中的属性或者方法,对子类进行规范化,设定子类的标准
抽象类只能被继承使用,不可以进行实例化
定义抽象使用
class 抽象类名(metaclass = abc.ABCMeta)
定义抽象方法使用
@abc.abstractclassmethod
import abc
class Man(metaclass= abc.ABCMeta):
@abc.abstractmethod
def say(self):
pass
@abc.abstractmethod
def eat(self):
pass
class Person(Man):
'''
def say(self):
pass
def eat(self):
pass
'''
pass
p = Person()
# > TypeError: Can't instantiate abstract class Person with abstract methods eat, say
#如果子类中不定义抽象方法就会报错
包
-
包的结构
__init__.py
包的标志文件模块
子包
-
导入包
import 包名
import 包名.模块名
-
导入后可以使用
__init__.py
中定义的文件__init__.py
里如果定义了__all__
,就只能使用__all__
里面定义的内容__init__.py
里如果没有定义__all__
,就可以使用全部文件
-
模块
-
定义模块的规范
函数,单一功能
类
测试代码
if __name__ = 'main':
代表此模块作为主线程执行时就执行以下内容
-
导入模块
import 模块名
from 模块名 import 类名
import 模块名 as 别名
-
模块、包的查找路径
默认路径,可使用
sys.path
查看添加路径,
sys.path.append()
,也就是向列表中添加一个新元素
-
模块的加载顺序
内存中已经加载好的模块
Python中内置的模块
按顺序查找sys.path中的内容
-
异常
异常是一个类,可以进行处理和使用
-
常见异常错误类
AssertErro 断言语句失败
AttributeErro 尝试方问未知的对象属性
FloatingPointErro 浮点计算错误
ImportErro 导入模块失败
IndexErro 下标引索值超出序列的范围
KeyErro 字典中查找一个不存在的关键字
NameErro 尝试访问一个不存在的变量
RuntimeErro 运行错误
OSErro 操作系统产生异常
SynataxErro 语法错误
IndentationErro 缩进错误
TypeErro 不同类型间的无效操作
-
异常处理
try: 问题语句 except 异常类 as e: #将异常类实例化为e print(e) else: 不出错执行语句 finally: 无论是否出错都执行的语句
-
手动引发异常
raise 异常类
注意:推荐使用自定义异常,并且标注清楚。方便别人或者自己进行查看和快速定位
-
自定义异常类
自定义异常必须是系统异常的子类
抛出异常时的文字提示
异常发生的行数
常用模块
calendar 日历模块
calendar() 以字符串形式返回某年的日历
isleap() 判断某年是否是闰年
leapdays() 获取指定年份之间的闰年个数
month() 以字符串形式返回某年某月的日历
monthrange() 获取某年某月第一天是周几和某月的天数,返回的是一个元组
monthcalendar() 返回某年某月以二维列表组成的矩阵,其中每周组成一个列表
weekday() 获取某年某月某日是周几
import calendar
c = calendar.calendar(2018)
print(c)
2018
January February March
Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa Su
1 2 3 4 5 6 7 1 2 3 4 1 2 3 4
8 9 10 11 12 13 14 5 6 7 8 9 10 11 5 6 7 8 9 10 11
15 16 17 18 19 20 21 12 13 14 15 16 17 18 12 13 14 15 16 17 18
22 23 24 25 26 27 28 19 20 21 22 23 24 25 19 20 21 22 23 24 25
29 30 31 26 27 28 26 27 28 29 30 31
April May June
Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa Su
1 1 2 3 4 5 6 1 2 3
2 3 4 5 6 7 8 7 8 9 10 11 12 13 4 5 6 7 8 9 10
9 10 11 12 13 14 15 14 15 16 17 18 19 20 11 12 13 14 15 16 17
16 17 18 19 20 21 22 21 22 23 24 25 26 27 18 19 20 21 22 23 24
23 24 25 26 27 28 29 28 29 30 31 25 26 27 28 29 30
30
July August September
Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa Su
1 1 2 3 4 5 1 2
2 3 4 5 6 7 8 6 7 8 9 10 11 12 3 4 5 6 7 8 9
9 10 11 12 13 14 15 13 14 15 16 17 18 19 10 11 12 13 14 15 16
16 17 18 19 20 21 22 20 21 22 23 24 25 26 17 18 19 20 21 22 23
23 24 25 26 27 28 29 27 28 29 30 31 24 25 26 27 28 29 30
30 31
October November December
Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa Su
1 2 3 4 5 6 7 1 2 3 4 1 2
8 9 10 11 12 13 14 5 6 7 8 9 10 11 3 4 5 6 7 8 9
15 16 17 18 19 20 21 12 13 14 15 16 17 18 10 11 12 13 14 15 16
22 23 24 25 26 27 28 19 20 21 22 23 24 25 17 18 19 20 21 22 23
29 30 31 26 27 28 29 30 24 25 26 27 28 29 30
31
print(calendar.isleap(2018))
# > False
print(calendar.leapdays(1990,2018))
# > 7
m = calendar.month(2018,8)
print(m)
# > August 2018
Mo Tu We Th Fr Sa Su
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
week,day = calendar.monthrange(2018,8)
print(week)
print(day)
# > 2
# > 31
list = calendar.monthcalendar(2018,8)
for i in list:
print(i)
# > [0, 0, 1, 2, 3, 4, 5]
# > [6, 7, 8, 9, 10, 11, 12]
# > [13, 14, 15, 16, 17, 18, 19]
# > [20, 21, 22, 23, 24, 25, 26]
# > [27, 28, 29, 30, 31, 0, 0]
print(calendar.weekday(2018,8,7))
# > 1
time 时间模块
-
time() 指一个时间戳,是从1970年1月1日0时到至今所经历的秒数
import time print(time.time()) # > 1533604231.9603899
时间元组,是一个包含时间内容的元组,组成由(年,月,日,时,分,秒,周几,年的第几天,夏令时)
-
localtime() 获取当前时间的元组
import time print(time.localtime()) # > time.struct_time(tm_year=2018, tm_mon=8, tm_mday=7, tm_hour=9, tm_min=14, tm_sec=51, tm_wday=1, tm_yday=219, tm_isdst=0)
-
timezone 标准时区与当前时区相差的秒数
import time print(time.timezone) # > -28800 #中国是东8区,相比于国际时间,要快8小时,28800/60/60 = 8
-
asctime() 返回系统格式化后的时间
import time print(time.asctime()) # > Tue Aug 7 09:20:40 2018
-
ctime() 返回系统格式化后的时间
import time print(time.ctime) # > Tue Aug 7 09:21:37 2018
-
maketime() 将时间元组变成时间戳
import time t = time.localtime() s = time.mktime(t) print(s) # > 1533605096.0
-
sleep() 使程序休眠指定秒数后继续执行
import time for i in range(1,11): time.sleep(1) print(i) # > 1 # > 2 # > ....
-
strftime() 自定义格式化时间,作用于时间元组
%Y 完整年份
%m 月份
%d 每月中的第几天
%H 24小时制
%M 分钟 60
%S 秒 60
import time t = time.localtime() st = time.strftime('%Y-%m-%d %H:%M:%S',t) print(st) # > 2018-08-07 09:41:23
注意:
print(time.strftime('%Y年%m月%d日 %H时%M分%S秒',time.localtime()))
同样方法,在windows下报错
UnicodeEncodeError: 'locale' codec can't encode character '\u5e74' in position 2: Illegal byte sequence
在ubuntu下正常执行,返回
2018年08月06日 20时28分31秒
-
strptime() 将时间字符串格式化为时间元组
import time st = '2018-8-7 12:00:00' t = time.strptime(st,'%Y-%m-%d %H:%M:%S') print(t) # > time.struct_time(tm_year=2018, tm_mon=8, tm_mday=7, tm_hour=12, tm_min=0, tm_sec=0, tm_wday=1, tm_yday=219, tm_isdst=-1)
datetime 理想的日期和时间模块
-
date类 提供一个理想的日期
year 属性
month 属性
day 属性
today() 返回当前日期
fromtimestamp() 从一个时间戳中获得日期
strftime() 自定义格式化日期
import datetime dt = datetime.datetime(2018,8,7) #将2018.8.7实例化为datetime类 print(dt.year) # > 2018 print(dt.month) # > 8 print(dt.day) # > 7 dt = datetime.datetime.today() print(dt) # > 2018-08-07 13:49:04.943500 import time t = time.time() #获取当前时间的时间戳 dt = datetime.datetime.fromtimestamp(t) print(dt) # > 2018-08-07 13:51:44.969500 print(dt.strftime('%Y.%m.%d')) # > 2018.08.07
-
datetime类 提供理想的日期和时间
year 属性
month 属性
day 属性
hour 属性
minute 属性
second 属性
mircosecond 属性
today() 获取当前日期和时间
fromtimestamp() 从时间戳中获取日期和时间
-
timedelta类 提供日期和时间的计算
可以通过
days = 天数 , hours = 小时数 , minutes = 分钟数
进行实例化timedelta类 + date类或者datetime类 = date类或者datetime类
date类或者datetime类 - date类或者datetime类 = timedelta类
import datetime dt1 = datetime.date(2018,6,15) dt2 = datetime.date(2018,8,7) td = dt2 - dt1 print(td) # > 53 days, 0:00:00 print(type(td)) # > <class 'datetime.timedelta'> dt3 = datetime.datetime(2018,6,15,8,30,15) dt4 = datetime.datetime(2018,8,7,14,4) td = dt4 - dt3 print(td) # > 53 days, 5:33:45 td = datetime.timedelta(days=10,hours=15,minutes=30) dt = datetime.datetime.now() temp = dt + td print(type(temp)) # > <class 'datetime.datetime'> print(temp) # > 2018-08-18 05:37:51.464770
timeit 代码时间测试模块
-
timeit(smlt = 测试代码,setup = 运行环境 ,number = 重复次数)
测试代码时间#现在我们测试一下列表生成式和循环加入列表中的两个代码谁执行的快 import timeit st1 = '[i for i in range(1000)]' st2 = ''' list = [] for i in range(1000): list.append(i) ''' t1 = timeit.timeit(stmt = st1,number = 100000) t2 = timeit.timeit(stmt = st2,number = 100000) print(t1) # > 3.6470414876165864 print(t2) # > 9.188953135455993 #当测试带参数的函数的写法 import timeit c = ''' def func(num): for i in range(num): print('repeat for {}'.format(i)) ''' t = timeit.timeit(stmt='func(num)', setup = c + 'num=2', number=100000) print(t) # > repeat for 1 # > repeat for 0 # > .... # > 1.1565270608769294
os 系统操作模块
-
绝对路径 总是从根目录上开始
D:\360安全浏览器下载\chromeinstall-8u151.exe
-
相对路径 以当前所在工作目录开始
\360安全浏览器下载\chromeinstall-8u151.exe
-
值
os.curdir
当前目录,用‘ . ’表示os.pardir
父亲路径,用‘ .. ’表示-
os.sep
系统分隔符windows下用‘ \ ’
linux下用‘ / ’
-
os.linesep
系统的换行符号windows下用‘ \r\n ’
linux下用‘ \n ’
-
os.name
系统名称windows下用‘ nt ’
linux下用‘ posix ’
-
函数
getcwd()
返回当前工作目录chdir()
改变工作目录listdir()
显示路径下所有文件和文件夹,返回一个列表makedirs()
创建一个空文件夹rename()
重命名文件removedirs()
删除空文件夹system()
运行系统shell命令getenv()
获取当前系统环境变量值putenv()
添加环境变量exit()
退出当前程序
-
path类中的函数
abspath()
将路径转化为绝对路径basename()
获取路径中的文件名部分dirname()
返回路径中的文件夹部分split()
将路径切割为文件夹和文件名组成的元组splittext()
将路径切割为文件和后缀名组成的元组join()
将两个路径拼接成一个路径exists()
判断文件或文件夹是否存在isfile()
判断是否为文件isdir()
判断是否为文件夹getsize()
返回文件大小getatime()
返回文件或文件夹最后读取时间ghetmtime()
返回文件或文件夹最后修改时间
shutil 文件操作模块
copy()
复制文件,还可以将文件重命名copy2()
尽可能保留元数据的情况下复制文件copyfile()
讲一个文件里的内容复制到另一个文件中copytree()
复制文件夹里所有内容move()
移动文件或文件夹rmtree()
删除文件或文件夹下的所有文件make_archive(压缩后的名称,压缩类型,需要压缩的文件)
压缩文件,返回压缩文件的路径unpack_archive(压缩包路径,解包之后的路径)
解压文件
collections 模块
-
namedtuple类
是一个可以命名的元组类型
定义方式
类名 = collections.namedtuple(类类型,属性组成的列表)
import collections Point = collections.namedtuple('Point',['x','y']) p = Point(15,22) print(p) # > Point(x=15, y=22)
-
deque类
解决频繁插入、删除带来的效率问题
定义方式:
对象 = collections.deque(列表)
import collections d = collections.deque([1,2,3,4,5]) d.appendleft(10) # > deque([10, 1, 2, 3, 4, 5])
-
defaultdict类
当读取一个不存在的key时,返回一个默认值
-
定义方式:
对象 = collections.defaultdict(带有返回值的函数)
import collections def pr(): return '默认值' d = collections.defaultdict(pr) d['a'] = 1 d['b'] = 2 print(d) # > defaultdict(<function pr at 0x028AB6F0>, {'a': 1, 'b': 2}) print(d['dd']) # > 默认值
-
Counter类
- 主要是统计元素在迭代对象中出现的次数,以元素:次数的字典形式返回
import collections s = 'afjkasdhfklashgkjash' dict = collections.Counter(s) print(dict) # > Counter({'a': 4, 'k': 3, 's': 3, 'h': 3, 'f': 2, 'j': 2, 'd': 1, 'l': 1, 'g': 1}) import collections s = ['ss','jack','alice','bob','jack','jack','jack'] dict = collections.Counter(s) print(dict) # > Counter({'jack': 4, 'ss': 1, 'alice': 1, 'bob': 1})
pickle 持久化模块
-
dump(数据,句柄)
,将数据持久化到文件import pickle file = r'C:\Users\wudi.HAPMMAGNA\Desktop\b.txt' l = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] with open(file, 'wb') as f: pickle.dump(l,f) #将数据以二进制方式写入到b.txt文件中
-
load(句柄)
,将持久化的数据提取到程序中import pickle file = r'C:\Users\wudi.HAPMMAGNA\Desktop\b.txt' with open(file, 'rb') as f: l = pickle.load(f) print(l) # > [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] #将b.txt文件中储存的数据读取到程序中
shelve 持久化模块
将数据以字典的形式写入文件中
shelve只能允许一个句柄进行写入,但可以允许多个句柄进行读取
writeback = Ture
在关闭文件时,检查更改,并将数据写入文件
import shelve
file = r'C:\Users\wudi.HAPMMAGNA\Desktop\a'
sv = shelve.open(file,writeback= True)
sv['one'] = 1
sv['two'] = 2
sv['three'] = 3
sv.close()
# 此时桌面上出现了三个文件,为a.dat、a.bak、a.dir
- 读取文件
import shelve
file = r'C:\Users\wudi.HAPMMAGNA\Desktop\a'
sv = shelve.open(file)
print(sv['one'])
sv.close()
# > 1
IO文件模块
文件是一种长久保存信息数据的集合,保存在磁盘上
文件一旦被打开,就需要进行关闭操作,否则可能造成文件信息的丢失
-
常用操作
-
读写模式
r
只读,指针在文件开头w
只写,指针在文件开头x
创建文件并只进行写入,如果文件已经存在则会报错,指针在文件开头a
追加写入,指针在文件末尾b
以二进制方式t
以文本方式+
读写
-
open(文件地址,读写模式)
打开文件、close()
关闭文件file = r'C:\Users\wudi.HAPMMAGNA\Desktop\b.txt' f = open(file) f.close()
-
with
语句使用上下文管理协议技术
自动关闭已经不再使用的文件
推荐一定文件读写一定要使用with语句
file = r'C:\Users\wudi.HAPMMAGNA\Desktop\b.txt' with open(file,'wb') as f: pass
-
读取操作
read(字符数)
如果不使用字符数,那么read将会将所有字符读取出来,如果指定字符,就按照指定字符数进行读取readline()
按行读取文件内容list(句柄)
将文件所有内容读取出来,以列表形式进行保存使用
while 数据
进行读取,当数据不为空时,就会一直循环下去,如果数据为空时,当前循环就会停止
# b.txt中有以下内容 # 关雎 # 关关雎鸠,在河之洲。窈窕淑女,君子好逑。 # 参差荇菜,左右流之。窈窕淑女,寤寐求之。 # 求之不得,寤寐思服。悠哉悠哉,辗转反侧。 # 参差荇菜,左右采之。窈窕淑女,琴瑟友之。 # 参差荇菜,左右芼之。窈窕淑女,钟鼓乐之。 file = r'C:\Users\wudi.HAPMMAGNA\Desktop\b.txt' with open(file, 'r') as f: data = f.read(1024) while data: print(data) data = f.read(1024) # > 关雎 # > 关关雎鸠,在河之洲。窈窕淑女,君子好逑。 # > 参差荇菜,左右流之。窈窕淑女,寤寐求之。 # > 求之不得,寤寐思服。悠哉悠哉,辗转反侧。 # > 参差荇菜,左右采之。窈窕淑女,琴瑟友之。 # > 参差荇菜,左右芼之。窈窕淑女,钟鼓乐之。 with open(file, 'r') as f: data = f.readline() while data: print(data) data = f.readline() # > 关雎 # > # > 关关雎鸠,在河之洲。窈窕淑女,君子好逑。 # > # > 参差荇菜,左右流之。窈窕淑女,寤寐求之。 # > # > 求之不得,寤寐思服。悠哉悠哉,辗转反侧。 # > # > 参差荇菜,左右采之。窈窕淑女,琴瑟友之。 # > # > 参差荇菜,左右芼之。窈窕淑女,钟鼓乐之。 with open(file,'r') as f: list3 = list(f) for i in list3: print(i) # > 结果同readline() ,为什么会出现这种情况? 使用read时,中间没有隔行,而使用其他时就会有隔行呢? #我们来做个试验,将所有读取到的内容添加到列表,看他们在列表中是如何组成的 file = r'C:\Users\wudi.HAPMMAGNA\Desktop\b.txt' list1 = [] list2 = [] with open(file, 'r') as f: data = f.read(1024) while data: list1.append(data) data = f.read(1024) with open(file, 'r') as f: data = f.readline() while data: list2.append(data) data = f.readline() with open(file,'r') as f: list3 = list(f) print(list1) print('@'*30) print(list2) print('@'*30) print(list3) # > ['关雎\n关关雎鸠,在河之洲。窈窕淑女,君子好逑。\n参差荇菜,左右流之。窈窕淑女,寤寐求之。\n求之不得,寤寐思服。悠哉悠哉,辗转反侧。\n参差荇菜,左右采之。窈窕淑女,琴瑟友之。\n参差荇菜,左右芼之。窈窕淑女,钟鼓乐之。'] # > @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ # > ['关雎\n', '关关雎鸠,在河之洲。窈窕淑女,君子好逑。\n', '参差荇菜,左右流之。窈窕淑女,寤寐求之。\n', '求之不得,寤寐思服。悠哉悠哉,辗转反侧。\n', '参差荇菜,左右采之。窈窕淑女,琴瑟友之。\n', '参差荇菜,左右芼之。窈窕淑女,钟鼓乐之。'] # > @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ # > ['关雎\n', '关关雎鸠,在河之洲。窈窕淑女,君子好逑。\n', '参差荇菜,左右流之。窈窕淑女,寤寐求之。\n', '求之不得,寤寐思服。悠哉悠哉,辗转反侧。\n', '参差荇菜,左右采之。窈窕淑女,琴瑟友之。\n', '参差荇菜,左右芼之。窈窕淑女,钟鼓乐之。'] # 可以看出,read中整个列表只有一个元素,也就是遇到'\n'时会自动进行换行,而在其他函数中,列表中有多个元素,遇到n时会换一次行,而单次打印内容时也会换一次行。
-
写入操作
write(数据)
将单次数据写入文件中writelines(数据序列)
将一个可迭代的数据序列按顺序写入文件
data = '我是数据' with open(file,'w') as f: f.write(data) # > 文件中的内容是我是数据 data = ['关关雎鸠,在河之洲。窈窕淑女,君子好逑。\n', '参差荇菜,左右流之。窈窕淑女,寤寐求之。\n'] with open(file,'w') as f: f.writelines(data) # > 关关雎鸠,在河之洲。窈窕淑女,君子好逑。 # > 参差荇菜,左右流之。窈窕淑女,寤寐求之。
-
指针操作
seek(指针偏移量,参照物)
-
指针偏移量
以字节byte为单位,和read函数有巨大区别,read是字符为单位
不同编码中汉字所占的字节数有所不同
-
参照物
0 表示在文件开头
1 表示指针当前位置
2 表示在文件末尾
-
# b.txt中有以下内容:阿富汗喀什的法律可攻可受的复合弓 #注意:需要保存为utf-8编码 # 如果我们需要读取文件末尾两个字符,就需要 file = r'C:\Users\wudi.HAPMMAGNA\Desktop\b.txt' with open(file, 'rb') as f: f.seek(-6,2) data = f.read(1024) while data: print(data.decode()) data = f.read(1024) # > 合弓 # seek中的含义为以文件末尾为指针位置,向左移动6个字节的位置,然后向后读取
-
logging 日志模块
主要是将文件运行的信息保存在磁盘文件上
logging不要频繁进行IO读写,运行过程中,只需要保存重要信息和错误信息
-
作用
程序调试
了解软件运行状况
分析定位问题
-
日志应包含信息
时间
地点
级别
内容
-
日志级别
DEBUG
INFO
WARNING
ERROR
CRITICAL
-
直接使用系统定制的logging
logging.debug(msg)
创建一条debug日志logging. info(msg)
创建一条info日志logging.warning(msg)
创建一条warning日志logging.error(msg)
创建一条error日志logging.critical(msg)
创建一条critical日志-
logging.basicConfig(file=文件名,format=格式化字符串,level=输出的最低级别)
配置loglevel对照以上日志级别作为参数,默认为warning
-
format的相应参数
%(asctime)s
时间和日期%(levelname)s
日志级别%(message)s
日志内容%(filename)s
程序名%(pathname)s
程序路径%(funcName)s
当前函数%(lineno)d
当前行数%(thread)s
当前线程ID%(ThreadName)s
当前线程名%(process)s
当前进程ID
import logging logging.basicConfig(format='%(asctime)s - %(levelname)s - %(message)s',level=logging.DEBUG) logging.debug('我是一条DEBUG日志') # > 2018-08-10 15:10:09,089 - DEBUG - 我是一条DEBUG日志
-
使用四大组件进行定制
-
Logger类 日志器 产生日志
getLogger(日志器名称)
创建一个日志器对象setlevel(日志级别)
设置处理日志的最低级别addHandler(处理器对象),removeHandler()
添加、删除一个处理器对象addFilter(过滤器对象),removeFilter()
添加、删除一个过滤器对象debug()、info()、.....创建一条日志
-
Filter类 过滤器 存疑
- 可被Logger、Handler使用
-
Formatter类 格式器 格式化输出日志
可以直接实例化使用
-
Formatter(fmt,datefmt)
fmt 格式化字符串
datefmt 格式化时间和日期字符串
-
Handler类 处理器 将日志输出到相应位置
-
Handler是基类,不能直接使用,需要使用其子类
StreamHandler()
向控制台输出FileHandler(file=文件)
向文件写入日志handlers.RotatingHandler(file=文件,maxBytes=大小,backupCount=日志保留个数)
向文件写入日志,如果超过指定大小,则新建日志文件-
handlers.TimeRotatingFileHandler(file=文件, when=时间, interval=间隔, backupCount=保留日志个数)
向文件写入日志,按时间进行分割-
when的参数
'S'
秒'M'
分'H'
时
-'D'
天
-'midnight'
每日午夜12:00
-'W0-W6'
周一 ~ 周日
-
handlers.HTTPHandler
发送给一个HTTP服务器handlers.SMTPHandler
发送给一个指定的邮件地址
-
setlevel(日志级别)
设置最低需要写入日志的级别setFormatter(格式器对象)
添加一个格式器对象,用来控制输出的日志格式addFilter(过滤器对象)、removeFilter()
#定义记录两个日志,每天记录一次 #一个日志all.log,所有级别的日志都被记录,格式为“日期和时间 - 级别 - 内容” #一个日志error.log,error以上的的日志都被记录,格式为“日期和时间 - 级别 - 文件名【:行号】 - 内容” import logging import logging.handlers import datetime #定义Logger logger = logging.getLogger('mylogger') logger.setLevel(logging.DEBUG) #定义debughandler格式器 formatter1 = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s') #定义记录所有日志的debughandler file1 = r'C:\Users\wudi.HAPMMAGNA\Desktop\all.log' debughandler = logging.handlers.TimedRotatingFileHandler(file1, when='midnight', interval=1, backupCount=3000) debughandler.setFormatter(formatter1) #定义errorhandler格式器 formatter2 = logging.Formatter('%(asctime)s - %(levelname)s - %(filename)s[:%(lineno)d] - %(message)s') #定义记录error以上级别的日志errorhandler file2 = r'C:\Users\wudi.HAPMMAGNA\Desktop\error.log' errorhandler = logging.handlers.TimedRotatingFileHandler(file2, when='midnight', interval=1, backupCount=3000) errorhandler.setFormatter(formatter2) errorhandler.setLevel(logging.ERROR) #为日志器添加处理器 logger.addHandler(debughandler) logger.addHandler(errorhandler) logger.debug('我是debug日志') logger.info('我是info日志') logger.warning('我是warning日志') logger.error('我是error日志') logger.critical('我是critical日志')
-
总结思路
实例化一个Logger对象
依照不同的日志文件实例化不同处理方式的Handler
给每个Hadnler对象添加实例化的Formatter格式器对象
输出
-
queue 同步队列模块
Queue类 先进先出队列
LifoQueue 后进先出队列
PriorityQueue 优先级队列
-
常用方法
qsize()
返回队列长度put()
将数据放入队列中get()
从队列中取出数据,并删除empty()
检测队列是否为空full()
检测队列是否已满
import queue
que = queue.Queue()
for i in range(1,11):
que.put(i)
print(que.get())
# > 1
print(que.empty())
# > False
threading 多线程模块
-
首先明确几个概念
阻塞: 程序在等待某个操作完成期间,自身无法干其他事情,处于挂起状态
非阻塞: 程序在等待某个操作完成期间,自身不被阻塞,还可以继续干其他事情,是为了提高程序的整体执行效率,让程序在IO过程中执行其他计算任务
并行: 利用富余的资源(多核)加速完成某个任务 多进程
并发: 利用有限的资源使多个任务被实时或者近似实时的执行,让多个任务都有机会被尽快执行,不一定能加快进度 多线程
异步: 不同程序单元执行过程中不需要通信也能完成某个任务,高效的组织非阻塞任务的方式,异步意味着无序
同步: 不同程序单元为了完成某个任务,在执行过程中需要靠通信进行步调一致,同步就意味着有序
-
定义线程的两种方式
-
通过传入函数来启动线程
import threading def func(): while True: pass t = threading.Thread(target=func,args=()) t.setDaemon(True) t.start() print(threading.enumerate()) # > [<_MainThread(MainThread, started 7176)>, <Thread(Thread-1, started daemon 9260)>] # > 可以看出开启了两个线程,一个是主线程,另一个是子线程
-
通过继承
threading.Thread
类,重写其init和run方法定义import threading import time class myThread(threading.Thread): def __init__(self): super().__init__() def run(self): print('{0} say hello'.format(self.name)) time.sleep(5) for i in range(5): t = myThread() t.start() t.join() print('over') # > Thread-1 say hello # > Thread-2 say hello # > Thread-3 say hello # > Thread-4 say hello # > Thread-5 say hello # > over
-
注意: 主线程执行完毕后,子线程并不会关闭,而是会等到子线程中的函数执行完毕后再关闭
-
线程常用的方法
-
start()
启动线程 -
join()
阻塞线程,等待该线程执行完毕 -
active_Count()
返回当前活动的线程数量 -
current_thread()
返回当前正在活动的线程 -
enumerate()
返回当前活动的线程对象列表
-
-
守护线程
当A线程是B线程的守护线程时,一旦B关闭,A线程不论是否已经完成,都会随着B线程而关闭
setDaemon(True)
设置一个线程为守护线程
-
GIL 全局解释器锁
- 在多线程中,每个线程都会在执行之前申请一把锁,这把锁就是GIL,但是这个锁只有一把,也就是只有等一个线程释放锁之后,其他线程才能申请这个锁,同一时间,线程只能并发,却不能并行.
-
多线程安全
在多线程中,对变量的读操作,并不会影响线程安全
在多线程中,对变量的写操作,很有可能会引起线程安全的问题
-
不安全线程的例子
import threading import time num = 0 loop = 1000000 def funa(loop): global num for i in range(loop): num += 1 print('funa done') def funb(loop): global num for i in range(loop): num -= 1 print('funb done') t1 = threading.Thread(target=funa,args=(loop,)) t2 = threading.Thread(target=funb,args=(loop,)) t1.start() t2.start() time.sleep(2) print(num) # > 281811 # > 结果并不是固定的,每次执行都会产生不同的结果
这是因为有可能在线程中,多个线程可能在同时更改这个变量,造成结果错误
-
锁
为了解决共享变量冲突的问题
-
Lock 类
只能执行一次锁定,锁定完成后必须释放锁,才能进行下一次锁定
可以使用上下文管理协议
acqurie()
上锁release()
解锁-
还是上一个例子,如果将全局变量加锁,同一时间,只能允许一个线程进行读写操作,这时候结果就一定为0
import threading import time lock = threading.Lock() num = 0 loop = 1000000 def funa(loop): global num for i in range(loop): with lock: num += 1 print('funa done') def funb(loop): global num for i in range(loop): lock.acquire() num -= 1 lock.release() print('funb done') t1 = threading.Thread(target=funa,args=(loop,)) t2 = threading.Thread(target=funb,args=(loop,)) t1.start() t2.start() time.sleep(2) print(num) # > funa done # > funb done # > 0
-
Rlock 类 可重入锁
- 与Lock相同用法,但是可以实现锁内再次上锁
# 使用Lock 实现两次上锁 import threading lock = threading.Lock() def funa(): lock.acquire() print('第一道锁上锁') lock.acquire() print('第二道锁上锁') lock.release() print('释放第二道锁') lock.release() print('释放第一道锁') t = threading.Thread(target=funa,args=()) t.start() t.join() print('done') # > 第一道锁上锁 # 可以看出,此时,一直是在等待解锁过程中,出现阻塞状态,后面的代码永远不会执行 # 使用Rlock 实现两次上锁 import threading lock = threading.RLock() def funa(): lock.acquire() print('第一道锁上锁') lock.acquire() print('第二道锁上锁') lock.release() print('释放第二道锁') lock.release() print('释放第一道锁') t = threading.Thread(target=funa,args=()) t.start() t.join() print('done') # > 第一道锁上锁 # > 第二道锁上锁 # > 释放第二道锁 # > 释放第一道锁 # > done #可以看出,程序按照预想的执行了
-
Condition 类 条件锁
当某一事件触发后线程进行等待或者执行操作
相比于Lock,Condition有一个等待池和一个锁定池,运行时处于锁定池中,阻塞时,处于等待池中
可使用上下文管理协议
-
常用方法
acqurie()
上锁release()
解锁notify(线程数量)
通知指定数量的线程运行notify_all()
通知所有线程开始执行wait()
使本线程处于阻塞状态,等待条件
案例
import threading import time con = threading.Condition() num = 0 class A(threading.Thread): def run(self): global num with con: while True: if num >20: print('产品大于20个了,我不生产了') con.notify() con.wait() num += 1 time.sleep(0.5) print('产品总数为:{0}'.format(num)) class B(threading.Thread): def run(self): global num with con: while True: if num < 5: print('产品小于5了,我不消费了') con.notify() con.wait() num -= 1 time.sleep(0.5) print('产品总数为:{0}'.format(num)) a = A() b = B() a.start() b.start() # > 结果太长不写了
-
Semaphore 类 信号量
该类主要控制线程对资源的最大使用数量,当线程超过指定数量时,就变为等待状态,一旦空出线程,其他线程就会接着执行
当一个线程申请时,信号量就会-1 ,一旦变为负值,其他线程就不允许申请了.当一个线程被放出时,信号量就会+1,一旦不是负值,其他线程就可以申请.
import threading import time st = 'st' se = threading.Semaphore(2) class A(threading.Thread): def run(self): with se: for i in range(3): print('我是{0},我被{1}访问了'.format(st, self.name)) time.sleep(1) print(threading.current_thread()) for i in range(20): t = A() t.start()
-
Event 类 事件信号灯
相当于设置一个红绿灯,当红绿灯为True时,线程才会向下执行,否则就会一直等待信号
-
常用方法
set()
设置信号灯为Trueclear()
设置信号灯为Falsewait()
本线程等待信号灯
import threading import time event = threading.Event() class A(threading.Thread): def run(self): print('等待信号灯') event.wait() print('信号灯通过') print('hello') a = A() a.start() time.sleep(2) event.set() a.join() print('done') # > 等待信号灯 # > #2秒后 # > 信号灯通过 # > hello # > done
-
Timer 类 定时器
Timer(间隔秒数,执行的函数,函数的参数)
指定多少秒后以一个线程执行某函数
import threading def func(): print('hello') timer = threading.Timer(2,func) timer .start() # > #2秒后 # > hello
-
生产者消费者模型
- 生产者只负责向队列中写入任务,而消费者只向队列中拿出任务
import threading, queue, time class Producer(threading.Thread): def __init__(self,queue): super().__init__() self.queue = queue def run(self): num = 0 while True: num += 1 self.queue.put(num) print('向仓库中放入%s'%num) time.sleep(0.5) class Consumer(threading.Thread): def __init__(self,queue): super().__init__() self.queue = queue def run(self): while True: data = self.queue.get() print('{0}消费了{1}'.format(self.name,data)) time.sleep(1.5) que = queue.Queue() p = Producer(que) p.start() for i in range(2): c = Consumer(que) c.start() # > 向仓库中放入1 # > Thread-2消费了1 # > 向仓库中放入2 # > Thread-3消费了2 # > 向仓库中放入3 # > Thread-2消费了3 # > 向仓库中放入4 # > Thread-3消费了4 # > 向仓库中放入5 # > 向仓库中放入6
多进程
推荐使用多进程,因为多进程中没有GIL锁,可以并行执行
在使用多进程时,主函数必须在
if __name__ == '__main__:'
下执行-
实现多进程的方式
-
Process 类
-
通过实例化Process类,将函数传入参数中
import multiprocessing def func(): print('hello') t = multiprocessing.Process(target=func,args=()) t.start()
-
通过继承Process类,并重写init和run方法
import multiprocessing class A(multiprocessing.Process): def run(self): print('hello') a = A() a.start()
-
使用
os.fork()
仅在Unix系统下有效-
使用Pool 进程池
pool池可以指定并行最大进程执行的数量,我认为和线程中的Semaphore信号量相似,同样有一个等待池和一个工作池,当工作池中的进程空出来时,等待池的其它线程就会顶替上去执行
可使用上下文管理协议
-
常用函数
apply(函数名,参数)
向工作池提交任务,阻塞的等待任务完成,并返回结果.-
apply_async(函数名,参数)
向工作池提交任务,返回消息对象,等待进程池所有任务执行完毕后,再拿回任务执行的结果因为apply_async是并行的,所以需要申明一个list用来保存消息对象,等待所有的任务完成后再列表中的单个消息对象返回结果
get()
返回结果ready()
如果调用完成,则返回Truesuccessful()
如果调用没有引发异常,返回Truewait()
等待结果执行完毕terminate()
立即终止该工作进程
close()
关闭进程池join()
阻塞,直到进程池所有任务完成terminate()
立即终止进程池进程
-
阻塞式进程池
import multiprocessing import random import time def getresult(x): return x * random.randint(1, 10) if __name__ == '__main__': pool = multiprocessing.Pool(2) for i in range(1, 5): res = pool.apply(getresult, (i,)) time.sleep(1) print(res) # > 4 # > 10 # > 9 # > 4
-
异步线程池
import multiprocessing import random def getresult(x): return x * random.randint(1, 10) if __name__ == '__main__': pool = multiprocessing.Pool(2) list = [] for i in range(1, 5): res = pool.apply_async(getresult, (i,)) list.append(res) p.close() p.join() print(list) for i in list: print(i.get()) # > [<multiprocessing.pool.ApplyResult object at 0x02C78630>, <multiprocessing.pool.ApplyResult object at 0x02C78690>, <multiprocessing.pool.ApplyResult object at 0x02C786F0>, <multiprocessing.pool.ApplyResult object at 0x02C78750>] # > 7 # > 12 # > 3 # > 20
-
-
进程常用方法
start()
启动进程join()
阻塞,知道本进程执行完毕daemon = True
设置守护进程terminate()
强制关闭进程close()
关闭进程is_alive()
返回进程是否存活name
返回进程名称pid
返回进程IDactive_chrildren()
返回活动的进程列表cpu_count()
返回电脑CPU核心数
-
进程之间的同步
多进程中,同步并不是向多线程那样重要,因为多使用IPC通信
其用法与多线程中相同类的用法一样
Condition类 条件变量
Event类 事件信号灯
Semaphore类 信号量
Lock 锁
Rlock 可重入锁
-
IPC通信 多进程之间的通信
-
源自于同一父进程
-
Queue类 先入先出队列
put()
放入元素get()
取出元素qsize()
返回队列长度empty()
检测队列是否为空full()
检测队列是否已满
-
JoinableQueue队列
每当一个任务完成后,可手动调用task_done,告诉系统一个任务已经执行完成,当所有任务执行完成后,将取消join的阻塞,向下执行代码
-
常用函数
Queue队列的其它方法
task_done()
任务已完成join()
阻塞,直到本队列中的所有元素被task_done
import multiprocessing,time def func(queue): while True: if queue.empty(): break data = queue.get() print('取出%s'%data) queue.task_done() time.sleep(0.5) if __name__ == '__main__': que = multiprocessing.JoinableQueue() for i in range(20): que.put(i) p = multiprocessing.Process(target=func, args=(que,)) p.start() que.join() print('当队列中的元素都被取完后才会看见我') # > 太长不打了
-
Pipe 类 管道 两个进程之间的通信
相当于管道,实例化后返回两个端口,默认是双工的
send()
发送消息recv()
接收消息
import multiprocessing,time class A(multiprocessing.Process): def __init__(self,con): super().__init__() self.con = con def run(self): while True: self.con.send('hello') time.sleep(1) class B(multiprocessing.Process): def __init__(self, con): super().__init__() self.con = con def run(self): while True: data = self.con.recv() print(data) time.sleep(1) if __name__ == '__main__': con1, con2 = multiprocessing.Pipe() a = A(con1) b = B(con2) a.start() b.start() # > hello # > ...
-
-
不是源自于同一父进程
-
Value类 将值放在系统的共享内存中,多个进程可以共同读写这块内存,
定义
Value(值类型,值)
-
值类型
u
unicodechari
signedintf
floatd
double
value
获取值
import multiprocessing class A(multiprocessing.Process): def run(self): s = v.value print('{0} get {1}'.format(self.name,s)) if __name__ == '__main__': v = multiprocessing.Value('u','哈') for i in range(5): p = A() p.start() # > A-1 get 哈 # > A-2 get 哈 # > A-4 get 哈 # > A-5 get 哈 # > A-3 get 哈
-
Array类 Vaule的数组
数组中存放的变量类型需要一致
定义
Array(值类型,列表)
返回的是一个可迭代的对象
import multiprocessing class A(multiprocessing.Process): def run(self): for i in arr: print(i,end='') if __name__ == '__main__': list = ['哈','哈','-','我','是','A','r','r','a','y'] arr = multiprocessing.Array('u',list) p = A() p.start() # > 哈哈-我是Array
-
Manager类 管理器
list 列表
dict 字典
Condition() 创建一个线程共享条件信号灯
Event() 创建一个线程共享事件信号灯
Lock() 创建一个线程共享锁
Rlock() 创建一个线程共享可重入视频
Queue() 创建一个共享先进先出队列
Semaphore() 创建一个线程共享信号量
-
managers
-
BaseManager类
原理: 通过Manager启动一个server进程监听socket,其他进程通过使用socket与主进程进行联系,进行信息交换
定义
BaseManager(address=(ip,端口),authkey=秘钥)
创建一个服务器Manager对象,其他客户端Manager通过与此对象连接,进行数据交换-
常用方法
start()
启动服务进程get_server()
返回一个服务对象server_forever()
永远执行下去connect()
将本地管理器连接到主服务管理器上shuntdown()
关闭本管理器register(对外暴露的方法名,callable=返回对象的函数)
-
分布式进程 Master-Worker模式
- Master 负责分发任务,并接受返回的最终结果
from multiprocessing.managers import BaseManager import multiprocessing import time class Master(BaseManager): pass task_que = multiprocessing.Queue() result_que = multiprocessing.Queue() def returntaskque(): return task_que def returnresultque(): return result_que if __name__ == '__main__': ip = 'XXXXXXXXXXXXXX' port = 8888 key = b'123' server = Master((ip,port),key) server.register('get_task_que', callable=returntaskque) server.register('get_result_que', callable=returnresultque) server.start() task = server.get_task_que() result = server.get_result_que() num = 0 while True: num += 1 task.put('工作%s'%num) time.sleep(1) if not result.empty(): print('结果是:%s'%result.get())
- Worker 负责处理任务,并返回结果
from multiprocessing.managers import BaseManager import time class Worker(BaseManager): pass if __name__ == '__main__': ip = 'XXXXXXXXXXXXXX' port = 8888 key = b'123' worker = Worker((ip,port),key) worker.register('get_task_que') worker.register('get_result_que') worker.connect() task = worker.get_task_que() result = worker.get_result_que() while True: if not task.empty(): data = task.get() print('收到{0}'.format(data)) time.sleep(2) result.put('完成{0}'.format(data)) time.sleep(1)
-
-
-
迭代器 Iterator
一次产生一个对象,可以装下无限大的对象
可迭代 Iterable 可用于for循环的对象
迭代器一定是一个可迭代的,但是可迭代的不一定是迭代器
-
将一个可迭代对象转换为迭代器
-
通过
iter()
函数from collections import Iterator list = [i for i in range(5)] print(isinstance(list, Iterator)) list = iter(list) print(isinstance(list, Iterator)) # > False # > True
-
通过
__iter__()
函数from collections import Iterator list = [i for i in range(5)] print(isinstance(list, Iterator)) list = list.__iter__() print(isinstance(list, Iterator)) # > False # > True
-
-
使用方式
-
使用
for
循环for i in list: print(i)
-
使用
next()
函数,直到没有值抛出StopIteration
异常list = iter([i for i in range(5)]) next(list) next(list) ....
-
使用
__next__()
函数,直到没有值抛出StopIteration
异常list = iter([i for i in range(5)]) list.__next__() list.__next__() ....
-
生成器
一边循环,一边计算下一个对象,直到遇见yield语句后停止,每次调用只返回一个值
-
满足三个条件
每次调用都产生一个用于for循环遍历的对象
元素到达最后一个后抛出
StopIteration
异常可以被next调用
-
定义生成器
-
直接使用生成器
列表生成表达式
list = [i for i in range(5)]
生成器
gen = (i for i in range(5))
-
函数中包含
yield
语句def func(n): st = '产品' num = 0 while num < n: yield st+str(num) num += 1 g = func(5) print(list(g)) # 将生成器转化为列表,方便查看,也可以使用next(g)挨个输出值 # > ['产品0', '产品1', '产品2', '产品3', '产品4']
-
可使用
send()
函数向生成器发送值- 注意: 生成器每执行一次都会在yield处停止,yield前的变量可以接收send发送的值,yield后是生成器返回的值
def func(): num = yield print(num) g = func() next(g) g.send(5) # > 5
特别注意: 无论是
next()
还是send()
,调用后都会在下一个yield语句处停止
-
-
yield from
相当于中间层,每当生成器生成一个元素,都由中间层将元素传递给主函数
包含
yield from
的生成器叫做委派生成器
def func(): yield from 'asds' g = func() print(next(g)) print(next(g)) print(next(g)) print(next(g)) # > a # > s # > d # > s
协程
非抢占式多任务,协程允许不同入口点不同位置执行程序
多线程之间的切换会消耗一定的资源,一些高并发的程序在执行时,如果使用多线程,多线程之间的切换会消耗大量的资源和时间,但协程之间的切换却消耗的资源少
-
协程的实现
使用
next()
预激协程使用
yield
获取返回值使用
send()
发送值
-
协程的四个状态
GEN-CREATED 等待开始执行
GEN-RUNNING 解释器正在执行
GEN-SUSPENED 在yield语句处暂停
GEN-CLOSED 执行结束
-
协程终止
- 可以使用哨兵值结束
-
获取协程的返回值
import time def average(): sum = 0 count = 0 avrge = 0 while True: data = yield (avrge,sum,count) if data == None: break sum = sum + data count += 1 avrge = sum / count print('协程结束') g = average() next(g) for i in range(1,20,3): avr,sum,count = g.send(i) print('总数为:{0} , 个数为:{1} , 平均数为{2}'.format(sum,count,avr)) time.sleep(1)
协程的消费者-生产者模型
import time
def Producer():
num = 0
while True:
num += 1
print('生产者:总产品为%s'%num)
num = yield num
time.sleep(0.5)
def Consumer():
num = 0
while True:
num = yield num
num -= 1
print('消费者:总产品为%s'%num)
time.sleep(0.5)
p = Producer()
c = Consumer()
num = next(p)
next(c)
while True:
num = c.send(num)
num = p.send(num)
结构化文件储存
为了解决不同设备之间的数据交换
-
xml库
-
构成: 处理指令,xml版本和编码
根元素: 树形结构的根元素,有且只能有一个
子元素: 可以有多个
属性: 定义在括号中
内容: 标签中定义的信息
注释: 使用``
-
保留字符
转义 保留字符需要用转义进行代替
CDATA定义的字符不进行转义,相当于python中的
r''
,<!{CDATA[不需要进行转义的字符]}>
-
命名空间
xmlns,是xml name space的缩写
直接在标签中添加相应空间
-
SAX (Simple API for xml)
基于事件驱动
包含解析器和事件处理两个部分
流式读取
速度快
-
DOM (Document Object Model)
-
minidom类
parse(file)
打开xml文件,返回节点树documentElement
返回xml的根节点creatElement(tag)
创建新节点createAttribute(attr)
创建此节点的新属性getElementsByTagName(tag)
获取此节点下的名为tag标签的集合getAttribute(attr)
返回此节点的attr属性的值parentNode
返回当前节点的父节点previousSibling
返回此节点的前一个兄弟节点nextSibling
返回此节点的下一个兄弟节点childNodes
返回当前节点的子节点列表firstChild
返回第一个子节点lastChild
返回最后一个子节点nodeName
返回此节点的标签名注意:节点中的汉字也是有节点的,是text节点
-
etree.ElementTree类
Element(标签名)
创建一个节点SubElement(parent,tag)
生成一个子节点,并添加到调用函数的节点ElementTree(tag)
生成一个节点树,将tag标签作为根节点树.write(file)
将节点树写入文件set(key,vlue)
修改属性append(子节点)
此节点最后添加子节点remove(子节点)
删除此节点子节点parse(file)
打开xml文件,返回节点树getroot()
返回节点树的根节点iter(tag)
返回此节点下的所有节点,如果指定标签名,则遍历所有指定标签名find()
查找第一个匹配的节点findall()
查找此节点下所有匹配的子节点,返回集合tag
标签名text
标签的文本值attrib
返回标签所有属性的字典
-
-
# 利用etree创建一个xml文件
import xml.etree.ElementTree
root = xml.etree.ElementTree.Element('Root')
name = xml.etree.ElementTree.SubElement(root,'Name')
name.text = 'Jack'
age = xml.etree.ElementTree.SubElement(root,'Age')
age.text = '22'
tree = xml.etree.ElementTree.ElementTree(root)
tree.write('a.xml')
-
json库
轻量级的数据交换工具 (JavaScripObjectNotation)
类似于字典,基于key-value形式
-
常用函数
-
以文件形式存在
dump(file)
写入文件,转化为json对象load(file)
读取文件,转化为python对象
-
以数据形式存在
dumps(data)
写入内容,转化为json对象loads(data)
读取内容,转化为python对象
-
import json
data = {
'name':'jack',
'age':18,
'sex':'male'
}
# 写入json文件
with open('a.json','w')as f:
json.dump(data,f)
#读取json文件
with open('a.json','r')as f:
data = json.load(f)
print(type(data))
print(data)
# > <class 'dict'>
# > {'name': 'jack', 'age': 18, 'sex': 'male'}
re 正则表达式模块
-
转义字符
-
重复限定符
.
匹配除换行符\n之外的所有字符^
匹配字符串开头$
匹配字符串结尾*
匹配前一个字符0次以上,贪婪模式+
匹配前一个字符1次以上,贪婪模式?
匹配前一个字符0次或1次,贪婪模式{m,n}
匹配前一个字符至少m次,最多n次,当没有n时则为无限次,贪婪模式[]
字符集,匹配字符集中的任意字符,字符客单个给出,也可范围给出()
分组,分组从0开始|
或者*? +? ?? {m,n}?
非贪婪模式,尽可能少的匹配字符import re text = '<div>name</div><div>age</div>' regex = re.search(r'<div>.*?</div>',text) print(regex.group()) # > <div>name</div>
-
特殊字符集
r
原始字符串,字符串中的特殊字符不需要再进行转义,否则则需要使用\
进行转义[\u4E00-\u9FA5]
汉字\b
单词边界\d
数字 [0-9]\s
任何空白字符 [\n\t\r\f\v<空格>]\w
匹配包括下划线的任何字字符 [A-Za-z0-9_]除汉字外,上述特殊集大写为与之相反的意思
-
-
flags 编译标志位
re.I
不区分大小写re.M
多行匹配re.U
根据Unicode字符集解析字符re.S
使.匹配包括换行符在内的所有字符 -
compile(pattern)
编译正则表达式,返回正则表达式对象- 正则表达式对象可以直接调用以下函数,也可使用re进行调用.
import re text = 'https://docs.python.org/3/library/re.html#regular-expression-syntax' #使用re进行调用 regex = re.search(r'\d',text) print(regex) # > <_sre.SRE_Match object; span=(24, 25), match='3'> #使用compile调用 par = re.compile(r'\d') regex = par.search(text) print(regex) # > <_sre.SRE_Match object; span=(24, 25), match='3'>
search(pattern,string[,flags])
查找,查找字符串的所有位置,返回一个匹配对象match(pattern,string[,flags])
匹配,匹配字符串的开头位置,与search不同,返回一个匹配对象
import re text = 'https://docs.python.org/3/library/re.html#regular-expression-syntax' regex = re.match('library',text) print(regex) # > None regex = re.match('https',text) print(regex) # > <_sre.SRE_Match object; span=(0, 5), match='https'>
-
findall(pattern,string[,flags])
查找字符串中所有匹配正则表达式的内容,返回一个匹配的字符串列表
import re text = 'https://docs.python.org/3/library/re.html#regular-expression-syntax' list = re.findall('o',text) print(list) # > ['o', 'o', 'o', 'o']
-
split(pattern,string[,flags])
按照正则表达式分割字符串对象,将正则表达式中的匹配选项替换为''
,并返回一个分割的字符串列表
import re text = 'https://docs.python.org/3/library/re.html#regular-expression-syntax' list = re.split(r'/',text) print(list) # > ['https:', '', 'docs.python.org', '3', 'library', 're.html#regular-expression-syntax']
-
sub(pattern,repl,string,count)
替换查找的字符串
import re text = 'https://docs.python.org/3/library/re.html#regular-expression-syntax' # 将所有字符替换为@ regex = re.sub(r'\W','@',text) print(regex) # > https@@@docs@python@org@3@library@re@html@regular@expression@syntax
-
Match 匹配对象
- 下面的regex即为一个匹配对象
import re text = 'https://docs.python.org/3/library/re.html#regular-expression-syntax' regex = re.search(r'\d',text) # > <_sre.SRE_Match object; span=(24, 25), match='3'>
匹配对象的布尔值始终为True,一旦没有匹配到的对象,则返回None
group(index)
当index没有或者为0时,返回匹配对象的整个字符串,当index不为0时,返回正则表达式中用()括起来的字符
regex.group() # > 3
-
groups()
返回被匹配字符串的元组
regex = re.search(r'(org).+(syntax$)',text) # > ('org', 'syntax')
-
start()
返回匹配开始位置
regex.start() # > 24
-
end()
返回匹配结束位置
regex.end() # > 25
-
span()
返回包含start和end的元组
regex.span() # > (24, 25)