函数
函数,可以当作是一堆功能代码的集合或者具备某个功能的工具(先定义后引用)
一般应用在
- 有重复代码出现,用函数来代替代码的重用性
- 代码太长增加函数的可读性
def 函数名(参数1,参数2,...):
XXXX
XXXX
return 值 # 返回值
函数名() #执行该函数
函数的参数
#发送邮件实例
import smtplib
from email.mime.text import MIMEText
from email.utils import formataddr
def send_email(email):
#邮件的文本
text = MIMEText ("nihao",'html','utf-8')
#邮件上显示的发件人
text['from'] = formataddr(['用户名','xhjh@163.com'])
#邮件上显示的主题
text['Subject']='主题'
#发送邮件
server = smtplib.smtp_ssl('smtp.126.com')
server.login('xhjh@163.com','获取的验证码')
server.sendmail('xhjh@163.com',email,msg.as_string())
server.quit()
v1 = '1234.126.com'
v2 = '10101.126.com'
v3 = 'wuxixi.126.com'
sen_email(v1)
sen_email(v2)
sen_email(v3)
参数
- 在定义函数时,在括号中添加变量,这个变量叫做函数的形式参数(形参)在传入函数的参数成为实际参数(实参)
- 这种绑定关系只能在函数体内使用
- 形参与实参的绑定关系在函数调用时生效,函数调用结束后解除绑定关系
- 实参可以是任何类型,包括可以是个函数,但结果是函数的返回值
def smt(a1,a2,a3) #定义函数smt有三个形参
print(a1+a2+a3) 6
smt(1,2,3) #执行smt函数并传入参数 1,2,3
位置传参
函数定义阶段按照从左到有的顺序依次定义的参数称之为位置参数
- 位置形参:按照从左到右的顺序直接定义的变量名,必须传值,有多少个形参就要传多少个参数
- 位置实参:按照顺序与形参一一对应
def add (n1,n2): # 位置形参
print(n1+n2)
add(1,2) # 位置实参
关键子传参
函数调用阶段按照key=value的形式传入值
关键字传参和位置传参混用的时候,关键字参数要在后面
def add (n1,n2):
print(n1+n2)
add(n1=1,n2=2)
默认参数
- 在定义阶段就已经赋值
- 默认参数,在执行函数不传参的时候根据默认参数执行
- 执行函数的时候传参的则根据传的参数执行
def add (n1,n2,n3=10):
print(n1+n2+n3)
add(1,2) 13
add(1,2,3) 6
动态形参
只能按照位置进行传参
接收位置传参保存成元组形式赋值给args
*
在传参的时候可以打散,在形参的时候只是接收多个值不具备打散,具体是接收几个参数看传的参数*形参名
def add (*args):
print(args)# 元组类型(123,) (567,89,) ()
add(123)
add(567,89)
add()
def add(x,y,z):
print(x,y,z) # 11,22,33
add(*[11,22,33])
只能按照关键字进行传参
** 接收关键字传参保存成字典形式赋值给kwargs
传参的时候**后面可以跟字典打散字典按关键字传参
**形参名
def func(**kwargs):
print(kwargs) #字典类型{'n1':'参数'} {'n1':'kjl','n2':'loki0'}
func(n1="参数")
func(n1='kjl',n2='loki0')
=====================================================================
def func(x,y,z):
print(x,y,x) # 1,2,3
func(**{"x"=1,"y"=2,"z"=3})
- ,*
可以按照关键字,也可以按照位置,位置传参放在,关键字传参放在*
位置在前,关键字在后,*后面的就是关键字传参
def add(*args,**kwargs):
print(args,kwar)
#(22,33,99){}
#(22, 33) {'n1': 18}
add(22,33,99)
add(22,33,n1=18)
def add(x,y,*,a,b):# 此时a,b就是关键字传参,a,b此时叫命名关键字形参
pass
- 注意事项
```python 1. 必须放在后面 def func1(args, kwargs): print(args, **kwargs)
2.参数和动态参数混合时,动态参数只能放在最后 def func2(a1, a2, a3, args, *kwargs): print(a1, a2, a3, args, kwargs)
3.默认值参数和动态参数同时存在 def func3(a1, a2, a3, a4=10, args, a5=20, *kwargs): print(a1, a2, a3, a4, a5, args, kwargs)
func3(11, 22, 33, 44, 55, 66, 77, a5=10, a10=123)
11 22 33 44 10 (55, 66, 77) {‘a10’: 123}
形参组合使用时的位置规则
位置形参,默认形参,args,命名关键字形参,*kwargs
<a name="5abdc374"></a>
##### 动态实参
动态参数,定义函数时在形参的位置用*或**可以接受任意个参数
在定义函数时可以用*和** ,在执行函数的时候,也可以使用
- 形参固定,实参用*和**
```python
def fun(a1,a2)
print(a1,a2)
fun(*[11,22]) # 11,22
func( **{"a1":11,"a2":22} ) # 11,22
- 形参用 和 ,实参也用 和
按照这种方式进行数据传递的时候,数据会重新拷贝一份def func(*args,**kwargs):
print(args,kwargs)
func( *[11,22,33], **{"k1":1,"k2":2} )
==========================================================================
# 小坑,([11,22,33], {"k1":1,"k2":2}), {}
func([11, 22, 33], {"k1": 1, "k2": 2})
函数返回值
在函数开发过程中,函数可以帮助我们实现某个功能,也可以让函数实现某功能之后返回给我们一些信息:
import request
from xml.etree import ElementTree as ET
def xml_to_list(city):
data_list = []
url = "http://ws.webxml.com.cn//WebServices/WeatherWebService.asmx/getWeatherbyCityName?theCityName={}".format(city)
res = requests.get(url=url)
root = ET.XML(res.text)
for node in root:
data_list.append(node.text)
return data_list
result = xml_to_list("北京")
print(result)
def add():
return 888
fure = add()
print(fure) # 888
需要变量接受函数的返回值
- 返回值可以是任意的类型,如果函数中没写return返回的是none
def add():
return [1,2,(12)]
res = add
print(res )# [1,2,(12)]
- 函数中没写返回值,或者return /return None ,执行函数获取的返回值都是None
def azs():
print("123")
return
res = azs()
print(res)# None
- return 后面的值如果有逗号,会将返回值转换成元组在返回
def fun():
return 1,2,34
v1= fun()
print(v1)# (1,2,34)
- 函数遇到return就会立即退出函数,终止函数中的所有代码,并且将return后的值当作本次运行的结果返回
- return,返回值是为了将函数执行的结果返回给调用者,以便于后续的其他操作
函数内存地址相关
函数在执行传参的时候,传递的时内存地址
可以节省内存
对于可变类型且函数中需改元素的内容,所有的地方都会修改:可变类型 dict set list
内存地址举例
def jkl(data):
print(data,id(data)) # 123 140247057684592
v1 = "123"
print(id(v1)) # 140247057684592
jkl(v1)
可变类型修改内部
def fun(data):
data.append(123)
v1 = [11,22,33]
fun(v1)
print(v1) [11,22,33,123]
可变类型,重新赋值
def fun(data):
data=['o','123']
v1 = [0,9]
okl= fun(v1)
print(okl)
# 特殊情况:不可变类型,无法修改内部元素,只能重新赋值。
def func(data):
data = "alex"
v1 = "wpq"
func(v1)
函数的返回值是内存地址
def func():
data = [1,2,3]
return data
v1 = func()
print(v1) # [1,2,3]
过程:
执行函数func()后
创建一块内存区域存放 [1,2,3],data变量指向这个内存地址
return data 返回data指向的内村地址
v1接受返回值,v1和data都指向[1,2,3]的内存地址(计数器为2)
函数指向完毕后,函数内部的变量都会被释放(就是删除data变量,内存地址的引用计数器-1)
最终v1指向的函数内部创建的那块内存地址
参数的默认值(面试)
def fun(a1,a2=18):
print(a1,a2)
python 在创建函数未执行时,如果发现函数的参数中有默认值,则在函数内部会创建一块区域并维护这个默认设置
执行函数未传值的时候,则a2指向的是函数维护的那个数值的地址
fun('123') 此时并未给a2传值,则a2指向内部维护值的内存地址
执行函数传值时,则让a2 指向新传入的值的地址
fun('123',100)
特定的情况下 默认参数的值是可变类型list dict set的时候 函数内部会修改这个值
在函数内存中会委会一块区域维护[1,2]
def fun(a1,b2=[1,2])
a2.append(56)
print(a1,a2)
fun(100) 100 [1,2,56]
fun(299) 299 [1,2,56,56]
fun(11,[100,200]) 11 [100,200,56]
fun(300) 300 [1,2,56,56,56]
def func(a1, a2=[1, 2]):
a2.append(a1)
return a2
v1 = func(10)
print(v1) 10 [1,2,10]
v2 = func(20)
print(v2) 20 [1,2,10,20]
v3 = func(30, [11, 22])
print(v3) 30 [11,22,30]
v4 = func(40)
print(v4) 40 [1,2,10,20 40]
def func(a1, a2=[1, 2]):
a2.append(a1)
return a2
v1 = func(10) 10 [1,2, 10]
v2 = func(20) 20 [1,2,10,20]
v3 = func(30, [11, 22]) 30 [11,22,30]
v4 = func(40) 40 [1,2,10,20,40]
print(v1)
print(v2)
print(v3)
print(v4)
函数和函数名
函数名相当于一个变量,这个变量代指函数
函数时可以被哈希的,所以函数可以做set的元素,以及字典的键
解释性语言,函数必须先定义才能被调用执行
def fun(a1,a2):
return a1+a2
res = fun(*[1,2])
print(res)
函数做元素
函数相当于变量,在列表等容器中也可以把函数当作时元素
def fun():
return 123
data_list = ["武沛齐", "func", func , func() ]
print( data_list[0] ) # 字符串"武沛齐"
print( data_list[1] ) # 字符串 "func"
print( data_list[2] ) # 函数 func
print( data_list[3] ) # 整数 123
res = data_list[2]()
print( res ) # 执行函数 func,并获取返回值;print再输出返回值。
print( data_list[2]() ) # 123
函数赋值
- 将函数名赋值给其他的变量,函数名其实就是变量,代指某个函数;如果将函数名赋值给另一个变量,则此变量也会代指该函数
def fun(a,b):
print(a,b)
xxx = fun
fun(1,2)
xxx(1,2)
- 对函数名重新赋值,将函数名修改为其他值,则函数名不在代指函数
```pythondef fun(a,b):
print(a,b)
fun(1,2)
fun = '123'
fun()
函数被重新定义后,就会赋予新的功能,在定义函数的时候,不要与内置的函数重名,否则会覆盖内置函数的功能
def func(a1,a2): print(a1+a2)
func(1,2)
def func(): print(666)
func()
<a name="3d8ec432"></a>
##### 函数名做参数和返回值
函数名就是一个变量,代指某个函数,所以他和其他的数据类型一样,可以当作参数和返回值
- 参数
def sunm (a):<br />return a+10<br />def hand(fun):<br />#fun = sunm<br /># sunm = 10 + 10<br />#所以 fun 也是 10+10<br />res = fun(10) 20<br />hand(sunm)
- 返回值
没有return是
def plus(num):
return num + 100
def handler():
print("执行handler函数")
return plus
result = handler()
data = result(20) # 120
print(data)
名称空间
- 可以理解为栈区存放名称与值的内存地址
- 名称空间就是把栈区进行划分为内置名称空间,全局名称空间,局部名称空间
- 各自名称空间内相同的变量,不会冲突
- 名称空间各自独立没有嵌套关系,只有优先级关系,嵌套只是好理解
内置名称空间
存放python解释器内置的名字如def class print
存活周期:解释器启动则产生,关闭则销毁
全局名称空间
存放顶级代码所产生的名字,不是函数内定义的也不是内置的剩下的就是存放在这
存活周期: python文件执行产生,运行完毕则销毁
def func(): # func = 函数的内存地址 ,所以func也是放在全局名称空间
a = 10 # 存放在局部名称空间
b =20 # 存放在局部名称空间
局部名称空间
存放在调用函数时运行函数体代码过程中产生的变量名
存活周期:调用函数时产生,函数调用完毕后销毁
调用一个函数产生一个局部空间,同一个函数调用多次也是产生多个
名称空间加载与销毁顺序
- 加载 :内置名称空间>>> 全局名称空间>>>局部名称空间
- 销毁: 局部名称空间>>>全局名称空间>>>内置名称空间
名称的查找顺序
- 以定义阶段为准,函数定义阶段会扫描语法的正确性,
- 当前所在的位置向上一层一层查找,不能向下查找,
- 函数可以嵌套寻找名称的时候可以在父函数中查找
- 名称空间的嵌套关系是在函数定义阶段,扫描语法时确定的与函数的调用无关系
LEGB名词
- local 当前函数内的作用域
- Enclosing 外层嵌套函数的作用域
- Global 全局作用域
- Bulit 内置作用域
作用域
作用域,相当与一块空间,这块空间里的数据是可以共享的,其他人没法获取
函数为作用域
python以函数为作用域名,所以在函数内创建的所有数据,可以被此函数使用,无法被其他函数所使用
def fun():
name = 'lili'
age= 18
print(age)
def hand():
age = 20
print(age)
fun()
hand()
全局作用域和局部作用域
- python中以函数为作用域,函数的作用域其实就是一个局部的作用域
- 在全局作用域中创建的变量叫做全局变量,可以在全局作用域中被使用,也可以被局部作用域中被使用 (内置名称空间和全局名称空间)
- 局部作用域,在各自函数内部创建的变量叫做局部变量,局部变量只能在自己的作用域中被使用,局部中 没有的则向上一级作用域查找(局部名称空间)
global关键字
默认情况,局部作用域对全局变量只能进行读取和修改内部元素(可变类型),无法对全局变量进行重新的赋值
读取:
CITY_LIST = ['北京','上海']
COUNTRY = '中国'
def download():
url = 'http://www.XX.com'
print(CITY_LIST)
print(COUNTRY)
修改内部元素,可变类型
CITY_LIST = ['北京','上海']
COUNTRY = '中国'
def download():
url = 'http://www.XX.com'
CITY_LIST[0]='山东'
CITY_LIST.append('杭州')
print(CITY_LIST)
print(COUNTRY)
download()
无法对全局变量进行重新赋值
COUNTRY = "中国"
CITY_LIST = ["北京","上海","深圳"]
def download():
url = "http://www.xxx.com"
# 不是对全部变量赋值,而是在局部作用域中又创建了一个局部变量 CITY_LIST 。
CITY_LIST = ["河北","河南","山西"]
print(CITY_LIST)
def upload():
file_name = "rose.zip"
print(COUNTRY)
print(CITY_LIST)
download()
upload()
global关键字
需要在局部作用域中对全局变量重新赋值,可以使用global关键字实现
将变量定义为全局变量
COUNTRY = "中国"
CITY_LIST = ["北京","上海","深圳"]
def download():
url = "http://www.xxx.com"
global CITY_LIST
CITY_LIST = ["河北","河南","山西"]
print(CITY_LIST)
global COUNTRY
COUNTRY = "中华人民共和国" # 直接把country重新赋值
print(COUNTRY)
def upload():
print(COUNTRY)
print(CITY_LIST)
print(COUNTRY)
download()
upload()
nonlocal关键字
对外层函数的变量进行重新赋值
从当前位置的上一层作为开始,如果没有,直到找到最外层的函数停止
x = 0
def f1():
x = 11
def f2():
nonlocal x
x = 22 # 修改的是f1中的x
f2()
print("f1的x",x)
f1()
函数嵌套
python中以函数为作用域,在作用域中定义的数据只能被当前作用域或者子作用域所使用
函数在作用域中
函数也是定义在作用域中的数据,在执行函数的时候,也是先在自己作用域中寻找,没有则向上一级作用域寻找
# 在全局作用域定义了函数fun
def fun():
print('123')
# 在全局作用域名中找到并指向fun函数
fun()
#在全局作用域中定义hand函数
def hand():
print('1234')
#优先在自己的作用域中寻找fun函数,没有则向上一级寻找
fun()
print('456')
#在全局作用域中寻找并指向hand()函数
hand()
def func():
print("你好")
func()
def execute():
print("开始")
func()
print("结束")
def func():
print(666)
func()
execute()
======================
你好
666
开始
666
结束
函数定义的位置
函数也可以定义在局部作用域,这样函数被局部作用域和其子作用域中调用(函数嵌套)
def fun():
print('123')
def hand():
print('456')
def iner():
print('789')
iner() # 如果return iner 执行iner的时候 注意循环执行iner造成最大递归深度问题
fun()
hand()
"""
生成图片验证码的示例代码,需要提前安装pillow模块(Python中操作图片中一个第三方模块)
pip3 install pillow
"""
import random
from PIL import Image, ImageDraw, ImageFont
def create_image_code(img_file_path, text=None, size=(120, 30), mode="RGB", bg_color=(255, 255, 255)):
""" 生成一个图片验证码 """
_letter_cases = "abcdefghjkmnpqrstuvwxy" # 小写字母,去除可能干扰的i,l,o,z
_upper_cases = _letter_cases.upper() # 大写字母
_numbers = ''.join(map(str, range(3, 10))) # 数字
chars = ''.join((_letter_cases, _upper_cases, _numbers))
width, height = size # 宽高
# 创建图形
img = Image.new(mode, size, bg_color)
draw = ImageDraw.Draw(img) # 创建画笔
def get_chars():
"""生成给定长度的字符串,返回列表格式"""
return random.sample(chars, 4)
def create_lines():
"""绘制干扰线"""
line_num = random.randint(*(1, 2)) # 干扰线条数
for i in range(line_num):
# 起始点
begin = (random.randint(0, size[0]), random.randint(0, size[1]))
# 结束点
end = (random.randint(0, size[0]), random.randint(0, size[1]))
draw.line([begin, end], fill=(0, 0, 0))
def create_points():
"""绘制干扰点"""
chance = min(100, max(0, int(2))) # 大小限制在[0, 100]
for w in range(width):
for h in range(height):
tmp = random.randint(0, 100)
if tmp > 100 - chance:
draw.point((w, h), fill=(0, 0, 0))
def create_code():
"""绘制验证码字符"""
if text:
code_string = text
else:
char_list = get_chars()
code_string = ''.join(char_list) # 每个字符前后以空格隔开
# Win系统字体
# font = ImageFont.truetype(r"C:\Windows\Fonts\SEGOEPR.TTF", size=24)
# Mac系统字体
# font = ImageFont.truetype("/System/Library/Fonts/SFNSRounded.ttf", size=24)
# 项目字体文件
font = ImageFont.truetype("MSYH.TTC", size=15)
draw.text([0, 0], code_string, "red", font=font)
return code_string
create_lines()
create_points()
code = create_code()
# 将图片写入到文件
with open(img_file_path, mode='wb') as img_object:
img.save(img_object)
return code
code = create_image_code("a2.png")
print(code)
嵌套引发的作用域的问题
- 优先在自己的作用域中寻找,自己没有去上级
- 在作用域中寻找值的时候,要确保此刻的值是什么
- 分析函数的执行,确定函数作用域链
闭包函数
闭包函数 = 名称空间与作用域+函数嵌套+函数对象
闭是指该函数是内嵌函数
包是指该函数包含对外层函数作用域名字的引用
def n1():
x = 345
def n2():
print(x)
n2()
# 无论在哪里调用n1 输出的x始终是345 因为名称空间的嵌套关系是在函数定义阶段确定的
def n1():
x = 345
def n2():
print(x)
return n2
# 此时在全局也可以调用n2函数,但是x始终是345
# 打破了名称空间的层级关系
def outher(args):
file = args
def info():
print(file)
return info
# 外层函数可以写参数,内层函数写针对该参数的具体逻辑处理并且内层函数无论如何调用始终是处理外层函数的值
闭包给函数体传参
# 正常给函数传参通过形参或者关键字参数直接传参就可以,但是特殊情况下不能给函数定义形参的时候可以通过闭包来实现
def outher(origin):
def func():
print(origin)
return func
# 此时给outher传参 func间接就可以使用到origin参数
装饰器
- 装饰器指的是定义的一个函数,该函数是用来为其他函数添加额外的功能的
- 在不修改原函数内容和调用方式的前提下,通过@函数可以实现在函数前后自定义执行一些功能(批量操作更有意义)
无参装饰器
from functools import wraps
# wraps用来将原函数的属性赋值给 装饰器
def outer(origin): # 接收原函数 因为语法糖@ outer函数只能有一个参数,用来接收原函数
@wraps(origin) # 将原函数的属性赋值给inner
def inner(*args, **kwargs):
print("before 110") # 新增的功能
res = origin(*args, **kwargs) # 调用原来的func函数
print("after") # 新增的功能
return res # 返回原函数的执行结果
return inner
@outer # func1 = outer(func1)
def func1(a1): # 此时的func1就是装饰后的函数了
"""这是func1"""
print("我是func1函数")
value = (11, 22, 33, 44)
return value
print(func1) # <function outer.<locals>.inner> 配置装饰后的内存地址
print(func1.__name__) # func1 # 没被装饰时的函数名字
print(func1.__name__) # inner # 装饰后的函数名字
help(func1) # 查看文档信息 这是func1没被装饰时的装饰信息
help(func1) # 查看装饰后的文档信息 None 因为inner里没有写注释信息
有参装饰器
可以自定义传参
info函数是获取信息用的
增加需求在获取信息前判断是去什么里获取信息
- 为什么需要在外层增加一层函数用来传值?
- 无法在inner(_args,kwargs)处传值,因为此处是用来 res = origin(args, _kwargs)对应的也就是和原函数(info)的传参对应的
- 无法在auth(origin)处传值,因为此处就是定义的无参装饰器,因为语法糖的原因在info函数前加@auth 等于 info= auth(info),这个info是语法糖自动传参的无法控制,如果在def auth(origin,file_type):加了形参,那么在用语法糖的时候就会报错提示我们少传了参数file_type
- def include(file_type):此处的形参是可以根据自己需求定义的
def include(file_type):
def auth(origin):
def inner(*args, **kwargs):
if file_type == "file":
print("去文件里拿数据")
res = origin(*args, **kwargs)
print("获取信息完毕")
elif file_type =="sql":
print("去数据库里拿数据")
res = origin(*args, **kwargs)
print("获取信息完毕")
return res
return inner
return auth
# 先执行include(file_type="sql") 得到返回值auth再执行@auth 也就是info = auth(info)
# 得到返回值 inner 我们再在全局调用info的时候就是调用的inner函数
@include(file_type="sql")
def info():
print("获取信息")
info()
叠加多个装饰器的加载,运行分析
def deco1(func1):
def warpp1(*args,**kwargs):
print('第一个')
res1 = func1(*args,**kwargs)
return res1
return warpp1
def deco2(func2):
def warpp2(*args,**kwargs):
print('第二个')
res2 = func2(*args,**kwargs)
return res2
return warpp2
def otuer(x): #
def deco3(func3):
def warpp3(*args,**kwargs):
print('第三个')
res3 = func3(*args,**kwargs)
return res3
return warpp3
return deco3
# 加载顺序自下而上
@deco1 # 第三运行这个
@deco2 # 第二运行这个
@outer(111) # 先运行这行 加载顺序自下而上
def index(x,y):
print("form index %s:%s" %(x,y) )
index(1,2)
=============================================================================
加载顺序 >>>>>> 自下而上
1. @outer(111) >> @deco3 >> @ index=deco3(index) >> warpp3
2. @deco2 >> index= deco2(warpp3) >> warpp2
3. @deco1 >> index=deco1(warpp2) >> warpp1
=============================================================================
执行顺序 >>> 自上而下的
此时调用 index(1,2)
1.也就是 调用 warpp1(1,2) 输出"第一个" 继续运行res1 = func1(1,2) 此时warpp1里的func1是warpp2是通过加载顺序第3步传进去的 也就是 res1 = warpp2(1,2)
2.调用warpp2(1,2) 输出"第二个" 继续运行res2 = func2(1,2) 此时warpp2里的func2是
warpp3 是通过加载顺序的第2步传进去 也就是 res2 = warpp3(1,2)
3.调用warpp3(1,2) 输出 "第三个" 继续运行res3 = func3(1,2) 此时 warpp3里的func3是index 是通过加载顺序的第1步骤传进去的 也就是 res3 = index(1,2)
4.调用index(1,2) 输出"form index 1:2"
5. 此时index(1,2)执行完后回到warpp3(1,2)继续往下运行return res3也就是return None
6. 此时warpp3(1,2)执行完毕回到warpp2(1,2)继续往下运行 return res2 也就是 return None
7.此时warpp2(1,2)执行完毕回到warpp1(1,2)继续往下运行 return res1 也就是 return None
迭代器
- 迭代器指的是迭代取值的工具,迭代是一个重复的过程,每一次重复都是基于上一次的结果而继续的单纯的重复并不是迭代
- 是内置
__next__
方法并且内置有__iter__
方法的对象就叫迭代器对象- 用来解决不依赖索引来取值
- 调用迭代器下的
__next__
方法可以取值,得到的是迭代器的下一个值,当取值超出范围后会报错- 调用迭代器下的
__iter__
方法得到的是迭代器本身 (为什么迭代器要有__iter__
方法,统一for循环工作原理)
for i in 迭代器对象.__iter__():
for i in 可迭代对象.__iter__():
l = [1,2,3]
i = 0
while i < len(l):
print(l[i]) # 依赖索引取值,如果l是字典就无法取值
i+=1
dic = {"1":1,"2":2,"3":3}
dic_d = dic__iter__()
while True:
print(dic_d.__next())
while True:
# 取不到值,同一个迭代器取值都取完的情况下,再取值取不到
# 想取在生成一个迭代器,dic_c = dic__iter()
try:
print(dic_d.__next())
except StopIteration:
break
可迭代对象
- 内置有
__iter__
方法的都叫可迭代对象- 字符串 列表 元组 集合 字典 文件对象 都是可迭代对象 ,文件对象也是迭代器对象
- 调用可迭代对象下的
__iter__
方法会返回一个迭代器 (可以转换为迭代器的对象)- 可迭代对象本身是没有
__next__
方法的
for 循环原理(for循环又叫迭代器循环)
for i in dict:
print(i)
1.调用d的 __iter__()得到一个迭代器对象
2.调用迭代器对象的__next__()拿到一个返回值赋值给i
3.循环步骤2,直到抛出StopIteration异常for循环会捕捉异常然后结束循环
迭代器总结
1.优点
- 得到一种统一取值的方法,对于序列和非序列都可以用迭代方式取值
- 取值过程中节省内存,用
__next__
每次只取一个2.缺点
- 取值没有索引取值方便,索引想取哪个取哪个
- 迭代器的声明周期是一次性的,值取完后就
- 只能取下一个值不能回到开始
生成器
生成器可以理解为迭代器,内置
__iter__
和__next
,就是自定义的迭代器生成器函数,函数中由yield存在时,这个函数就是生成器函数
- 调用生成器函数,函数并不会执行函数体代码而是返回一个生成器对象
def fun(): # 生成器函数
print('1')
yiled 1
object = func() # 生成器对象 创建生成器对象内部是根据生成器类(generator)创建的对象
def func():
print(111)
yield 1
print(222)
yield 2
print(333)
yield 3
print(444)
data = func()
# data.__next()方法会触发函数体代码的运行遇到yiled就停下来将yiled后面的值当作本次next调用的结果
# 下次执行`__next__`时,会从上一次的位置基础上再继续向下执行。
# 当取值范围超出后或中途遇到return就报错StopIteration
生成器的特点是,记录在函数中的执行位置,将函数挂起到某个状态
使用next(),进入生成器函数并执行当中的代码
def func():
print(111)
yield 1 #执行到此位置就不在执行后面的代码,并将1返回调用者 下次使用next()后会进去函数,但是不从头开始,而是从上一次位置开始执行现在也就是当前位置往下执行
print(222)
yield 2
print(333)
yield 3
print(444)
data = func()
v1 = next(data) # next和__next__一样的效果
print(v1)
v2 = next(data)
print(v2)
v3 = next(data)
print(v3)
v4 = next(data)
print(v4) # 结束或中途遇到return,程序报:StopIteration 错误,生成器中的数据执行完毕
import random
def gen_random_num(max_count):
counter = 0
while counter < max_count:
yield random.randint(1000, 9999)
counter += 1
data_list = gen_random_num(3000000)
# 再使用时,去 data_list 中获取即可。
yield表达式
def func(name):
print("是{}".format(name))
while True:
x = yiled # yiled表达式
print("你好{}:{}".format(name,x))
ovject = func("liu")
ovject.send("hello") # send是给yiled赋值,yiled会将值赋值给x # 报错第一次不能赋值非空值
# 对于刚开始的生成器不能直接send赋值一个非空值 需要先触发生成器进入函数代码体当遇到yiled暂停下次就可以使用send赋值给当前yiled一个非空值
# ovject.send(None) 等同于next(ovject) 用来触发进入函数体代码
def dog(name):
print("准备")
while True:
x = yiled 111
print("dog:{}吃了{}")
go = dog("ww")
res = g.send(None) # res = 111
res1=g.send(["骨头","肉"]) # 将值给yiled然后yiled再将值给x 也就是x=["骨头","肉"] 而不是 yiled ["骨头","肉"] # res1 = 111
1.res = g.send(None)
#触发函数代码运行输出"准备" 遇到yiled停下来,将yiled后面的值111作为本次调用的结果返回
2. res1=g.send(["骨头","肉"])
# 将值给yiled然后yiled再将值给x 也就是x=["骨头","肉"] 输出 "dog:ww吃了["骨头","肉"]"然后继续运行遇到yiled停止将yiled后的111返回
yield from
def foo():
yield 2
yield 2
yield 2
def fun():
yield 1
yield 1
yield 1
yield from foo() # 执行foo函数并将foo中的yield值返回
yield 1
yield 1
for item in func():
print(item) 1 1 1 2 2 2 1 1
匿名函数
lambda 参数:函数体
- 匿名函数,则是基于lambda表达式实现定义一个可以没有名字的函数
- 参数支持任意参数,函数体,只能支持单行代码
- lambda定义一个函数返回该函数的内存地址
- 主要用于临时调用一次的场景
返回值,默认将函数单行代码执行的结果返回给函数的执行者
应用场景
lambda X:函数体
lambda X1,x2:函数体
lambda *args,**kwargs:函数体
def XXX(X):
return x+10
lambda X:X+10
func = lambda X:x+10
#等于 lambda x:return x=10
v1 = func(10)
print(v1) 20
调用
1.res = (lambda X,y:x+10)(1,2) # (1,2)是给x,y传值
2.func = lambda X:x+10
1.找出薪资最高的人
sal={"sir":3000,"tom":"7000","lk":100000,'mn':2000}
def func(k):
return sal[k]
max(sal,key=func)
# max原理和for循环一样,传入一个可迭代对象,得到迭代器,一次取一个值比较直到得到最大的
# key是每次对比的依据,每次迭代出来的数据会传给func函数,函数返回值作为比较依据
max(sal,key=lambda x :sal[x])
三元运算
简单的函数,可以基于lambda表达式实现
结果 = 条件成立时返回值 if 条件 else 不成立时返回值
func = lambda x: "大了" if x > 66 else "小了"
v1 = func(1)
print(v1) # "小了"
v2 = func(100)
print(v2) # "大了"
推导式
推导式又叫生成式可以让我们通过一行代码实现创建 list dict tuple set的同时,初始化一些值
data = []
for i in range(300):
data.append(i)
列表
num_list = [i for i in range(10)]
num_list = [ [i,i] for i in range(10) if i > 6 ]
# i是列表中的元素 ,每循环一次判断后面的条件成立将i放进列表中
# i可以是表达式函数等,只要能有返回值就将返回值放到列表中
# 列表推导式里面的数据是属于推导式这个作用域名的和函数作用域一样
#推导式支持嵌套
data = [[i,j] for j in range(5) for i in range(10)]
for j in range(5):
for i in range(10):
data.append([i,j])
---------------------------------------------------------------------------
def num():
retuen [lambda x: i*x for i in range(4)] # 生成4个函数放到列表里
result = [m(2) for m in num()]
print(result)
1.执行num() 并获取返回值,返回值是[函数,函数,函数,函数] 函数就是lambda函数此时i=3
2. for循环返回值,也就是循环列表 m代指的是函数
3.将返回值的元素(2)执行以下 [6,6,6,6]
----------------------------------------------------------------------------
def num():
retuen (lambda x: i*x for i in range(4)) #返回一个生成器对象此时并不执行循环 返回一个生成器对象
result = [m(2) for m in num()]
print(result)
1.执行num() 并获取返回值 返回值是一个生成器对象
2. for循环返回值,也就是循环生成器,循环一次的时候就生成一个函数,是通过lambda x: i*x for i in range(4)生成的此时i等于0 再循环的时候又生成一个函数,此时 i等于1
也就是 for m in num() 循环一次执行一次 m(2)
3.m(2) x=2 结果是0
4. for再次循环生成器,生成器再次生成一个函数此时i=1
5.m(2) x=2 结果是2
集合
num_set = { (i,i,i) for i in range(10)}
num_set = { (i,i,i) for i in range(10) if i>3}
字典
num_dict = { i:(i,11) for i in range(10)}
num_dict = { i:(i,11) for i in range(10) if i>7}
元组没有推导式,推导式不会立即执行内部循环去生成数据,得到的是一个生成器 也就生成器表达式
data = (i for i in range(10))
# 此时data里没有值,没当执行netx()时才生成一个值,数据过大时节省内存
print(data)
for item in data:
print(item)
函数递归
- 函数的递归调用:是函数嵌套条用的一种特殊形式,在调用一个函数的过程中直接或间接的调用到本身
- 递归的本质就是循环
- 递归调用不应该无限的循环下去,必须在满足某些条件下结束递归调用,结束递归也就是结束函数运行,可以使用return
- 用函数的方式来重复某些代码的运行
# 直接调用
def f1():
print("111")
f1()
f1()
# 一直调用f1 相当于死循环,每调用一次就申请一次内存空间占用内存 python设置调用自己最多1000次 超出1000次报错
sys.getrecursionlimit() #查询允许递归的最大层数
sys.setrecursionlimit(2000) # 设置允许递归的最大层数为2000层
# 间接调用
def f1():
print("f1")
f2()
def f2():
print("f2")
f1()
f1()
递归调用的两个阶段
- 回溯
- 回溯就是一层一层调用下去,上层什么时间结束需要看下层什么时间结束
- 递推
- 满足某些条件下层结束调用,上一层也就结束了,一层一层往上结束掉就是递推
递归应用
# 打印列表里的值
l = [1,2[3,4,[5,6,[7,8,[9,10]]]]]
for x in l :
if type(x) is list:
# 如果是列表再循环再判断
for z in x:
if type(z) is list:
for s in z:
if type(s) is list:
# 一直嵌套下去 本质就是循环第一层代码
else:
print(x)
else:
print(x)
def func1(list1):
for i in list1:
if type(i) is list:
func1(i)
else:
print('i')
模块
- 模块就是一系列功能的集合体 内置模块 第三方模块 自定义模块一个py文件就是一个模块
主文件只有是以主文件的形式运行此脚本时下面的代码才会执行,被导入时则不会被执行。
程序执行的入口文件
执行py文件时
__name__="__main__"
导入一个py文件时
__name__='模块名'
├── commons
│ ├── __init__.py
│ ├── convert.py
│ ├── page.py
│ ├── tencent
│ │ ├── __init__.py
│ │ ├── sms.py
│ │ └── wechat.py
│ └── utils.py
├── many.py
└── run.py
'''
我们通常是执行 run.py 去运行程序,其他的py文件都是一些功能代码。当我们去执行一个文件时,文件内部的 `__name__`变量的值为 `__main__`,所以,主文件经常会看到:
python
import many
from commons import page
from commons import utils
def start():
v1 = many.show()
v2 = page.pagination()
v3 = utils.encrypt()
if __name__ == '__main__':
start()
'''
只有是以主文件的形式运行此脚本时start函数才会执行,被导入时则不会被执行。
- 第三方模块
安装第三方模块
pip3 install 模块名称
或
python setup.py build 编译源码
python setup.py install 安装源码
安装目录:
Windows系统,即:Python安装路径的scripts目录下
C:\Python39\Scripts\pip3.exe
C:\Python39\Scripts\pip3.9.exe
手动安装:
下载get-pip.py文件:https://bootstrap.pypa.io/get-pip.py
打开终端进入目录,用Python解释器去运行已下载的 `get-pip.py`文件即刻安装成功。
- 引用
import func
func.x #模块名加点进行引用 去func的名称空间找x
func.y # 如果当前文件也有x 使用这种方式引用不会冲突 因为是不同的名称空间
- python文件的两种用途
当作程序运行
当作模块导入
__name__ # 当py文件运行时 显示 __main__ 当py文件当作模块导入的时候显示模块名就是py文件名
自定义模块和包
含多个py文件的文件夹就是包 package
在包下面有一个默认为空的`__int__.py`的文件,用于描述当前包的信息,(在导入包下面的模块的时候,会自动加载)/py2必须有,py3可有可无
导入包就是导入包下的init文件
注意:
1.自定义模块名称的时候,不能于内置以及第三方模块同名
2.项目执行文件一般在项目的跟目录,如执行文件嵌套的目录,就需要手动在sys.path中添加路径
3.pycharm中默认会将项目目录加入到sys.path中
导入
定义好模块和包后,想使用其中的功能必须要先导入再使用
导入就是将模块或包加载到内存中,以后用就去内存中拿
导入路径当前脚本所在的目录
import 和 from 导入优先级
1.在内存查找 2.在硬盘查找 硬盘的查找顺序按照sys.path的路径进行查找
sys.path
sys.modules 查看已经加载到内存中的模块
# 值是一个列表,存放了一系列的文件夹
# 第一个文件夹是当前执行文件所在的文件夹
[
E:\python\experiment\txt
E:\python\experiment\txt
D:\PyCharm\plugins\python\helpers\pycharm_display
D:\Python39\python39.zip
D:\Python39\DLLs
D:\Python39\lib
D:\Python39
D:\Python39\lib\site-packages
D:\PyCharm\plugins\python\helpers\pycharm_matplotlib_backend
]
导入任意的模块和包,必须写在路径下面,才能被找到,
可以手动在sys.path中添加指定的路径,然后再导入
import sys
sys.path.append('路径')
import XXXX # 导入路径a下的一个文件
导入方式
将某个文件的内容加载到内存中,然后在内存中拿过来使用,在Python开发中常用的导入的方式有2类方式
第一类: import xxx ,多用于导入sys.path目录下的py文件
第二类:from XXX import XXX ,用于多层嵌套和导入模块中的某个成员 from会把xxx立的所有内容加载到内存
绝对导入
在导入模块时,对于from XX import XX模式,支持绝对导入
绝对导入只能用在包中的py文件中(即:嵌套在文件中的py文件才可以使用,项目根目录下无法使用
导入别名
在项目中导入成员/模块/包,有重名,那么导入会覆盖之前的导入,导入的时候重命名就不会覆盖
相对导入
# 仅限于包内使用,不能跨出包(包内模块之间的导入)
.. 代表上一层文件夹
. 代表当前文件夹
from XXXX import XX as us
import xxx as us
导入的模块改名为us
首次导入模块的过程
import func
1.执行func.py
2.产生func.py的名称空间将func.py运行过程中产生的名称都放到该名称空间里
3.在当前文件中产生一个名字func,该名字指向2中产生的名称空间
4.在当前文件再次导入该模块还是引用第一次产生的结果
软件开发目录规范
object/
bin/ 存放可执行文件,入口文件
start.py
core/ 存放业务逻辑代码供bin调用
src.py
api/ 存放接口文件,接口主要用于业务逻辑提供数据操作
db/ 存放数据库相关代码
db.py
lib/ 存放共享模块自定义的模块
common.py
config/ 存放配置信息
settings.py
log/ 存放日志文件
run.py 程序启动文件
setup.py 安装部署打包的脚本
requirements.txt 第三方模块的相关信息
README 说明文件
内置模块
序列化
序列化是把内存的数据类型转换为一个特定类型的格式内容
该格式的数据可以用于存储或传输给其他平台使用
反序列化
将特定格式的数据转换为内存中的数据
json
json模块时python内部的一个模块,可以将python和json格式的数据互相转换
json格式,是一种数据格式,本质上是字符串,常用于网络数据传输
# Python中的数据类型的格式
data = [
{"id": 1, "name": "武沛齐", "age": 18},
{"id": 2, "name": "alex", "age": 18},
('wupeiqi',123),
]
# JSON格式
value = '[{"id": 1, "name": "武沛齐", "age": 18}, {"id": 2, "name": "alex", "age": 18},["wupeiqi",123]]'
核心功能
跨语言数据传输,例如:
A系统用Python开发,有列表类型和字典类型等。
B系统用Java开发,有数组、map等的类型。
语言不同,基础数据类型格式都不同。
json格式,每种语言都是将自己数据类型转换为json格式,也可以将json格式的数据转换为自己的数据类型。
- 数据类型 >>>> json 序列化
```python import json
data = [ {“id”: 1, “name”: “武沛齐”, “age”: 18}, {“id”: 2, “name”: “alex”, “age”: 18}, ]
res = json.dumps(data) print(res) # ‘[{“id”: 1, “name”: “\u6b66\u6c9b\u9f50”, “age”: 18}, {“id”: 2, “name”: “alex”, “age”: 18}]’
res = json.dumps(data, ensure_ascii=False) print(res) # ‘[{“id”: 1, “name”: “武沛齐”, “age”: 18}, {“id”: 2, “name”: “alex”, “age”: 18}]’
- json>>>>数据类型 反序列化
```python
import json
data_string = '[{"id": 1, "name": "武沛齐", "age": 18}, {"id": 2, "name": "alex", "age": 18}]'
data_list = json.loads(data_string)
print(data_list)
- 序列化结果写入文件
with open('test.json',mode='wt',encoding='utf8') as f:
json.dump([1,'aaa','222'],f)
- 文件读取json格式数据进行反序列化
with open('test.json',mode='rt',encoding='utf8') as f:
json.load(f)
- json转换其他不支持的格式
import json
from fecimal import Decimal
from datetime import datetime
data = [
{'id':1,'name':'ya','age':18,'size':Decimal('18.99')},
{'id':2,'name':'l','age':19,'time':datatime.now()}
]
class MyJSONEncoder(json.JSONEncoder):
def default(self,o):
if type(o) == Decimal:
return str(o)
elif type(o) == datetime:
return str(o)
return super().default(o)
res = json.dumps(data,cls=MyJSONEncoder)
print(res)
类型要求
python的数据类型转换为 json 格式,对数据类型是有要求的,默认只支持:
+-------------------+---------------+
| Python | JSON |
+===================+===============+
| dict | object |
+-------------------+---------------+
| list, tuple | array |
+-------------------+---------------+
| str | string |
+-------------------+---------------+
| int, float | number |
+-------------------+---------------+
| True | true |
+-------------------+---------------+
| False | false |
+-------------------+---------------+
| None | null |
+-------------------+---------------+
其他功能
- json.dumps 序列化生成一个字符串
- json.loads 反序列化生成python数据类型
- json.dump 将数据序列化并写入文件
时间处理
处理时间的模块有 time 和datetime
time
import time
v1 = time.time
获取当前时间戳 时间戳就是1970到现在经历过的秒数
v2= time.timezone
时区
time.strftime('%Y-%m-%d') #获取当前年月日 2021-09-08
res = time.localtime() #结构化时间
'''
time.struct_time(tm_year=2021, tm_mon=9, tm_mday=8, tm_hour=15, tm_min=47, tm_sec=14, tm_wday=2, tm_yday=251, tm_isdst=0)
tm_year 当前是哪一年
tm_mon 当前年的月份
tm_mday 当前年的日
tm_yday 当前年的哪一天
'''
time.sleep(5)
停止5秒,在执行后续的代码
datetime
from datetime import datetime, timezone, timedelta
v1 = datetime.now() # 当前本地时间
print(v1)
tz = timezone(timedelta(hours=7)) # 当前东7区时间
v2 = datetime.now(tz)
print(v2)
v3 = datetime.utcnow() # 当前UTC时间
print(v3)
from datetime import datetime, timedelta
v1 = datetime.now()
print(v1)
# 时间的加减
v2 = v1 + timedelta(days=140, minutes=5)
print(v2) 当前时间加140天后5分钟的时间
# datetime类型 + timedelta类型
from datetime import datetime, timezone, timedelta
v1 = datetime.now()
print(v1)
v2 = datetime.utcnow() # 当前UTC时间
print(v2)
# datetime之间相减,计算间隔时间(不能相加)
data = v1 - v2
print(data.days, data.seconds / 60 / 60, data.microseconds)
# datetime类型 - datetime类型
# datetime类型 比较 datetime类型
字符串时间转换
# 字符串格式的时间 ---> 转换为datetime格式时间
text = "2021-11-11"
v1 = datetime.strptime(text,'%Y-%m-%d') # %Y 年,%m,月份,%d,天。
print(v1)
# datetime格式 ----> 转换为字符串格式
v1 = datetime.now()
val = v1.strftime("%Y-%m-%d %H:%M:%S")
print(val)
时间戳转换为datetime时间
# 时间戳格式 --> 转换为datetime格式
ctime = time.time() # 11213245345.123
v1 = datetime.fromtimestamp(ctime)
print(v1)
# datetime格式 ---> 转换为时间戳格式
v1 = datetime.now()
val = v1.timestamp()
print(val)
# struct_time转换为时间戳
ctime = time.location()
print(time.mktime(s_time))
random
import random
print(random.random()) # (0,1)大于0 小于1 的float数
random.randint(1,3) # 大于等于1 小于等于3的整数
random.randrange(1,3) # 大于等于1 且小于3的整数
random.choice([1,'23',[4,5]]) # 每次从里面随机取一个
random.sample([1,23,[4,5]],2) # 列表元素每次从里面取任意两个组合
random.uniform(1,3) # 大于1小于3的小数
item = [1,3,5,7,9]
random.shuffle(item) # 打乱item的顺序
OS
得到当前工作目录,即当前Python脚本工作的目录路径: os.getcwd()
得到一个生成器对象目录下的所有文件和目录名 os.ealk('文件夹路径')
返回指定目录下的所有文件和目录名:os.listdir()
函数用来删除一个文件:os.remove()
删除多个目录目录为空则删除,并递归到上级目录如果也是空则删除:
os.removedirs(r“c:\python”)
检验给出的路径是否是一个文件:os.path.isfile()
检验给出的路径是否是一个目录:os.path.isdir()
判断是否是绝对路径:os.path.isabs()
检验给出的路径是否真地存:os.path.exists()
返回一个路径的目录名和文件名:os.path.split() e.g os.path.split('/home/swaroop/byte/code/poem.txt') 结果:('/home/swaroop/byte/code', 'poem.txt')
分离扩展名:os.path.splitext()
e.g os.path.splitext('/usr/local/test.py') 结果:('/usr/local/test', '.py')
获取路径名:os.path.dirname()
获得绝对路径: os.path.abspath()
获取文件名:os.path.basename()
运行shell命令: os.system()
读取操作系统环境变量HOME的值:os.getenv("HOME")
返回操作系统所有的环境变量: os.environ
设置系统环境变量,仅程序运行时有效:os.environ.setdefault('HOME','/home/alex')
给出当前平台使用的行终止符:os.linesep Windows使用'\r\n',Linux and MAC使用'\n'
指示你正在使用的平台:os.name 对于Windows,它是'nt',而对于Linux/Unix用户,它是'posix'
重命名:os.rename(old, new)
创建多级目录:os.makedirs(r“c:\python\test”)
创建单个目录:os.mkdir(“test”)
列出指定目录下的所有文件个子目录:os.listdir()
获取文件属性:os.stat(file)
修改文件权限与时间戳:os.chmod(file)
获取文件大小:os.path.getsize(filename)
结合目录名与文件名:os.path.join(dir,filename)
改变工作目录到dirname: os.chdir(dirname)
获取当前终端的大小: os.get_terminal_size()
杀死进程: os.kill(10884,signal.SIGKILL)
python3.5以后推出了新的pathlib模块
获取上级目录的方式
from pathlib import path
res = Path(__file).parent.parent
parent得到上级目录
sys
sys.argv # 获取的是解释器后的参数值 在cmd python3.9 run.py 1 2 3 获取到就是1 2 3
sys.argv 命令行参数List,第一个元素是程序本身路径
sys.exit(n) 退出程序,正常退出时exit(0)
sys.version 获取Python解释程序的版本信息
sys.maxint 最大的Int值
sys.path 返回模块的搜索路径,初始化时使用PYTHONPATH环境变量的值
sys.platform 返回操作系统平台名称
sys.stdout.write('please:') #标准输出 , 引出进度条的例子, 注,在py3上不行,可以用print代替
val = sys.stdin.readline()[:-1] #标准输入
sys.getrecursionlimit() #获取最大递归层数
sys.setrecursionlimit(1200) #设置最大递归层数
sys.getdefaultencoding() #获取解释器默认编码
sys.getfilesystemencoding #获取内存数据存到文件里的默认编码
shutil
文件 文件夹 压缩包 处理模块
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') 仅拷贝状态信息
shutil.copy(src,dst) 拷贝文件和权限
shutil.copy2(src,dst) 拷贝文件和状态信息
shutil.copytree('f1','f2',ignore=shutil.ignore_patterns('*.pyc','tmp*'))
拷贝文件夹
目标目录不能存在,对f2目录的父级目录也要有权限,ignore是排除.pyc 和 tmp开头的文件
shutil.rmtree('f1') 删除文件夹
shutil.move(src,dst) 递归的移动文件 类似与mv命令
shutil.make_archive(base_name,format,....)
创建压缩包并返回文件路径如zip tar
base_name : 压缩包文件名或者路径
format:压缩包种类 zip tar bztar gztar
root_dir: 需要压缩的文件夹路径
owner: 用户默认当前用户
group: 组 默认当前组
logger:用于记录日志,通常是logging.logger对象
import tarfile 解压
#压缩
t = tarfile.open('路径',w)
#解压
t = tarfile.open('路径',r)
t.extractall('路径') 解压的目标文件夹
t.close()
import zipfille
压缩
z = zipfille.ZipFile('text.zip','w') 创建一个压缩包
z.write('123.txt') 往压缩包添加文件
z.close()
hash
hash是一类算法 该算法接收传入的内容经过算法得到一串hash值
只要传入的内容一样,得到的md5值必然是一样的
不可逆的无法由hash值反解出内容
只要使用的hash算法不变无论校验的内容有多大得到的hash值长度都是固定的
import hashlib
m = hashlib.md5()
m.update('hello'.encode('utf-8'))
m.update('world'.encode('utf-8'))
res = m.hexdigest() # helloworld经过加密的结果
m = hashlib.md5('hello'.encode('utf-8'))
密码加盐
import hashlib
m = hashlib.md5()
m.update('CCC'.encode('utf-8'))
m.upate('文本内容'.encode('utf-8'))
m.update('NNN'.encode('utf-8'))
res = m.hexdigest()
# ccc NNN就是加盐,为需要加密的内容前后或中间添加一段内容
subprocess
import subprocess # 执行系统命令
obj = subprocess.Popen('ls/',shell=True,stdout = subprocess.PIPE,stderr = subprocess.PIPE)
# 正确结果放到 stdout 错误结果放到 stderr
n1 = obj.stdout.read() # 从正确的结果里获取内容 字节类型
n1.decode('utf-8') # 使用系统编码进行编译 windows是GBK
日志相关
logging
import logging
logging.basicConfig(
filename ="access.log" # 不指定默认打印到终端
level = logging.info,# 记录日志级别大于等于info的
format='%(asctime)s - %(name)s -%(levelname)s -%(module)s:%(message)s'日志格式
datefmt = '%Y-%m-%d %H:%M:%S %p'时间格式
日志级别
critical 50
error 40
warning 30
info 20
debug 10
level 30
输出日志
logging.debug()
logging.info()
logging.warning()
logging.error()
logging.critical()
)
日志字典
除了加时间,还可以自定义一大堆格式,下表就是所有支持的格式
%(name)s Logger的名字
%(levelno)s 数字形式的日志级别
%(levelname)s 文本形式的日志级别
%(pathname)s 调用日志输出函数的模块的完整路径名,可能没有
%(filename)s 调用日志输出函数的模块的文件名
%(module)s 调用日志输出函数的模块名
%(funcName)s 调用日志输出函数的函数名
%(lineno)d 调用日志输出函数的语句所在的代码行
%(created)f 当前时间,用UNIX标准的表示时间的浮 点数表示
%(relativeCreated)d 输出日志信息时的,自Logger创建以 来的毫秒数
%(asctime)s 字符串形式的当前时间。默认格式是 “2003-07-08 16:49:45,896”。逗号后面的是毫秒
%(thread)d 线程ID。可能没有
%(threadName)s 线程名。可能没有
%(process)d 进程ID。可能没有
%(message)s 用户输出的消息
日志配置字典
standard_format='%(asctime)s - %(name)s -%(levelname)s -%(module)s:%(message)s'
LOGGING_DIC = {
'version': 1,
'disable_existing_loggers': False,
# formatters是存放日志格式的
# standard simple txt是自定义的日志格式名字
# format的值是自定义standard simple txt的格式
# handlers是控制日志输出的位置文件或终端,文件可以是多个
# loggers是产生日志的,产生的日志传递给handlers
# filters提供了细度设备来决定输出哪条日志记录,对日志过滤
'formatters': {
'standard': {'format': standard_format},
'simple': {'format': standard_format},
'txt': {'format': standard_format},
},
'filters': {},
'handlers': {
# 打印到屏幕
'console': {
'level': 'DEBUG', # level指定日志的级别
'class': 'logging.StreamHandler', # 指定输出的位置为屏幕
'formatter': 'simple' # 日志格式
},
# 保存到文件
'default': {
'level': 'DEBUG',
'class': 'logging.handlers.RotatingFileHander', # 输出到文件
'formatter': 'standard', # 日志格式是formatters里的
'filename': 'a1.log', # 日志文件名
'maxbytes': 1024*1024*5, # 日志文件大小默认单位是字节 超出设置的大小后将旧的日志文件重命名并新建一个日志文件名保存新的日志文件
'backupCount': 5,# 超出大小的最多保存几份
'encoding': 'utf-8',
},
'other': {
'level': 'DEBUG', # 收集DEBUG以上的日志
'class': 'logging.FileHandler', #保存到文件
'formatter': 'text', # 日志格式 格式是formatters中定义的格式
'filename': 'a2.log', # 日志文件名
'encoding': 'utf-8', # 写日志的编码
},
},
'loggers': {
# kkk日志产生者,可以配置多个产生者
'kkk': {
'handlers': ['default', 'consoel'], # 将kkk产生的日志传递给default 和 console就是handlers中配置的
'level': 'DEBUG', # loggers有level,handlers也有level不会冲突 loggers(第一层日志级别限制) handlers(第二层日志级别限制)
'propagate': False, # 默认为True,向上(更高level的logger)传递
}
},
}
1.拿到日志产生者loggers来产生日志 一般将日志字典放到settings文件中
2.日志产生者kkk
3.导入日志配置字典 LOGGING_DIC
import settings
import logging
from logging import config,getLogger
config.dictConfig(settings.LOGGING_DIC) # 让logging加载日志字典配置
logger1 = getLogger('kkk') #获取日志字典里的日志生产者kkk , getlogger获取一个没有的日志生产者匹配到的会是loggers中生产者为空的那个首先得在loggers中定义一个名字为空的日志生产者,并且日志名就是getlogger中传的参数
logger1.info('这是info日志') # 产生日志 并根据kkk里的level放行 kkk放行后根据handlers里的level放行
日志轮转
当日志文件过大时不能删除日志文件并且将新的日志写到新文件
就是maxbytes和backupCount设置选项
日志命名
%(name)s Logger的名字 就是日志字典中loggers中配置的日志生产者的名字,在生产环境中要见名知意
filter
import logging
class IgnoreBackuplogFilter(logging.Filter):
# 忽略带 db backup的日志
def filter(self,record):固定写法,record是日志对象
return 'db backup' not in record.getMessage()
日志对象.addFilter(IgnoreBackuplogFilter())
正则表达式
import re
'.' 默认匹配除\n之外的任意一个字符,若指定flag DOTALL,则匹配任意字符,包括换行
'^' 匹配字符开头,若指定flags MULTILINE,这种也可以匹配上(r"^a","\nabc\neee",flags=re.MULTILINE)
'$' 匹配字符结尾, 若指定flags MULTILINE ,re.search('foo.$','foo1\nfoo2\n',re.MULTILINE).group() 会匹配到foo1
'*' 匹配*号前的字符0次或多次, re.search('a*','aaaabac') 结果'aaaa'
'+' 匹配前一个字符1次或多次,re.findall("ab+","ab+cd+abb+bba") 结果['ab', 'abb']
'?' 匹配前一个字符1次或0次 ,re.search('b?','alex').group() 匹配b 0次
'{m}' 匹配前一个字符m次 ,re.search('b{3}','alexbbbs').group() 匹配到'bbb'
'{n,m}' 匹配前一个字符n到m次,re.findall("ab{1,3}","abb abc abbcbbb") 结果'abb', 'ab', 'abb']
'|' 匹配|左或|右的字符,re.search("abc|ABC","ABCBabcCD").group() 结果'ABC'
'(...)' 分组匹配, re.search("(abc){2}a(123|45)", "abcabca456c").group() 结果为'abcabca45'
'\A' 只从字符开头匹配,re.search("\Aabc","alexabc") 是匹配不到的,相当于re.match('abc',"alexabc") 或^
'\Z' 匹配字符结尾,同$
'\d' 匹配数字0-9
'\D' 匹配非数字
'\w' 匹配[A-Za-z0-9]
'\W' 匹配非[A-Za-z0-9]
's' 匹配空白字符、\t、\n、\r , re.search("\s+","ab\tc1\n3").group() 结果 '\t'
'(?P...)' 分组匹配 re.search("(?P[0-9]{4})(?P[0-9]{2})(?P[0-9]{4}
re.match 从头开始匹配
re.search 匹配包含,只找一个
re.findall 把所有匹配到的字符放到以列表中的元素返回
re.split 以匹配到的字符当做列表分隔符
re.sub 匹配字符并替换
re.fullmatch 全部匹配
字符相关
- 匹配文本中的’hao’
```python import re text = ‘你hao,他hao,大家hao’ data.list= re.findall(‘hao’,text)# findall从左到右依次匹配,用hao从左到右单个字符依次匹配,第一次用hao和你ha匹配匹配不到,第二次用hao和hao匹配到,第三次用hao和ao,匹配,按顺序匹配下去 print(data.list) [‘hao’,’hao’,’hao’]
你hao,他hao,大家hao
- [abc]` 匹配a或b或c 字符。
```python
import re
text = "你hao,abc,oliklabc"
data_list = re.findall("[abc]", text)
print(data_list) # ['a', 'b', 'c', 'a', 'b', 'c']
import re
text = "你hao,abc,oliklabc"
data_list = re.findall("l[abc]", text)
print(data_list) # ['la']
- 匹配abc以外的字符[^abc]
```python import re
text = “你wffbbupceiqiff” data_list = re.findall(“[^abc]”, text) print(data_list) # [‘你’, ‘w’, ‘f’, ‘f’, ‘u’, ‘p’, ‘e’, ‘i’, ‘q’, ‘i’, ‘f’, ‘f’]
- `[a-z]` 匹配a~z的任意字符( [0-9]也可以 )。只支持[]里放字符
```python
import re
text = "alexrootrootadmin"
data_list = re.findall("t[a-z]", text)
print(data_list) # ['tr', 'ta']
.
代指除换行符以外的任意一个字符。\.
表示只是一个点
```python import re
text = “alexraotrootadmin” data_list = re.findall(“r.o”, text) print(data_list) # [‘rao’, ‘roo’]
```python
import re
text = "alexraotrootadmin"
data_list = re.findall("r.+o", text)
# 贪婪匹配 +代表可以出现一次或者多次
print(data_list) # ['raotroo']
import re
text = "alexraotrootadmin"
data_list = re.findall("r.+?o", text)
# 非贪婪匹配
print(data_list) # ['rao']
\w
代指字母或数字或下划线(汉字)。
```python import re
text = “北京武沛alex齐北 京武沛alex齐” data_list = re.findall(“武\w+x”, text) print(data_list) # [‘武沛alex’, ‘武沛alex’]
- \d 表示是一个数字
```python
import re
text = "root-ad32min-add3-admd1in"
data_list = re.findall("d\d", text)
print(data_list) # ['d3', 'd3', 'd1']
- \s代表任意的空白符,空格制表符
```python import re
text = “root admin add admin” data_list = re.findall(“a\w+\s\w+”, text) print(data_list) # [‘admin add’]
<a name="0defd4fc"></a>
##### 数量相关
- `*` 前一个字符重复0次或更多次
```python
import re
text = "他是大B个,确实是个大2B。"
data_list = re.findall("大2*B", text)
print(data_list) # ['大B', '大2B']
+
重复1次或更多次
```python import re
text = “他是大B个,确实是个大2B,大3B,大66666B。” data_list = re.findall(“大\d+B”, text) print(data_list) # [‘大2B’, ‘大3B’, ‘大66666B’]
- `?` 重复0次或1次
```python
import re
text = "他是大B个,确实是个大2B,大3B,大66666B。"
data_list = re.findall("大\d?B", text)
print(data_list) # ['大B', '大2B', '大3B']
{n}
重复n次
```python import re
text = “楼主太牛逼了,在线想要 442662578@qq.com和xxxxx@live.com谢谢楼主,手机号也可15131255789,搞起来呀” data_list = re.findall(“151312\d{5}”, text) print(data_list) # [‘15131255789’]
- `{n,}` 重复n次或更多次
```python
import re
text = "楼主太牛逼了,在线想要 442662578@qq.com和xxxxx@live.com谢谢楼主,手机号也可15131255789,搞起来呀"
data_list = re.findall("\d{9,}", text)
print(data_list) # ['442662578', '15131255789']
{n,m}
重复n到m次
```python import re
text = “楼主太牛逼了,在线想要 442662578@qq.com和xxxxx@live.com谢谢楼主,手机号也可15131255789,搞起来呀” data_list = re.findall(“\d{10,15}”, text) print(data_list) # [‘15131255789’]
<a name="e4cda2b6"></a>
##### 括号(分组)
- 提取数据区域( )
```python
import re
text = "楼主太牛逼了,在线想要 442662578@qq.com和xxxxx@live.com谢谢楼主,手机号也可15131255789,搞起来呀"
data_list = re.findall("15131(2\d{5})", text)
print(data_list) # ['255789']
import re
text = "楼主太牛逼了,在线想要 442662578@qq.com和xxxxx@live.com谢谢楼主,手机号也可15131255789,搞起来15131266666呀"
data_list = re.findall("15(13)1(2\d{5})", text)
print(data_list) # [ ('13', '255789') ]
import re
text = "楼主太牛逼了,在线想要 442662578@qq.com和xxxxx@live.com谢谢楼主,手机号也可15131255789,搞起来呀"
data_list = re.findall("(15131(2\d{5}))", text)
print(data_list) # [('15131255789', '255789')]
- 获取指定区域 + 或条件
|
或
```python import re
text = “楼主15131root太牛15131alex逼了,在线想要 442662578@qq.com和xxxxx@live.com谢谢楼主,手机号也可15131255789,搞起来呀” data_list = re.findall(“15131(2\d{5}|r\w+太)”, text) print(data_list) # [‘root太’, ‘255789’]
```python
import re
text = "楼主15131root太牛15131alex逼了,在线想要 442662578@qq.com和xxxxx@live.com谢谢楼主,手机号也可15131255789,搞起来呀"
data_list = re.findall("(15131(2\d{5}|r\w+太))", text)
print(data_list) # [('15131root太', 'root太'), ('15131255789', '255789')]
开始和结束
# 4. 起始和结束
上述示例中都是去一段文本中提取数据,只要文本中存在即可。
但,如果要求用户输入的内容必须是指定的内容开头和结尾,比就需要用到如下两个字符。
- `^` 开始
- `$` 结束
import re
text = "啊442662578@qq.com我靠"
email_list = re.findall("^\w+@\w+.\w+$", text, re.ASCII)
print(email_list) # []
================================================================================
import re
text = "442662578@qq.com"
email_list = re.findall("^\w+@\w+.\w+$", text, re.ASCII)
print(email_list) # ['442662578@qq.com']
特殊字符
由于正则表达式中 `* . \ { } ( ) ` 等都具有特殊的含义,所以如果想要在正则中匹配这种指定的字符,需要转义,例如:
re模块
python中提供了re模块,可以处理正则表达式并对文本进行处理。
- findall,获取匹配到的所有数据
```python import re
text = “dsf130429191912015219k13042919591219521Xkk” data_list = re.findall(“(\d{6})(\d{4})(\d{2})(\d{2})(\d{3})([0-9]|X)”, text) print(data_list) # [(‘130429’, ‘1919’, ‘12’, ‘01’, ‘521’, ‘9’), (‘130429’, ‘1959’, ‘12’, ‘19’, ‘521’, ‘X’)]
- match,从起始位置开始匹配,匹配成功返回一个对象,未匹配成功返回None
```python
import re
text = "大小逗2B最逗3B欢乐"
data = re.match("逗\dB", text)
print(data) # None
import re
text = "逗2B最逗3B欢乐"
data = re.match("逗\dB", text)
if data:
content = data.group() # "逗2B"
print(content)
- search,浏览整个字符串去匹配第一个,未匹配成功返回None
```python import re
text = “大小逗2B最逗3B欢乐” data = re.search(“逗\dB”, text) if data: print(data.group()) # “逗2B”
- sub,替换匹配成功的位置
```python
import re
text = "逗2B最逗3B欢乐"
data = re.sub("\dB", "沙雕", text)
print(data) # 逗沙雕最逗沙雕欢乐
import re
text = "逗2B最逗3B欢乐"
data = re.sub("\dB", "沙雕", text, 1)
print(data) # 逗沙雕最逗3B欢乐
- split,根据匹配成功的位置分割
```python import re
text = “逗2B最逗3B欢乐” data = re.split(“\dB”, text) print(data) # [‘逗’, ‘最逗’, ‘欢乐’]
```python
import re
text = "逗2B最逗3B欢乐"
data = re.split("\dB", text, 1)
print(data) # ['逗', '最逗3B欢乐']
- finditer 得到一个迭代器
```python import re
text = “逗2B最逗3B欢乐” data = re.finditer(“\dB”, text) for item in data: print(item.group())
```python
import re
text = "逗2B最逗3B欢乐"
data = re.finditer("(?P<xx>\dB)", text)
# 命名分组 (?P<名称>正则)
for item in data:
print(item.groupdict())
```python
text = "dsf130429191912015219k13042919591219521Xkk"
data_list = re.finditer("\d{6}(?P<year>\d{4})(?P<month>\d{2})(?P<day>\d{2})\d{3}[\d|X]", text)
for item in data_list:
info_dict = item.groupdict()
print(info_dict)
深浅拷贝
浅拷贝
- 不可变类型,不拷贝。
按理说拷贝v1之后,v2的内存地址应该不同,但由于python内部优化机制,内存地址是相同的,因为对不可变类型而言,如果以后修改值,会重新创建一份数据,不会影响原数据,所以,不拷贝也无妨。 ```python import copy
v1 = “武沛齐” print(id(v1)) # 140652260947312
v2 = copy.copy(v1) print(id(v2)) # 140652260947312
- 可变类型,只拷贝第一层。 <br />![](1.%E5%87%BD%E6%95%B0/%E6%8B%B7%E8%B4%9D1.png#crop=0&crop=0&crop=1&crop=1&id=cU9T3&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=)
```python
import copy
v1 = ["武沛齐", "root", [44, 55]]
print(id(v1)) # 140405837216896
print(id(v1[2])) # 140405837214592
v2 = copy.copy(v1)
print(id(v2)) # 140405837214784
print(id(v2[2])) # 140405837214592
深拷贝
- 不可变类型,不拷贝
特殊的元组: ```python import copy
v1 = “武沛齐” print(id(v1)) # 140188538697072
v2 = copy.deepcopy(v1) print(id(v2)) # 140188538697072
- 元组元素中无可变类型,不拷贝
```python
import copy
v1 = ("武沛齐", "root")
print(id(v1)) # 140243298961984
v2 = copy.deepcopy(v1)
print(id(v2)) # 140243298961984
- 元素中有可变类型,找到所有【可变类型】或【含有可变类型的元组】 均拷贝一份
```python import copy
v1 = (“武沛齐”, “root”, [11, [44, 55], (11, 22), (11, [], 22), 33]) v2 = copy.deepcopy(v1)
print(id(v1)) # 140391475456384 print(id(v2)) # 140391475456640
print(id(v1[2])) # 140352552779008 print(id(v2[2])) # 140352552920448
print(id(v1[2][1])) # 140642999940480 print(id(v2[2][1])) # 140643000088832
print(id(v1[2][2])) # 140467039914560 print(id(v2[2][2])) # 140467039914560
print(id(v1[2][3])) # 140675479841152 print(id(v2[2][3])) # 140675480454784
- 可变类型,找到所有层级的 【可变类型】或【含有可变类型的元组】 均拷贝一份 <br />![](1.%E5%87%BD%E6%95%B0/%E6%8B%B7%E8%B4%9D2.png#crop=0&crop=0&crop=1&crop=1&id=HMRkA&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=)
```python
import copy
v1 = ["武沛齐", "root", [11, [44, 55], (11, 22), (11, [], 22), 33]]
v2 = copy.deepcopy(v1)
print(id(v1)) # 140391475456384
print(id(v2)) # 140391475456640
print(id(v1[2])) # 140352552779008
print(id(v2[2])) # 140352552920448
print(id(v1[2][1])) # 140642999940480
print(id(v2[2][1])) # 140643000088832
print(id(v1[2][2])) # 140467039914560
print(id(v2[2][2])) # 140467039914560
print(id(v1[2][3])) # 140675479841152
print(id(v2[2][3])) # 140675480454784
import copy
v1 = ["武沛齐", "root", [44, 55]]
v2 = copy.deepcopy(v1)
print(id(v1)) # 140405837216896
print(id(v2)) # 140405837214784
print(id(v1[2])) # 140563140392256
print(id(v2[2])) # 140563140535744
定义函数形式
- 定义无参数函数
- 定义有参数函数
- 定义空函数,代码体为pass
调用函数
- 语句形式 :只加括号调用 add()
- 表达式形式 res = add()*10
- 函数调用可以当作参数
内置函数
- map()映射
l = ['123','456','789']
map(lambda name:name+'_int',l)
#第一个参数传函数就是处理的规则,第二个参数传可迭代对象
#每从l里迭代出来一个值就交给lambda处理
#map()得到的是一个生成器
- filter() 过滤
filter(函数,可迭代对象)
l = ['123','4563','789','1113']
res = ( i for i in l if i.endswitch('3'))
filter(lambda i:i.endswitch('3'),l)
# 将每一次从l中迭代出来的值交给lambda处理,如果lambda返回值为True,filter则将这个值保留下来
- reduce()
python3中reduce()已经不是内置函数了
# reduce用来将多个值合并
from functools import reduce
reduce(lambda x,y:x+y,[11,22,33],10)
#10 是初始值 不制定默认为空 如果不设置初始值 则从列表中取两个值
# 从列表中取一个值 11和初始值10 传给x,y 得到返回值 21 作为初始值
# 再从列表中取一个值 22和初始值21 传给x,y 得到返回值 43 作为初始值
# 再从列表中取一个值 33 和初始值43 传给x,y 得到返回值 76 作为初始值
- exec()
exec("print(123)")
执行字符串形式的语句
- zip()
a = {1,2,3,}
b = {a,b,c}
list(zip(a,b))
[(1,a),(2,b),(3,c)]
- id()
查看内存地址
- enumerate()
维护一个数值,可以指定数值的开始,默认0开始
v1 = ['l','liu','y']
for num,valie in enumerate(v1)
#for num,valie in enumerate(v1,1)
print(num,valie)
# 0 l
# 1 liu
# 2 y
-----------------------------------------
# 1 l
# 2 liu
# 3 y
- sorted()
将字典按照键从小到大排序,然后在按照如下格式拼接起来。(微信支付API内部处理需求)
info = {
'sign_type': "MD5",
'out_refund_no': "12323",
'appid': 'wx55cca0b94f723dc7',
'mch_id': '1526049051',
'out_trade_no': "ffff",
'nonce_str': "sdfdffd",
'total_fee': 9901,
'refund_fee': 10000
}
data = "&".join(["{}={}".format(key, value) for key, value in sorted(info.items(), key=lambda x: x[0])])
#sorted 和 max()原理一样 对键进行排序,key是告诉sorted根据什么排序
print(data)