Python 3 Study

字符串,列表,元组,字典,集合

购物车

(1)丶要求用户输入总资产,例如:2000。
(2)丶显示商品列表,让用户根据序号选择商品,加入购物车。
(3)丶购买,如果商品总额大于总资产,提示账户余额不足,否则,购买成功。
(4)丶附加:可充值、某商品移除购物车。
例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#!/usr/bin/env python
# -*- coding=utf-8 -*-
goods = [
{"name": "电脑", "price": 1999},
{"name": "鼠标", "price": 10},
{"name": "游艇", "price": 20},
{"name": "美女", "price": 998},
]
zongzichang=0
i1=input('请输入你的总资产:')
zongzichang=int(i1)
#输出商品列表,包括商品名和价格
for i1 in goods:
print(i1['name'], i1['price'])
#{"电脑":{"num":1,"single_price":1999}}
car_dir={}
while True:
i2=input('请输入购买的商品(输入q来退出购买):')
if 'q' == i2:
break
for items in goods:
if items['name'] == i2:
name=items['name']
if i2 in car_dir.keys():
car_dir[name]['num']+=1
else:
car_dir[i2]={"num":1,'single_price':items['price']}
else:
pass
print(car_dir)
sum=0
for i3 in car_dir:
price=car_dir[i3]['num']*car_dir[i3]['single_price']
sum=sum+price
if sum <= zongzichang:
print('购买成功')
else:
print('账户余额不足,请充值')

元素分类

(1)丶有如下值集合 [11,22,33,44,55,66,77,88,99,90…],将所有大于 66 的值保存至字典的第一个key中,
将小于 66 的值保存至第二个key的值中,即: {‘k1’: 大于66的所有值, ‘k2’: 小于66的所有值}。
例:

1
2
3
4
5
6
7
8
9
10
11
12
13
#!/usr/bin/env python
# -*- coding=utf-8 -*-
a = [11,22,33,44,55,66,77,88,99,90]
b = {
"key1":[],
"key2":[],
}
for i in a:
if i > 66:
b["key1"].append(i)
else:
b["key2"].append(i)
print(b)

用户登录隐藏密码(三次输入机会)。

例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#!/usr/bin/env python
# -*- coding=utf-8 -*-
import getpass #模块功能让用户输入的内容不可见
time=1
while True:
user=input('请输入用户名:')
password=getpass.getpass("请输入密码:") #隐藏用户输入的内容
if user == 'Hello' and password == 'Python':
print(user,password)
break
else:
pass
time+=1
if time >3:
break

输出商品列表

(1)丶用户输入序号,显示用户选中的商品。
例:

1
2
3
4
5
6
7
8
9
10
11
12
13
#!/usr/bin/env python
# -*- coding=utf-8 -*-
#商品列表有4个,我设置了可以输入4次
li = ["手机", "电脑", '鼠标垫', '游艇']
time=1
for key,i in enumerate(li,1):
print(key,i)
while True:
xuanze=int(input('请输入序号:'))
print(li[xuanze-1])
time+=1
if time > 4:
break

查找列表元组字典

(1)丶查找所有的元素,移除每个元素的空格,并查找以 a或A开头 并且以 c 结尾的所有元素,放在列表中并统计次数。
例:

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
#!/usr/bin/env python
# -*- coding=utf-8 -*-
name={
"li":["alec", " aric", "Alex", "Tony", "rain"],
"tu":("alec", " aric", "Alex", "Tony", "rain"),
"dic":{'k1': "alex", 'k2': ' aric', "k3": "Alex", "k4": "Tony"},
}
new_dic={}
for i1 in name:
if isinstance((name[i1]),list) or isinstance((name[i1]),tuple):
for i2 in name[i1]:
i3=i2.strip()
if (i3.startswith("a") or i3.startswith("A")) and i3.endswith("c"):
print(i3)
if i3 in new_dic:
new_dic[i3]+=1
else:
new_dic[i3]=1
else:
for i4 in name[i1].values():
i5=i4.strip()
if (i5.startswith("a") or i5.startswith("A")) and i5.endswith("c"):
print(i5)
if i5 in new_dic:
new_dic[i5]+=1
else:
new_dic[i5]=1
print(new_dic)

寻找差异

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#! /usr/bin/env python
# -*- coding:utf-8 -*-
# 数据库中原有
old_dict = {
"1":{'hostname':'c1','cpu_count':2,'mem_capicity':80},
"2":{'hostname':'c1','cpu_count':2,'mem_capicity':80},
"3":{'hostname':'c1','cpu_count':2,'mem_capicity':80},
}
# 新汇报的数据
new_dict = {
"1":{'hostname':'c1','cpu_count':2,'mem_capicity':800},
"3":{'hostname':'c1','cpu_count':2,'mem_capicity':80},
"4":{'hostname':'c2','cpu_count':2,'mem_capicity':80},
}
del_set=set(old_dict.keys()).difference(set(new_dict.keys())) #{'2'},需要删除的key,放在set集合里
add_set=set(new_dict.keys()).difference(set(old_dict.keys())) #{'4'},需要新增的key,放在set集合里
update_set=set(old_dict.keys()).intersection(set(new_dict.keys())) #{'1','3'},交集key,放在set集合里

三元运算

例:

1
2
3
4
5
#!/usr/bin/env python
# -*- coding=utf-8 -*-
arg1,arg2 = 1,2
result = arg1 if arg1>arg2 else arg2 #如果条件成立,那么将 "arg1" 赋值给result变量,否则,将"arg2"赋值给result变量
print(result)

函数

函数一

(1)丶写函数,检查传入字典的每一个value的长度,如果大于2,那么仅保留前两个长度的内容,并将新内容返回给调用者。

例:

1
2
3
4
5
6
7
8
9
10
#!/usr/bin/env python
# -*- coding=utf-8 -*-
def zidian(args):
for key,value in args.items():
if len(value) > 2:
args[key]=value[0:2]
return args

dic = {'k1':"alex",'k2':[11,22,33,44],"k3":(11,22,33),"k4":"liyunliang"}
print(zidian(dic))

函数二

(1)丶写函数,检查获取传入列表或元组对象的所有奇数位索引对应的元素,并将其作为新列表返回给调用者。
例:

1
2
3
4
5
6
7
8
9
10
11
12
#!/usr/bin/env python
# -*- coding=utf-8 -*-
def liebiao(args):
new_list=[]
for i in range(len(args)):
if i%2 == 1:
new_list.append(args[i])
else:
pass
return new_list
list = [11,22,33,44,55,66,77]
print(liebiao(list))

函数三

(1)写函数,检查用户传入的对象(字符串、无嵌套的列表和元组)的每一个元素是否含有空内容。
例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#!/usr/bin/env python
# -*- coding=utf-8 -*-
def space_jiancha(args):
for i in args:
if isinstance(i,int): #判断是否是int类型,int类型不能使用isspace
pass
else:
if i.isspace():
return True
break
return False

list =[11,22,"liyunliang"," "]
ret=space_jiancha(list) #ret等于True或False
if ret:
print("用户传入的对象有空格")
else:
print("用户传入的对象没有空格")

def和lambda定义函数区别

(1)丶lambda用于非常简单的函数,一行完成,如下两个函数功能相同。
例:

1
2
3
4
5
6
7
8
9
#!/usr/bin/env python
# -*- coding=utf-8 -*-
def sumnumber(args1,args2):
return args1+args2

my_lambda=lambda args1,args2:args1+args2

print(sumnumber(1,2))
print(my_lambda(1,2))

斐波那契数列取第十个值

例:

1
2
3
4
5
6
7
8
9
10
11
#!/usr/bin/env python
# -*- coding=utf-8 -*-
# 0 1 1 2 3 5 8 13 21 34 55 89
def f1(end,arg1,arg2):
if end == 10:
return arg1
arg3 = arg1 + arg2
r = f1(end+1,arg2,arg3)
return r
ret= f1(1,0,1)
print(ret)

冒泡排序

例:

1
2
3
4
5
6
7
8
9
10
11
#!/usr/bin/env python
# -*- coding=utf-8 -*-
li = [112,3,6,50,40,1] #将列表中数字从小到大依次排列
print(li)
for i in range(1,len(li)):
for j in range(len(li)-i):
if li[j] > li[j+1]:
temp = li[j]
li[j] = li[j+1]
li[j+1] = temp
print(li)

装饰器

单层装饰

1
2
3
4
5
6
7
8
9
10
11
12
13
#!/usr/bin/env python
# -*- coding:utf-8 -*-
def outer(func): #装饰器outer
def inner(*args,**argss): #可接收任意参数
print("你好!")
r = func(*args,**argss)
print("再见!")
return r
return inner
@outer #相当于outer(f2)
def f2(a1,a2): #此函数若需要添加一些额外的功能,不改变原函数代码的情况可以使用装饰器
return a1 + a2
print(f2(1,2)) #输出为:'你好!' '再见!' 3

二层装饰

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#!/usr/bin/env python
# -*- coding:utf-8 -*-
def outer(func): #装饰器outer
def inner(*args,**argss): #可接收任意参数
print("你好!")
r = func(*args,**argss)
print("再见!")
return r
return inner
def outer_1(func): #装饰器outer_1
def inner(*args,**argss):
print("欢迎来到测试系统!")
r = func(*args,**argss)
return r
return inner
@outer_1 #相当于outer_1(f2)
@outer #相当于outer(f2)
def f2(a1,a2): #此函数若需要添加一些额外的功能,不改变原函数代码的情况可以使用装饰器
return a1 + a2
@outer_1
@outer
def f3(a1,a2,a3):
return a1 + a2 +a3
print(f2(1,2))
print(f3(1,2,3))

内置函数

常用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
abs()     #取绝对值
all() #循环参数,如果每个元素为真,那么all的返回值为真,0,None,空值为假,其余为真
any() #循环参数,只要一个元素为真,则返回值为真
bin() #二进制,0b表示
oct() #八进制,0o表示
int() #十进制
hex() #十六进制,0x表示
bool() #转换成布尔值,返回真或假
bytes() #转换为字节类型
str() #转换为字符类型
chr() #接收一个数字,找到对应的字符,c=chr(65) ,c的值为A
ord() #接收一个字符,找到对应的数字, c=ord("A"),c的值为65
dir() #查看帮助
help() #查看帮助
divmod() #取商和余数
enumrate() #为可迭代的对象添加序号
eval() #执行一个字符串形式的表达式,有返回值
filter() #过滤器,循环可迭代的对象,获取每一个参数,函数(参数),filter(函数,可迭代的对象)
map() #map(函数,可迭代的对象)
globals() #获取所有全局变量,返回一个字典
locals() #获取所有局部变量,返回一个字典
id() #查看变量的内存地址,如a="li",id(a)
input() #接受一个标准输入数据,返回为 string 类型,与python2的写法不同,2为raw_input()
isinstance() #判断一个变量是否是某种类型,返回True/False
len() #输出一个变量的长度,可测量str,列表,元组,字典的长度
max() #取最大值
min() #取最小值
pow() #取幂数
round() #对小数四舍五入
range() #可创建一个整数列表,一般用在 for 循环中,用法为:range(start, stop[, step]),>=start,<stop,step为步长
sorted() #对所有可迭代的对象进行排序操作,sort只用于列表,用法为:sorted(iterable, key=None, reverse=False),reverse=True表示降序,False表示升序(默认),生成新的对象,数字和字符串无法比较,字符串根据bytes值排序
sum() #求和
type() #查看对象的类型
open() #打开文件,用法为:open(name[, mode[, buffering]]),name为文件名,mode为打开方式,buffering可省略
list() #可定义一个列表,也可用于将元组转换为列表。
tuple() #可定义一个元组,也可用于将列表转换为元组。
dict() #可创建一个字典
set() #可创建一个无序不重复元素集,可进行关系测试,删除重复数据,还可以计算交集、差集、并集等。

