python-examples

主要章节和小节重新按照如下逻辑划分:
-
一、Python基础
-
- 1 数字
- 2 字符串
- 3 列表
- 4 流程控制
- 5 编程风格
- 6 函数
- 7 输入和输出
- 8 数据结构
- 9 模块
- 10 错误和异常
- 11 类和对象

-
二、Python模块
-
- 1 时间模块
- 2 文件操作
- 3 常见迭代器
- 4 yield 用法
- 5 装饰器用法
- 6 枚举 用法
- 7 列表生成式
- 8 字典生成式
- 9 lambda 表达式
- 10 正则 常用

-
三、Python绘图
-
- matplotlib 常用
- seaborn 常用
- plotly 常用
- pyecharts 常用
- turtle 常用
- 三维绘图

- 四、Python 数据分析

-
五、Python 基础算法
-
- 数组
- 链表
- 递归
- 排序和搜索
- 动态规划
- 图
- 树
- 字符串
- 其他

-
六、机器学习
-
- 机器学习本质
- boosting 系列
- 神经网络系列
- 强化学习系列
- 推荐系统
- CV 路线
- NLP 系列

一、Python基础
Python基础主要总结Python常用内置函数;Python独有的语法特性、关键词nonlocal, global等;内置数据结构包括:列表(list), 字典(dict), 集合(set), 元组(tuple) 以及相关的高级模块collections中的Counter, namedtuple, defaultdict,heapq模块。目前共有90个小例子。
1 求绝对值
绝对值或复数的模
>>> abs(-6)
6
2 元素都为真
接受一个可迭代对象,如果可迭代对象的所有元素都为真,那么返回 True,否则返回False
有0,所以不是所有元素都为真
>>> all([1,0,3,6])
False
所有元素都为真
>>> all([1,2,3])
True
3 元素至少一个为真
接受一个可迭代对象,如果可迭代对象里至少有一个元素为真,那么返回True,否则返回False
没有一个元素为真
>>> any([0,0,0,[]])
False
至少一个元素为真:
>>> any([0,0,1])
True
4 ascii展示对象
调用对象的 __repr__ 方法,获得该方法的返回值,如下例子返回值为字符串
>>> class Student():
def __init__(self,id,name):
self.id = id
self.name = name
def __repr__(self):
return 'id = '+self.id +', name = '+self.name
调用:
>>> xiaoming = Student(id='1',name='xiaoming')
>>> xiaoming
id = 1, name = xiaoming
>>> ascii(xiaoming)
'id = 1, name = xiaoming'
5 十转二
将十进制转换为二进制:
>>> bin(10)
'0b1010'
6 十转八
十进制转换为八进制:
>>> oct(9)
'0o11'
7 十转十六
十进制转换为十六进制:
>>> hex(15)
'0xf'
8 判断是真是假
测试一个对象是True, 还是False.
>>> bool([0,0,0])
True
>>> bool([])
False
>>> bool([1,0,1])
True
9 字符串转字节
字符串转换为字节类型
>>> s = "apple"
>>> bytes(s,encoding='utf-8')
b'apple'
10 转为字符串
数值型等转换为字符串类型
>>> i = 100
>>> str(i)
'100'
11 是否可调用
判断对象是否可被调用
In [1]: callable(str)
Out[1]: True
In [2]: callable(int)
Out[2]: True
In [3]: xiaoming
Out[3]: id = 001, name = xiaoming
In [4]: callable(xiaoming)
Out[4]: False
如果想让xiaoming能被调用 xiaoming(), 需要重写Student类的__call__方法:
In [1]: class Student():
...: def __init__(self,id,name):
...: self.id = id
...: self.name = name
...: def __repr__(self):
...: return 'id = '+self.id +', name = '+self.name
...: def __call__(self):
...: print('I can be called')
...: print(f'my name is {self.name}')
...:
...:
In [2]: t = Student('001','xiaoming')
In [3]: t()
I can be called
my name is xiaoming
12 十转ASCII
查看十进制整数对应的ASCII字符
In [1]: chr(65)
Out[1]: 'A'
13 ASCII转十
查看某个ASCII字符对应的十进制数
In [1]: ord('A')
Out[1]: 65
14 类方法
classmethod 装饰器对应的函数不需要实例化,不需要 self 参数,但第一个参数需要是表示自身类的 cls 参数,可以来调用类的属性,类的方法,实例化对象等。
In [1]: class Student():
...: def __init__(self,id,name):
...: self.id = id
...: self.name = name
...: def __repr__(self):
...: return 'id = '+self.id +', name = '+self.name
...: @classmethod
...: def f(cls):
...: print(cls)
15 执行字符串表示的代码
将字符串编译成python能识别或可执行的代码,也可以将文字读成字符串再编译。
In [1]: s = "print('helloworld')"
In [2]: r = compile(s,"<string>", "exec")
In [3]: r
Out[3]: <code object <module> at 0x0000000005DE75D0, file "<string>", line 1>
In [4]: exec(r)
helloworld
16 创建复数
创建一个复数
In [1]: complex(1,2)
Out[1]: (1+2j)
17 动态删除属性
删除对象的属性
In [1]: delattr(xiaoming,'id')
In [2]: hasattr(xiaoming,'id')
Out[2]: False
18 转为字典
创建数据字典
In [1]: dict()
Out[1]: {}
In [2]: dict(a='a',b='b')
Out[2]: {'a': 'a', 'b': 'b'}
In [3]: dict(zip(['a','b'],[1,2]))
Out[3]: {'a': 1, 'b': 2}
In [4]: dict([('a',1),('b',2)])
Out[4]: {'a': 1, 'b': 2}
19 一键查看对象所有方法
不带参数时返回当前范围内的变量、方法和定义的类型列表;带参数时返回参数的属性,方法列表。
In [96]: dir(xiaoming)
Out[96]:
['__class__',
'__delattr__',
'__dict__',
'__dir__',
'__doc__',
'__eq__',
'__format__',
'__ge__',
'__getattribute__',
'__gt__',
'__hash__',
'__init__',
'__init_subclass__',
'__le__',
'__lt__',
'__module__',
'__ne__',
'__new__',
'__reduce__',
'__reduce_ex__',
'__repr__',
'__setattr__',
'__sizeof__',
'__str__',
'__subclasshook__',
'__weakref__',
'name']
20 取商和余数
分别取商和余数
In [1]: divmod(10,3)
Out[1]: (3, 1)
21 枚举对象
返回一个可以枚举的对象,该对象的next()方法将返回一个元组。
In [1]: s = ["a","b","c"]
...: for i ,v in enumerate(s,1):
...: print(i,v)
...:
1 a
2 b
3 c
22 计算表达式
将字符串str 当成有效的表达式来求值并返回计算结果取出字符串中内容
In [1]: s = "1 + 3 +5"
...: eval(s)
...:
Out[1]: 9
23 查看变量所占字节数
In [1]: import sys
In [2]: a = {'a':1,'b':2.0}
In [3]: sys.getsizeof(a) # 占用240个字节
Out[3]: 240
24 过滤器
在函数中设定过滤条件,迭代元素,保留返回值为True的元素:
In [1]: fil = filter(lambda x: x>10,[1,11,2,45,7,6,13])
In [2]: list(fil)
Out[2]: [11, 45, 13]
25 转为浮点类型
将一个整数或数值型字符串转换为浮点数
In [1]: float(3)
Out[1]: 3.0
如果不能转化为浮点数,则会报ValueError:
In [2]: float('a')
# ValueError: could not convert string to float: 'a'
26 字符串格式化
格式化输出字符串,format(value, format_spec)实质上是调用了value的__format__(format_spec)方法。
In [104]: print("i am {0},age{1}".format("tom",18))
i am tom,age18
| 3.1415926 | {:.2f} | 3.14 | 保留小数点后两位 |
|---|---|---|---|
| 3.1415926 | {:+.2f} | +3.14 | 带符号保留小数点后两位 |
| -1 | {:+.2f} | -1.00 | 带符号保留小数点后两位 |
| 2.71828 | {:.0f} | 3 | 不带小数 |
| 5 | {:0>2d} | 05 | 数字补零 (填充左边, 宽度为2) |
| 5 | {:x<4d} | 5xxx | 数字补x (填充右边, 宽度为4) |
| 10 | {:x<4d} | 10xx | 数字补x (填充右边, 宽度为4) |
| 1000000 | {:,} | 1,000,000 | 以逗号分隔的数字格式 |
| 0.25 | {:.2%} | 25.00% | 百分比格式 |
| 1000000000 | {:.2e} | 1.00e+09 | 指数记法 |
| 18 | {:>10d} | ' 18' | 右对齐 (默认, 宽度为10) |
| 18 | {:<10d} | '18 ' | 左对齐 (宽度为10) |
| 18 | {:^10d} | ' 18 ' | 中间对齐 (宽度为10) |
27 冻结集合
创建一个不可修改的集合。
In [1]: frozenset([1,1,3,2,3])
Out[1]: frozenset({1, 2, 3})
因为不可修改,所以没有像set那样的add和pop方法
28 动态获取对象属性
获取对象的属性
In [1]: class Student():
...: def __init__(self,id,name):
...: self.id = id
...: self.name = name
...: def __repr__(self):
...: return 'id = '+self.id +', name = '+self.name
In [2]: xiaoming = Student(id='001',name='xiaoming')
In [3]: getattr(xiaoming,'name') # 获取xiaoming这个实例的name属性值
Out[3]: 'xiaoming'
29 对象是否有这个属性
In [1]: class Student():
...: def __init__(self,id,name):
...: self.id = id
...: self.name = name
...: def __repr__(self):
...: return 'id = '+self.id +', name = '+self.name
In [2]: xiaoming = Student(id='001',name='xiaoming')
In [3]: hasattr(xiaoming,'name')
Out[3]: True
In [4]: hasattr(xiaoming,'address')
Out[4]: False
30 返回对象的哈希值
返回对象的哈希值,值得注意的是自定义的实例都是可哈希的,list, dict, set等可变对象都是不可哈希的(unhashable)
In [1]: hash(xiaoming)
Out[1]: 6139638
In [2]: hash([1,2,3])
# TypeError: unhashable type: 'list'
31 一键帮助
返回对象的帮助文档
In [1]: help(xiaoming)
Help on Student in module __main__ object:
class Student(builtins.object)
| Methods defined here:
|
| __init__(self, id, name)
|
| __repr__(self)
|
| Data descriptors defined here:
|
| __dict__
| dictionary for instance variables (if defined)
|
| __weakref__
| list of weak references to the object (if defined)
32 对象门牌号
返回对象的内存地址
In [1]: id(xiaoming)
Out[1]: 98234208
33 获取用户输入
获取用户输入内容
In [1]: input()
aa
Out[1]: 'aa'
34 转为整型
int(x, base =10) , x可能为字符串或数值,将x 转换为一个普通整数。如果参数是字符串,那么它可能包含符号和小数点。如果超出了普通整数的表示范围,一个长整数被返回。
In [1]: int('12',16)
Out[1]: 18
35 isinstance
判断object是否为类classinfo的实例,是返回true
In [1]: class Student():
...: def __init__(self,id,name):
...: self.id = id
...: self.name = name
...: def __repr__(self):
...: return 'id = '+self.id +', name = '+self.name
In [2]: xiaoming = Student(id='001',name='xiaoming')
In [3]: isinstance(xiaoming,Student)
Out[3]: True
36 父子关系鉴定
In [1]: class undergraduate(Student):
...: def studyClass(self):
...: pass
...: def attendActivity(self):
...: pass
In [2]: issubclass(undergraduate,Student)
Out[2]: True
In [3]: issubclass(object,Student)
Out[3]: False
In [4]: issubclass(Student,object)
Out[4]: True
如果class是classinfo元组中某个元素的子类,也会返回True
In [1]: issubclass(int,(int,float))
Out[1]: True
37 创建迭代器类型
使用iter(obj, sentinel), 返回一个可迭代对象, sentinel可省略(一旦迭代到此元素,立即终止)
In [1]: lst = [1,3,5]
In [2]: for i in iter(lst):
...: print(i)
...:
1
3
5
In [1]: class TestIter(object):
...: def __init__(self):
...: self.l=[1,3,2,3,4,5]
...: self.i=iter(self.l)
...: def __call__(self): #定义了__call__方法的类的实例是可调用的
...: item = next(self.i)
...: print ("__call__ is called,fowhich would return",item)
...: return item
...: def __iter__(self): #支持迭代协议(即定义有__iter__()函数)
...: print ("__iter__ is called!!")
...: return iter(self.l)
In [2]: t = TestIter()
In [3]: t() # 因为实现了__call__,所以t实例能被调用
__call__ is called,which would return 1
Out[3]: 1
In [4]: for e in TestIter(): # 因为实现了__iter__方法,所以t能被迭代
...: print(e)
...:
__iter__ is called!!
1
3
2
3
4
5
38 所有对象之根
object 是所有类的基类
In [1]: o = object()
In [2]: type(o)
Out[2]: object
39 打开文件
返回文件对象
In [1]: fo = open('D:/a.txt',mode='r', encoding='utf-8')
In [2]: fo.read()
Out[2]: '\ufefflife is not so long,\nI use Python to play.'
mode取值表:
| 字符 | 意义 |
|---|---|
'r' |
读取(默认) |
'w' |
写入,并先截断文件 |
'x' |
排它性创建,如果文件已存在则失败 |
'a' |
写入,如果文件存在则在末尾追加 |
'b' |
二进制模式 |
't' |
文本模式(默认) |
'+' |
打开用于更新(读取与写入) |
40 次幂
base为底的exp次幂,如果mod给出,取余
In [1]: pow(3, 2, 4)
Out[1]: 1
41 打印
In [5]: lst = [1,3,5]
In [6]: print(lst)
[1, 3, 5]
In [7]: print(f'lst: {lst}')
lst: [1, 3, 5]
In [8]: print('lst:{}'.format(lst))
lst:[1, 3, 5]
In [9]: print('lst:',lst)
lst: [1, 3, 5]
42 创建属性的两种方式
返回 property 属性,典型的用法:
class C:
def __init__(self):
self._x = None
def getx(self):
return self._x
def setx(self, value):
self._x = value
def delx(self):
del self._x
# 使用property类创建 property 属性
x = property(getx, setx, delx, "I'm the 'x' property.")
使用python装饰器,实现与上完全一样的效果代码:
class C:
def __init__(self):
self._x = None
@property
def x(self):
return self._x
@x.setter
def x(self, value):
self._x = value
@x.deleter
def x(self):
del self._x
43 创建range序列
- range(stop)
- range(start, stop[,step])
生成一个不可变序列:
In [1]: range(11)
Out[1]: range(0, 11)
In [2]: range(0,11,1)
Out[2]: range(0, 11)
44 反向迭代器
In [1]: rev = reversed([1,4,2,3,1])
In [2]: for i in rev:
...: print(i)
...:
1
3
2
4
1
45 四舍五入
四舍五入,ndigits代表小数点后保留几位:
In [11]: round(10.0222222, 3)
Out[11]: 10.022
In [12]: round(10.05,1)
Out[12]: 10.1
46 转为集合类型
返回一个set对象,集合内不允许有重复元素:
In [159]: a = [1,4,2,3,1]
In [160]: set(a)
Out[160]: {1, 2, 3, 4}
47 转为切片对象
class slice(start, stop[, step])
返回一个表示由 range(start, stop, step) 所指定索引集的 slice对象,它让代码可读性、可维护性变好。
In [1]: a = [1,4,2,3,1]
In [2]: my_slice_meaning = slice(0,5,2)
In [3]: a[my_slice_meaning]
Out[3]: [1, 2, 1]
48 拿来就用的排序函数
排序:
In [1]: a = [1,4,2,3,1]
In [2]: sorted(a,reverse=True)
Out[2]: [4, 3, 2, 1, 1]
In [3]: a = [{'name':'xiaoming','age':18,'gender':'male'},{'name':'
...: xiaohong','age':20,'gender':'female'}]
In [4]: sorted(a,key=lambda x: x['age'],reverse=False)
Out[4]:
[{'name': 'xiaoming', 'age': 18, 'gender': 'male'},
{'name': 'xiaohong', 'age': 20, 'gender': 'female'}]
####49 求和函数
求和:
In [181]: a = [1,4,2,3,1]
In [182]: sum(a)
Out[182]: 11
In [185]: sum(a,10) #求和的初始值为10
Out[185]: 21
50 转元组
tuple() 将对象转为一个不可变的序列类型
In [16]: i_am_list = [1,3,5]
In [17]: i_am_tuple = tuple(i_am_list)
In [18]: i_am_tuple
Out[18]: (1, 3, 5)
51 查看对象类型
class type(name, bases, dict)
传入一个参数时,返回 object 的类型:
In [1]: class Student():
...: def __init__(self,id,name):
...: self.id = id
...: self.name = name
...: def __repr__(self):
...: return 'id = '+self.id +', name = '+self.name
...:
...:
In [2]: xiaoming = Student(id='001',name='xiaoming')
In [3]: type(xiaoming)
Out[3]: __main__.Student
In [4]: type(tuple())
Out[4]: tuple
52 聚合迭代器
创建一个聚合了来自每个可迭代对象中的元素的迭代器:
In [1]: x = [3,2,1]
In [2]: y = [4,5,6]
In [3]: list(zip(y,x))
Out[3]: [(4, 3), (5, 2), (6, 1)]
In [4]: a = range(5)
In [5]: b = list('abcde')
In [6]: b
Out[6]: ['a', 'b', 'c', 'd', 'e']
In [7]: [str(y) + str(x) for x,y in zip(a,b)]
Out[7]: ['a0', 'b1', 'c2', 'd3', 'e4']
53 nonlocal用于内嵌函数中
关键词nonlocal常用于函数嵌套中,声明变量i为非局部变量; 如果不声明,i+=1表明i为函数wrapper内的局部变量,因为在i+=1引用(reference)时,i未被声明,所以会报unreferenced variable的错误。
def excepter(f):
i = 0
t1 = time.time()
def wrapper():
try:
f()
except Exception as e:
nonlocal i
i += 1
print(f'{e.args[0]}: {i}')
t2 = time.time()
if i == n:
print(f'spending time:{round(t2-t1,2)}')
return wrapper
54 global 声明全局变量
先回答为什么要有global,一个变量被多个函数引用,想让全局变量被所有函数共享。有的伙伴可能会想这还不简单,这样写:
i = 5
def f():
print(i)
def g():
print(i)
pass
f()
g()
f和g两个函数都能共享变量i,程序没有报错,所以他们依然不明白为什么要用global.
但是,如果我想要有个函数对i递增,这样:
def h():
i += 1
h()
此时执行程序,bang, 出错了! 抛出异常:UnboundLocalError,原来编译器在解释i+=1时会把i解析为函数h()内的局部变量,很显然在此函数内,编译器找不到对变量i的定义,所以会报错。
global就是为解决此问题而被提出,在函数h内,显示地告诉编译器i为全局变量,然后编译器会在函数外面寻找i的定义,执行完i+=1后,i还为全局变量,值加1:
i = 0
def h():
global i
i += 1
h()
print(i)
55 链式比较
i = 3
print(1 < i < 3) # False
print(1 < i <= 3) # True
56 不用else和if实现计算器
from operator import *
def calculator(a, b, k):
return {
'+': add,
'-': sub,
'*': mul,
'/': truediv,
'**': pow
}[k](a, b)
calculator(1, 2, '+') # 3
calculator(3, 4, '**') # 81
57 链式操作
from operator import (add, sub)
def add_or_sub(a, b, oper):
return (add if oper == '+' else sub)(a, b)
add_or_sub(1, 2, '-') # -1
58 交换两元素
def swap(a, b):
return b, a
print(swap(1, 0)) # (0,1)
59 去最求平均
def score_mean(lst):
lst.sort()
lst2=lst[1:(len(lst)-1)]
return round((sum(lst2)/len(lst2)),1)
lst=[9.1, 9.0,8.1, 9.7, 19,8.2, 8.6,9.8]
score_mean(lst) # 9.1
60 打印99乘法表
打印出如下格式的乘法表
1*1=1
1*2=2 2*2=4
1*3=3 2*3=6 3*3=9
1*4=4 2*4=8 3*4=12 4*4=16
1*5=5 2*5=10 3*5=15 4*5=20 5*5=25
1*6=6 2*6=12 3*6=18 4*6=24 5*6=30 6*6=36
1*7=7 2*7=14 3*7=21 4*7=28 5*7=35 6*7=42 7*7=49
1*8=8 2*8=16 3*8=24 4*8=32 5*8=40 6*8=48 7*8=56 8*8=64
1*9=9 2*9=18 3*9=27 4*9=36 5*9=45 6*9=54 7*9=63 8*9=72 9*9=81
一共有10 行,第i行的第j列等于:j*i,
其中,
i取值范围:1<=i<=9
j取值范围:1<=j<=i
根据例子分析的语言描述,转化为如下代码:
for i in range(1,10):
...: for j in range(1,i+1):
...: print('%d*%d=%d'%(j,i,j*i),end="\t")
...: print()
61 全展开
对于如下数组:
[[[1,2,3],[4,5]]]
如何完全展开成一维的。这个小例子实现的flatten是递归版,两个参数分别表示带展开的数组,输出数组。
from collections.abc import *
def flatten(lst, out_lst=None):
if out_lst is None:
out_lst = []
for i in lst:
if isinstance(i, Iterable): # 判断i是否可迭代
flatten(i, out_lst) # 尾数递归
else:
out_lst.append(i) # 产生结果
return out_lst
调用flatten:
print(flatten([[1,2,3],[4,5]]))
print(flatten([[1,2,3],[4,5]], [6,7]))
print(flatten([[[1,2,3],[4,5,6]]]))
# 结果:
[1, 2, 3, 4, 5]
[6, 7, 1, 2, 3, 4, 5]
[1, 2, 3, 4, 5, 6]
numpy里的flatten与上面的函数实现有些微妙的不同:
import numpy
b = numpy.array([[1,2,3],[4,5]])
b.flatten()
array([list([1, 2, 3]), list([4, 5])], dtype=object)
62 列表等分
from math import ceil
def divide(lst, size):
if size <= 0:
return [lst]
return [lst[i * size:(i+1)*size] for i in range(0, ceil(len(lst) / size))]
r = divide([1, 3, 5, 7, 9], 2)
print(r) # [[1, 3], [5, 7], [9]]
r = divide([1, 3, 5, 7, 9], 0)
print(r) # [[1, 3, 5, 7, 9]]
r = divide([1, 3, 5, 7, 9], -3)
print(r) # [[1, 3, 5, 7, 9]]
63 列表压缩
def filter_false(lst):
return list(filter(bool, lst))
r = filter_false([None, 0, False, '', [], 'ok', [1, 2]])
print(r) # ['ok', [1, 2]]
64 更长列表
def max_length(*lst):
return max(*lst, key=lambda v: len(v))
r = max_length([1, 2, 3], [4, 5, 6, 7], [8])
print(f'更长的列表是{r}') # [4, 5, 6, 7]
r = max_length([1, 2, 3], [4, 5, 6, 7], [8, 9])
print(f'更长的列表是{r}') # [4, 5, 6, 7]
65 求众数
def top1(lst):
return max(lst, default='列表为空', key=lambda v: lst.count(v))
lst = [1, 3, 3, 2, 1, 1, 2]
r = top1(lst)
print(f'{lst}中出现次数最多的元素为:{r}') # [1, 3, 3, 2, 1, 1, 2]中出现次数最多的元素为:1
66 多表之最
def max_lists(*lst):
return max(max(*lst, key=lambda v: max(v)))
r = max_lists([1, 2, 3], [6, 7, 8], [4, 5])
print(r) # 8
67 列表查重
def has_duplicates(lst):
return len(lst) == len(set(lst))
x = [1, 1, 2, 2, 3, 2, 3, 4, 5, 6]
y = [1, 2, 3, 4, 5]
has_duplicates(x) # False
has_duplicates(y) # True
68 列表反转
def reverse(lst):
return lst[::-1]
r = reverse([1, -2, 3, 4, 1, 2])
print(r) # [2, 1, 4, 3, -2, 1]
69 浮点数等差数列
def rang(start, stop, n):
start,stop,n = float('%.2f' % start), float('%.2f' % stop),int('%.d' % n)
step = (stop-start)/n
lst = [start]
while n > 0:
start,n = start+step,n-1
lst.append(round((start), 2))
return lst
rang(1, 8, 10) # [1.0, 1.7, 2.4, 3.1, 3.8, 4.5, 5.2, 5.9, 6.6, 7.3, 8.0]
70 按条件分组
def bif_by(lst, f):
return [ [x for x in lst if f(x)],[x for x in lst if not f(x)]]
records = [25,89,31,34]
bif_by(records, lambda x: x<80) # [[25, 31, 34], [89]]
71 map实现向量运算
#多序列运算函数—map(function,iterabel,iterable2)
lst1=[1,2,3,4,5,6]
lst2=[3,4,5,6,3,2]
list(map(lambda x,y:x*y+1,lst1,lst2))
### [4, 9, 16, 25, 16, 13]
72 值最大的字典
def max_pairs(dic):
if len(dic) == 0:
return dic
max_val = max(map(lambda v: v[1], dic.items()))
return [item for item in dic.items() if item[1] == max_val]
r = max_pairs({'a': -10, 'b': 5, 'c': 3, 'd': 5})
print(r) # [('b', 5), ('d', 5)]
73 合并两个字典
def merge_dict(dic1, dic2):
return {**dic1, **dic2} # python3.5后支持的一行代码实现合并字典
merge_dict({'a': 1, 'b': 2}, {'c': 3}) # {'a': 1, 'b': 2, 'c': 3}
74 topn字典
from heapq import nlargest
# 返回字典d前n个最大值对应的键
def topn_dict(d, n):
return nlargest(n, d, key=lambda k: d[k])
topn_dict({'a': 10, 'b': 8, 'c': 9, 'd': 10}, 3) # ['a', 'd', 'c']
75 异位词
from collections import Counter
# 检查两个字符串是否 相同字母异序词,简称:互为变位词
def anagram(str1, str2):
return Counter(str1) == Counter(str2)
anagram('eleven+two', 'twelve+one') # True 这是一对神器的变位词
anagram('eleven', 'twelve') # False
76 逻辑上合并字典
(1) 两种合并字典方法 这是一般的字典合并写法
dic1 = {'x': 1, 'y': 2 }
dic2 = {'y': 3, 'z': 4 }
merged1 = {**dic1, **dic2} # {'x': 1, 'y': 3, 'z': 4}
修改merged['x']=10,dic1中的x值不变,merged是重新生成的一个新字典。
但是,ChainMap却不同,它在内部创建了一个容纳这些字典的列表。因此使用ChainMap合并字典,修改merged['x']=10后,dic1中的x值改变,如下所示:
from collections import ChainMap
merged2 = ChainMap(dic1,dic2)
print(merged2) # ChainMap({'x': 1, 'y': 2}, {'y': 3, 'z': 4})
77 命名元组提高可读性
from collections import namedtuple
Point = namedtuple('Point', ['x', 'y', 'z']) # 定义名字为Point的元祖,字段属性有x,y,z
lst = [Point(1.5, 2, 3.0), Point(-0.3, -1.0, 2.1), Point(1.3, 2.8, -2.5)]
print(lst[0].y - lst[1].y)
使用命名元组写出来的代码可读性更好,尤其处理上百上千个属性时作用更加凸显。
78 样本抽样
使用sample抽样,如下例子从100个样本中随机抽样10个。
from random import randint,sample
lst = [randint(0,50) for _ in range(100)]
print(lst[:5])# [38, 19, 11, 3, 6]
lst_sample = sample(lst,10)
print(lst_sample) # [33, 40, 35, 49, 24, 15, 48, 29, 37, 24]
79 重洗数据集
使用shuffle用来重洗数据集,值得注意shuffle是对lst就地(in place)洗牌,节省存储空间
from random import shuffle
lst = [randint(0,50) for _ in range(100)]
shuffle(lst)
print(lst[:5]) # [50, 3, 48, 1, 26]
80 10个均匀分布的坐标点
random模块中的uniform(a,b)生成[a,b)内的一个随机数,如下生成10个均匀分布的二维坐标点
from random import uniform
In [1]: [(uniform(0,10),uniform(0,10)) for _ in range(10)]
Out[1]:
[(9.244361194237328, 7.684326645514235),
(8.129267671737324, 9.988395854203773),
(9.505278771040661, 2.8650440524834107),
(3.84320100484284, 1.7687190176304601),
(6.095385729409376, 2.377133802224657),
(8.522913365698605, 3.2395995841267844),
(8.827829601859406, 3.9298809217233766),
(1.4749644859469302, 8.038753079253127),
(9.005430657826324, 7.58011186920019),
(8.700789540392917, 1.2217577293254112)]
81 10个高斯分布的坐标点
random模块中的gauss(u,sigma)生成均值为u, 标准差为sigma的满足高斯分布的值,如下生成10个二维坐标点,样本误差(y-2*x-1)满足均值为0,标准差为1的高斯分布:
from random import gauss
x = range(10)
y = [2*xi+1+gauss(0,1) for xi in x]
points = list(zip(x,y))
### 10个二维点:
[(0, -0.86789025305992),
(1, 4.738439437453464),
(2, 5.190278040856102),
(3, 8.05270893133576),
(4, 9.979481700775292),
(5, 11.960781766216384),
(6, 13.025427054303737),
(7, 14.02384035204836),
(8, 15.33755823101161),
(9, 17.565074449028497)]
82 chain高效串联多个容器对象
chain函数串联a和b,兼顾内存效率同时写法更加优雅。
from itertools import chain
a = [1,3,5,0]
b = (2,4,6)
for i in chain(a,b):
print(i)
### 结果
1
3
5
0
2
4
6
83 操作函数对象
In [31]: def f():
...: print('i\'m f')
...:
In [32]: def g():
...: print('i\'m g')
...:
In [33]: [f,g][1]()
i'm g
创建函数对象的list,根据想要调用的index,方便统一调用。
84 生成逆序序列
list(range(10,-1,-1)) # [10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
第三个参数为负时,表示从第一个参数开始递减,终止到第二个参数(不包括此边界)
85 函数的五类参数使用例子
python五类参数:位置参数,关键字参数,默认参数,可变位置或关键字参数的使用。
def f(a,*b,c=10,**d):
print(f'a:{a},b:{b},c:{c},d:{d}')
默认参数c不能位于可变关键字参数d后.
调用f:
In [10]: f(1,2,5,width=10,height=20)
a:1,b:(2, 5),c:10,d:{'width': 10, 'height': 20}
可变位置参数b实参后被解析为元组(2,5);而c取得默认值10; d被解析为字典.
再次调用f:
In [11]: f(a=1,c=12)
a:1,b:(),c:12,d:{}
a=1传入时a就是关键字参数,b,d都未传值,c被传入12,而非默认值。
注意观察参数a, 既可以f(1),也可以f(a=1) 其可读性比第一种更好,建议使用f(a=1)。如果要强制使用f(a=1),需要在前面添加一个星号:
def f(*,a,**b):
print(f'a:{a},b:{b}')
此时f(1)调用,将会报错:TypeError: f() takes 0 positional arguments but 1 was given
只能f(a=1)才能OK.
说明前面的*发挥作用,它变为只能传入关键字参数,那么如何查看这个参数的类型呢?借助python的inspect模块:
In [22]: for name,val in signature(f).parameters.items():
...: print(name,val.kind)
...:
a KEYWORD_ONLY
b VAR_KEYWORD
可看到参数a的类型为KEYWORD_ONLY,也就是仅仅为关键字参数。
但是,如果f定义为:
def f(a,*b):
print(f'a:{a},b:{b}')
查看参数类型:
In [24]: for name,val in signature(f).parameters.items():
...: print(name,val.kind)
...:
a POSITIONAL_OR_KEYWORD
b VAR_POSITIONAL
可以看到参数a既可以是位置参数也可是关键字参数。
86 使用slice对象
生成关于蛋糕的序列cake1:
In [1]: cake1 = list(range(5,0,-1))
In [2]: b = cake1[1:10:2]
In [3]: b
Out[3]: [4, 2]
In [4]: cake1
Out[4]: [5, 4, 3, 2, 1]
再生成一个序列:
In [5]: from random import randint
...: cake2 = [randint(1,100) for _ in range(100)]
...: # 同样以间隔为2切前10个元素,得到切片d
...: d = cake2[1:10:2]
In [6]: d
Out[6]: [75, 33, 63, 93, 15]
你看,我们使用同一种切法,分别切开两个蛋糕cake1,cake2. 后来发现这种切法极为经典,又拿它去切更多的容器对象。
那么,为什么不把这种切法封装为一个对象呢?于是就有了slice对象。
定义slice对象极为简单,如把上面的切法定义成slice对象:
perfect_cake_slice_way = slice(1,10,2)
#去切cake1
cake1_slice = cake1[perfect_cake_slice_way]
cake2_slice = cake2[perfect_cake_slice_way]
In [11]: cake1_slice
Out[11]: [4, 2]
In [12]: cake2_slice
Out[12]: [75, 33, 63, 93, 15]
与上面的结果一致。
对于逆向序列切片,slice对象一样可行:
a = [1,3,5,7,9,0,3,5,7]
a_ = a[5:1:-1]
named_slice = slice(5,1,-1)
a_slice = a[named_slice]
In [14]: a_
Out[14]: [0, 9, 7, 5]
In [15]: a_slice
Out[15]: [0, 9, 7, 5]
频繁使用同一切片的操作可使用slice对象抽出来,复用的同时还能提高代码可读性。
87 lambda 函数的动画演示
有些读者反映,lambda函数不太会用,问我能不能解释一下。
比如,下面求这个 lambda函数:
def max_len(*lists):
return max(*lists, key=lambda v: len(v))
有两点疑惑:
- 参数
v的取值? lambda函数有返回值吗?如果有,返回值是多少?
调用上面函数,求出以下三个最长的列表:
r = max_len([1, 2, 3], [4, 5, 6, 7], [8])
print(f'更长的列表是{r}')
程序完整运行过程,动画演示如下:

结论:
-
参数v的可能取值为
*lists,也就是tuple的一个元素。 -
lambda函数返回值,等于lambda v冒号后表达式的返回值。
88 粘性之禅
7 行代码够烧脑,不信试试~~
def product(*args, repeat=1):
pools = [tuple(pool) for pool in args] * repeat
result = [[]]
for pool in pools:
result = [x+[y] for x in result for y in pool]
for prod in result:
yield tuple(prod)
调用函数:
rtn = product('xyz', '12', repeat=3)
print(list(rtn))
快去手动敲敲,看看输出啥吧~~
89 元类
xiaoming, xiaohong, xiaozhang 都是学生,这类群体叫做 Student.
Python 定义类的常见方法,使用关键字 class
In [36]: class Student(object):
...: pass
xiaoming, xiaohong, xiaozhang 是类的实例,则:
xiaoming = Student()
xiaohong = Student()
xiaozhang = Student()
创建后,xiaoming 的 __class__ 属性,返回的便是 Student类
In [38]: xiaoming.__class__
Out[38]: __main__.Student
问题在于,Student 类有 __class__属性,如果有,返回的又是什么?
In [39]: xiaoming.__class__.__class__
Out[39]: type
哇,程序没报错,返回 type
那么,我们不妨猜测:Student 类,类型就是 type
换句话说,Student类就是一个对象,它的类型就是 type
所以,Python 中一切皆对象,类也是对象
Python 中,将描述 Student 类的类被称为:元类。
按照此逻辑延伸,描述元类的类被称为:元元类,开玩笑了~ 描述元类的类也被称为元类。
聪明的朋友会问了,既然 Student 类可创建实例,那么 type 类可创建实例吗? 如果能,它创建的实例就叫:类 了。 你们真聪明!
说对了,type 类一定能创建实例,比如 Student 类了。
In [40]: Student = type('Student',(),{})
In [41]: Student
Out[41]: __main__.Student
它与使用 class 关键字创建的 Student 类一模一样。
Python 的类,因为又是对象,所以和 xiaoming,xiaohong 对象操作相似。支持:
- 赋值
- 拷贝
- 添加属性
- 作为函数参数
In [43]: StudentMirror = Student # 类直接赋值 # 类直接赋值
In [44]: Student.class_property = 'class_property' # 添加类属性
In [46]: hasattr(Student, 'class_property')
Out[46]: True
元类,确实使用不是那么多,也许先了解这些,就能应付一些场合。就连 Python 界的领袖 Tim Peters 都说:
“元类就是深度的魔法,99%的用户应该根本不必为此操心。”
90 对象序列化
对象序列化,是指将内存中的对象转化为可存储或传输的过程。很多场景,直接一个类对象,传输不方便。
但是,当对象序列化后,就会更加方便,因为约定俗成的,接口间的调用或者发起的 web 请求,一般使用 json 串传输。
实际使用中,一般对类对象序列化。先创建一个 Student 类型,并创建两个实例。
class Student():
def __init__(self,**args):
self.ids = args['ids']
self.name = args['name']
self.address = args['address']
xiaoming = Student(ids = 1,name = 'xiaoming',address = '北京')
xiaohong = Student(ids = 2,name = 'xiaohong',address = '南京')
导入 json 模块,调用 dump 方法,就会将列表对象 [xiaoming,xiaohong],序列化到文件 json.txt 中。
import json
with open('json.txt', 'w') as f:
json.dump([xiaoming,xiaohong], f, default=lambda obj: obj.__dict__,\
ensure_ascii=False, indent=2, sort_keys=True)
生成的文件内容,如下:
[
{
"address": "北京",
"ids": 1,
"name": "xiaoming"
},
{
"address": "南京",
"ids": 2,
"name": "xiaohong"
}
]
二、Python字符串和正则
字符串无所不在,字符串的处理也是最常见的操作。本章节将总结和字符串处理相关的一切操作。主要包括基本的字符串操作;高级字符串操作之正则。目前共有25个小例子
91 反转字符串
st="python"
#方法1
''.join(reversed(st))
#方法2
st[::-1]
92 字符串切片操作
字符串切片操作——查找替换3或5的倍数
In [1]:[str("java"[i%3*4:]+"python"[i%5*6:] or i) for i in range(1,15)]
OUT[1]:['1',
'2',
'java',
'4',
'python',
'java',
'7',
'8',
'java',
'python',
'11',
'java',
'13',
'14']
93 join串联字符串
In [4]: mystr = ['1',
...: '2',
...: 'java',
...: '4',
...: 'python',
...: 'java',
...: '7',
...: '8',
...: 'java',
...: 'python',
...: '11',
...: 'java',
...: '13',
...: '14']
In [5]: ','.join(mystr) #用逗号连接字符串
Out[5]: '1,2,java,4,python,java,7,8,java,python,11,java,13,14'
94 字符串的字节长度
def str_byte_len(mystr):
return (len(mystr.encode('utf-8')))
str_byte_len('i love python') # 13(个字节)
str_byte_len('字符') # 6(个字节)
以下是正则部分
import re
95 查找第一个匹配串
s = 'i love python very much'
pat = 'python'
r = re.search(pat,s)
print(r.span()) #(7,13)
96 查找所有1的索引
s = '山东省潍坊市青州第1中学高三1班'
pat = '1'
r = re.finditer(pat,s)
for i in r:
print(i)
# <re.Match object; span=(9, 10), match='1'>
# <re.Match object; span=(14, 15), match='1'>
97 \d 匹配数字[0-9]
findall找出全部位置的所有匹配
s = '一共20行代码运行时间13.59s'
pat = r'\d+' # +表示匹配数字(\d表示数字的通用字符)1次或多次
r = re.findall(pat,s)
print(r)
# ['20', '13', '59']
98 匹配浮点数和整数
?表示前一个字符匹配0或1次
s = '一共20行代码运行时间13.59s'
pat = r'\d+\.?\d+' # ?表示匹配小数点(\.)0次或1次,这种写法有个小bug,不能匹配到个位数的整数
r = re.findall(pat,s)
print(r)
# ['20', '13.59']
# 更好的写法:
pat = r'\d+\.\d+|\d+' # A|B,匹配A失败才匹配B
99 ^匹配字符串的开头
s = 'This module provides regular expression matching operations similar to those found in Perl'
pat = r'^[emrt]' # 查找以字符e,m,r或t开始的字符串
r = re.findall(pat,s)
print(r)
# [],因为字符串的开头是字符`T`,不在emrt匹配范围内,所以返回为空
IN [11]: s2 = 'email for me is guozhennianhua@163.com'
re.findall('^[emrt].*',s2)# 匹配以e,m,r,t开始的字符串,后面是多个任意字符
Out[11]: ['email for me is guozhennianhua@163.com']
100 re.I 忽略大小写
s = 'That'
pat = r't'
r = re.findall(pat,s,re.I)
In [22]: r
Out[22]: ['T', 't']
101 理解compile的作用
如果要做很多次匹配,可以先编译匹配串:
import re
pat = re.compile('\W+') # \W 匹配不是数字和字母的字符
has_special_chars = pat.search('ed#2@edc')
if has_special_chars:
print(f'str contains special characters:{has_special_chars.group(0)}')
###输出结果:
# str contains special characters:#
### 再次使用pat正则编译对象 做匹配
again_pattern = pat.findall('guozhennianhua@163.com')
if '@' in again_pattern:
print('possibly it is an email')
102 使用()捕获单词,不想带空格
使用()捕获
s = 'This module provides regular expression matching operations similar to those found in Perl'
pat = r'\s([a-zA-Z]+)'
r = re.findall(pat,s)
print(r) #['module', 'provides', 'regular', 'expression', 'matching', 'operations',\
'similar', 'to', 'those', 'found', 'in', 'Perl']
看到提取单词中未包括第一个单词,使用?表示前面字符出现0次或1次,但是此字符还有表示贪心或非贪心匹配含义,使用时要谨慎。
s = 'This module provides regular expression matching operations similar to those found in Perl'
pat = r'\s?([a-zA-Z]+)'
r = re.findall(pat,s)
print(r) #['This', 'module', 'provides', 'regular', 'expression', 'matching', 'operations',\
'similar', 'to', 'those', 'found', 'in', 'Perl']
103 split分割单词
使用以上方法分割单词不是简洁的,仅仅是为了演示。分割单词最简单还是使用split函数。
s = 'This module provides regular expression matching operations similar to those found in Perl'
pat = r'\s+'
r = re.split(pat,s)
print(r) # ['This', 'module', 'provides', 'regular', 'expression', 'matching', 'operations',\
'similar', 'to', 'those', 'found', 'in', 'Perl']
### 上面这句话也可直接使用str自带的split函数:
s.split(' ') #使用空格分隔
### 但是,对于风格符更加复杂的情况,split无能为力,只能使用正则
s = 'This,,, module ; \t provides|| regular ; '
words = re.split('[,\s;|]+',s) #这样分隔出来,最后会有一个空字符串
words = [i for i in words if len(i)>0]
104 match从字符串开始位置匹配
注意match,search等的不同:
- match函数
import re
### match
mystr = 'This'
pat = re.compile('hi')
pat.match(mystr) # None
pat.match(mystr,1) # 从位置1处开始匹配
Out[90]: <re.Match object; span=(1, 3), match='hi'>
- search函数 search是从字符串的任意位置开始匹配
In [91]: mystr = 'This'
...: pat = re.compile('hi')
...: pat.search(mystr)
Out[91]: <re.Match object; span=(1, 3), match='hi'>
105 替换匹配的子串
sub函数实现对匹配子串的替换
content="hello 12345, hello 456321"
pat=re.compile(r'\d+') #要替换的部分
m=pat.sub("666",content)
print(m) # hello 666, hello 666
106 贪心捕获
(.*)表示捕获任意多个字符,尽可能多的匹配字符
content='<h>ddedadsad</h><div>graph</div>bb<div>math</div>cc'
pat=re.compile(r"<div>(.*)</div>") #贪婪模式
m=pat.findall(content)
print(m) #匹配结果为: ['graph</div>bb<div>math']
107 非贪心捕获
仅添加一个问号(?),得到结果完全不同,这是非贪心匹配,通过这个例子体会贪心和非贪心的匹配的不同。
content='<h>ddedadsad</h><div>graph</div>bb<div>math</div>cc'
pat=re.compile(r"<div>(.*?)</div>")
m=pat.findall(content)
print(m) # ['graph', 'math']
非贪心捕获,见好就收。
108 常用元字符总结
. 匹配任意字符
^ 匹配字符串开始位置
$ 匹配字符串中结束的位置
* 前面的原子重复0次、1次、多次
? 前面的原子重复0次或者1次
+ 前面的原子重复1次或多次
{n} 前面的原子出现了 n 次
{n,} 前面的原子至少出现 n 次
{n,m} 前面的原子出现次数介于 n-m 之间
( ) 分组,需要输出的部分
109 常用通用字符总结
\s 匹配空白字符
\w 匹配任意字母/数字/下划线
\W 和小写 w 相反,匹配任意字母/数字/下划线以外的字符
\d 匹配十进制数字
\D 匹配除了十进制数以外的值
[0-9] 匹配一个0-9之间的数字
[a-z] 匹配小写英文字母
[A-Z] 匹配大写英文字母
110 密码安全检查
密码安全要求:1)要求密码为6到20位; 2)密码只包含英文字母和数字
pat = re.compile(r'\w{6,20}') # 这是错误的,因为\w通配符匹配的是字母,数字和下划线,题目要求不能含有下划线
# 使用最稳的方法:\da-zA-Z满足`密码只包含英文字母和数字`
pat = re.compile(r'[\da-zA-Z]{6,20}')
选用最保险的fullmatch方法,查看是否整个字符串都匹配:
pat.fullmatch('qaz12') # 返回 None, 长度小于6
pat.fullmatch('qaz12wsxedcrfvtgb67890942234343434') # None 长度大于22
pat.fullmatch('qaz_231') # None 含有下划线
pat.fullmatch('n0passw0Rd')
Out[4]: <re.Match object; span=(0, 10), match='n0passw0Rd'>
111 爬取百度首页标题
import re
from urllib import request
#爬虫爬取百度首页内容
data=request.urlopen("http://www.baidu.com/").read().decode()
#分析网页,确定正则表达式
pat=r'<title>(.*?)</title>'
result=re.search(pat,data)
print(result) <re.Match object; span=(1358, 1382), match='<title>百度一下,你就知道</title>'>
result.group() # 百度一下,你就知道
112 批量转化为驼峰格式(Camel)
数据库字段名批量转化为驼峰格式
分析过程
# 用到的正则串讲解
# \s 指匹配: [ \t\n\r\f\v]
# A|B:表示匹配A串或B串
# re.sub(pattern, newchar, string):
# substitue代替,用newchar字符替代与pattern匹配的字符所有.
# title(): 转化为大写,例子:
# 'Hello world'.title() # 'Hello World'
# print(re.sub(r"\s|_|", "", "He llo_worl\td"))
s = re.sub(r"(\s|_|-)+", " ",
'some_database_field_name').title().replace(" ", "")
#结果: SomeDatabaseFieldName
# 可以看到此时的第一个字符为大写,需要转化为小写
s = s[0].lower()+s[1:] # 最终结果
整理以上分析得到如下代码:
import re
def camel(s):
s = re.sub(r"(\s|_|-)+", " ", s).title().replace(" ", "")
return s[0].lower() + s[1:]
# 批量转化
def batch_camel(slist):
return [camel(s) for s in slist]
测试结果:
s = batch_camel(['student_id', 'student\tname', 'student-add'])
print(s)
# 结果
['studentId', 'studentName', 'studentAdd']
113 str1是否为str2的permutation
排序词(permutation):两个字符串含有相同字符,但字符顺序不同。
from collections import defaultdict
def is_permutation(str1, str2):
if str1 is None or str2 is None:
return False
if len(str1) != len(str2):
return False
unq_s1 = defaultdict(int)
unq_s2 = defaultdict(int)
for c1 in str1:
unq_s1[c1] += 1
for c2 in str2:
unq_s2[c2] += 1
return unq_s1 == unq_s2
这个小例子,使用python内置的defaultdict,默认类型初始化为int,计数默次数都为0. 这个解法本质是 hash map lookup
统计出的两个defaultdict:unq_s1,unq_s2,如果相等,就表明str1、 str2互为排序词。
下面测试:
r = is_permutation('nice', 'cine')
print(r) # True
r = is_permutation('', '')
print(r) # True
r = is_permutation('', None)
print(r) # False
r = is_permutation('work', 'woo')
print(r) # False
以上就是使用defaultdict的小例子,希望对读者朋友理解此类型有帮助。
114 str1是否由str2旋转而来
stringbook旋转后得到bookstring,写一段代码验证str1是否为str2旋转得到。
思路
转化为判断:str1是否为str2+str2的子串
def is_rotation(s1: str, s2: str) -> bool:
if s1 is None or s2 is None:
return False
if len(s1) != len(s2):
return False
def is_substring(s1: str, s2: str) -> bool:
return s1 in s2
return is_substring(s1, s2 + s2)
测试
r = is_rotation('stringbook', 'bookstring')
print(r) # True
r = is_rotation('greatman', 'maneatgr')
print(r) # False
115 正浮点数
从一系列字符串中,挑选出所有正浮点数。
该怎么办?
玩玩正则表达式,用正则搞它!
关键是,正则表达式该怎么写呢?
有了!
^[1-9]\d*\.\d*$
^ 表示字符串开始
[1-9] 表示数字1,2,3,4,5,6,7,8,9
^[1-9] 连起来表示以数字 1-9 作为开头
\d 表示一位 0-9 的数字
* 表示前一位字符出现 0 次,1 次或多次
\d* 表示数字出现 0 次,1 次或多次
\. 表示小数点
\$ 表示字符串以前一位的字符结束
^[1-9]\d*\.\d*$ 连起来就求出所有大于 1.0 的正浮点数。
那 0.0 到 1.0 之间的正浮点数,怎么求,干嘛不直接汇总到上面的正则表达式中呢?
这样写不行吗:^[0-9]\d*\.\d*$
OK!
那我们立即测试下呗
In [85]: import re
In [87]: recom = re.compile(r'^[0-9]\d*\.\d*$')
In [88]: recom.match('000.2')
Out[88]: <re.Match object; span=(0, 5), match='000.2'>
结果显示,正则表达式 ^[0-9]\d*\.\d*$ 竟然匹配到 000.2 ,认为它是一个正浮点数~~~!!!!
晕!!!!!!
所以知道为啥要先匹配大于 1.0 的浮点数了吧!
如果能写出这个正则表达式,再写另一部分就不困难了!
0.0 到 1.0 间的浮点数:^0\.\d*[1-9]\d*$
两个式子连接起来就是最终的结果:
^[1-9]\d*\.\d*|0\.\d*[1-9]\d*$
如果还是看不懂,看看下面的正则分布剖析图吧:

