1. python相关介绍说明
python是一种广泛使用的解释型,高级编程,通用型编程语言,由吉多范罗苏姆创造,第一次发布与1991年,它的设计哲学强调代码可读下和简洁性。使用空格缩进进行划分代码块。
编译型语言:源代码一次编译为机器码,多次执行后输出,例如:c,c++等语言
解释型语言:源代码要通过代码解析器解释为机器语言,再执行输出;例如:python, javascript等
python特点:
优点: 简单易上手,代码风格简,免费开源,完善的基础代码库,众多的第三方库,代码可读性高,程序可移植
缺点:相对于编译型语言速度会慢点,代码不能加密
python的主要应用场景:网络爬虫,数据处理,web编程,人工智能,机器学习,自动化运维,数据处理,数据挖掘,大数据领域,自动化测试。
2.python数据类型
python的数字类型(三种)
整数(int):可正可负。python3中不限大小
浮点型(float): 可正可负。可使用科学计数法表示:
1.1e2
= 1.1x10^2 =110.0复数: 由实数和虚数部分构成。
12 +3j
,或者complex(12,3)
方法介绍:
type(xx) : 返回xx的类型
id(xx): 返回xx指向的地址id
举例: a=1 b=1 —->此时id(a)是等于id(b)的,因为他们指向的地址是相同的
进制的默认表示:
二进制: 0b+二进制的数: 0b1010 =10
八进制:0o+八进制的数: 0o12 =10
十六进制: 0x+十六进制的数:0x11 =17
方法介绍:
bin(xx) : 其他进制转换为二进制:bin(0o12) = 0b1010
oct(xx): 其他进制转八进制: oct(0b1010) = 0o12
int(‘xx’,x) : 其他进制转十进制: int(‘0b1010’,2) =10, int(‘0o12’,8) =10
hex(xx): 其他进制转十六进制进制:hex(0b1010) = 0xa
python的布尔类型(True,False)
方法介绍:
isinstance(xx,x): 判断xx是否是x类型,返回布尔类型:isinstance(12,int) =True
bool(xx): 布尔类型: bool(0) =False bool(1) =True
python的字符串类型(‘xx’ “xx” ‘’’xxx ‘’’)
在字符串中存在
''或者""或者'''
的时候,使用反斜杠转义\: ‘I\’m yuan’ = I’m yuan
‘’’中的的字符串中存在换行的,返回的数据也是换行的:
'''你好
吗''' ==> 你好
吗
在
''
或者""
中换行使用\n
来进行换行操作
特殊符号的转义字符:
\a 发出系统铃声 \' 单引号 \n 换行 \\ 反斜杠
\" 双引号 \b 退格 \t 纵向制表符 \v 横向制表符
\t 回车 \f 换页
方法介绍:
a[x] : 获取a的下标为x(为负时,是从右到左)的字符: a=”qwerty” a[1]=w a[-1]=y
a[x:xx]: 获取从下标x到下标xx的字符串(不包括xx) a[0:2] = qw a[-3:-1]=rt
a[x:]: 获取从下标x到字符串结尾 a[2:] =erty
a[:xx]: 获取字符串从o位到下标xx的字符串(不包括xx) a[:3] =qwe
占位符: %c字符 %s字符串 %d整数 %f浮点数
a="my name is %s,l\'m from %s" %("yuan","sichuan")
print(a) = my name is yuan,l'm from sichuan
b="my name is yuan","l\'m from sichuan"
print(b) = ('my name is yuan', "l'm from sichuan")
3.python运算
算术运算符: + - / % // * 对应 加 减 乘 除 取余 取整 取幂
比较运算符:> < == >= <= != <>(低版本存在,为不等于,python3已废弃)
赋值运算符:= += -= = /= //= %= *=
位运算符: & | << >> ~ ^ 按位与 按位或 左移动 右移动 按位取反 按位异或
逻辑运算符:and or not 与 或 非
成员运算符:in not in 在xx里面 不在xx里面
身份运算符: is is not 是xx 不是xx
10//3 =3 2**3 =8
a=3 a**=2=a**2 =9
a=1 b=2 (a=1 and b=2) (not a=1)
a="123" b="12" b in a =>True
a="123" b="123" a is b =>True
is与==的区别:is用于判断两个变量
引用的对象是否是同一个
, ==用于判断引用的变量的值
是否相等
a=[1,2,3] b=a[:] (a is b)=False (a==b)=True
运算符的优先级:(优先级越大的越优先)
运算符说明 | Python运算符 | 优先级 | |
---|---|---|---|
小括号 | () | 20 | |
索引运算符 | x[index] 或 x[index:index2[:index3]] | 18、19 | |
属性访问 | x.attrbute | 17 | |
乘方 | ** | 16 | |
按位取反 | ~ | 15 | |
符号运算符 | +(正号)或 -(负号) | 14 | |
乘、除 | *、/、//、% | 13 | |
加、减 | +、- | 12 | |
位移 | >>、<< | 11 | |
按位与 | & | 10 | |
按位异或 | ^ | 9 | |
按位或 | \ | 8 | |
比较运算符 | ==、!=、>、>=、<、<= | 7 | |
is 运算符 | is、is not | 6 | |
in 运算符 | in、not in | 5 | |
逻辑非 | not | 4 | |
逻辑与 | and | 3 | |
逻辑或 | or | 2 |
4.python流程控制语句
条件控制
if 条件:
处理
elif 条件2:
处理
else:
处理
三元表达式: 条件为真的结果 if 条件判断 else 条件为假的结果
a=1 b=2 a if a>b else b # a = a > b?a:b
c=a if a>b else b # c = a > b?a:b
for循环
for variable in sequence:
do something
else:
do something
举例:
result=0
for i in range(101):
result+=i
else:
print('i的值大于100')
print('最后的结果是:%d'%(result))
方法介绍:
range[xx]: 生成序列(最后一位不包含xx) :
for i in range(101):
end=”” : 不换行
print('12',end='')
print('13')
多层嵌套:
for i in range(101):
for j in range(2):
xxx
xxx
while循环
while condition: # 开发中避免生成死循环(while True:xx)
do something
举例:
i=1
result=0
while i<=100:
result+=i
i+=1
else:
print("条件不满足")
print("最后的结果:%d" %result)
python中替代switch
方式一: 字典的方式
def A():
xx;
def B():
xx
def default():
xx
switch = {'A': case1,'B': case2}
choice = 'A' # 获取选择
switch.get(choice, default)() # 执行对应的函数,如果没有就执行默认的函数
方式二: lambda表达式
result = {
'A': lambda x: x * 5,
'B': lambda x: x + 7
}[value](x)
break(跳出)与continute(继续)
break跳出循环(只是最近的循环,外侧的不会跳出),此层的for循环会跳出,不在执行
continute继续,在for循环中满足此条件的不在执行,接着相信执行
for i in range(2):
for j in range(5):
if j==2:
break
else
print(j) #当j==2时会跳出j层的循环,但是不会跳出i层的循环
for i in range(6):
if i==5:
break
xxx #当i==5时,不执行xxx,其他的还是继续执行xxx
5.python的数据结构
列表(list)
一种数据构成的有效序列(有序,成员可重复出现),一定的线性序列排列而成的数据项集合,适合对元素查询,插入和删除.
方法介绍:
创建list:
a=[1,2,4] a=[] a=list()
添加元素:
i.append(5)
==>i=[1,2,4,5]删除元素:
del i[1]
==> i=[1,4]获取长度
len(i)
==>3获取某个下标的元素
i[1]
==>2i[-1]
==>4获取某个元素对应的下标:
i.index(2)
==>1 ==>元素不存在会报错将某个元素插入到某个下标下:
i.insert(1,6)
==>[1,6,2,4]拿出某个下标元素,使其元列表减少此元素: pop =
i.pop(2)
=4 i=[1,2]
集合(set)
创建set:
a= {1,2,3} 或者 s= set(), 不能设置为: a ={}, 此处a为字典dict了
添加元素:
s.add(1)
添加集合(相同元素不再添加):
s.update([1,2,3])
移除某个元素(不存在报错):
s.remove(2)
移除某个元素(不存在不报错):
s.discard(6)
随机取出一个元素并删除:
s.pop()
获取集合长度:
len(s)
清空集合:
s.clear()
两集合取并集:
s.union(d)
s =set()
s.add(2) # {2}
s.update({2,4,5})
s.update([2,4,5]) #{2,4,5}
s.remove(2) #{4,5}
s.discard(6) #{4,5}
a=a.pop() #返回一个随机元素,并将s中此元素删除
d={4,7,9}
s.union(d) #{4,5,7,9}
元组(tuple)
形式: ()或者 tuple()
注意:当元组中只有一个元素时,要加
,
来区分:s=(1,)
元组的整个内容
不能新增,修改,删除
等变动操作,可以去取
元素:s[xx] 或者 s[x:xx] 当元组中的是列表或者集合,可以修改对应的某个列表或者集合元素.
s =(1,2,4,["111","222","333"])
s[-1][1]="666" # s[-1] = ["111","222","333"]
print(s) ==>(1,2,4,["111","666","333"])
字典(dict)
形式:s= {key:vaule,key2:vaule2} 或者 s={} 或者 s = dict()
key是不能重复的,如果放入相同的key,后者会覆盖前者;字典中的key必须是不可变
添加元素:
s.update({key:vaule})
删除字典中key对应的数据以及整个字典:
del s[key]
del s
获取字典中key的值:
s[key]
修改key的值:
s[key]=xxx
获取字典的大小:
len(s)
注意: d={[‘2’]:’we’} ==>错误,典中的key必须是不可变
range类型
range转list:
a = range(5)
print(list(a)) ==>[0, 1, 2, 3, 4]
range(start,stop,step)
=>start开始值,stop结束值,step步长
a =range(1,10,2) print(list(a)) #[1, 3, 5, 7, 9]
a = range(1,-10,-3) print(list(a)) #[1, -2, -5, -8]
可变与不可变
可变类型:列表,字典
不可变类型:数字,字符串,元组
6.python高级特性
切片
可切片的对象:
字符串,列表,元组
。集合与字典是不可切片
a=[1,2,3,4,5,6,7,8,9]
a[1:3] #[2, 3]
a[::5] #[1, 6] =>从下标为1的开始,每5个切片
列表生成式
[expr for iter_var in iterable]
[expr for iter_var in iterable if cond_expr]
range_ =[x*2 for x in range(1,5)] #[2, 4, 6, 8]
说明:range(1,5)的数按照各自的2倍生成列表
range_ =[x for x in range(1,5) if x%2==0] #[2, 4]
说明:range(1,5)的数能被2整除的数生成列表
range_=[[x,y] for x in range(1,3) for y in range(6)]
#[[1, 0], [1, 1], [1, 2], [1, 3], [1, 4], [1, 5], [2, 0], [2, 1], [2, 2], [2, 3], [2, 4], [2, 5]]
迭代
判断一个对象是否是可迭代,再进行迭代处理.同时要先引入依赖包
from collections.abc import Iterable
a = "123"
print(isinstance(a, Iterable)) # Ture
for i in a:
print(i)
多个参数迭代:
i = [[1, 2, 3], [2, 3, 4], [4, 5, 6]]
for x, y, z in i:
print("x+y+z=", x + y + z)
x+y+z= 6
x+y+z= 9
x+y+z= 15
生成器(generator)
生成器与生成式的区别在于最外层是(),是一种一边循环一边计算的机制,可以节约内存空间
generator = (x * 2 for x in range(1, 8))
print(next(generator)) # 2
print(next(generator)) #4
for i in generator:
print(i)
7.python函数
形式:
def 函数名(入参):
业务逻辑
return
举例:
def circle_area(r):
result =3.14*r*2
return result
定义一个空函数:
def 函数名(入参):
pass
注意:
函数返回多个值时用逗号隔开,此时返回的类型是元组()
递归
递归说到底,就是自己调用自己。需要特别注意的点:当自己写递归函数时,首要步骤是要先写函数的最终结束条件.
def factorial(n):
if n == 1:
return 1
else:
return n * factorial(n-1)
8.python参数
形参: def fn(a,b): xxx
实参: fn(2,3)
位置参数
默认参数:
def fn(a,b=3): xx
注意要在
最后一个
参数或者都有一个默认值
,此时调用可以:fn(3)或者fn(3,4)必需要的参数在前,默认参数在后,否则会报错
def test(a=1,b=2,c=3):xx # test(b=4)
注意: 编写默认参数时,默认参数必须
指向不可变的对象
: def t(l=[]):xx ==>这种是可变参数,存在问题
可变参数
形式:
def fn(*numbers):xxx
可变参数是在参数前加
*
,调用可传入多个参数,使用逗号隔开
:fn(1,2)
命名关键字参数
形式:
def persion(name,*,sex):xx
命名关键字使用
*
做分隔,*
之前的参数,基于位置参数,*
后面的参数,在调用的时候必需指定其参数名. sex是命名关键字,调用要命名:persion(“元”,sex=’男’)
关键字参数
形式:
def persion(name, **keys):
print('name:', name, 'other:', keys)
persion("swer", age=12, where="xx") # name: swer other: {'age': 12, 'where': 'xx'}
other_info = {"pet": "cat"}
persion("swer", **other_info) # name: swer other: {'pet': 'cat'}
改变的只是内部,将info拷贝一份用于调用,不影响外部的
定义参数的顺序:
形参,默认,可变,命名关键字参数,关键字参数
在关键字参数前必定是一个命名关键字参数,所以前面的命名关键字参数的*,可以去掉:
def persion(name,sex="男",*num,pet_name,**info)
9.python函数式编程
查询python函数用法: help(要查询的函数):eg: help(filter)
高阶函数之lambda表达式
lambda表达式的使用场景:
一般适用于创建一些临时性的,小巧的函数。使用 lambda 来创建会显得很简洁,尤其是在高阶函数的使用中
lambda表达式:
power = lambda x:x**2
power = lambda x,n:x**n #两个入参 print(power(2,3)) = 8
定义一个函数,传入一个list,将list每个元素的值加1:
def add(l = []):
return [x +1 for x in l]
print(add([1,2,3])) #[2, 3, 4]
值加2呢? 使用lambda
def add(func, l=[]):
return [func(x) for x in l]
print(add(lambda x: x + 2, [1, 2, 3]))
高阶函数之map
map的基本格式
map(func, *iterables) #map(lambda表达式(或者方法),可变参数)
map()函数接收两个以上的参数,开头一个是函数,剩下的是序列,将传入的函数依次作用到序列
的每个元素,并把结果作为新的序列返回。也就是类似map(func,[1,2,3])
result =map(lambda x:x+1,[1,2,3])
type(result) # map
list(result) #[2,3,4]
多个参数: 当参数个数不对应或者不一致时,以少的为准.
result2 =map(lambda x,y:x+y,[1,2,3],[4,5,6])
list(result2) #[5,7,9]
result3=map(lambda x,y:x+y,[1,2,3],[4,5])
list(result3) #[5,7]
高阶函数之reduce累积
reduce函数的基本格式
reduce(function, sequence, initial=None) #函数,序列,初始值)
reduce把一个函数作用在一个序列上,这个函数必须接收两个参数,reduce函数把结果继续和序列的
下一个元素做累积计算,跟递归有点类似,reduce函数会被上一个计算结果应用到本次计算中.
reduce(func, [1,2,3]) = func(func(1, 2), 3)
先要引入包:
from functools import reduce
result = reduce(lambda x,y:x*y,[1,2,3,4]) # 24=1*2*3*4
result =reduce(lambda x,y:x*y,[1,2,3,4],2) # 48=2*1*2*3*4
高阶函数之filter过滤
filter函数的基本格式
filter(function_or_None, iterable)
filter()
接收一个函数和一个序列。把传入的函数依次作用于每个元素,然后根据返回值是True还是False决定保留还是丢弃该元素。
def my_fun(x): # 筛选出my_fun返回True的参数
return x%2==0
result = filter(my_fun,[1,2,3,4,5])
type(result) # <class 'filter'>
list(result) # [2,4]
高阶函数之sorted排序
sorted的基本格式
sorted(iterable, key=None, reverse=False)
说明:
iterable
— 可迭代对象。
key
— 主要是用来进行比较的元素,只有一个参数,具体的函数的参数就是取自于可迭代对象中,指 定可迭代对象中的一个元素来进行排序。
reverse
— 排序规则,reverse = True 降序 , reverse = False 升序(默认)。对元组中某个下标数进行排序
sorted([1,2,6,88,23,67,45]) #[1, 2, 6, 23, 45, 67, 88]
sorted([1, 2, 6, 88, 23, 67, 45], reverse=True) #[88, 67, 45, 23, 6, 2, 1]
data = [['python', 89], ['c', 45], ['java', 120], ['javascript', 67]]
data2 = sorted(data, key=lambda x: x[1]) #对第二元素排序(默认升序)
闭包-nonlocal
在python中支持
函数中调用函数并返回里面的函数
def my_power():
n = 2
def power(x):
return x ** n
return power #注意:这里返回不要加(),eg:power()
n = 3
print(my_power()(4)) # 16 闭包
n = 2
def my_power():
def power(x):
return x ** n
return power #注意:这里返回不要加(),eg:power()
n = 3
print(my_power()(4)) # 64 非闭包
my_power函数在返回的时候,将其引用的值n一同带回,n的值被新的函数所使用,这种情况我们称之为闭包。同时
在调用这个函数前设置n=2是不会在函数中使用此值的
。当
内层函数没有n值的时候,会使用外层最后的n设置的值
,这种的不是闭包使用mypower()._closure方法会返回闭包带的环境变量的值,也就说明是闭包在函数中的函数是不能改变外层函数的值的,如果要改变,要使用
nonlocal
关键字
def my_power():
n = 2
def power(x):
nonlocal n
n += 1
return x ** n
return power
print(my_power()(3))
装饰器-@
使用
@
来处理:装饰器模式(Decorator Pattern)允许向一个现有的对象
添加新的功能
,同时又不改变其结构
。这种类型的设计模式属于结构型模式,它是作为现有的类的一个包装。这种模式创建了一个装饰类,用来包装原有的类,并在保持类方法签名完整性的前提下,提供了额外的功能。
添加要装饰的功能方法:
def change_func(func): # 装饰后的函数
def test():
print("交易前处理")
func()
print("交易后处理")
return test
原函数添加@装饰:
@change_func
def my_func():
print("这是交易处理")
my_func()
10.python之核心
工程结构包,模块等
包 > 模块 > 类 >方法
包与目录的区别是在此目录下是否有
__init__.py
文件,有就是包
.py
后缀的文件就称之为模块
命名空间-global/globals()/locals()
命名空间是
变量到对象的映射集合
。一般都是通过字典
来实现的。主要可以分为三类:
- 每个函数都有着自己的命名空间,叫
局部命名空间
,它记录了函数的变量,包括函数的参数和局部定义的变量。- 每个模块拥有它自建的命名空间,叫
全局命名空间
,它记录了模块的变量,包括函数、类、其它导入的模块、模块级的变量和常量。- 还有就是
内置命名空间
,任何模块均可访问它
,它存放着内置的函数和异常。命名空间的查找顺序:
局部命名空间,全局命名空间,内置命名空间,最后都没有找到就会报NameError错
当函数嵌套时的查找规则:
先在当前 (嵌套的或 lambda) 函数的命名空间中搜索
然后是在父函数的命名空间中搜索
接着是模块命名空间中搜索
最后在内置命名空间中搜索
def my_func():
name = " wiggin "
def func_son():
name = "xdclass "
print(name) # xdclass
func_son()
print(name) # wiggin
my_func()
print(name) # msg
命名空间的生命周期:
内置命名空间
在 Python 解释器启动时创建,会一直保留,不被删除。模块的
全局命名空间
在模块定义被读入时创建,通常模块命名空间也会一直保存到解释器退出。当函数被调用时创建一个
局部命名空间
,当函数返回结果 或 抛出异常时,被删除。每一个递归调用的函数都拥有自己的命名空间在python的函数中和全局同名的变量,如果你有修改变量的值就会变成局部变量,在修改之前对该变量的引用自然就会出现没定义这样的错误了,如果确定要引用全局变量,并且要对它修改,必须加上
global
关键字。
a = 1
def my_func(str):
global a # 必须要定义a,否则要报
if a == 1:
print(str)
print(a) # 1
a = 24
my_func("file")
print(a) # 24 此时对a值进行了修改
命名空间的访问:
- 局部命名空间的访问: 使用
locals()
locals 返回一个名字/值对的
dictionary
。这个 dictionary 的键是字符串形式的变量名字,dictionary 的值是变量的实际值。
def my_func():
a = 1
b = 2
print(locals())
my_func() # {'a': 1, 'b': 2}
2.全局命名空间的访问-通过
globals()
a = 1
b = 2
print(globals())
{
'__name__': '__main__',
'__doc__': None,
'__package__': None,
'__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x0000013D66AD6CD0>,
'__spec__': None,
'__annotations__': {},
'__builtins__': <module 'builtins' (built-in)>,
'__file__': 'E:\\py_workspace\\start_project\\sf.py',
'__cached__': None,
'my_func': <function my_func at 0x0000013D66B1F040>,
'a': 1,
'b': 2
}
locals 与 globals 之间的区别:
locals 是只读的,但globals是可读写
的
def my_func():
x = 123
print(locals()) #{'x': 123}
locals()["x"] = 456
print("x=", x) #x= 123
y = 123
my_func()
globals()["y"] = 111
print("y=", y) #y= 111
导入模块-import/as/from
使用
import
关键字进行模块的导入
import xxx
导入模块并重命名
as
import xxx as xd
特别注意:当模块首次被导入时,会执行模块里面的代码
使用xx(importlib)
模块进行模块
的导入:
import importlib
module = importlib.import_module("xdclass_python_chapter12_class3")
导入一个包中的模块
from
from package import module
导入多层包中的模块
from x.xx import module
要一次性导入包中所有的模块,可以使用通配符
*
。但是要配合__init__.py
导入变量
样式:
from 模块名字 import 变量名字 as 本模块命名此变量名
导入一个变量:
from class3 import a
导入多个变量:
from class3 import a, b
导入的变量非常多,可以使用
*
from class3 import *
导包机制
对
from class5_import import a
:
在
sys.modules
中查找符号”class5_import”如果符号存在,则获得符号
class5_import
对应的module
对象<module class5_import>
- 从
<module class5_import>
的dict中获得符号”a”对应的对象,如果”a”不存在,则抛出异常
如果符号class5_import不存在,则创建一个新的
module
对象,注意,这时,module对象的dict为空 执行class5_import.py中的表达式,填充
的dict - 从
的dict中获得”a”对应的对象,如果”a”不存在,则抛出异常
init .py的作用及用法
__init__.py
文件是模块的入口它告诉使用者那些功能会被导出和使用。作用:
- 标志所在目录是一个模块包,本身也是一模块
- 可用于定义模糊导入的时候要导入的内容
- 导入包的时候,会执行init.py里的内容
- 可用于批量导入模块
举例:
在yuan目录下存在:
__init__.py,bread.py,coffee.py
文件
bread.py
:
def get_bread():
print('A bread.')
def test():
print('TEST')
coffee.py
:
def get_coffee():
print('A cup of coffee.')
def test2():
print('TEST2')
如果配置
__init__.py
为:
from .bread import *
from .coffee import *
在其他模块可调用:
import yuan
yuan.test() #TEST
如果配置
__init__.py
为:
from .bread import get_bread
from .coffee import get_coffee
在其他模块可调用:
import yuan
yuan.get_bread() #A bread. 无法调用test()
__ all__
用法:某些模块高频使用
场景如果配置
__init__.py
为:
__all__ =['bread']
在其他模块可调用:
from yuan import *
bread.get_bread() # coffee未导入,此处无法调用coffee.get_coffee()
all , name的作用及用法
__all__
是list结构,放在ini里,可以标志模糊导入时的模块。放在普通的模块下。标志一个模块中,允许那些属性被导入到其他模块.同一目录下的两个模块:
model1.py, model2.py
model1.py
__all__ = ['getName']
def getName():
print('name')
def getSex():
print('sex')
model2.py
直接调用model1
中的方法:
from model1 import *
getName() #getSex没有加入__all__中,所以只能调用getName()
__name__
的作用及其用法
__name__
这个系统变量显示了当前模块执行过程中的名称
,如果当前程序运行在这个模块中,__name__
的名称就是__main__
如果不是,则为这个模块的名称。
__main__
一般作为函数的入口,类似于C语言,尤其在大型工程中,常常有if __name__ =="__main__
“: 来表明整个工程开始运行的入口定义模块
test.py
def my_fun():
if __name__=="__main__":
print("this is main") ##此方法在本模块中执行结果为这个:__name__ = __main__
else:
print(__name__) #此方法在其他模块中执行结果为: __name__ = test
单元测试
会经常使用__name__
11.python的错误处理
异常捕获与处理
错误SyntaxError
:还没运行,在语法解析的时候,就发现语法存在问题。
异常ZeroDivisionError
:运行的时候,会发生错误
警告DeprecationWarning
:
import warnings
def fxn():
warnings.warn("deprecated", DeprecationWarning)
异常的处理形式:
try:
做想做的事
except 可能发生的异常:
except 可能发生的异常2:
finally:
举例:
try:
print(10 / 0)
except ZeroDivisionError:
print("除数不能为0")
raise #抛出异常
finally:
print("finally结果")
还有一种异常处理:python提供的语法,叫预处理,预定义清理, 来避免因为异常而导致程序奔溃,比如在进行IO操作的时候,可以使用。一旦运行时发生异常,程序会自动帮你关闭文件,避免整个程序奔溃
with open("myfile.txt") as f:
for line in f:
print(line,end="")
异常的抛出及自定义异常
class MyException(Exception):
def __init__(self,parameter):
err="非法入参{0},分母不能为0",format(parameter)
Exception.__init__(self,err)
self.parameter=parameter
def my_func(x):
if x==0:
raise MyException(x)
else:
print(10/x)
my_func(0)
debug分析问题
第一步从日志中最下面分析异常跟java的debug一样,不同与快捷键:
F7是下一步
设置断点 在行号后单击(双击取消)F8 跳过
Alt + Shift + F9 运行debug模式
参考资料:cnblogs.com/lijunjiang2015/p/7689822.html
单元测试
单元测试(Unit Testing):又称模块测试,是针对程序模块来进行正确性检查的测试工作
编写测试类:
先进入要测试的方法中:双击要测试的方法 ->右键 Go TO到Test(或者ctrl+shift+t) ->create new test
要测试的方法:
class MyTest:
def my_func(x):
return 10/x
测试类:
import unittest
from unittest import TestCase
from Test01 import MyTest
class TestMy_func(TestCase):
def my_func(self):
test = MyTest()
self.assertEqual(test.my_func(2),5) #10/2=5 所以返回的是OK
if __name__=='__main__':
unittest.main()
12.IO操作
输入输出流
print("请输入内容,按回车健结束:")
str =input()
print("你输入的内容是:",str)
文件读取open
形式:
open(file, mode='r', buffering=-1, encoding=None, errors=None, newline=None,closefd=True, opener=None)
参数说明:
x
写模式,新建一个文件,二u给文件已存在会报错b
二进制模式+
打开一个文件进行跟新(可读可写)r
以只读方式打开文件。文件的指针将会放在文件的开头。这是默认模式。rb
以二进制格式打开一个文件用于只读。文件指针将会放在文件的开头。这是默认模式。一般用于非文本文件如图片等r+
打开一个文件用于读写。文件指针将会放在文件的开头。rb+
以二进制格式打开文件用于读写。文件指针将会放在文件的开头。这是默认模式。一般用于非文本文件如图片等w
打开一个文件只用于写入。如果该文件已存在则打开文件,并从头开始编辑,即原有内容会被删除。如果文件不存在则创建文件。wb
以二进制格式打开一个文件只用于写入。如果该文件已存在则打开文件.并从头开始编辑,即原有内容会被删除。如果文件不存在则创建文件。一般用于非文本如图片等w+
打开一个文件用于读写。如果该文件已存在则打开文件.并从头开始编辑,即原有内容会被删除。如果文件不存在则创建文件。wb+
以二进制格式打开一个文件用于读写。如果该文件已存在则打开文件.并从头开始编辑,即原有内容会被删除。如果文件不存在则创建文件。一般用于非文本如图片等a
打开一个文件用于追加。如果该文件已存在,文件指针将会放到文件结尾。也就是说,新的内容会写在原有内容之后。 如果文件不存在,创建文件用于读写入ab+
以二进制格式打开一个文件用于追加。如果该文件已存在,文件指针将会放到文件结尾。 如果文件不存在,创建文件用于读写入写法一:
try:
file_path="C:\\Users\\DELL\\Desktop\\python\\python笔记.md"
file =open(file_path,"r",encoding="utf-8")
#content =file.readline() 读取第一行
#content = file.read() 一次性读取所有
content = file.readlines() 每行数据放入list中返回
print(content)
finally:
file.close()
写法二:
file_path="C:\\Users\\DELL\\Desktop\\python\\python笔记.md"
with open(file_path,"r",encoding="utf-8") as file:
#content =file.readline()
#content = file.read()
content = file.readlines()
print(content)
文件写入write
写入文件内容时,需要些使用open打开文件,再使用write写入:
w - 写入
,每次写入都会覆盖前面的a - 追加
(示append) ,保留前面的
file_path = "D:\we.txt"
with open(file_path, "w", encoding="utf-8") as file:
file.write('好好学习')
文件夹操作
创建文件夹: 以使用
os.mkdir(dir_name)
来在当前目录下创建一个目录
import os
os.mkdir("test") #当前目录下创建test文件夹
创建
多级
文件夹os.makedirs
import os
os.makedirs("test\\my") #当前目录下创建test/my多层文件夹 == >相对路径
os.makedirs("D:\\wer\\yrt") #D盘下创建wer/yrt多次文件夹 == >绝对路径
获取当前所在目录
os.getcwd
import os
print(os.getcwd()) #E:\py_workspace\model #只获取到了文件所在的文件夹路径
改变当前的工作目录
chdir
: 进入子目录
import os
os.chdir("test") #进入当前目录下中的test目录
print(os.getcwd()) #E:\py_workspace\model\test
删除空文件夹
os.rmdir
import os
os.rmdir("test")
删除多层空文件夹
os.removedirs
import os
os.removedirs("test\\my")
判断一个路径是否是文件夹
os.path.isdir(dir)
import os
os.path.isdir("test\\my") # False
获取一个路径下所有的文件和目录名
os.listdir(path)
import os
print(os.listdir("test")) #['we']
删除
文件夹下所有子文件夹: shutil.rmtree
:
import shutil
shutil.rmtree("D:\cache")
StringIO与BytesIO
使用场景
在某些场景下,我们
只是要缓存
相应的文本内容,方便后续处理,此时,我并不需要往新建文件并写入
,我只想直接在内存中缓存这些文本。
StringIO
跟ByteIo
的区别在于前者写入字符串
,后者写入二进制
赋值StringIO,读取readline
from io import StringIO
str_io = StringIO("hello world")
print(str_io.readline())
str_io.close()
写入write,读取getvalue
from io import StringIO
str_io = StringIO()
str_io.write("hello")
str_io.write("world")
print(str_io.getvalue())
str_io.close()
可使用
with语法自动关闭
:
from io import StringIO
with StringIO() as str_io:
str_io.write("hello")
str_io.write("world")
print(str_io.getvalue())
13.面向对象编程
面向对象及其三大特性
并不是我们要说的面向对象,在这里,没有对象也可以进行面向对象编程
面向对象的三大特性分别为
封装、继承和多态
多态:python中的多态,本质也是通过继承获得多态的能力。(不同的 子类对象调用 相同的 父类
方法,产生 不同的 执行结果,可以增加代码的外部 调用灵活度)
类的定义class及实例化
类中
所有类方法
,无论是否要用到,都必须有self
参数定义类
class
:
# 定义person类
class Person():
# 所有类方法,无论是否要用到,都必须有self参数
def method_self(self):
print("method_self")
实例化: 使用
类名()
,即可完成实例化
persion = Person();
注意:定义类的时候,类名需使用
驼峰
的命名方式(即单词首字母大写
,如果类名由多个单词组成,同样每个单词的首字母都大写)实例化多个对象 它们之间是不相等的,因为他们的id不同:
p1 = Person();
p2 = Person();
# 通过id函数证明实例化出来的两个对象都是不同的
print(id(p1))
print(id(p2))
类的构造函数def __init__()
主要用来在创建对象的时候去初始化对象。
python中不支持多个构造函数
class Person():
#自定义构造函数
def __init__(self, name, age):
self.name = name
self.age = age
def print(self):
print('name = %s, age = %s' % (self.name, self.age))
p1 = Person('张三', '35');
p1.print()
当
使用了构造函数
,且构造函数式带参的且无默认参数,此时,实例化对象的时候,必须显式入参
,否则会报错。若要避免错误,可以在构造函数使用默认参数
类变量与实例变量self.__class__/__dict__
类变量(属于类的): 在类中且在函数体之外。类变量通常不作为实例变量使用。类变量在整个实例化的对象中是公用的。
实例变量(属于实例化之后的变量的):定义在方法中的变量,用 self 绑定到实例上,只作用于当前实例的类
定义及访问
:访问
实例变量
可以使用对象名.属性
,访问类变量
可以使用类名.属性
, 类似与java中的静态变量,但是它可变
class Person():
num = 0
def __init__(self, name, age):
self.name = name
self.age = age
p1 = Person('张三', '35');
# 访问实例变量
print(p1.name)
# #访问类变量
print(Person.num)
修改实例变量与类变量的值
如果要修改实例变量的值,可以使用
对象名.属性=新值
的方式进行赋值如果要修改类变量的值,可以使用
类名.属性=新值
的方式进行赋值,在实例方法里,可以使用self.__class__
p1.name = '里斯'
Person.num = 24
class Person():
num = 0
def __init__(self, name, age):
self.__class__.num = 23 #实例方法中修改类变量
self.name = name
self.age = age
实例变量查找规则
:先从实例变量里面找,找不到,看看类变量有没有,没有的话,去父类找,还是找不到报错在对象里面,有一个
__dict__
对象,存储着对象相关的属性
。 类也有对应的__dict__
,存储着与类相关的信息
print(p1.__dict__) #{'name': '里斯', 'age': '35'}
print(Person.__dict__) # {'__module__': '__main__', 'num': 23, '__init__': <function Person.__init__ at 0x00000225126DC430>, '__dict__': <attribute '__dict__' of 'Person' objects>, '__weakref__': <attribute '__weakref__' of 'Person' objects>, '__doc__': None}
self关键字
作用:
self
指向实例本身,我们在实例方法中要访问实例变量,必须使用self.变量名
的形式。
self
名称不是必须的,在python中self不是关键词
,你可以定义成a或b或其它名字
都可以,但是约定成俗(为了和其他编程语言统一,减少理解难度)
def __init__(aa,name):
aa.name = name
在实例方法中,如果不使用
self.属性名
的方式去访问对应的属性会报错
如果直接使用
类名.实例方法名
也会报错
class Person():
# 自定义构造函数
def __init__(self, na):
self.name = na
print(name) # 编译时就报错
def print(self):
print('name = %s' % self.name)
p1 = Person('张三', '35');
print(Person.print()) # 执行时报错
类方法@classmethod与静态方法@staticmethod
类方法: 使用装饰器
@classmethod
。第一个参数
必须是当前类对象
,该参数名一般约定为“cls
”(不是cls也没关系,但是,约定俗成的东西不建议变动),通过它来传递类的属性和方法(不能传实例的属性和方法);其调用对象
可以是实例对象和类
虽然
能对象能访问类方法
,但是不建议
这么用
class Person():
num = 0
@classmethod
def change_num(cls, change):
cls.num = change
print(cls.num)
Person.change_num(34) # 通常使用方式
p1 = Person();
p1.change_num(23) #不建议这样使用
静态方法:使用装饰器
@staticmethod
。参数随意
,没有“self”和“cls”参数,但是方法体中不能使用类或实例的任何属性和方法
;静态方法是类中的函数,
不需要实例
。静态方法主要是用来存放逻辑性的代码
,逻辑上属于类,但是和类本身没有关系,也就是说在静态方法中,不会涉及到类中的属性和方法的操作。可以理解为,静态方法是个独立的、单纯的函数,它仅仅托管于某个类的名称空间中,便于使用和维护
@staticmethod
def print_hello():
print("hello world")
访问限制(属性字段前加__定义为私有变量)
定义私有变量 :当我们不希望外部可以随意更改类内部的数据,可以
将变量定义为私有变量
,并提供相应的操作方法为了保证外部不可以随意更改实例内部的数据,可以在构造函数中,在属性的名称前加上两个下划线__
,这样改属性就变成了一个私有变量,只有内部可以访问,外部不能访问
方法私有化,同样也是在方法名前面加上__
class Person():
# 自定义构造函数
def __init__(self, name):
self.__name = name
def __change(self, name):
self.__name = name
person = Person("xx")
print(person.name) #运行报错,name为私有变量,外部不可访问
person.__change('we') # 运行报错,change为私有方法,外部不可访问
打破访问限制(__xx__/_类_属性/方法
)
在python中,仅属性或方法
前面
加上__
时,表示私有,如果后面
再加上__
,此时含义就发生改变,变成了
普通
的属性或方法。
class Person():
# 自定义构造函数
def __init__(self, name):
self.__name__ = name
def __change__(self, name):
self.__name = name
person = Person("xx")
print(person.__name__) # xx
person.__change__('we') # 此时name变为了we
通过对象的
__dict__
来查看上面的访问限制中的例子:发现多出了
_类_属性/方法
,因此可以根据这种方式来访问类的私有属性。
class Person():
# 自定义构造函数
def __init__(self, name):
self.__name = name
def __change(self, name):
self.__name = name
person = Person("xx")
print(person.__dict__) # {'_Person__name': 'xx'}
print(Person.__dict__) # { '_Person__change': ...}
# 打破访问限制
print(person._Person__name) # xx
# 打破访问限制
person._Person__change('we')
print(person._Person__name) # we
14.面向对象高级特性
继承(子类(父类)
)
在定义类的时候,
类名后有个括号
,当括号里写着另外一个类
的名字时,表示该类继承于另外一个类
- 通过继承,调用子类方法
class Person():
# 自定义构造函数
def __init__(self, name):
self.name = name
def change(self, name):
self.name = name
# People继承
class People(Person):
pass
p = People('人类')
print(p.name) # 人类
p.change('f')
print(p.name) # f
- 子类可以有
自己的属性与方法
class People(Person):
def work(self):
print('work')
p = People('人类')
print(p.work())
子类具备父类所有的属性与功能
,但是父类并不具备子类的属性与功能当子类
有自己的构造方法
时,将会覆盖父类的构造方法
# People继承
class People(Person):
def __init__(self):
print('People')
p = People() #People
- 子类重写父类方法
特别注意:
当且仅当
子类方法与父类同名
,入参相同
,才可称之为重写父类
的方法不要为了获得某些属性或功能而继承
class Person():
# 自定义构造函数
def __init__(self, name):
self.name = name
def change(self, name):
self.name = name
# People继承
class People(Person):
def __init__(self):
print('People')
def change(self, name): # 重写
print('name is %s' % name)
p = People() #People
p.change('qq') # name is qq
super作用及用法
子类自己写了
init方法
,程序运行的时候就不会去调用父类的构造
当子类不创建(或者没有)父类中的属性时,此时通过
子类.父类属性
会报错。怎样处理?方式一:
直接通过类名去调用
,但是这种方式通常不建议
class People(Person):
def __init__(self, name):
Person.__init__(self, name)
p = People('wangwu')
print(p.name)
方式二:
通过super去进行调用
class People(Person):
def __init__(self, name):
super(People, self).__init__(name)
p = People('wangwu')
print(p.name)
super() 函数是用于
调用父类
(超类)的一个方法。一般是用来解决多重继承问题
的,直接用类名调用父类方法在使用单继承的时候没问题,但是如果使用多继承,会涉及到查找顺序(MRO)、重复调用(钻石继承
)等种种问题。super函数的基本语法
super(类名,self)
在python3中,
可以直接使用super()而无需任何参数来调用父类
抽象方法(abc/@abstractmethod/ABCMeta )与多态
抽象方法
指一些只有方法声明
,而没有具体方法体的方法。抽象方法一般存在于抽象类或接口
中。抽象类
的一个特点是它不能直接被实例化,子类
要么是抽象类
,要么,必须实现父类抽象类里定义的抽象方法
抽象类的
子类必须实现抽象类
中所定义的所有方法
,否则,程序不能正确运行在python3中可以通过使用
abc模块
轻松的定义抽象类
from abc import ABCMeta, abstractmethod
# 定义抽象类时,使用metaclass=ABCMeta
class Person(metaclass=ABCMeta):
@abstractmethod
def do(self):
pass
# 子类实现抽象父类方法
class P(Person):
def do(self):
print('do something')
p = P()
p.do() # do something
多态: 不同的 子类对象调用 相同的 父类方法,产生 不同的 执行结果,可以增加代码的外部 调用灵活度
多态的好处: 增加了程序的
灵活性
;增加了程序额可扩展性
# 定义animal类
from abc import ABCMeta, abstractmethod
# 定义抽象类时,使用metaclass=ABCMeta
class Animal(metaclass=ABCMeta):
@abstractmethod
def eat(self):
pass
@abstractmethod
def run(seft):
pass
def activity(self):
self.eat()
self.run()
class Dog(Animal):
def eat(self):
print("Dog is eating")
def run(self):
print("dog is running")
class Cat(Animal):
def eat(self):
print("cat is eating")
def run(self):
print("cat is running")
# 不同的子类对象,调用父类的activity方法,产生不同的执行结果
dog = Dog()
cat = Cat()
dog.activity()
cat.activity()
多重继承
如果使用多继承来表达,肯定也是同时继承两个类
表达式 :
子类(父类1,父类2)
当继承多个父类时,如果父类中
有相同的方法
,那么子类会优先使用最先被继承的方法
class Person():
def A(self):
print('A')
def C(self):
print('Person- C')
class Man():
def B(self):
print('B')
def C(self):
print('Man-C')
class People(Person,Man):
pass
p = People()
p.A()
p.B()
p.C() # Person- C Person在前,优先继承
多重继承问题(菱形继承(super().__init__()
)、查找的优先级)
新式类与旧式类
新式类都从object继承(python3中,默认都继承自object),经典类不需要。
新式类的MRO(method resolution order 基类搜索顺序)算法采用
C3算法
广度优先搜索,而旧式类的MRO算法
是采用深度优先搜索新式类相同父类只执行一次构造函数,经典类重复执行多次。
菱形继承(钻石继承)
要执行父类构造有两种方式,一种是直接
使用父类名字调用构造
,一种是使用super()
,
class A():
def __init__(self):
print('A')
class B1(A):
def __init__(self):
A.__init__(self)
class B2(A):
def __init__(self):
A.__init__(self)
class C(B1, B2):
def __init__(self):
B1.__init__(self) # A
B2.__init__(self) # A
c = C()
解决菱形继承
多次调用构造
的问题 : 使用super()
class A():
def __init__(self):
print('A')
class B1(A):
def __init__(self):
super().__init__()
class B2(A):
def __init__(self):
super().__init__()
class C(B1, B2):
def __init__(self):
super().__init__() # A
c = C()
枚举类Enum/name/value/@unique
定义:在数学和计算机科学理论中,一个集的枚举是列出某些有穷序列集的所有成员的程序,或者是一种特定类型对象的计数。这两种类型经常(但不总是)重叠
创建枚举类:
from enum import Enum, unique
# 创建 Enum
class MyEn(Enum):
A = 1
B = 2
C = 3
# 使用 Enum
x = MyEn.B
if x == MyEn.A or x == MyEn.B:
print('into enum')
print(MyEn.B.name) #B
print(MyEn.B.value) #2
print(MyEn(3)) #MyEn.C
通过名字获取枚举成员
MyEn.B
通过枚举成员获取名字(
Cls.X.name
)以及值(Cls.X.value
):MyEn.B.name /MyEn.B.value
通过值(
value
)获取枚举成员 :Cls(value)
MyEn(3)
特性:
定义枚举时,其枚举成员的名称(name)不允许相同
默认情况下,不同的成员值允许相同。但是两个相同值的成员,其第二个成员名称是第一个成员名称的别名;因此在访问枚举成员时,
只能获取第一个成员
。如果要限定枚举里面所有的值必须唯一,可以在定义枚举类时,加上@unique
from enum import Enum, unique
class MyEn(Enum):
A = 1
B = 1
print(MyEn(1)) # MyEn.A
from enum import Enum, unique
@unique
class MyEn(Enum):
A = 1
B = 1
print(MyEn(1)) #加了@unique,表示枚举唯一,此时存在相同的值,不唯一,所以执行会报错
- 枚举成员的比较(
Cls.X.value == xx
)注意:枚举的比较,只能是成员与成员,或者值与值,不能成员与值比较
from enum import Enum
class MyEn(Enum):
A = 1
B = 2
print(MyEn.A == 1) # False
print(MyEn.A.value == 1) # True
print(MyEn.A.value is 1) # True
15.网络编程
socket
套接字(socket)是一个抽象层,应用程序可以通过它发送或接收数据,可对其进行像对文件一样的打开、读写和关闭等操作。套接字允许应用程序将I/O插入到网络中,并与网络中的其他应用程序进行通信。网络套接字是IP地址与端口的组合.
socket是一个接口,在用户进程与TCP/IP协议之间充当中间人,完成TCP/IP协议的书写,用户只需理解接口即可
在python里面,提供了两个级别访问的网络服务:
- 低级别的网络服务支持基本的
Socket
,它提供了标准的 BSD Sockets API,可以访问底层操作系统Socket接口的全部方法。- 高级别的网络服务模块 SocketServer, 它提供了服务器中心类,可以简化网络服务器的开发。
通信协议可分为TCP、UDP:
- TCP(Transmission Control Protocol传输控制协议)是一种面向连接的,可靠的,基于字节流的传输通信协议。
- UDP(User Data Protocol,用户数据报协议)是无连接的,即发送数据之前不需要建立连接,UDP尽最大努力交付,即不保证可靠交付(类似于发短信,我只管发,能不能接受到跟我关系不大)
Google QUIC正式更名 HTTP/3 协议 https://cloud.tencent.com/developer/news/355488
UDP实现通信
使用socket开发的基本格式:
socket.socket(family,type,proto)
family
: 套接字家族可以使AF_UNIX
或者AF_INET
type
: 套接字类型可以根据是面向连接(TCP)的还是非连接分(UDP)为SOCK_STREAM
或SOCK_DGRAM
protocol
: 一般不填默认为0
AF_INET
: AF_UNIX需经过多个协议层的编解码,消耗系统cpu,并且数据传输需要经过网卡,受到网卡带宽的限制。AF_UNIX数据到达内核缓冲区后,由内核根据指定路径名找到接收方socket对应的内核缓冲区,直接将数据拷贝过去,不经过协议层编解码,节省系统cpu,并且不经过网卡,因此不受网卡带宽的限制。
AF_UNIX
: AF_UNIX的传输速率远远大于AF_INET
AF_INET
不仅可以用作本机的跨进程通信,同样的可以用于不同机器之间的通信,其就是为了在不同机器之间进行网络互联传递数据而生。而AF_UNIX则只能用于本机内进程之间的通信。服务端:
import socket
server = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
server.bind(("127.0.0.1", 8888))
print("UDP 服务端启动")
# 开机之后一直等着我跟他通信
while True:
# 接收到信息,信息里面包括客户端发送的内容以及客户端的信息(ip,端口)
data, client = server.recvfrom(1024)
print("接受到客户端发来的消息:", data.decode('utf-8'))
server.sendto("您好,这是服务端".encode("utf-8"), client)
客户端
import socket
client = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# 有服务端(ip,端口)
client.sendto("您好,我是客户端".encode('utf-8'), ("127.0.0.1", 8888))
# 接受服务端的消息
data, server = client.recvfrom(1024)
print(data.decode("utf-8"))
client.close()
TCP实现通信
服务端
import socket
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind(("127.0.0.1", 9999))
# 同一时刻 ,允许多少客户端跟我通信
server.listen(5)
print("服务端已启动,等待客户端连接=======》")
client, address = server.accept()
print("客户端已经连接")
while True:
recv = client.recv(1024)
msg = recv.decode("utf-8")
if msg == "close":
print("服务端关闭=======》")
server.close()
break
print("客户端发送的内容:", msg)
print("请输入回复内容:")
client.send(input().encode("utf-8"))
客户端
import socket
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 拨打电话
client.connect(("127.0.0.1", 9999))
# 通话
while True:
print("请输入内容:")
send_data = input()
client.send(send_data.encode("utf-8"))
if send_data == "close":
client.close()
print("关闭连接")
break
data = client.recv(1024).decode("utf-8")
print("接受到服务端响应:", data)
requests模块发送http请求
安装requests模块
import requests
result = requests.get("xx")
text = result.content
print(text.decode('utf-8'))
16.多线程
线程与进程的区别
进程:是系统进行分配和管理资源的基本单位
线程:进程的一个执行单元,是进程内调度的实体、是CPU调度和分派的基本单位,是比进程更小的独立运行的基本单位。线程也被称为轻量级进程,线程是程序执行的最小单位。
Python中的多线程
在python3之前的版本,可以使用
thread、threading
两个模块新建线程在python3中,可以可以使用
_thread、threading
两个模块新建线程。thread
模块已被废弃
。为了兼容性,Python3 将 thread 重命名为 “_thread
“。
threading
是高级模块,对_thread
进行了封装。所以在平常的开发中,更加推荐使用threading模块
使用
_thread
模块创建线程基本语法:
_thread.start_new_thread(函数, 函数入参)
import _thread
import time
# 为线程定义一个函数
def print_name(thread_name):
count = 0
while count < 5:
time.sleep(1)
count += 1
print(thread_name)
_thread.start_new_thread(print_name, ("Thread-1",))
_thread.start_new_thread(print_name, ("Thread-2",))
while 1:
pass
使用
threading
创建线程 - 方式1基本语法:
threading.Thread(group=None, target=None, name=None, args=(), kwargs=None, *, daemon=None)
参数说明:
group
:在当前的版本保持为None,不要进行修改target:
这个线程要干的活(函数)name
: 线程名args
: 上面target参数的入参(),tuple类型,对应taget参数中的可变参数- kwargs:上面target参数的入参,dict类型,对应taget参数中的关键字参数
import threading
def my_fun():
print("Hello world")
threading.Thread(target=my_fun).start()
- 使用threading创建线程 (直接通过继承)
import threading
class MyThread(threading.Thread):
def run(self):
print("hello world")
thread = MyThread()
thread.start()
线程的状态及其转换
NEW:
Thead.start()
=> RANNABLERANNABLE : 获取到相应的锁 => BLOCKED
RANNABLE : 运行结束 => TERMINATED
BLOCKED: 等待获取锁 => RANNABLE
WAITING: 被唤醒 => RANNABLE
TIME_WAITING: 时间到了被唤醒 => RANNABLE
RANNABLE :
thearding.Event().waiit()
=> WAITINGRANNABLE :
thearding.Event().waiit(3)
=> TIME_WAITING
线程安全性问题
线程安全性问题的三要素:
1 多线程环境下
2 共享变量
3 并发对共享变量进行修改
如何解决线程安全性问题?
1 单线程化
2 不要共享变量
3 对共享变量的操作串行化
使用到
锁threading.Lock()
import threading
class MyThread(threading.Thread):
sum = 0
lock = threading.Lock()
def run(self):
with MyThread.lock:
for i in range(1000000):
MyThread.sum += 1
thread = MyThread()
thread2 = MyThread()
thread3 = MyThread()
thread.start()
thread2.start()
thread3.start()
thread.join()
thread2.join()
thread3.join()
print(MyThread.sum)
多进程以及GIL机制
在Cpython解释器中,同一个进程下开启的多线程,同一时刻只能有一个线程执行,无法利用多核优势。
延伸阅读: python中的GIL详解: https://www.cnblogs.com/SuKiWX/p/8804974.html
既然同一时刻只有一个线程在运行,那为什么还会有线程安全性问题?
延伸阅读: https://segmentfault.com/q/1010000008235951
https://www.cnblogs.com/hwlong/p/8952510.html#_label5
多进程使用
Multiprocessing
模块,总体的使用跟多线程差不多
# 为线程定义一个函数
import multiprocessing
import time
def print_name(thread_name):
count = 0
while count < 5:
time.sleep(1)
count += 1
print(thread_name)
if __name__ == "__main__":
process = multiprocessing.Process(target=print_name, args=("多进程",))
process.start()
process.join()
17.正则表达式
正则表达式regexp-findall/match/search
作用: 使用单个字符串来描述、匹配一系列符合某个句法规则的字符串。在很多文本编辑器里,正则表达式通常被用来检索、替换那些符合某个模式的文本
特点:
灵活性、逻辑性和功能性非常强
可以迅速地用极简单的方式达到字符串的复杂控制
使用:
findall
方法, 需要导入re
模块
re.findall(pattern, string, flags=0)
参数说明
pattern
:匹配的正则表达式
string
:要匹配的字符串
flags
:标志位,用于控制正则表达式的匹配方式,如:是否区分大小写,多行匹配等。
flags
可选值:
re.I
:使匹配对大小写不敏感re.L
:做本地化识别(locale-aware)匹配re.M
:多行匹配,影响^
和$
re.S
:使.
匹配包括换行在内的所有字符re.U
:根据Unicode字符集解析字符。这个标志影响\w, \W, \b, \B
.re.X
:该标志通过给予你更灵活的格式以便你将正则表达式写得更易于理解。
import re
str = 'how old are you?'
result = re.findall('you',str)
print(result) # ['you'] ,当匹配不到时,返回 [] ,当匹配多个时,返回['xx','xx'...]
match
方法
re.match
尝试从字符串的起始位置匹配
一个模式,如果不是起始位置匹配成功的话,match()
就返回none
。要获取匹配的结果,可以使用group(n)
,匹配结果又多个的时候,n从0开始递增。当匹配结果有多个的时候,也可以使用groups()
一次性获取所有匹配的结果
import re
str = 'howh old are you?'
result = re.match('how', str)
print(result.group(0)) #how
search
方法
re.search
扫描整个
字符串并返回第一个
成功的匹配
import re
str = 'how old are you?'
result = re.search('how', str)
print(result.group(0)) #how
元字符
正则表达式语言由两种基本字符类型组成:原义(正常)文本字符和元字符。元字符使正则表达式具有处理能力。所谓元字符就是指那些
在正则表达式中具有特殊意义的专用字符
,可以用来规定其前导字符(即位于元字符前面的字符)在目标对象中的出现模式元字符参考
\d
匹配一个数字字符。等价于[0-9]
。grep
要加上-P
,perl
正则支持
\D
匹配一个非数字字符。等价于[^0-9]
。grep要加上-P,perl正则支持
\f
匹配一个换页符。等价于\x0c和\cL
。
\n
匹配一个换行符。等价于\x0a和\cJ
。
\r
匹配一个回车符。等价于\x0d和\cM。
\s
匹配任何不可见字符,包括空格、制表符、换页符等等。等价于[ \f\n\r\t\v]
。
\S
匹配任何可见字符。等价于[^ \f\n\r\t\v]
。
\t
匹配一个制表符。等价于\x09和\cI。
\v
匹配一个垂直制表符。等价于\x0b和\cK。
\w
匹配包括下划线的任何单词字符。类似但不等价于“[A-Za-z0-9_]”,这里的”单词”字符使用Unicode字符集。
\W
匹配任何非单词字符。等价于“[^A-Za-z0-9_]
”。
\
将下一个字符标记符、或一个向后引用、或一个八进制转义符。例如,“\\n
”匹配\n
。“\n”匹配换行符。序列“\”匹配“\”而“(”则匹配“(”。即相当于多种编程语言中都有的“转义字符”的概念。
^
匹配输入字行首。如果设置了RegExp对象的Multiline属性,^也匹配“\n”或“\r”之后的位置。….
匹配单一字符
匹配字符串中所有的数字
import re
str = "hel0,234 5 6nme sd"
result = re.findall("\d", str)
print(result) #['0', '2', '3', '4', '5', '6']
匹配字所有非数字(包括空格等字符)
result = re.findall("\D", str)
#['h', 'e', 'l', ',', ' ', ' ', 'n', 'm', 'e', ' ', 's', 'd']
正则匹配换页符 -
\f
匹配换行符 -
\n
匹配回车符 -
\r
重复出现数量匹配
匹配字符集
边界匹配
表达式之组
贪婪与非贪婪
贪婪就是想要的东西越多越好,非贪婪就是拿到自己想要的.
Python里数量词
默认是贪婪
的,总是尝试匹配尽可能多的字符
,如果需要正则进行非贪婪的匹配,这个时候,就需要在正则表达式后面加个问号?
18.爬虫
robots协议
什么是robots协议
Robots协议是国际互联网界通行的道德规范,基于以下原则建立:
1、搜索技术应服务于人类,同时尊重信息提供者的意愿,并维护其隐私权;
2、网站有义务保护其使用者的个人信息和隐私不被侵犯。
robots协议的主要功能
Robots协议用来告知搜索引擎哪些页面能被抓取,哪些页面不能被抓取;可以屏蔽一些网站中比较大的文件,如:图片,音乐,视频等,节省服务器带宽;可以屏蔽站点的一些死链接。方便搜索引擎抓取网站内容;设置网站地图连接,方便引导蜘蛛爬取页面。
robots协议的写法
较为常见的有:
User-agent:指定对哪些爬虫生效
Disallow:指定要屏蔽的网址
19.Python开发规范
https://zh-google-styleguide.readthedocs.io/en/latest/google-python-styleguide/python_style_rules/
from pathlib import Path
folder_path = Path('C:\\Users\\XC-YJS\\Desktop\\文档\\')
file_list = folder_path.glob('*.xls*')
lists = []
for i in file_list:
file_name = i.name
lists.append(file_name)
print(i.parent)
print(i.suffix)
print(i.stem)
print(lists)