用法示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
abs(-1)    #返回值为1
all([1,0]) #0为假,返回False
any([1,0]) #1为真,返回True
bin(17) #十进制转二进制
act(17) #十进制转八进制
hex(17) #十进制转十六进制
int('0b11',base=2) #二进制转换为十进制,输出为3,0b可有可无,base=2表示二进制
int('0o11',base=8) #八进制转换为十进制,输出为9,0o可有可无
int('0x11',base=16) #十六进制转换为十进制,输出为17 ,0x可有可无
bool(1) #返回值为True
bytes("liyunliang",encoding="utf-8") #str转换为bytes,以uft-8的格式编码
str(b"liyunliang",encoding="utf-8") #bytes转换为str,以utf-8的格式编码
chr(65) #输出为字符A
ord("A") #输出为数字65
dir(hash) #查看hash有关的用法
help(hash) #查看hash有关的用法
divmod(10,3) #输出为元组(3,1)
li = [11,22,33]
for k,v in enumerate(li, 1): #不指定为1的话默认从0开始
print(k,v) #输出为1 11 2 22 3 33
eval("1+2") #输出为数字3
eval("a+2",{"a":1}) #输出为数字3
def f1(x):
if x > 22:
return True
else:
return False
ret=filter(f1,[11,22,33,44]) #ret为可迭代的filter对象
for i in ret:
print(i) #输出为过滤得到的数字33和44
ret=map(f1,[11,22,33,44]) #ret为可迭代的map对象
for i in ret:
print(i) #输出为False,False,True,True
id(1) #查看数字1的内存地址
u=input("请输入用户名:") #则输入的数据为用户名
isinstance(1,(int)) #判断1是否属于int类型,返回True,判断单个类型()可加可不加;isinstance(1,(int,str)),判断1是否属于int或者str类型
len("li") #返回值为2
max([11,22,33]) #返回值为33
min([11,22,33]) #返回值为11
pow(2, -1) #返回值为0.5,2的-1次方
round(0.654,2) #返回值为0.65,2表示保留两位小数
sorted([22,44,11,33]) #返回值为[11,22,33,44]
sorted([11,22,44,33],key=lambda x: x*-1) #返回值为[44,33,22,11],利用key来降序
sorted([11,22,44,33],reverse=True) #返回值为[44,33,22,11]
sum([11,22,33,44]) #返回值为110
type(1) #可查看1的类型

f = open('test.txt','r') #以只读的方式打开test.txt
f.read([size]) #size未指定则返回整个文件,如果文件大小>2倍内存则有问题.f.read()读到文件尾时返回""(空字串)
f.readline() #读取一行
f.readlines([size]) #返回包含size行的列表,size 未指定则返回全部行
for line in f: print line #通过迭代器访问
f.write("hello\n") #如果要写入字符串以外的数据,先将他转换为字符串.
f.tell() #返回一个整数,表示当前文件指针的位置(就是到文件头的比特数).
f.seek(偏移量,[起始位置]) #用来移动文件指针.
f.close() #关闭文件
with open('test.txt','r') as f: #以只读的方式打开文件test.txt,不需要close文件
模式说明:
r #以只读方式打开文件。文件的指针将会放在文件的开头。这是默认模式。
rb #以二进制格式打开一个文件用于只读。文件指针将会放在文件的开头。这是默认模式。
r+ #打开一个文件用于读写。文件指针将会放在文件的开头。
rb+ #以二进制格式打开一个文件用于读写。文件指针将会放在文件的开头。
w #打开一个文件只用于写入。如果该文件已存在则将其覆盖。如果该文件不存在,创建新文件。
wb #以二进制格式打开一个文件只用于写入。如果该文件已存在则将其覆盖。如果该文件不存在,创建新文件。
w+ #打开一个文件用于读写。如果该文件已存在则将其覆盖。如果该文件不存在,创建新文件。
wb+ #以二进制格式打开一个文件用于读写。如果该文件已存在则将其覆盖。如果该文件不存在,创建新文件。
a #打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾。也就是说,新的内容将会被写入到已有内容之后。如果该文件不存在,创建新文件进行写入。
ab #以二进制格式打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾。也就是说,新的内容将会被写入到已有内容之后。如果该文件不存在,创建新文件进行写入。
a+ #打开一个文件用于读写。如果该文件已存在,文件指针将会放在文件的结尾。文件打开时会是追加模式。如果该文件不存在,创建新文件用于读写。
ab+ #以二进制格式打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾。如果该文件不存在,创建新文件用于读写。

用户登录(密码文件)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
#!/usr/bin/env python
# 基于密码数据文件的用户登录逻辑
import hashlib
def register(user,pwd):
'''
提供用户注册功能
:param user:
:param pwd:
:return:
'''
with open('db','a',encoding="utf-8") as f1:
f1.write(user+'|'+jiami(pwd)+'\n')

def jiami(pwd):
'''
提供密码MD5加密功能
:param pwd:
:return:
'''
temp = hashlib.md5(bytes('curry',encoding="utf-8"))
temp.update(bytes(pwd,encoding="utf-8"))
return temp.hexdigest()

def login(user,pwd):
'''
提供用户登录功能
:param user:
:param pwd:
:return:
'''
with open('db','r',encoding="utf-8") as f2:
for line in f2:
u = line.strip().split("|")[0]
p = line.strip().split("|")[1]
if u == user and p == jiami(pwd):
return True
else:
return False

def chpwd(user,pwd):
'''
提供用户修改密码功能
:param user:
:param pwd:
:return:
'''
new_data = ""
with open('db','r',encoding="utf-8") as f3:
for line in f3:
u = line.strip().split("|")[0]
p = line.strip().split("|")[1]
if u == user and p == jiami(pwd):
new_pwd = input("请输入新的密码:")
line = line.replace(p,jiami(new_pwd)) #文件内容替换
else:
pass
new_data += line
with open('db','w',encoding="utf-8") as f4:
f4.write(new_data)
print("密码更改成功")

def main():
'''
主函数
:return:
'''
i = input("1.登录,2.注册,3.修改密码")
if i == "2":
user = input("用户名:")
pwd = input("密码:")
register(user,pwd)
elif i == "1":
user = input("用户名:")
pwd = input("密码:")
ret = login(user,pwd)
if ret:
print("登录成功")
else:
print("登录失败")
elif i == "3":
user = input("用户名:")
pwd = input("密码:")
chpwd(user,pwd)

if __name__ == "__main__":
main()

加密

(1)丶设计一个函数,对传入的字符串(假设字符串中只包含小写字母和空格)进行加密操作,
加密的规则是a变d,b变e,c变f,…….,x变a,y变b,z变c,空格不变,返回加密后的字符串

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#!/usr/bin/env python
# -*- coding=utf-8 -*-
def jiami(arg):
temp = ''
for i in arg:
if i == ' ':
temp += i
elif ord(i) < 120:
temp += chr(ord(i) + 3)
else:
temp += chr(ord(i) - 23)
return temp

inp = input("请输入字符串(只包含小写字母和空格):")
new_inp = jiami(inp)
print(new_inp)

不常用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
ascii()       #对象的类中找_repr_,获取其返回值
例:
class Foo:
def _repr_(self):
return "hello"

r=ascii(Foo())
print(r) #返回值为<__main__.Foo object at 0x013E8B10>

bytearray() #字节列表
callable() #是否可执行,返回True/False,例如函数可执行,返回True
compile() #接收一个字符串,把该字符串编译为python可执行的代码
exec() #与eval类似,无返回值,只执行代码
例:
exec("for i in range(10):print(i)")
hash() #得到一个对象的哈希值,如字典key,hash("liyun"),输出为哈希值:-416968271
issubclass() #用于判断参数 class 是否是类型参数 classinfo 的子类,语法为:issubclass(class, classinfo)
例:
class A:
pass
class B(A):
pass
issubclass(B,A) #返回值为True
iter() #用来生成迭代器,语法为:iter(object[, sentinel])
例:
for i in iter([11,22,33]):
print(i)
obj = iter([11,22,33]) #创建一个可迭代的对象
print(next(obj)) #从11开始取值,每次取下一个值
reversed() #反转,生成一个可迭代的对象
例:
for i in reversed([11,44,33]):
print(i) #输出为33 44 11
slice() #根据索引切片
例:
[11,22,33,44,55,66][slice(1,5,2)] #输出为[22,44],列表截取1-4元素布长为2的切片
zip() #用于将可迭代的对象作为参数,将对象中对应的元素打包成一个个元组
例:
li1 = [1,2,3,4]
li2 = ["a","b","c","d"]
r = zip(li1 ,li2)
for i in r:
print(i) #输出为:(1,'a') (2,'b') (3,'c') (4,'d')

正则表达式re

常用符号

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
'^'  #匹配字符串的开头,若放在[]内,则表示非。
re.findall('^s','sb') #输出为:['s']
re.findall('[^s]','bssim') #输出为:['b', 'i', 'm']

'$' #匹配字符串的末尾。
re.findall('b$','sb') #输出为:['b']

'.' #匹配任意字符,除了换行符,当re.DOTALL标记被指定时,则可以匹配包括换行符的任意字符。
re.findall('.','bssim\nc') #输出为:['b', 's', 's', 'i', 'm', 'c']

'*' #匹配0个或多个的表达式。
re.findall('.*m','bssim\nc') #输出为:['bssim']
re.findall('.*c','bssim\nc') #输出为:['c']

'+' #匹配1个或多个的表达式。
re.findall('.+c','bssim\nc') #输出为:[]
re.findall('.+m','bssim\nc') #输出为:['bssim']

'?' #匹配0个或1个由前面的正则表达式定义的片段,非贪婪方式
re.findall('.?m','bssim\nc') #输出为:['im']
re.findall('.m?','bssim\nc') #输出为:['b', 's', 's', 'im', 'c']

'|' #或运算
re.findall('s|i','bssim\nc') #输出为:['s', 's', 'i']

'{m}' #匹配前一个字符m次
re.findall('ab{2}','abb abc abbcbbb') #输出为:['abb', 'abb']

'{n,m}' #匹配前一个字符n到m次
re.findall("ab{1,3}","abb abc abbcbbb") #输出为:['abb', 'ab', 'abb']

'(...)' #分组匹配

'\' #后面接常用符号,可将其普通化,接普通字符,可将其特殊化

'\A' #从字符开头匹配
re.findall("\Aabc","alexabc") #从开头匹配'abc',输出为:[],匹配不到

'\Z' #匹配字符结尾,同'$'

'\d' #匹配数字0-9
re.findall('\d','ab1cd2ef3') #输出为:['1','2','3']