三、Python文件、日期和多线程
Python文件IO操作涉及文件读写操作,获取文件后缀名,修改后缀名,获取文件修改时间,压缩文件,加密文件等操作。
Python日期章节,由表示大日期的calendar, date模块,逐渐过渡到表示时间刻度更小的模块:datetime, time模块,按照此逻辑展开。
Python多线程希望透过5个小例子,帮助你对多线程模型编程本质有些更清晰的认识。
一共总结最常用的26个关于文件和时间处理模块的例子。
116 获取后缀名
import os
file_ext = os.path.splitext('./data/py/test.py')
front,ext = file_ext
In [5]: front
Out[5]: './data/py/test'
In [6]: ext
Out[6]: '.py'
117 文件读操作
import os
# 创建文件夹
def mkdir(path):
isexists = os.path.exists(path)
if not isexists:
os.mkdir(path)
# 读取文件信息
def openfile(filename):
f = open(filename)
fllist = f.read()
f.close()
return fllist # 返回读取内容
118 文件写操作
# 写入文件信息
# example1
# w写入,如果文件存在,则清空内容后写入,不存在则创建
f = open(r"./data/test.txt", "w", encoding="utf-8")
print(f.write("测试文件写入"))
f.close
# example2
# a写入,文件存在,则在文件内容后追加写入,不存在则创建
f = open(r"./data/test.txt", "a", encoding="utf-8")
print(f.write("测试文件写入"))
f.close
# example3
# with关键字系统会自动关闭文件和处理异常
with open(r"./data/test.txt", "w") as f:
f.write("hello world!")
119 路径中的文件名
In [11]: import os
...: file_ext = os.path.split('./data/py/test.py')
...: ipath,ifile = file_ext
...:
In [12]: ipath
Out[12]: './data/py'
In [13]: ifile
Out[13]: 'test.py'
120 批量修改文件后缀
批量修改文件后缀
本例子使用Python的os模块和 argparse模块,将工作目录work_dir下所有后缀名为old_ext的文件修改为后缀名为new_ext
通过本例子,大家将会大概清楚argparse模块的主要用法。
导入模块
import argparse
import os
定义脚本参数
def get_parser():
parser = argparse.ArgumentParser(
description='工作目录中文件后缀名修改')
parser.add_argument('work_dir', metavar='WORK_DIR', type=str, nargs=1,
help='修改后缀名的文件目录')
parser.add_argument('old_ext', metavar='OLD_EXT',
type=str, nargs=1, help='原来的后缀')
parser.add_argument('new_ext', metavar='NEW_EXT',
type=str, nargs=1, help='新的后缀')
return parser
后缀名批量修改
def batch_rename(work_dir, old_ext, new_ext):
"""
传递当前目录,原来后缀名,新的后缀名后,批量重命名后缀
"""
for filename in os.listdir(work_dir):
# 获取得到文件后缀
split_file = os.path.splitext(filename)
file_ext = split_file[1]
# 定位后缀名为old_ext 的文件
if old_ext == file_ext:
# 修改后文件的完整名称
newfile = split_file[0] + new_ext
# 实现重命名操作
os.rename(
os.path.join(work_dir, filename),
os.path.join(work_dir, newfile)
)
print("完成重命名")
print(os.listdir(work_dir))
实现Main
def main():
"""
main函数
"""
# 命令行参数
parser = get_parser()
args = vars(parser.parse_args())
# 从命令行参数中依次解析出参数
work_dir = args['work_dir'][0]
old_ext = args['old_ext'][0]
if old_ext[0] != '.':
old_ext = '.' + old_ext
new_ext = args['new_ext'][0]
if new_ext[0] != '.':
new_ext = '.' + new_ext
batch_rename(work_dir, old_ext, new_ext)
121 xls批量转换成xlsx
import os
def xls_to_xlsx(work_dir):
"""
传递当前目录,原来后缀名,新的后缀名后,批量重命名后缀
"""
old_ext, new_ext = '.xls', '.xlsx'
for filename in os.listdir(work_dir):
# 获取得到文件后缀
split_file = os.path.splitext(filename)
file_ext = split_file[1]
# 定位后缀名为old_ext 的文件
if old_ext == file_ext:
# 修改后文件的完整名称
newfile = split_file[0] + new_ext
# 实现重命名操作
os.rename(
os.path.join(work_dir, filename),
os.path.join(work_dir, newfile)
)
print("完成重命名")
print(os.listdir(work_dir))
xls_to_xlsx('./data')
# 输出结果:
# ['cut_words.csv', 'email_list.xlsx', 'email_test.docx', 'email_test.jpg', 'email_test.xlsx',\
'geo_data.png', 'geo_data.xlsx','iotest.txt', 'pyside2.md', 'PySimpleGUI-4.7.1-py3-none-any.whl',\
'test.txt', 'test_excel.xlsx', 'ziptest', 'ziptest.zip']
122 定制文件不同行
比较两个文件在哪些行内容不同,返回这些行的编号,行号编号从1开始。
定义统计文件行数的函数
# 统计文件个数
def statLineCnt(statfile):
print('文件名:'+statfile)
cnt = 0
with open(statfile, encoding='utf-8') as f:
while f.readline():
cnt += 1
return cnt
统计文件不同之处的子函数:
# more表示含有更多行数的文件
def diff(more, cnt, less):
difflist = []
with open(less, encoding='utf-8') as l:
with open(more, encoding='utf-8') as m:
lines = l.readlines()
for i, line in enumerate(lines):
if line.strip() != m.readline().strip():
difflist.append(i)
if cnt - i > 1:
difflist.extend(range(i + 1, cnt))
return [no+1 for no in difflist]
主函数:
# 返回的结果行号从1开始
# list表示fileA和fileB不同的行的编号
def file_diff_line_nos(fileA, fileB):
try:
cntA = statLineCnt(fileA)
cntB = statLineCnt(fileB)
if cntA > cntB:
return diff(fileA, cntA, fileB)
return diff(fileB, cntB, fileA)
except Exception as e:
print(e)
比较两个文件A和B,拿相对较短的文件去比较,过滤行后的换行符\n和空格。
暂未考虑某个文件最后可能有的多行空行等特殊情况
使用file_diff_line_nos 函数:
if __name__ == '__main__':
import os
print(os.getcwd())
'''
例子:
fileA = "'hello world!!!!''\
'nice to meet you'\
'yes'\
'no1'\
'jack'"
fileB = "'hello world!!!!''\
'nice to meet you'\
'yes' "
'''
diff = file_diff_line_nos('./testdir/a.txt', './testdir/b.txt')
print(diff) # [4, 5]
关于文件比较的,实际上,在Python中有对应模块difflib , 提供更多其他格式的文件更详细的比较,大家可参考:
https://docs.python.org/3/library/difflib.html?highlight=difflib#module-difflib
123 获取指定后缀名的文件
import os
def find_file(work_dir,extension='jpg'):
lst = []
for filename in os.listdir(work_dir):
print(filename)
splits = os.path.splitext(filename)
ext = splits[1] # 拿到扩展名
if ext == '.'+extension:
lst.append(filename)
return lst
r = find_file('.','md')
print(r) # 返回所有目录下的md文件
124 批量获取文件修改时间
# 获取目录下文件的修改时间
import os
from datetime import datetime
print(f"当前时间:{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
def get_modify_time(indir):
for root, _, files in os.walk(indir): # 循环D:\works目录和子目录
for file in files:
absfile = os.path.join(root, file)
modtime = datetime.fromtimestamp(os.path.getmtime(absfile))
now = datetime.now()
difftime = now-modtime
if difftime.days < 20: # 条件筛选超过指定时间的文件
print(f"""{absfile}
修改时间[{modtime.strftime('%Y-%m-%d %H:%M:%S')}]
距今[{difftime.days:3d}天{difftime.seconds//3600:2d}时{difftime.seconds%3600//60:2d}]"""
) # 打印相关信息
get_modify_time('./data')
打印效果:
当前时间:2019-12-22 16:38:53
./data\cut_words.csv
修改时间[2019-12-21 10:34:15]
距今[ 1天 6时 4]
当前时间:2019-12-22 16:38:53
./data\cut_words.csv
修改时间[2019-12-21 10:34:15]
距今[ 1天 6时 4]
./data\email_test.docx
修改时间[2019-12-03 07:46:29]
距今[ 19天 8时52]
./data\email_test.jpg
修改时间[2019-12-03 07:46:29]
距今[ 19天 8时52]
./data\email_test.xlsx
修改时间[2019-12-03 07:46:29]
距今[ 19天 8时52]
./data\iotest.txt
修改时间[2019-12-13 08:23:18]
距今[ 9天 8时15]
./data\pyside2.md
修改时间[2019-12-05 08:17:22]
距今[ 17天 8时21]
./data\PySimpleGUI-4.7.1-py3-none-any.whl
修改时间[2019-12-05 00:25:47]
距今[ 17天16时13]
125 批量压缩文件
import zipfile # 导入zipfile,这个是用来做压缩和解压的Python模块;
import os
import time
def batch_zip(start_dir):
start_dir = start_dir # 要压缩的文件夹路径
file_news = start_dir + '.zip' # 压缩后文件夹的名字
z = zipfile.ZipFile(file_news, 'w', zipfile.ZIP_DEFLATED)
for dir_path, dir_names, file_names in os.walk(start_dir):
# 这一句很重要,不replace的话,就从根目录开始复制
f_path = dir_path.replace(start_dir, '')
f_path = f_path and f_path + os.sep # 实现当前文件夹以及包含的所有文件的压缩
for filename in file_names:
z.write(os.path.join(dir_path, filename), f_path + filename)
z.close()
return file_news
batch_zip('./data/ziptest')
126 32位加密
import hashlib
# 对字符串s实现32位加密
def hash_cry32(s):
m = hashlib.md5()
m.update((str(s).encode('utf-8')))
return m.hexdigest()
print(hash_cry32(1)) # c4ca4238a0b923820dcc509a6f75849b
print(hash_cry32('hello')) # 5d41402abc4b2a76b9719d911017c592
127 年的日历图
import calendar
from datetime import date
mydate = date.today()
year_calendar_str = calendar.calendar(2019)
print(f"{mydate.year}年的日历图:{year_calendar_str}\n")
打印结果:
2019
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 1 2 3 1 2 3
7 8 9 10 11 12 13 4 5 6 7 8 9 10 4 5 6 7 8 9 10
14 15 16 17 18 19 20 11 12 13 14 15 16 17 11 12 13 14 15 16 17
21 22 23 24 25 26 27 18 19 20 21 22 23 24 18 19 20 21 22 23 24
28 29 30 31 25 26 27 28 25 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 2 3 4 5 6 7 1 2 3 4 5 1 2
8 9 10 11 12 13 14 6 7 8 9 10 11 12 3 4 5 6 7 8 9
15 16 17 18 19 20 21 13 14 15 16 17 18 19 10 11 12 13 14 15 16
22 23 24 25 26 27 28 20 21 22 23 24 25 26 17 18 19 20 21 22 23
29 30 27 28 29 30 31 24 25 26 27 28 29 30
July August September
Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa 



