此文章会每天更新一次
代码中的#>表示的是输出结果
str = input('请输入内容:')
print(str)
#>> 此时控制台上打印的一定是你输入的字符串
num = input('请输入数字:')
print(type(num))
# >> <class 'str'>
#此时可以看见,就算输入数字类型的变量,num接收的值依然是一个字符串类型
num = int(num) #强制转换为int类型
print(type(num))
# >> <class 'int'>
#此时控制台返回的是num变量的类型,可以看到,类型已经是int了
#常用用法
print('Hello World!')
# 自定义用法
print(value,sep = ' ',end = '\n')
value表示需要打印的值
sep表示打印多个值之间的分隔符,默认是一个空格
end表示打印结束后的分隔符,默认是\n
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
#通过这种方式就完成了两个变量值的交换
a = 'Jack'
b = "Jack"
c = "Bob,'haha',Jack"
print(c)
#其结果为Bob,'haha',Jack
格式化的两种方式
利用%
%s:格式化字符串
%d:格式化整数
n = '我是XX'
print('你好,%s'%n)
n = '我是XX'
print('你好,{0}'.format(n))
#还可以这么使用
age = 18
name = 'Bob'
print('我是{0},我今年{1}岁,你也{1}岁'.format(name,age))
字符串的转义
使用转义字符
\\:表示
\ 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 = [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 = (1,2,3)
元组具有list中的部分属性,但只可以进行读取操作,不能进行写入操作
可以将一个list列表转化为一个元组tuple
t = tuple(list)
字典的创建方式
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}
定义方式
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
乘法运算符 *
cython
print(5 * 2) # > 10
当对象为非数字型时,打印N次,并组成一个新对象
print('a'*3) # > aaa
print([1,2,3] * 2) # > [1, 2, 3, 1, 2, 3]
除法运算符 /
print(6 / 2) # > 3
cython
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 不在里面
“`cython
print(‘a’ not in ‘abc’)
##顺序结构
- 按照代码编写的方式逐行进行
##分支、判断结构
- if…else语句
cython
a = 10
b = 5
if a > b:
print(a,'>',b)
else:
print(a,'<',b)
# > 10 > 5
- if..elif..else语句
- 注意,以上代码并不严谨,缺少相等的时候
cython
a = 10
b = 10
if a > b:
print(a,'>',b)
elif:
print(a,'=',b)
else:
print(a,'<',b)
# > 10 = 10
##循环结构
- 使用for进行循环(知道具体循环次数的时候)
“`cython
s = ‘abcdefg’
for i in s:
print(i)
# > a
# > b
# > c
# > d
# > e
# > f
# > g
```
- 使用while进行循环(不知道具体循环次数)
- while
cython
n = 0
while n < 10:
print(n)
n = n + 1
# > 0
# > 1
# > 2
# > 3
# > 4
# > 5
# > 6
# > 7
# > 8
# > 9
- break 跳出整个循环
- 当需要跳出整个循环的时候可以用break
“`cython
n = 1
while n < 10:
print(n)
n = n + 1
if n == 3:
break
# > 1
# > 2
```
- continue 结束本次循环
- 当某些条件时,跳过本次循环
cython
n = 0
while n < 10:
n = n + 1
if n == 3:
continue
print(n)
# > 1
# > 2
# > 4
# > 5
# > 6
# > 7
# > 8
# > 9
# > 10
##定义函数
- 使用def进行定义,后面跟着方法名和()
```cython
def func():
print('hello')
```
##调用,传入函数
- 调用函数时,用函数名加括号
“`cython
#调用函数
func()
# > hello
```
- 传参时,只用函数名
“`cython
#如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
```
##函数的参数
- 位置参数
- 根据定义函数时参数的位置,自动进行参数传递,此时,必须严格按照参数的顺序进行传参
“`cython
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,否则参数就会传递不正确,严重时,会报错
```
- 关键字参数
- 根据指定的关键字进行传递参数,传参的顺序是否正确,不会影响函数的参数传递
“`cython
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.
```
- 默认参数
- 在定义函数时,就传递默认值,被传递默认值的参数如果不进行再次传参,就会使用默认值
“`cython
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.
```
- 可变参数
- 解包:是将可迭代的对象变为单个元素
- 明确一个概念,可迭代的对象可以进行解包操作,而且可以不使用解包的操作符号
```cython
list = [1,2,3]
a , b , c = list
# > 1
# > 2
# > 3
#可以看出,对于一个知道长度的可迭代对象我们可以用赋值的方式将其解包
```
- 使用\*对可迭代的对象进行解包
```cython
#但如果对于一个我们不知道长度的列表如何进行解包呢?
#这就是*的用法
list = [1,2,3,4]
print(*list)
# > 1 2 3 4
#可以看出,此时列表list已经被用*解包为单个元素
```
- 使用**对字典进行解包
```cython
#首先我们使用*对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()函数进行压包
```cython
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')]
#可以看出,将三个序列压包后,每个序列中下标相同的元素,重新组成了一个元组,而这些元组都构成了一个列表
```
- 可变参数函数
- 了解了*解包之后我们就可以明白函数的可变参数是怎么回事了
- 定义一个可变参数的函数
“`cython
#注意:这里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确实是将单个元素传入了函数中
```
- 可变参数的关键字函数
```cython
#刚才对字典解包后得出**dict = Jack = 24 , bob = 18
#仔细一想,如果把**dict当成参数,这不就是一个关键字参数嘛
```
- 定义一个可变参数的关键字函数
```cython
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
# 可以看出,即使有些参数是不填的函数也可以正常运行,这是不可变参数函数所不能达到的效果
```
- 参数混用
- 需要遵照函数(位置函数,关键字函数,可变参数函数,可变参关键字函数)的顺序进行
cython
def func(name , age = 24 , *args , **kwargs):
pass
##函数的返回值
- 所有函数都有返回值,表示函数调用已经结束
- 定义函数返回值,使用return
“`cython
def func(a):
return a+10
b = func(1) # > 11
```
- 即使没有定义return,解释器也会自动加上 return None
##函数文档
- 当使用help()函数时,显示的帮助文档
- 也可以使用doc调用文档
- 当你在不知道怎么使用函数或者是类时,都可以调用帮助文档,里面会给你详细说明各种用法
- 自定义函数帮助文档,在开头加上”’ ”’即可,如果使用的ide,会自动给你加上其他参数
```cython
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()函数,归并操作,将列表的两个值经过函数计算后与列表中的第三个值再次进行相同计算,直到列表元素完成循环。
#计算列表中所有元素的乘积
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
默认排序
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')]
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)
# > .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)
# > [.funb at 0x000001F1CE5A8A60>, .funb at 0x000001F1CE5A8AE8>, .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)
# > [.funb..funa at 0x000001E2944C8AE8>, .funb..funa at 0x000001E2944C8B70>, .funb..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]
存疑
##思想
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的函数称为非绑定方法,可以通过对象进行访问
cython
p = Person()
p.say()
# > say
使用类().方法
访问
Person().say()
# > say
类.方法(类())
访问 cython
Person.say(Person())
# > say
无self的函数称为绑定方法,只能通过类进行访问
p = Person()
p.sleep
# > TypeError: sleep() takes 0 positional arguments but 1 was given
#可以看出,绑定方法不可以用对象进行访问,因为方法中没有定义传入对象本身的参数,所以出现了sleep函数不需要参数,但传入了一个参数,传入的参数就是对象本身
Person.sleep()
# > sleep
对象.__class__.方法
访问 cython
p.__class__.sleep()
# > sleep
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())
# > [, , ]
#可以看出,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())
#[, , , ]
#可以看出继承顺序遵照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:
# > --------------------
# > 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就是传入该类对象的值
使用类的描述符进行多个类属性检查
“`python
#上面学习了如何使用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() 指一个时间戳,是从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秒
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)
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))
# >
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))
# >
print(temp)
# > 2018-08-18 05:37:51.464770
timeit(smlt = 测试代码,setup = 运行环境 ,number = 重复次数)
测试代码时间
```python
#现在我们测试一下列表生成式和循环加入列表中的两个代码谁执行的快
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
```
绝对路径 总是从根目录上开始
`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()` 返回文件或文件夹最后修改时间
copy()
复制文件,还可以将文件重命名
copy2()
尽可能保留元数据的情况下复制文件
copyfile()
讲一个文件里的内容复制到另一个文件中
copytree()
复制文件夹里所有内容
move()
移动文件或文件夹
rmtree()
删除文件或文件夹下的所有文件
make_archive(压缩后的名称,压缩类型,需要压缩的文件)
压缩文件,返回压缩文件的路径
unpack_archive(压缩包路径,解包之后的路径)
解压文件
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(列表)`
```python
import collections
d = collections.deque([1,2,3,4,5])
d.appendleft(10)
# > deque([10, 1, 2, 3, 4, 5])
```
defaultdict类
- 当读取一个不存在的key时,返回一个默认值
- 定义方式:`对象 = collections.defaultdict(带有返回值的函数)`
```python
import collections
def pr():
return '默认值'
d = collections.defaultdict(pr)
d['a'] = 1
d['b'] = 2
print(d)
# > defaultdict(, {'a': 1, 'b': 2})
print(d['dd'])
# > 默认值
```
Counter类
- 主要是统计元素在迭代对象中出现的次数,以元素:次数的字典形式返回
```python
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})
```
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只能允许一个句柄进行写入,但可以允许多个句柄进行读取
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
文件是一种长久保存信息数据的集合,保存在磁盘上
文件一旦被打开,就需要进行关闭操作,否则可能造成文件信息的丢失
常用操作
读写模式
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不要频繁进行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=输出的最低级别)
配置log
level对照以上日志级别作为参数,默认为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类 过滤器 存疑
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类 先进先出队列
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
首先明确几个概念
阻塞: 程序在等待某个操作完成期间,自身无法干其他事情,处于挂起状态
非阻塞: 程序在等待某个操作完成期间,自身不被阻塞,还可以继续干其他事情,是为了提高程序的整体执行效率,让程序在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)>, ]
# > 可以看出开启了两个线程,一个是主线程,另一个是子线程
通过继承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 全局解释器锁
多线程安全
在多线程中,对变量的读操作,并不会影响线程安全
在多线程中,对变量的写操作,很有可能会引起线程安全的问题
不安全线程的例子
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 实现两次上锁
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()
设置信号灯为True
clear()
设置信号灯为False
wait()
本线程等待信号灯
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()
如果调用完成,则返回True
successful()
如果调用没有引发异常,返回True
wait()
等待结果执行完毕
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())
# > [, , , ]
# > 7
# > 12
# > 3
# > 20
进程常用方法
start()
启动进程
join()
阻塞,知道本进程执行完毕
daemon = True
设置守护进程
terminate()
强制关闭进程
close()
关闭进程
is_alive()
返回进程是否存活
name
返回进程名称
pid
返回进程ID
active_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
unicodechar
i
signedint
f
float
d
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模式
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())
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)
一次产生一个对象,可以装下无限大的对象
可迭代 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__()
函数
“`python
from collections import Iterator
list = [i for i in range(5)]
print(isinstance(list, Iterator))
list = list.iter()
print(isinstance(list, Iterator))
“`
使用方式
使用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()
函数向生成器发送值
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''
,
命名空间
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'}
转义字符
重复限定符
.
匹配除换行符\n之外的所有字符
^
匹配字符串开头
$
匹配字符串结尾
*
匹配前一个字符0次以上,贪婪模式
+
匹配前一个字符1次以上,贪婪模式
?
匹配前一个字符0次或1次,贪婪模式
{m,n}
匹配前一个字符至少m次,最多n次,当没有n时则为无限次,贪婪模式
[]
字符集,匹配字符集中的任意字符,字符客单个给出,也可范围给出
()
分组,分组从0开始
|
或者
*? +? ?? {m,n}?
非贪婪模式,尽可能少的匹配字符
import re
text = 'nameage'
regex = re.search(r'.*?',text)
print(regex.group())
# > name
特殊字符集
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)
编译正则表达式,返回正则表达式对象
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 匹配对象
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)