'\D' #匹配非数字
re.findall('\D','ab1cd2ef3') #输出为:['a','b','c','d','e','f']

'\w' #匹配字母和数字和下划线
re.findall('\w','a1$_#c2') #输出为:['a','1','_','c','2']

'\W' #匹配非字母和数字和下划线
re.findall('\W','a1_$#c2') #输出为:['$','#']

'\s' #匹配空白字符,如换行符等
re.findall('\s','a\t\nb') #输出为:['\t','\n']

'\S' #匹配任何非空白字符,除换行符等除外

'\1' #表示对应的分组在此处出现一次,如'(ab)(cd)\1\2',则\1实际指的是(ab),\2指的是(cd)

方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
re.findall()            #把所有匹配到的字符放到以列表中的元素返回
re.findall(string, start, end) #可以与re.compile()一起使用,字符串指定位置start到end查找
re.findall('\d+a','b13ac4ad') #输出['13a', '4a']
re.findall('(\d+)a','b13ac4ad') #输出['13', '4'],找到13a和4a,并去掉a输出
re.findall('((\d+)a)','b13ac4ad') #输出[('13a', '13'), ('4a', '4')],组内再分组
re.findall('(\w+){4}','lishizheng') #输出['g'],(\w)虽然重复了四次,但是表达式只有一个,默认输出末尾字符

re.match(pattern, string, flags) #从头开始匹配,re.match只匹配字符串的开始,如果字符串开始不符合正则表达式,则匹配失败,函数返回None;而re.search匹配整个字符串,直到找到一个匹配。
r1 = re.match('h\w+','hello world')
r2 = re.match('(h)(\w+)','hello world')
r3 = re.match('(?P<n1>h)(?P<n2>\w+)','hello world')
print(r1.group(),r1.groups(),r1.groupdict()) #输出为hello () {} ,用法和search相同
print(r2.group(),r2.groups(),r2.groupdict()) #输出为hello ('h', 'ello') {}
print(r3.group(),r3.groups(),r3.groupdict()) #输出为hello ('h', 'ello') {'n1': 'h', 'n2': 'ello'}

re.search(pattern, string, flags) #匹配整个字符串,直到找到一个匹配,返回一个match对象,未匹配成功返回None
re.search(pattern, string, flags).group() #group()默认等于group(0),即所有的元素,输出为字符串
re.search('www', 'www.runoob.com').span()) #可查看匹配的位置

re.sub(pattern, repl, string, count,flag) #匹配字符并替换,pattern为正则,repl为需要替换成的字符串,也可为函数,string为原始字符串,count为匹配模式后替换的最大数量,flag为标志位
re.sub('[0-9]','|', 'a1b2c3d e8',count=2) #输出为新的字符串:'a|b|c3d e8'
re.sub('[0-9]','|', 'a1b2c3d e8') #没有count或者等于0默认全部替换,输出为新的字符串:'a|b|c|d e|'

re.compile() #编译正则表达式
rule = re.compile('[0-9]',re.I) #将正则编译放到一个变量
rule.split('a1b2c3d e8') #根据这个规则来进行分割,输出为:['a', 'b', 'c', 'd e', '']
rule.findall('run88oob123google456', 0, 10) #查找字符串0-10的位置的所有数字,输出为:['8','8','1','2']

re.finditer(pattern, string, flags=0) #和 findall 类似,在字符串中找到正则表达式所匹配的所有子串,并把它们作为一个迭代器返回。

re.split() #将匹配到的格式当做分割点对字符串分割成列表
re.split('[0-9]', 'a1b2c3d e8') #输出为:['a', 'b', 'c', 'd e', '']
re.split('(ab)','123ab456ab789',1) #输出为:['123', 'ab', '456ab789'],加()表示连表达式匹配一起输出,1表示匹配一次,默认匹配多次

flag说明
re.I #使匹配对大小写不敏感
re.L #做本地化识别(locale-aware)匹配
re.M #多行匹配,影响 ^ 和 $
re.S #使 . 匹配包括换行在内的所有字符
re.U #根据Unicode字符集解析字符。这个标志影响 \w, \W, \b, \B.
re.X #该标志通过给予你更灵活的格式以便你将正则表达式写得更易于理解。

正则表达式之计算器

用户输入数学表达式,含+-/和(),计算结果,例如:’1 - 2 ((60-30 +(-40/5) (9-25/3 + 7 /399/42998 +10 568/14 )) - (-43)/ (16-3*2))’

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#! /usr/bin/env python
# -*- coding:utf-8 -*-
import re
def f1(arg):
sum1 = eval(arg) #执行一个字符串形式的算术表达式
return sum1

inp = input("请输入您要计算的数学表达式,其中包含加减乘除:")
exp = inp
while True:
list = re.split('\(([^()]+)\)', exp , 1) #根据第一个括号内不含括号的表达式进行分割
if len(list) == 3:
temp1 = f1(list[1])
temp2 = list[0] + str(temp1) +list[2]
exp = temp2
else:
sum = f1(list[0])
break
print("%s = %d" %(inp,sum))

常用模块

time

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import time             #导入time模块,如果执行`from time import time`,则调用时少写一个time
print(time.clock()) #返回处理器时间,3.3开始已废弃 , 改成了time.process_time()测量处理器运算时间,不包括sleep时间,不稳定,mac上测不出来输出为:8.552505542023591e-07
print(time.altzone) #返回与utc时间的时间差,以秒计算,输出为:-32400
rint(time.asctime()) #返回时间格式"Fri Aug 19 11:14:16 2016",输出为:Tue Mar 27 13:45:09 2018
print(time.localtime()) #返回本地时间 的struct time对象格式,输出为:time.struct_time(tm_year=2018, tm_mon=3, tm_mday=27, tm_hour=13, tm_min=45, tm_sec=9, tm_wday=1, tm_yday=86, tm_isdst=0)
print(time.gmtime(time.time()-800000)) #返回utc时间的struc时间对象格式,输出为:time.struct_time(tm_year=2018, tm_mon=3, tm_mday=17, tm_hour=23, tm_min=31, tm_sec=49, tm_wday=5, tm_yday=76, tm_isdst=0)
print(time.asctime(time.localtime())) #输出为:Tue Mar 27 13:45:09 2018
print(time.ctime()) #输出为:Tue Mar 27 13:45:09 2018
truct = time.strptime("2016/05/22","%Y/%m/%d") #将日期字符串 转成 struct时间对象格式
print(string_2_struct) #输出为:time.struct_time(tm_year=2016, tm_mon=5, tm_mday=22, tm_hour=0, tm_min=0, tm_sec=0, tm_wday=6, tm_yday=143, tm_isdst=-1)
struct_2_stamp = time.mktime(string_2_struct) #将struct时间对象转成时间戳
print(struct_2_stamp) #输出为:1463846400.0
print(time.gmtime(time.time()-86640)) #将utc时间戳转换成struct_time格式,输出为:time.struct_time(tm_year=2018, tm_mon=3, tm_mday=26, tm_hour=5, tm_min=49, tm_sec=7, tm_wday=0, tm_yday=85, tm_isdst=0)
print(time.strftime("%Y-%m-%d %H:%M:%S",time.gmtime())) #将utc struct_time格式转成指定的字符串格式,输出为:2018-03-27 05:53:07
print(time.strftime('%Y%m%d%H%M',time.localtime(time.time()))) #取本地时间的时间戳,输出为:201803271406
print(time.strftime('%Y%m%d%H%M',time.localtime())) #取本地时间的时间戳,输出为:201803271406
print(time.strftime('%Y%m%d%H%M')) #取本地时间的时间戳,输出为:201803271406

datetime

1
2
3
4
5
6
7
8
9
10
import datetime    #导入datetime模块
print(datetime.datetime.now()) #输出为:2018-03-27 13:56:12.918226
print(datetime.date.fromtimestamp(time.time())) #输出为:2018-03-27
print(datetime.datetime.now() + datetime.timedelta(3)) #当前时间加三天,输出为:2018-03-30 13:58:55.264512
print(datetime.datetime.now() + datetime.timedelta(-3)) #当前时间减三天,输出为:2018-03-24 13:58:55.264512
print(datetime.datetime.now() + datetime.timedelta(hours=3)) #当前时间加三小时,输出为:2018-03-27 16:58:55.264512
print(datetime.datetime.now() + datetime.timedelta(minutes=30)) #当前时间加30分钟
print(datetime.datetime.now() + datetime.timedelta(weeks=1)) #当前时间加一周
c_time = datetime.datetime.now()
print(c_time.replace(minute=3,hour=2)) #时间替换

sys

1
2
3
4
5
6
7
8
9
import sys         #导入sys模块
sys.argv #命令行参数List,执行python文件时可以传入多个参数,参数以列表的形式保存,第一个元素是程序本身路径
sys.exit(n) #退出程序,正常退出时exit(0)
sys.version #获取Python解释程序的版本信息
sys.maxint #最大的Int值
sys.path #返回模块的搜索路径,初始化时使用PYTHONPATH环境变量的值,默认当前路径优先,python\lib\site-packages 路径存放所有第三方安装的模块
sys.platform #返回操作系统平台名称
sys.stdout.write('please:')
val = sys.stdin.readline()[:-1]

进度条作业

1
2
3
4
5
6
7
8
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import sys,time
for i in range(31):
sys.stdout.write('\r') #清除屏幕输出
sys.stdout.write('%s%% | %s' %(int(i/30*100),int(i/30*100)*'*'))
sys.stdout.flush() #刷新,每一次都输出
time.sleep(0.5)

将用户传入的参数变为文件夹

#例如文件名为createdir.py,则执行`python create.py a b c`就会在当前目录新建a,b,c三个目录
1
2
3
4
5
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import sys,os
for i in range(1,len(sys.argv)):
os.mkdir(sys.argv[i])

pickle和json

用于序列化和反序列化的两个模块,提供了四个功能:dumps、dump、loads、load,pickle用于python内部,json可与其他语言共用

1
2
3
4
pickle.loads()     #将字符串转化为python数据类型列表,元组,字典,可从文件中读取列表,元组,字典类型的数据,主要使用带s的    
pickle.load()
pickle.dumps() #将python数据类型转化为字符串,与open结合可将列表,元组,字典类型的数据放进文件中
pickle.dump()

购物车相关

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import pickle
account_info ={
1:{
'name':'liyunliang',
'passwd':'lylapqh',
'tel':'13888675421',
'balance':15000
}
}
f = open('account_db','wb')
f.write(pickle.dumps(account_info)) #将字典的信息写进数据文件中
# pickle.dump(account_info,f) 和上面等价
f.close()
1
2
3
4
5
6
7
8
9
10
11
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import pickle
account_file = open('account_db','rb')
account_dic = pickle.loads(account_file.read()) #将数据文件读到字典里
# account_dic = pickle.load(account_file) 和上面等价
account_file.close()
account_dic[1]['balance'] -= 500 #购物后找到对应人账户余额减去商品价格
f = open('account_db','wb')
f.write(pickle.dumps(account_dic)) #将新的字典写入数据文件
f.close()

os

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
os.getcwd()                 #获取当前工作目录,即当前python脚本工作的目录路径
os.chdir("dirname") #改变当前脚本工作目录;相当于shell下cd
os.curdir #返回当前目录: ('.')
os.pardir #获取当前目录的父目录字符串名:('..')
os.makedirs('dir1/dir2') #可生成多层递归目录
os.removedirs('dirname1') #若目录为空,则删除,并递归到上一级目录,如若也为空,则删除,依此类推
os.mkdir('dirname') #生成单级目录;相当于shell中mkdir dirname
os.rmdir('dirname') #删除单级空目录,若目录不为空则无法删除,报错;相当于shell中rmdir dirname
os.listdir('dirname') #列出指定目录下的所有文件和子目录,包括隐藏文件,并以列表方式打印
os.remove() #删除一个文件
os.rename("oldname","new") #重命名文件/目录
os.stat('path/filename') #获取文件/目录信息
os.sep #操作系统特定的路径分隔符,win下为"\\",Linux下为"/"
os.linesep #当前平台使用的行终止符,win下为"\t\n",Linux下为"\n"
os.pathsep #用于分割文件路径的字符串
os.name #字符串指示当前使用平台。win->'nt'; Linux->'posix'
os.system("bash command") #运行shell命令,直接显示
os.environ #获取系统环境变量
os.path.abspath(path) #返回path规范化的绝对路径
os.path.split(path) #将path分割成目录和文件名二元组返回
os.path.dirname(path) #返回path的目录。其实就是os.path.split(path)的第一个元素
os.path.basename(path) #返回path最后的文件名。如何path以/或\结尾,那么就会返回空值。即os.path.split(path)的第二个元素
os.path.exists(path) #如果path存在,返回True;如果path不存在,返回False
os.path.isabs(path) #如果path是绝对路径,返回True
os.path.isfile(path) #如果path是一个存在的文件,返回True。否则返回False
os.path.isdir(path) #如果path是一个存在的目录,则返回True。否则返回False
os.path.join(path1[, path2[, ...]]) #将多个路径组合后返回,第一个绝对路径之前的参数将被忽略
os.path.getatime(path) #返回path所指向的文件或者目录的最后存取时间
os.path.getmtime(path) #返回path所指向的文件或者目录的最后修改时间

hashlib

用于加密相关的操作,代替了md5模块和sha模块,主要提供 SHA1, SHA224, SHA256, SHA384, SHA512 ,MD5 算法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
import hashlib

# ######## md5 ########
hash = hashlib.md5()
# help(hash.update)
hash.update(bytes('admin', encoding='utf-8'))
print(hash.hexdigest())
print(hash.digest())

######## sha1 ########
hash = hashlib.sha1()
hash.update(bytes('admin', encoding='utf-8'))
print(hash.hexdigest())

# ######## sha256 ########
hash = hashlib.sha256()
hash.update(bytes('admin', encoding='utf-8'))
print(hash.hexdigest())

# ######## sha384 ########
hash = hashlib.sha384()
hash.update(bytes('admin', encoding='utf-8'))
print(hash.hexdigest())

# ######## sha512 ########
hash = hashlib.sha512()
hash.update(bytes('admin', encoding='utf-8'))
print(hash.hexdigest())

以上加密算法虽然依然非常厉害,但时候存在缺陷,即:通过撞库可以反解。所以,有必要对加密算法中添加自定义key再来做加密。
# ######## md5 ########
hash = hashlib.md5(bytes('898oaFs09f',encoding="utf-8"))
hash.update(bytes('admin',encoding="utf-8"))
print(hash.hexdigest())

hmac

python内置还有一个 hmac 模块,它内部对我们创建 key 和 内容 进行进一步的处理然后再加密

1
2
3
4
import hmac 
h = hmac.new(bytes('898oaFs09f',encoding="utf-8"))
h.update(bytes('admin',encoding="utf-8"))
print(h.hexdigest())

random

1
2
3
4
import random
print(random.random()) #随机生成0-1之间的小数
print(random.randint(1, 2)) #随机生成1或者2
print(random.randrange(1, 10)) #随机生成1-10中的一个数

4位的随机数验证码。

例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#!/usr/bin/env python
# -*- coding=utf-8 -*-
import random
while True:
temp=""
for i in range(4):
num = random.randrange(0,4)
if num == 0 or num == 2:
rad1 = random.randrange(0,10)
temp += str(rad1)
else:
rad2 = random.randrange(65,91)
temp += chr(rad2) #chr()为找到数字对应的字符
print(temp)
shuru = str(input("请输入验证码:"))
if shuru == temp:
break
else:
pass

XML

XML是实现不同语言或程序之间进行数据交换的协议,XML文件格式如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<data>
<country name="Liechtenstein">
<rank updated="yes">2</rank>
<year>2023</year>
<gdppc>141100</gdppc>
<neighbor direction="E" name="Austria" />
<neighbor direction="W" name="Switzerland" />
</country>
<country name="Singapore">
<rank updated="yes">5</rank>
<year>2026</year>
<gdppc>59900</gdppc>
<neighbor direction="N" name="Malaysia" />
</country>
<country name="Panama">
<rank updated="yes">69</rank>
<year>2026</year>
<gdppc>13600</gdppc>
<neighbor direction="W" name="Costa Rica" />
<neighbor direction="E" name="Colombia" />
</country>
</data>

操作XML

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
#!/usr/bin/env python
# -*- coding:utf-8 -*-
from xml.etree import ElementTree as ET #解析文件或者字符串,做修改,都在内存中进行,原对象不改变,如需修改得重新write写入
str_xml = open('xo.xml', 'r').read() #打开文件,读取XML内容,内容为xml格式的字符串
print(ET.XML(str_xml)) #将xml公司的字符串解析成xml特殊对象,获取xml文件的根节点
print(ET.parse("xo.xml")) #直接解析xml文件
print(ET.parse("xo.xml").getroot()) #获取xml文件的根节点
print(ET.parse("xo.xml").getroot().tag) #顶层标签
root = ET.parse("xo.xml").getroot() #与下面的相同
root = ET.XML(str_xml)

for child in root: #遍历XML文档的第二层
print(child.tag, child.attrib) #第二层节点的标签名称和标签属性
for i in child: #遍历XML文档的第三层
print(i.tag,i.text) #第二层节点的标签名称和内容

for node in root.iter('year'): #遍历XML中所有的指定的year节点
print(node.tag, node.text) #节点的标签名称和内容

for node in root.iter('year'): #循环所有的year节点
new_year = int(node.text) + 1 #将year节点中的内容自增1
node.text = str(new_year)
node.set('name', 'SubHy') #设置属性
node.set('age', '18')
del node.attrib['name'] #删除属性
tree = ET.ElementTree(root) #保存文件
tree.write("newnew.xml", encoding='utf-8')

缩进的保存一个XML文件

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
#!/usr/bin/env python
# -*- coding:utf-8 -*-
from xml.etree import ElementTree as ET
from xml.dom import minidom
def mkxml(elem,filename):
rough_string = ET.tostring(elem,'utf-8')
reparsed = minidom.parseString(rough_string)
raw_str = reparsed.toprettyxml(indent="\t")
f = open(filename,'w',encoding='utf-8')
f.write(raw_str)
f.close()


root = ET.Element("famliy") # 创建根节点
# son1 = ET.Element('son', {'name': '儿1'}) #创建大儿子
son1 = root.makeelement('son', {'name': '儿1'})
# son2 = ET.Element('son', {"name": '儿2'}) #创建小儿子
son2 = root.makeelement('son', {"name": '儿2'})
# grandson1 = ET.Element('grandson', {'name': '儿11'}) #在大儿子中创建两个孙子
grandson11 = son1.makeelement('grandson', {'name': '儿11'})
# grandson2 = ET.Element('grandson', {'name': '儿12'})
grandson12 = son1.makeelement('grandson', {'name': '儿12'})
son1.append(grandson11) #把孙子添加到儿子1节点中
son1.append(grandson12)
root.append(son1) #把儿子添加到根节点中
root.append(son2)
mkxml(root,"xxxoo.xml")

xml命名空间,例如以H代指一个字符串”http://www.company.com"

1
2
3
4
5
6
7
8
9
10
11
#!/usr/bin/env python
# -*- coding:utf-8 -*-
from xml.etree import ElementTree as ET
ET.register_namespace('H',"http://www.company.com") #some name
# build a tree structure
root = ET.Element("{http://www.company.com}STUFF")
body = ET.SubElement(root, "{http://www.company.com}MORE_STUFF", attrib={"{http://www.company.com}hhh": "123"})
body.text = "STUFF EVERYWHERE!"
# wrap it in an ElementTree instance, and save as XML
tree = ET.ElementTree(root)
tree.write("page.xml",xml_declaration=True,encoding='utf-8',method="xml")

requests第三方模块

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
import requests,json
ret = requests.get('https://github.com/timeline.json') #无参数示例,get请求
print(ret.url) #输出请求网址的地址字符串类型'https://github.com/timeline.json'
print(ret.text) #返回请求解析得到的字符串内容
print(ret.content) #返回解析的二进制数据
print(ret.cookies) #输出请求网址的cookies信息
print(ret.headers) #输出请求网址的headers所有信息
print(ret.status_code) #输出请求页面的状态(状态码)
print(ret.history) #输出请求的历史记录(以列表的形式显示)
print(requests.codes.ok) #requests请求状态码,为200
ret.encoding = 'utf-8' #当解析到的输出乱码时,可设置编码

payload = {'key1': 'value1', 'key2': 'value2'}
ret = requests.get("http://httpbin.org/get", params=payload) #有参数示例,get请求
print(ret.url) #返回字符串'http://httpbin.org/get?key1=value1&key2=value2'
print(ret.text)

ret = requests.post("http://httpbin.org/post", data=payload) #基本POST请求示例
print(ret.text)

url = 'https://api.github.com/some/endpoint'
payload = {'some': 'data'}
headers = {'content-type': 'application/json'}
ret = requests.post(url, data=json.dumps(payload), headers=headers) #发送请求头和数据实例
print(ret.text)
print(ret.cookies)

QQ号是否在线检测

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#!/usr/bin/env python
# -*- coding:utf-8 -*-
#输入参数:QQ号码 String。返回数据:String,Y = 在线;N = 离线;E = QQ号码错误;A = 商业用户验证失败;V = 免费用户超过数量
import requests
from xml.etree import ElementTree as ET
def qqonlinedetect(args):
r = requests.get('http://www.webxml.com.cn//webservices/qqOnlineWebService.asmx/qqCheckOnline?qqCode='+args)
result = r.text
node = ET.XML(result) #解析XML格式内容
if node.text == "Y": #获取内容
return True
else:
return False
if __name__ == "__main__":
ret = qqonlinedetect(input("请输入QQ号:"))
if ret:
print("您的QQ号在线")
else:
print("您的QQ号离线")

列车时刻表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import urllib
import requests
from xml.etree import ElementTree as ET
#使用内置模块urllib发送HTTP请求,或者XML格式内容,该例中车次为:K113
"""
f = urllib.request.urlopen('http://www.webxml.com.cn/WebServices/TrainTimeWebService.asmx/getDetailInfoByTrainCode?TrainCode=K113&UserID=')
result = f.read().decode('utf-8')
"""
#使用第三方模块requests发送HTTP请求,获取XML格式内容
r = requests.get('http://www.webxml.com.cn/WebServices/TrainTimeWebService.asmx/getDetailInfoByTrainCode?TrainCode=K113&UserID=')
result = r.text
root = ET.XML(result) # 解析XML格式内容
for node in root.iter('TrainDetailInfo'):
print(node.find('TrainStation').text,node.find('StartTime').text,node.find('KM').text)

configparser

configparser用于处理特定格式的文件,其本质上是利用open来操作文件。
xx文件如下

1
2
3
4
5
6
[section1]  # 节点
k1 = v1 # 值
k2:v2 # 值

[section2] # 节点
k1 = v1 # 值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import configparser
config = configparser.ConfigParser()
config.read('xx', encoding='utf-8')
ret = config.sections() #获取所有节点
ret = config.items('section1') #获取指定节点下所有的键值对
ret = config.options('section1') #获取指定节点下所有的键
v = config.get('section1','k1') #获取指定节点下的key
has_sec = config.has_section('section1') #检查指定节点是否在config中,返回True/False
config.add_section("SEC_1") #添加一个节点,可以写到原文件,也可生成新文件
config.write(open('xx', 'w'))
config.remove_section("section1") #移除一个节点
config.write(open('xx', 'w'))
has_opt = config.has_option('section2', 'k1') #检测指定节点和key是否在config中,同时存在返回True,否则返回False
config.remove_option('section1', 'k1') #删除指定节点下的key及对应的值
config.write(open('xx', 'w'))
config.set('section1', 'k10', "123") #新增一个节点,新增对应的key和值
config.write(open('xx', 'w'))

logging

用于便捷记录日志且线程安全的模块。
注:只有【当前写等级】大于【日志等级】时,日志文件才被记录。
日志等级:

1
2
3
4
5
6
7
8
CRITICAL = 50
FATAL = CRITICAL
ERROR = 40
WARNING = 30
WARN = WARNING
INFO = 20
DEBUG = 10
NOTSET = 0

单文件日志记录

1
2
3
4
5
6
7
8
9
10
11
12
import logging
logging.basicConfig(filename='log.log', #定义日志的格式,以及写等级
format='%(asctime)s - %(name)s - %(levelname)s -%(module)s: %(message)s',
datefmt='%Y-%m-%d %H:%M:%S %p',
level=10) #level=10也可以换成level=logging.DEBUG

logging.debug('debug')
logging.info('info')
logging.warning('warning')
logging.error('error')
logging.critical('critical')
logging.log(10,'debug') #用法和logging.debug一致

多文件日志记录

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import logging
# 定义文件
file_1_1 = logging.FileHandler('l1_1.log', 'a', encoding='utf-8')
fmt = logging.Formatter(fmt="%(asctime)s - %(name)s - %(levelname)s -%(module)s: %(message)s")
file_1_1.setFormatter(fmt)
file_1_2 = logging.FileHandler('l1_2.log', 'a', encoding='utf-8')
fmt = logging.Formatter()
file_1_2.setFormatter(fmt)
# 定义日志
logger1 = logging.Logger('s2', level=logging.DEBUG)
logger1.addHandler(file_1_1)
logger1.addHandler(file_1_2)
# 写日志
logger1.critical('1111')

subprocess

用于执行shell命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import subprocess
ret1 = subprocess.check_output(["echo","lyl"]) #执行命令不显示结果,如果正确执行 ,则返回执行结果(字节方式),否则抛异常
ret2 = subprocess.call("echo lyl") #执行命令并显示结果,如果正确执行 ,则返回0,否则抛异常
ret3 = subprocess.check_call("echo lyl") #执行命令并显示结果,如果正确执行 ,则返回0,否则抛异常
ret4 = subprocess.Popen("echo lyl") #执行命令并显示结果,如果正确执行,返回值为Popen对象
print(ret1,ret2,ret3,ret4)

# 进入某环境,依赖再输入,如:python
obj = subprocess.Popen(["python"], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True)
obj.stdin.write("print(1)\n") #执行的命令
obj.stdin.write("print(2)")
obj.stdin.close()
cmd_out = obj.stdout.read() #输出
obj.stdout.close()
cmd_error = obj.stderr.read() #错误
obj.stderr.close()
print(cmd_out) #输出:1,2
print(cmd_error)

out_error_list = obj.communicate()
print(out_error_list) #输出:('1\n2\n', '')

out_error_list = obj.communicate('print("hello")') #可直接接收命令并输出
print(out_error_list) #输出:('hello\n', '')

shutil

可进行高级的文件、文件夹、压缩包处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
shutil.copyfileobj(open('old.xml','r'), open('new.xml', 'w'))         #拷贝文件
shutil.copyfile('f1.log', 'f2.log') #拷贝文件
shutil.copymode('f1.log', 'f2.log') #仅拷贝权限,内容、组、用户均不变,文件不存在会报错
shutil.copystat('f1.log', 'f2.log') #仅拷贝状态的信息,包括:mode bits, atime, mtime, flags,文件不存在会报错
shutil.copy('f1.log', 'f2.log') #拷贝文件和权限
shutil.copy2('f1.log', 'f2.log') #拷贝文件和状态信息
shutil.copytree("G:\mysite", "G:\mysite1",ignore=shutil.ignore_patterns('*.pyc', 'tmp*')) #递归拷贝文件夹,忽略pyc类型的文件和tmp开头的文件
shutil.rmtree("G:\mysite1") #递归的删除文件夹
shutil.move("G:\mysite", "G:\mysite1") #递归的去移动文件,它类似mv命令,其实就是重命名

# shutil.make_archive(base_name, format,...)
# 创建压缩包并返回文件路径,例如:zip、tar
#base_name: 压缩包的文件名,也可以是压缩包的路径。只是文件名时,则保存至当前目录,否则保存至指定路径,
# 如:www =>保存至当前路径
#如:/Users/wupeiqi/www =>保存至/Users/wupeiqi/
# format: 压缩包种类,“zip”, “tar”, “bztar”,“gztar”
# root_dir: 要压缩的文件夹路径(默认当前目录)
# owner: 用户,默认当前用户
# group: 组,默认当前组
# logger: 用于记录日志,通常是logging.Logger对象
shutil.make_archive("G:\\aaaaa","zip",root_dir="G:\mysite") #将G:\mysite下的文件打包到G:\aaaaa.zip

# shutil 对压缩包的处理是调用 ZipFile 和 TarFile 两个模块来进行的
import zipfile
z = zipfile.ZipFile('laxi.zip', 'w') #压缩后的文件为laxi.zip
z.write('log.log') #往压缩文件添加内容
z.write('xx')
z.close()
z = zipfile.ZipFile('laxi.zip', 'a') #以追加的方式打开
z.write('a1.py') #往压缩文件中追加文件
z.close()
z = zipfile.ZipFile('laxi.zip', 'r') #解压缩
z.extractall() #全部解压
print(z.namelist()) #查看压缩包里的文件
z.extract(member="log.log") #指定文件解压
z.close()

import tarfile
tar = tarfile.open('your.zip','w') #压缩后的文件为you.zip
tar.add('log.log', arcname='new.log') #往压缩文件添加内容并改文件名
tar.add('xx', arcname='cmdb.log')
tar.close()
tar = tarfile.open('your.zip','r')
print(tar.getnames()) #查看压缩包里的文件
tar.extractall() #全部解压
tar.extract(member="new.log")
tar.close()

paramiko

paramiko是一个用于做远程控制的模块,使用该模块可以对远程服务器进行命令或文件操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import paramiko
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect('192.168.1.2', 22, 'root', 'rootadmin') #使用用户名和密码远程执行命令
# private_key_path = '/home/auto/.ssh/id_rsa' #密钥认证来远程执行
# key = paramiko.RSAKey.from_private_key_file(private_key_path)
# ssh.connect('192.168.1.2', 22, 'root', key)
stdin, stdout, stderr = ssh.exec_command('df -h')
print (stdout.read())
ssh.close()

import os,sys,paramiko
t = paramiko.Transport(("192.168.229.70",22))
t.connect(username="root",password="rootadmin")
# pravie_key_path = '/home/auto/.ssh/id_rsa' #密钥认证
# key = paramiko.RSAKey.from_private_key_file(pravie_key_path)
# t.connect(username='root',pkey=key)
sftp = paramiko.SFTPClient.from_transport(t)
sftp.put("a1.py","/tmp/test.py") #服务器上传文件
sftp.get("/mancenter-3.9.3.war","G:\\mancenter.war") #服务器下载文件
t.close()

模块拾遗

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#!/usr/bin/env python
# -*- coding:utf-8 -*-
print(vars()) #python自带的全局变量
print(__doc__) #py文件的注释
print(__package__) #指定文件所在的包,用.分割,当前文件返回None
print(__loader__)
print(__file__) #当前文件路径
print(__name__) #如果是主文件,在当前文件执行而不是调用,__name__ == "__main__",否则等于模块名
print(__cached__) #其他文件的缓存值,当前为None
print(__builtins__) #内置函数在这里面
def f1():
print("执行了")
return True

if __name__ == "__main__": #调用主文件前,必须加这个条件,只有当执行自身的文件才会执行f1(),被导入不执行f1()
f1()

os.path添加路径到sys.path

(1)丶当前文件路径为0410\s2.py,想添加0410\li路径,添加路径后直接导入li下的s1.py文件

1
2
3
4
5
6
import os,sys
temp1 = os.path.dirname(__file__) #当前文件路径
temp2 = "li"
temp = os.path.join(temp1,temp2) #路径拼接
sys.path.append(temp) #路径添加到sys.path
import s1

字符串格式化

百分号方式

%[(name)][flags][width].[precision]typecode
(1)丶(name) 可选,用于选择指定的key
(2)丶flags 可选,可供选择的值有:

  • 右对齐;正数前加正好,负数前加负号;
  • 左对齐;正数前无符号,负数前加负号;
    空格 右对齐;正数前加空格,负数前加负号;
    0 右对齐;正数前无符号,负数前加负号;如果是数字的话用0填充空白处
    (3)丶width 可选,占有宽度
    (4)丶.precision 可选,小数点后保留的位数
    (5)丶typecode 必选,可供选择的值有:
    s,获取传入对象的str方法的返回值,并将其格式化到指定位置
    r,获取传入对象的repr方法的返回值,并将其格式化到指定位置
    c,整数:将数字转换成其unicode对应的值,10进制范围为 0 <= i <= 1114111(py27则只支持0-255);字符:将字符添加到指定位置
    o,将整数转换成 八 进制表示,并将其格式化到指定位置
    x,将整数转换成十六进制表示,并将其格式化到指定位置
    d,将整数、浮点数转换成 十 进制表示,并将其格式化到指定位置
    e,将整数、浮点数转换成科学计数法,并将其格式化到指定位置(小写e)
    E,将整数、浮点数转换成科学计数法,并将其格式化到指定位置(大写E)
    f, 将整数、浮点数转换成浮点数表示,并将其格式化到指定位置(默认保留小数点后6位)
    F,同上
    g,自动调整将整数、浮点数转换成 浮点型或科学计数法表示(超过6位数用科学计数法),并将其格式化到指定位置(如果是科学计数则是e;)
    G,自动调整将整数、浮点数转换成 浮点型或科学计数法表示(超过6位数用科学计数法),并将其格式化到指定位置(如果是科学计数则是E;)
    %,当字符串中存在格式化标志时,需要用 %%表示一个百分号

注:Python中百分号格式化是不存在自动将整数转换成二进制表示的方式

1
2
3
4
5
6
print("i am %s" % "SubHyCan")
print("i am %s age %d" % ("SubHyCan", 24))
print("i am %(name)s age %(age)d" % {"name": "SubHyCan", "age": 18})
print("percent %.2f" % 99.97623)
print("i am %(pp).2f" % {"pp": 123.425556, })
print("i am %(pp).2f%%" % {"pp": 99.425556, })

format方式

[[fill]align][sign][#][0][width][,][.precision][type]
(1)丶fill 可选,空白处填充的字符
(2)丶align 可选,对齐方式(需配合width使用)
<,内容左对齐
>,内容右对齐(默认)
=,内容右对齐,将符号放置在填充字符的左侧,且只对数字类型有效。 即使:符号+填充物+数字
^,内容居中
(3)丶sign 可选,有无符号数字
+,正号加正,负号加负;
-,正号不变,负号加负;
空格 ,正号空格,负号加负;
(4)丶# 可选,对于二进制、八进制、十六进制,如果加上#,会显示 0b/0o/0x,否则不显示
(5)丶, 可选,为数字添加分隔符,如:1,000,000
(6)丶width 可选,格式化位所占宽度
(7)丶.precision 可选,小数位保留精度
(8)丶type 可选,格式化类型
传入” 字符串类型 “的参数
s,格式化字符串类型数据
空白,未指定类型,则默认是None,同s
传入“ 整数类型 ”的参数
b,将10进制整数自动转换成2进制表示然后格式化
c,将10进制整数自动转换为其对应的unicode字符
d,十进制整数
o,将10进制整数自动转换成8进制表示然后格式化;
x,将10进制整数自动转换成16进制表示然后格式化(小写x)
X,将10进制整数自动转换成16进制表示然后格式化(大写X)
传入“ 浮点型或小数类型 ”的参数
e, 转换为科学计数法(小写e)表示,然后格式化;
E, 转换为科学计数法(大写E)表示,然后格式化;
f , 转换为浮点型(默认小数点后保留6位)表示,然后格式化;
F, 转换为浮点型(默认小数点后保留6位)表示,然后格式化;
g, 自动在e和f中切换
G, 自动在E和F中切换
%,显示百分比(默认显示小数点后6位)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
print("i am {}, age {}, {}".format("seven", 24, 'SubHyCan')) 
print("i am {}, age {}, {}".format(*["seven", 24, 'SubHyCan']))
print("i am {0}, age {1}, really {0}".format("seven", 18))
print("i am {0}, age {1}, really {0}".format(*["seven", 18]))
print("i am {name}, age {age}, really {name}".format(name="seven", age=18))
print("i am {name}, age {age}, really {name}".format(**{"name": "seven", "age": 18}))
print("i am {0[0]}, age {0[1]}, really {0[2]}".format([1, 2, 3], [11, 22, 33]))
print("i am {:s}, age {:d}, money {:f}".format("seven", 18, 88888.1))
print("i am {:s}, age {:d}".format(*["seven", 18]))
print("i am {name:s}, age {age:d}".format(name="seven", age=18))
print("i am {name:s}, age {age:d}".format(**{"name": "seven", "age": 18}))
print("numbers: {:b},{:o},{:d},{:x},{:X}, {:%}".format(15, 15, 15, 15, 15, 15.87623, 2))
print("numbers: {:b},{:o},{:d},{:x},{:X}, {:%}".format(15, 15, 15, 15, 15, 15.87623, 2))
print("numbers: {0:b},{0:o},{0:d},{0:x},{0:X}, {0:%}".format(15))
print("numbers: {num:b},{num:o},{num:d},{num:x},{num:X}, {num:%}".format(num=15))

面向对象

三种编程方式为:面向过程编程,函数式编程,面向对象编程
面向对象三大特性:封装,继承,多态

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
class Foo():                               #Foo后的()可省略
def f1(self):
print(self) #self是python内部默认的参数,指向引用类的对象或实例
def f2(self,arg2):
print(self,arg)
obj = Foo() #通过类对象指针,创建一个引用类Foo的对象或实例
print(obj) #和类中print(self)的值是一样的
obj.f1()
obj.f2("shuaiguo")

### 封装
class Foo1():
def __init__(self): #封装,__init__构造方法,在遇到将类指向一个对象或实例时,会初始化执行
self.name = "lyl"
self.age = 18
def detail(self):
print (self.name,self.age)
obj1 = Foo1()
obj1.detail()
print (obj1.name,obj1.age) #直接调用obj1对象的name属性和age属性

class Foo2(Foo1):
def __init__(self, name, age): #封装,子类Foo2和父类Foo1都封装了__init__方法,则子类会覆盖父类的方法
super().__init__() #使用super函数可直接调用父类与子类相同的方法
self.xinbie = name
self.xinzuo = age
def detail(self):
print (self.xinbie,self.xinzuo)
obj1 = Foo2('男', "射手")
obj1.detail() #Python默认会将obj1传给self参数,即:obj1.detail(obj1),所以,此时方法内部的 self = obj1,即:self.name 是 lyl ;self.age 是 18
print (obj1.name,obj1.age)

游戏人生

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
class Person:
def __init__(self, na, gen, age, fig):
self.name = na
self.gender = gen
self.age = age
self.fight =fig
def grassland(self):
"""注释:草丛战斗,消耗200战斗力"""
self.fight = self.fight - 200
def practice(self):
"""注释:自我修炼,增长100战斗力"""
self.fight = self.fight + 200
def incest(self):
"""注释:多人游戏,消耗500战斗力"""
self.fight = self.fight - 500
def detail(self):
"""注释:当前对象的详细情况"""
temp = "姓名:%s ; 性别:%s ; 年龄:%s ; 战斗力:%s" % (self.name, self.gender, self.age, self.fight)
print (temp)

# ##################### 开始游戏 #####################
cang = Person('苍', '女', 18, 1000) #创建苍角色
dong = Person('木木', '男', 20, 1800) #创建木木角色
bo = Person('多多', '女', 19, 2500) #创建多多角色

cang.incest() #苍参加一次多人游戏
dong.practice()#木木自我修炼了一次
bo.grassland() #多多参加一次草丛战斗
#输出当前所有人的详细情况
cang.detail()
dong.detail()
bo.detail()

继承

父类和子类,父类也称为基类,子类也称为派生类,派生类会继承基类的对象,python的类可继承多个类,Java和C#中则只能继承一个类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class D(object):
def bar(self):
print ('D.bar')

class C(D):
def bar(self):
print ('C.bar')

class B(D):
def bar(self):
print ('B.bar')

class A(B, C): #多类继承,A有多个基类,继承优先级B优于C
def bar(self):
print ('A.bar')

a = A()
# 执行bar方法时
# 首先去A类中查找,如果A类中没有,则继续去B类中找,如果B类中么有,则继续去C类中找,如果C类中么有,则继续去D类中找,如果还是未找到,则报错
# 所以,查找顺序:A --> B --> C --> D
# 在上述查找bar方法的过程中,一旦找到,则寻找过程立即中断,便不会再继续找了
a.bar()

多态

Pyhon不支持Java和C#这一类强类型语言中多态的写法,但是原生支持多态,其Python崇尚”鸭子类型”;python”鸭子类型”如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class F1:
pass
class S1(F1):
def show(self):
print 'S1.show'
class S2(F1):
def show(self):
print 'S2.show'
def Func(obj): #def Func(F1 obj): 伪代码实现java和C#的多态
print obj.show() # print obj.show()
s1_obj = S1()
Func(s1_obj)

s2_obj = S2()
Func(s2_obj)

反射

python中的反射功能是由以下四个内置函数提供:hasattr、getattr、setattr、delattr,该四个函数分别用于对对象内部执行:
检查是否含有某成员、获取成员、设置成员、删除成员,通过字符串的形式操作对象相关的成员。
反射是通过字符串的形式操作对象相关的成员,一切事物都是对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#!/usr/bin/env python
# -*- coding:utf-8 -*-
inp = input("请输入url(以模块名/对象的格式):")
mode,obejects = inp.split("/")
dd = __import__("lib." + mode, fromlist=True) #以字符串的方式导入模块,假设输入的模块都在lib下,模块都存在
if hasattr(dd,obejects): #判断用户输入的对象是否在模块中,如果存在,则返回设定的值或者url
ret= getattr(dd,obejects,)()
print(ret)
else:
print("404") #对象不存在,报404
print(hasattr(dd,"f1")) #False
temp = setattr(dd,"f1","lyl") #往内存中写入对象
print(hasattr(dd,"f1")) #True
delattr(dd,"f1") #在内存中删除对象
print(hasattr(dd,"f1")) #False

类成员

类成员分为三大类,分别为:
字段(普通字段,静态字段)
方法(普通方法,静态方法,类方法)
属性,也可称为特性(普通属性,也可称为普通特性);可通过@property装饰器或者静态字段方式创建

通过类来访问:静态字段,静态方法
通过对象来访问:普通字段,普通方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
class F1:
country = "静态字段" #静态字段,在内存中只保存一份,如果每个对象相同的字段,就使用它
def __init__(self,name):
self.name = name #普通字段
self.original_price = 100

@classmethod #定义一个类方法,此方法可通过类或者对象类访问
def f1(cls): #cls表示这个类方法属于的类F1
print("类方法")
print(cls)

@staticmethod #定义一个静态方法,此方法类似于在类中定义了一个普通函数,此方法可通过类或对象来访问
def f2():
print("静态方法")

def PT(self):
return "普通方法"

@property #在普通方法的基础上添加@property装饰器,特性其实就是将普通方法伪装成字段来访问获取
def price(self): #静态字段方式实现:如可新建三个对应函数get_price, set_price, del_price,通过PRICE = property(get_price, set_price, del_price, '价格属性描述...')来创建
print("特性")
return self.original_price

@price.setter
def price(self, value): #特性的访问特性,传参,可修改特性
self.original_price = value

@price.deleter #特性的访问特性,可删除特性
def price(self):
del self.original_price


obj = F1("普通字段")
print (obj.name) #通过对象访问普通字段
print (obj.price) #通过对象访问特性
print (obj.PT()) #通过对象访问普通方法
obj.price = 200
print (obj.price) #修改
del obj.price #删除
print (F1.country) #通过类访问静态字段

类成员修饰符

公有和私有,私有成员命名时,前两个字符是下划线。(特殊成员除外,例如:initcalldict等)
公有成员:对象可以访问;类内部可以访问;派生类中可以访问
私有成员:仅类内部可以访问;外部访问可通过公有成员曲线来访问;强制访问可通过(对象._类名__私有字段)来访问,但是不建议使用

1
2
3
4
5
6
7
8
9
10
11
12
class C:
def __init__(self):
self.name = '公有字段'
self.__foo = "私有字段"

def F1(self):
ret = self.__foo
return ret

obj = C()
print(obj.__foo) #无法访问,执行报错
print(obj.F1()) #通过F1来访问,找关系

类的特殊成员

1.doc,表示类的描述信息
2.moduleclass,表示当前操作的对象在那个模块和当前操作的对象的类是什么
3.init,构造方法,通过类创建对象时,自动触发执行
4.del,析构方法,当对象在内存中被释放时(比如内存辣鸡回收),自动触发执行
注:此方法一般无须定义,因为Python是一门高级语言,程序员在使用时无需关心内存的分配和释放,因为此工作都是交给Python解释器来执行,所以,析构函数的调用是由解释器在进行垃圾回收时自动触发执行的。
5.call,对象后面加括号,触发执行,即对象()或者类()(),执行类中的call方法
6.dict,类或对象中的所有成员,类的普通字段属于对象;类中的静态字段和方法等属于类
7.str,如果一个类中定义了str方法,那么在打印 对象 时,默认输出该方法的返回值
8.getitemsetitemdelitem,用于索引操作,如字典。以上分别表示获取、设置、删除数据
9.getslicesetslicedelslice,该三个方法用于分片操作,如:列表
10.iter,用于迭代器,之所以列表、字典、元组可以进行for循环,是因为类型内部定义了 iter
11.newmetaclass;obj对象是C类的一个实例,C类对象是 type类的一个实例,即:C类对象是通过type类的构造方法创建,类其实也是对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
class C(object):                            #object是一切类的基类
"""
这是一个类
"""
def __init__(self,sq):
self.name = 'lyl'
self.sq = sq
def __call__(self):
return "call"
def __str__(self):
return "lyl"
def __getitem__(self, key):
print ("__getitem__",key)
def __setitem__(self, key, value):
print ("__setitem__",key,value)
def __delitem__(self, key):
print ("__delitem__",key)
def __getslice__(self, i, j):
print ("__getslice__",i,j)
def __setslice__(self, i, j, sequence):
print ("__setslice__",i,j)
def __delslice__(self, i, j):
print ("__delslice__",i,j)
def __iter__(self):
return iter(self.sq)
obj = C([1,2,3,4])
print(obj.__doc__) #输出描述信息:这是一个类
print(obj.__module__)
print(obj.__class__) #输出当前对象操作的类
print(obj()) #因为定义了__call_,所以输出为:call
print(C.__dict__,obj.__dict__) #输出类中的所有成员,输出对象中的所有成员
print(obj) #因为定义了__str__,输出为:lyl,无对应则返回一个对象
result = obj['k1'] #自动触发执行 __getitem__,输出:__getitem__ k1
obj['k2'] = 'wupeiqi' #自动触发执行 __setitem__,输出:__setitem__ k2 wupeiqi
del obj['k1'] #自动触发执行 __delitem__,输出:__delitem__ k1
obj[-1:1] #自动触发执行 __getslice__,输出:__getitem__ slice(-1, 1, None)
obj[0:1] = [11,22,33,44] #自动触发执行 __setslice__,输出:__setitem__ slice(0, 1, None) [11, 22, 33, 44]
del obj[0:2] #自动触发执行 __delslice__,输出:__delitem__ slice(0, 2, None)
for i in obj:
print (i) #输出1,2,3,4,如果没有指定__iter__,则会无限循环下去

obj = iter([1,2,3,4]) #以上步骤可以看出,for循环迭代的其实是 iter([1,2,3,4]),所以执行流程可以变更为这个
for i in obj:
print (i)

类创建的两种方式:
一丶普通方法

1
2
3
class Foo(object):
def func(self):
print("nihao")

二丶特殊方式(type类的构造函数)

1
2
3
def func(self):
print("nihao")
Foo = type('Foo',(object,), {'func': func}) #type第一个参数:类名;第二个参数:当前类的基类;第三个参数:类的成员,可以为一个lamba表达式

异常处理

常用的异常:
AttributeError 试图访问一个对象没有的树形,比如foo.x,但是foo没有属性x
IOError 输入/输出异常;基本上是无法打开文件
ImportError 无法引入模块或包;基本上是路径问题或名称错误
IndentationError 语法错误(的子类) ;代码没有正确对齐
IndexError 下标索引超出序列边界,比如当x只有三个元素,却试图访问x[5]
KeyError 试图访问字典里不存在的键
KeyboardInterrupt Ctrl+C被按下
NameError 使用一个还未被赋予对象的变量
SyntaxError Python代码非法,代码不能编译(个人认为这是语法错误,写错了)
TypeError 传入对象类型与要求的不符合
UnboundLocalError 试图访问一个还未被设置的局部变量,基本上是由于另有一个同名的全局变量,
导致你以为正在访问它
ValueError 传入一个调用者不期望的值,即使值的类型是正确的

常用的异常处理框架:

1
2
3
4
5
6
7
8
9
10
try:
a = 1 + "lyl" #主代码块
except IndexError as e: #如果异常类型为IndexError,则执行该块,可定义多个异常处理
print(e) #异常时,执行该块,输出为异常信息
except Exception as e: #万能的异常处理,所有异常都能匹配
print(e)
else:
pass #主代码块执行完,执行该块
finally:
pass #无论异常与否,最终执行该块

主动触法异常:

1
2
3
4
try:
raise Exception('错误了。。。')
except Exception as e:
print (e) #输出为:错误了。。。

自定义异常:

1
2
3
4
5
6
7
8
9
class MyException(Exception):
def __init__(self, msg):
self.message = msg
def __str__(self):
return self.message
try:
raise MyException('我的异常')
except MyException as e:
print (e) #输出为:我的异常

单例模式

1丶单例模式,即只有一个实例
2丶静态方法+静态字段
3丶所有的实例中封装的内容相同时,用单例模式,多个实例会浪费内存
4丶单利模式存在的目的是保证当前内存中仅存在单个实例,避免内存浪费

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
from wsgiref.simple_server import make_server
# ########### 单例类定义 ###########
class DbHelper(object):
__instance = None
def __init__(self):
self.hostname = '1.1.1.1'
self.port = 3306
self.password = 'pwd'
self.username = 'root'
@staticmethod
def singleton():
if DbHelper.__instance:
return DbHelper.__instance
else:
DbHelper.__instance = DbHelper()
return DbHelper.__instance
def fetch(self):
# 连接数据库
# 拼接sql语句
# 操作
pass
def create(self):
# 连接数据库
# 拼接sql语句
# 操作
pass
def remove(self):
# 连接数据库
# 拼接sql语句
# 操作
pass
def modify(self):
# 连接数据库
# 拼接sql语句
# 操作
pass

class Handler(object):
def index(self):
obj = DbHelper.singleton() #对于Python单例模式,创建对象时不能再直接使用:obj = Foo(),而应该调用特殊的方法:obj = Foo.singleton()
print id(obj)
obj.create()
return 'index'
def news(self):
return 'news'

def RunServer(environ, start_response):
start_response('200 OK', [('Content-Type', 'text/html')])
url = environ['PATH_INFO']
temp = url.split('/')[1]
obj = Handler()
is_exist = hasattr(obj, temp)
if is_exist:
func = getattr(obj, temp)
ret = func()
return ret
else:
return '404 not found'

if __name__ == '__main__':
httpd = make_server('', 8002, RunServer)
print "Serving HTTP on port 8001..."
httpd.serve_forever()

Socket

socket通常也称作”套接字”,用于描述IP地址和端口,是一个通信链的句柄,应用程序通常通过”套接字”向网络发出请求或者应答网络请求。
socket起源于Unix,而Unix/Linux基本哲学之一就是“一切皆文件”,对于文件用【打开】【读写】【关闭】模式来操作。socket就是该模式的一个实现,socket即是一种特殊的文件,一些socket函数就是对其进行的操作(读/写IO、打开、关闭)

socket和file的区别:
file模块是针对某个指定文件进行【打开】【读写】【关闭】
socket模块是针对 服务器端 和 客户端Socket 进行【打开】【读写】【关闭】

sk = socket.socket(socket.AF_INET,socket.SOCK_STREAM,0)
参数一:地址簇
  socket.AF_INET IPv4(默认)
  socket.AF_INET6 IPv6
  socket.AF_UNIX 只能够用于单一的Unix系统进程间通信

参数二:类型
  socket.SOCK_STREAM  流式socket , for TCP (默认)
  socket.SOCK_DGRAM   数据报式socket , for UDP
  socket.SOCK_RAW 原始套接字,普通的套接字无法处理ICMP、IGMP等网络报文,而SOCK_RAW可以;其次,SOCK_RAW也可以处理特殊的IPv4报文;此外,利用原始套接字,可以通过IP_HDRINCL套接字选项由用户构造IP头。
  socket.SOCK_RDM 是一种可靠的UDP形式,即保证交付数据报但不保证顺序。SOCK_RAM用来提供对原始协议的低级访问,在需要执行某些特殊操作时使用,如发送ICMP报文。SOCK_RAM通常仅限于高级用户或管理员运行的程序使用。
  socket.SOCK_SEQPACKET 可靠的连续数据包服务

参数三:协议
  0  (默认)与特定的地址家族相关的协议,如果是 0 ,则系统就会根据地址格式和套接类别,自动选择一个合适的协议

sk.bind(address)
  将套接字绑定到地址。address地址的格式取决于地址族。在AF_INET下,以元组(host,port)的形式表示地址。

sk.listen(backlog)
  开始监听传入连接。backlog指定在拒绝连接之前,可以挂起的最大连接数量。
backlog等于5,表示内核已经接到了连接请求,但服务器还没有调用accept进行处理的连接个数最大为5
这个值不能无限大,因为要在内核中维护连接队列

sk.setblocking(bool)
  是否阻塞(默认True),如果设置False,那么accept和recv时一旦无数据,则报错。

sk.accept()
  接受连接并返回(conn,address),其中conn是新的套接字对象,可以用来接收和发送数据。address是连接客户端的地址。
接收TCP 客户的连接(阻塞式)等待连接的到来

sk.connect(address)
  连接到address处的套接字。一般,address的格式为元组(hostname,port),如果连接出错,返回socket.error错误。

sk.connect_ex(address)
  同上,只不过会有返回值,连接成功时返回 0 ,连接失败时候返回编码,例如:10061

sk.close()
  关闭套接字

sk.recv(bufsize[,flag])
  接受套接字的数据。数据以字符串形式返回,bufsize指定最多可以接收的数量。flag提供有关消息的其他信息,通常可以忽略。

sk.recvfrom(bufsize[.flag])
  与recv()类似,但返回值是(data,address)。其中data是包含接收数据的字符串,address是发送数据的套接字地址。

sk.send(string[,flag])
  将string中的数据发送到连接的套接字。返回值是要发送的字节数量,该数量可能小于string的字节大小。即:可能未将指定内容全部发送。

sk.sendall(string[,flag])
  将string中的数据发送到连接的套接字,但在返回之前会尝试发送所有数据。成功返回None,失败则抛出异常。
内部通过递归调用send,将所有内容发送出去。

sk.sendto(string[,flag],address)
  将数据发送到套接字,address是形式为(ipaddr,port)的元组,指定远程地址。返回值是发送的字节数。该函数主要用于UDP协议。

sk.settimeout(timeout)
  设置套接字操作的超时期,timeout是一个浮点数,单位是秒。值为None表示没有超时期。一般,超时期应该在刚创建套接字时设置,因为它们可能用于连接的操作(如 client 连接最多等待5s )

sk.getpeername()
  返回连接套接字的远程地址。返回值通常是元组(ipaddr,port)。

sk.getsockname()
  返回套接字自己的地址。通常是一个元组(ipaddr,port)

sk.fileno()
  套接字的文件描述符

WEB服务应用,客户端为浏览器,在浏览器中输入:127.0.0.1:8081来与服务端交互,浏览器页面返回:Hello World

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import socket
def handle_request(client):
buf = client.recv(1024)
client.send(bytes("HTTP/1.1 200 OK\r\n\r\n",encoding="utf-8"))
client.send(bytes("Hello, World",encoding="utf-8"))

def main():
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.bind(('localhost',8081))
sock.listen(5)

while True:
connection, address = sock.accept()
handle_request(connection)
connection.close()

if __name__ == '__main__':
main()

聊天机器人(单连接版)
服务端只能处理一个客户端连接,当有两个连接时,第二个连接处于等待状态,必须等到服务端处理完第一个连接,才能处理它
服务端:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import socket
ip_port = ('127.0.0.1',9999) #服务端用于客户端连接的ip和端口
sk = socket.socket()
sk.bind(ip_port) #开启服务端
sk.listen(5) #监听的最大连接数
while True:
print ('server waiting...')
conn,addr = sk.accept() #阻塞,直到有客户端连接,才往下执行,conn为连接的对象,addr为客户端IP和端口
conn.sendall(bytes('我是聊天机器人',encoding = "utf-8")) #发送消息给客户端
while True:
client_data = conn.recv(1024) #接收客户端的消息
if str(client_data,encoding="utf-8") == "q": #如果接收到的消息是q,即退出循环,等待下一个客户端连接
break
else:
conn.sendall(client_data + bytes("是一个大帅锅",encoding="utf-8")) #每次接收到除q外的字符串时,返回接收到的值加是一个大帅锅
conn.close() #连接关闭

客户端:

1
2
3
4
5
6
7
8
9
10
11
12
import socket
ip_port = ('127.0.0.1',9999) #连接服务端所用的IP和端口
sk = socket.socket()
sk.connect(ip_port) #客户端连接到一个服务端
while True:
server_reply = sk.recv(1024) #阻塞,接收服务端发来的消息,直到接收到才继续往下执行
print (str(server_reply,encoding="utf-8"))
inp = input("请输入内容:")
sk.sendall(bytes(inp,encoding="utf-8")) #向服务端发送输入的消息
if inp == "q": #当输入q时,客户端与服务端的连接关闭
break
sk.close() #连接关闭

IO多路复用

I/O多路复用指:通过一种机制,可以监视多个描述符,一旦某个描述符就绪(一般是读就绪或者写就绪),能够通知程序进行相应的读写操作
Python中有一个select模块,其中提供了:select、poll、epoll三个方法,分别调用系统的 select,poll,epoll 从而实现IO多路复用

select
select最早于1983年出现在4.2BSD中,它通过一个select()系统调用来监视多个文件描述符的数组,当select()返回后,
该数组中就绪的文件描述符便会被内核修改标志位,使得进程可以获得这些文件描述符从而进行后续的读写操作。
select目前几乎在所有的平台上支持,其良好跨平台支持也是它的一个优点,事实上从现在看来,这也是它所剩不多的优点之一。
select的一个缺点在于单个进程能够监视的文件描述符的数量存在最大限制,在Linux上一般为1024,不过可以通过修改宏定义甚至重新编译内核的方式提升这一限制。
另外,select()所维护的存储大量文件描述符的数据结构,随着文件描述符数量的增大,其复制的开销也线性增长。
同时,由于网络响应时间的延迟使得大量TCP连接处于非活跃状态,但调用select()会对所有socket进行一次线性扫描,所以这也浪费了一定的开销。

poll
poll在1986年诞生于System V Release 3,它和select在本质上没有多大差别,但是poll没有最大文件描述符数量的限制。
poll和select同样存在一个缺点就是,包含大量文件描述符的数组被整体复制于用户态和内核的地址空间之间,而不论这些文件描述符是否就绪,它的开销随着文件描述符数量的增加而线性增大。
另外,select()和poll()将就绪的文件描述符告诉进程后,如果进程没有对其进行IO操作,那么下次调用select()和poll()的时候将再次报告这些文件描述符,所以它们一般不会丢失就绪的消息,这种方式称为水平触发(Level Triggered)。

epoll
直到Linux2.6才出现了由内核直接支持的实现方法,那就是epoll,它几乎具备了之前所说的一切优点,被公认为Linux2.6下性能最好的多路I/O就绪通知方法。
epoll可以同时支持水平触发和边缘触发(Edge Triggered,只告诉进程哪些文件描述符刚刚变为就绪状态,它只说一遍,如果我们没有采取行动,那么它将不会再次告知,这种方式称为边缘触发),理论上边缘触发的性能要更高一些,但是代码实现相当复杂。
epoll同样只告知那些就绪的文件描述符,而且当我们调用epoll_wait()获得就绪文件描述符时,返回的不是实际的描述符,而是一个代表就绪描述符数量的值,
你只需要去epoll指定的一个数组中依次取得相应数量的文件描述符即可,这里也使用了内存映射(mmap)技术,这样便彻底省掉了这些文件描述符在系统调用时复制的开销。
另一个本质的改进在于epoll采用基于事件的就绪通知方式。在select/poll中,进程只有在调用一定的方法后,内核才对所有监视的文件描述符进行扫描,
而epoll事先通过epoll_ctl()来注册一个文件描述符,一旦基于某个文件描述符就绪时,内核会采用类似callback的回调机制,迅速激活这个文件描述符,当进程调用epoll_wait()时便得到通知。

Windows Python:
提供: select
Mac Python:
提供: select
Linux Python:
提供: select、poll、epoll

对于select方法:
句柄列表11, 句柄列表22, 句柄列表33 = select.select(句柄序列1, 句柄序列2, 句柄序列3, 超时时间)
参数: 可接受四个参数(前三个必须)
返回值:三个列表
select方法用来监视文件句柄,如果句柄发生变化,则获取该句柄。
1、当 参数1 序列中的句柄发生可读时(accetp和read),则获取发生变化的句柄并添加到 返回值1 序列中
2、当 参数2 序列中含有句柄时,则将该序列中所有的句柄添加到 返回值2 序列中
3、当 参数3 序列中的句柄发生错误时,则将该发生错误的句柄添加到 返回值3 序列中
4、当 超时时间 未设置,则select会一直阻塞,直到监听的句柄发生变化
当 超时时间 = 1时,那么如果监听的句柄均无任何变化,则select会阻塞 1 秒,之后返回三个空列表,如果监听的句柄有变化,则直接执行。

利用select实现伪同时处理多个Socket客户端请求:
服务端

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
import socket
import select
sk1 = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sk1.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sk1.bind(('127.0.0.1',8002))
sk1.listen(5)
sk1.setblocking(0)
inputs = [sk1,]
while True:
readable_list, writeable_list, error_list = select.select(inputs, [], inputs, 1)
for r in readable_list:
print(r)
# 当客户端第一次连接服务端时
if r == sk1:
print ('accept a connect')
request, address = r.accept()
request.setblocking(0)
inputs.append(request)
#当客户端连接上服务端之后,服务端接收数据
else:
received = r.recv(1024)
received_str = str(received,encoding="utf-8")
# 当正常接收客户端发送的数据时
if received: #如果接收到客户端的数据不为空,则给客户端返回接收到的字符串加上你好
print ('received data:',received_str )
r.sendall(received + bytes("你好",encoding="utf-8"))
# 当客户端关闭程序时
else: #如果从客户端接收到的数据为空,则移除该客户端连接
inputs.remove(r)
sk1.close()

客户端

1
2
3
4
5
6
7
8
9
10
11
12
import socket
ip_port = ('127.0.0.1',8002)
sk = socket.socket()
sk.connect(ip_port)
while True:
inp = input('please input:')
if inp == "":
break
sk.sendall(bytes(inp,encoding="utf-8"))
recived_str = str(sk.recv(1024),encoding="utf-8")
print(recived_str)
sk.close()

socketServer模块

SocketServer内部使用 IO多路复用 以及 “多线程” 和 “多进程” ,从而实现并发处理多个客户端请求的Socket服务端。
即:每个客户端请求连接到服务器时,Socket服务端都会在服务器是创建一个“线程”或者“进程” 专门负责处理当前客户端的所有请求。

ThreadingTCPServer
ThreadingTCPServer实现的Soket服务器内部会为每个client创建一个 “线程”,该线程用来和客户端进行交互。

1、ThreadingTCPServer基础
使用ThreadingTCPServer:
创建一个继承自 SocketServer.BaseRequestHandler 的类
类中必须定义一个名称为 handle 的方法
启动ThreadingTCPServer

2、ThreadingTCPServer源码剖析
内部调用流程为:
启动服务端程序
执行 TCPServer.init 方法,创建服务端Socket对象并绑定 IP 和 端口
执行 BaseServer.init 方法,将自定义的继承自SocketServer.BaseRequestHandler 的类 MyRequestHandle赋值给 self.RequestHandlerClass
执行 BaseServer.server_forever 方法,While 循环一直监听是否有客户端请求到达 …
当客户端连接到达服务器
执行 ThreadingMixIn.process_request 方法,创建一个 “线程” 用来处理请求
执行 ThreadingMixIn.process_request_thread 方法
执行 BaseServer.finish_request 方法,执行 self.RequestHandlerClass() 即:执行 自定义 MyRequestHandler 的构造方法(自动调用基类BaseRequestHandler的构造方法,在该构造方法中又会调用 MyRequestHandler的handle方法)
SocketServer的ThreadingTCPServer之所以可以同时处理请求得益于 select 和 Threading 两个东西,其实本质上就是在服务器端为每一个客户端创建一个线程,当前线程用来处理对应客户端的请求,所以,可以支持同时n个客户端链接(长连接)。

SocketServer实现服务器:
服务端可同时处理多个客户端连接请求
服务端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import socketserver
class MyServer(socketserver.BaseRequestHandler):
def handle(self):
print("新的连接开始")
# print self.request,self.client_address,self.server
conn = self.request
conn.sendall(bytes('我是聊天机器人',encoding = "utf-8"))
while True:
client_data = conn.recv(1024) #接收客户端的消息
if str(client_data,encoding="utf-8") == "q": #如果接收到的消息是q,即退出循环,等待下一个客户端连接
break
else:
conn.sendall(client_data + bytes("是一个大帅锅",encoding="utf-8")) #每次接收到除q外的字符串时,返回接收到的值加是一个大帅锅
conn.close()

if __name__ == '__main__':
server = socketserver.ThreadingTCPServer(('127.0.0.1',8009),MyServer)
server.serve_forever()

客户端

1
2
3
4
5
6
7
8
9
10
11
12
13
import socket
ip_port = ('127.0.0.1',8009)
sk = socket.socket()
sk.connect(ip_port)
sk.settimeout(5)
while True:
data = sk.recv(1024)
print ('receive:',str(data,encoding="utf-8"))
inp = input('please input:')
sk.sendall(bytes(inp,encoding="utf-8"))
if inp == 'q':
break
sk.close()

FTP作业
服务端

1
2


客户端

1
2


推荐文章