函数
函数,可以当作是一堆功能代码的集合或者具备某个功能的工具(先定义后引用)
一般应用在
- 有重复代码出现,用函数来代替代码的重用性
- 代码太长增加函数的可读性
def 函数名(参数1,参数2,...):XXXXXXXXreturn 值 # 返回值函数名() #执行该函数
函数的参数
#发送邮件实例import smtplibfrom email.mime.text import MIMETextfrom email.utils import formataddrdef 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) 6smt(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) 13add(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,33add(*[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,3func(**{"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>##### 动态实参动态参数,定义函数时在形参的位置用*或**可以接受任意个参数在定义函数时可以用*和** ,在执行函数的时候,也可以使用- 形参固定,实参用*和**```pythondef fun(a1,a2)print(a1,a2)fun(*[11,22]) # 11,22func( **{"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 requestfrom xml.etree import ElementTree as ETdef 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_listresult = xml_to_list("北京")print(result)
def add():return 888fure = add()print(fure) # 888需要变量接受函数的返回值
- 返回值可以是任意的类型,如果函数中没写return返回的是none
def add():return [1,2,(12)]res = addprint(res )# [1,2,(12)]
- 函数中没写返回值,或者return /return None ,执行函数获取的返回值都是None
def azs():print("123")returnres = azs()print(res)# None
- return 后面的值如果有逗号,会将返回值转换成元组在返回
def fun():return 1,2,34v1= fun()print(v1)# (1,2,34)
- 函数遇到return就会立即退出函数,终止函数中的所有代码,并且将return后的值当作本次运行的结果返回
- return,返回值是为了将函数执行的结果返回给调用者,以便于后续的其他操作
函数内存地址相关
函数在执行传参的时候,传递的时内存地址
可以节省内存
对于可变类型且函数中需改元素的内容,所有的地方都会修改:可变类型 dict set list
内存地址举例def jkl(data):print(data,id(data)) # 123 140247057684592v1 = "123"print(id(v1)) # 140247057684592jkl(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 datav1 = 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 a2v1 = 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 a2v1 = 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+a2res = fun(*[1,2])print(res)
函数做元素
函数相当于变量,在列表等容器中也可以把函数当作时元素
def fun():return 123data_list = ["武沛齐", "func", func , func() ]print( data_list[0] ) # 字符串"武沛齐"print( data_list[1] ) # 字符串 "func"print( data_list[2] ) # 函数 funcprint( data_list[3] ) # 整数 123res = data_list[2]()print( res ) # 执行函数 func,并获取返回值;print再输出返回值。print( data_list[2]() ) # 123
函数赋值
- 将函数名赋值给其他的变量,函数名其实就是变量,代指某个函数;如果将函数名赋值给另一个变量,则此变量也会代指该函数
def fun(a,b):print(a,b)xxx = funfun(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 + 100def handler():print("执行handler函数")return plusresult = handler()data = result(20) # 120print(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= 18print(age)def hand():age = 20print(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_LISTCITY_LIST = ["河北","河南","山西"]print(CITY_LIST)global COUNTRYCOUNTRY = "中华人民共和国" # 直接把country重新赋值print(COUNTRY)def upload():print(COUNTRY)print(CITY_LIST)print(COUNTRY)download()upload()
nonlocal关键字
对外层函数的变量进行重新赋值
从当前位置的上一层作为开始,如果没有,直到找到最外层的函数停止
x = 0def f1():x = 11def f2():nonlocal xx = 22 # 修改的是f1中的xf2()print("f1的x",x)f1()
函数嵌套
python中以函数为作用域,在作用域中定义的数据只能被当前作用域或者子作用域所使用
函数在作用域中
函数也是定义在作用域中的数据,在执行函数的时候,也是先在自己作用域中寻找,没有则向上一级作用域寻找
# 在全局作用域定义了函数fundef 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 randomfrom PIL import Image, ImageDraw, ImageFontdef 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 = textelse: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_stringcreate_lines()create_points()code = create_code()# 将图片写入到文件with open(img_file_path, mode='wb') as img_object:img.save(img_object)return codecode = create_image_code("a2.png")print(code)
嵌套引发的作用域的问题
- 优先在自己的作用域中寻找,自己没有去上级
- 在作用域中寻找值的时候,要确保此刻的值是什么
- 分析函数的执行,确定函数作用域链
闭包函数
闭包函数 = 名称空间与作用域+函数嵌套+函数对象
闭是指该函数是内嵌函数
包是指该函数包含对外层函数作用域名字的引用
def n1():x = 345def n2():print(x)n2()# 无论在哪里调用n1 输出的x始终是345 因为名称空间的嵌套关系是在函数定义阶段确定的def n1():x = 345def n2():print(x)return n2# 此时在全局也可以调用n2函数,但是x始终是345# 打破了名称空间的层级关系
def outher(args):file = argsdef 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) # 将原函数的属性赋值给innerdef 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 valueprint(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 resreturn innerreturn 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 res1return warpp1def deco2(func2):def warpp2(*args,**kwargs):print('第二个')res2 = func2(*args,**kwargs)return res2return warpp2def otuer(x): #def deco3(func3):def warpp3(*args,**kwargs):print('第三个')res3 = func3(*args,**kwargs)return res3return warpp3return 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) >> warpp32. @deco2 >> index= deco2(warpp3) >> warpp23. @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 None6. 此时warpp3(1,2)执行完毕回到warpp2(1,2)继续往下运行 return res2 也就是 return None7.此时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 = 0while 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__()拿到一个返回值赋值给i3.循环步骤2,直到抛出StopIteration异常for循环会捕捉异常然后结束循环
迭代器总结
1.优点
- 得到一种统一取值的方法,对于序列和非序列都可以用迭代方式取值
- 取值过程中节省内存,用
__next__每次只取一个2.缺点
- 取值没有索引取值方便,索引想取哪个取哪个
- 迭代器的声明周期是一次性的,值取完后就
- 只能取下一个值不能回到开始
生成器
生成器可以理解为迭代器,内置
__iter__和__next,就是自定义的迭代器生成器函数,函数中由yield存在时,这个函数就是生成器函数
- 调用生成器函数,函数并不会执行函数体代码而是返回一个生成器对象
def fun(): # 生成器函数print('1')yiled 1object = func() # 生成器对象 创建生成器对象内部是根据生成器类(generator)创建的对象
def func():print(111)yield 1print(222)yield 2print(333)yield 3print(444)data = func()# data.__next()方法会触发函数体代码的运行遇到yiled就停下来将yiled后面的值当作本次next调用的结果# 下次执行`__next__`时,会从上一次的位置基础上再继续向下执行。# 当取值范围超出后或中途遇到return就报错StopIteration
生成器的特点是,记录在函数中的执行位置,将函数挂起到某个状态使用next(),进入生成器函数并执行当中的代码def func():print(111)yield 1 #执行到此位置就不在执行后面的代码,并将1返回调用者 下次使用next()后会进去函数,但是不从头开始,而是从上一次位置开始执行现在也就是当前位置往下执行print(222)yield 2print(333)yield 3print(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 randomdef gen_random_num(max_count):counter = 0while counter < max_count:yield random.randint(1000, 9999)counter += 1data_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 111print("dog:{}吃了{}")go = dog("ww")res = g.send(None) # res = 111res1=g.send(["骨头","肉"]) # 将值给yiled然后yiled再将值给x 也就是x=["骨头","肉"] 而不是 yiled ["骨头","肉"] # res1 = 1111.res = g.send(None)#触发函数代码运行输出"准备" 遇到yiled停下来,将yiled后面的值111作为本次调用的结果返回2. res1=g.send(["骨头","肉"])# 将值给yiled然后yiled再将值给x 也就是x=["骨头","肉"] 输出 "dog:ww吃了["骨头","肉"]"然后继续运行遇到yiled停止将yiled后的111返回
yield fromdef foo():yield 2yield 2yield 2def fun():yield 1yield 1yield 1yield from foo() # 执行foo函数并将foo中的yield值返回yield 1yield 1for 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+10lambda X:X+10
func = lambda X:x+10#等于 lambda x:return x=10v1 = 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=32. 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 结果是04. for再次循环生成器,生成器再次生成一个函数此时i=15.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__`,所以,主文件经常会看到:pythonimport manyfrom commons import pagefrom commons import utilsdef 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.exeC:\Python39\Scripts\pip3.9.exe手动安装:下载get-pip.py文件:https://bootstrap.pypa.io/get-pip.py打开终端进入目录,用Python解释器去运行已下载的 `get-pip.py`文件即刻安装成功。
- 引用
import funcfunc.x #模块名加点进行引用 去func的名称空间找xfunc.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.pathsys.modules 查看已经加载到内存中的模块# 值是一个列表,存放了一系列的文件夹# 第一个文件夹是当前执行文件所在的文件夹[E:\python\experiment\txtE:\python\experiment\txtD:\PyCharm\plugins\python\helpers\pycharm_displayD:\Python39\python39.zipD:\Python39\DLLsD:\Python39\libD:\Python39D:\Python39\lib\site-packagesD:\PyCharm\plugins\python\helpers\pycharm_matplotlib_backend]导入任意的模块和包,必须写在路径下面,才能被找到,可以手动在sys.path中添加指定的路径,然后再导入import syssys.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 usimport xxx as us导入的模块改名为us
首次导入模块的过程import func1.执行func.py2.产生func.py的名称空间将func.py运行过程中产生的名称都放到该名称空间里3.在当前文件中产生一个名字func,该名字指向2中产生的名称空间4.在当前文件再次导入该模块还是引用第一次产生的结果
软件开发目录规范object/bin/ 存放可执行文件,入口文件start.pycore/ 存放业务逻辑代码供bin调用src.pyapi/ 存放接口文件,接口主要用于业务逻辑提供数据操作db/ 存放数据库相关代码db.pylib/ 存放共享模块自定义的模块common.pyconfig/ 存放配置信息settings.pylog/ 存放日志文件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>>>>数据类型 反序列化```pythonimport jsondata_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 jsonfrom fecimal import Decimalfrom datetime import datetimedata = [{'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 timev1 = time.time获取当前时间戳 时间戳就是1970到现在经历过的秒数v2= time.timezone时区time.strftime('%Y-%m-%d') #获取当前年月日 2021-09-08res = 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, timedeltav1 = 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, timedeltav1 = datetime.now()print(v1)# 时间的加减v2 = v1 + timedelta(days=140, minutes=5)print(v2) 当前时间加140天后5分钟的时间# datetime类型 + timedelta类型
from datetime import datetime, timezone, timedeltav1 = datetime.now()print(v1)v2 = datetime.utcnow() # 当前UTC时间print(v2)# datetime之间相减,计算间隔时间(不能相加)data = v1 - v2print(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.123v1 = 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 randomprint(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 pathres = Path(__file).parent.parentparent得到上级目录
sys
sys.argv # 获取的是解释器后的参数值 在cmd python3.9 run.py 1 2 3 获取到就是1 2 3sys.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 tarbase_name : 压缩包文件名或者路径format:压缩包种类 zip tar bztar gztarroot_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 hashlibm = 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 hashlibm = 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 错误结果放到 stderrn1 = obj.stdout.read() # 从正确的结果里获取内容 字节类型n1.decode('utf-8') # 使用系统编码进行编译 windows是GBK
日志相关
logging
import logginglogging.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 50error 40warning 30info 20debug 10level 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.日志产生者kkk3.导入日志配置字典 LOGGING_DICimport settingsimport loggingfrom logging import config,getLoggerconfig.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 loggingclass 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 字符。```pythonimport retext = "你hao,abc,oliklabc"data_list = re.findall("[abc]", text)print(data_list) # ['a', 'b', 'c', 'a', 'b', 'c']
import retext = "你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]也可以 )。只支持[]里放字符```pythonimport retext = "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’]
```pythonimport retext = "alexraotrootadmin"data_list = re.findall("r.+o", text)# 贪婪匹配 +代表可以出现一次或者多次print(data_list) # ['raotroo']
import retext = "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 表示是一个数字```pythonimport retext = "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次或更多次```pythonimport retext = "他是大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次```pythonimport retext = "他是大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次或更多次```pythonimport retext = "楼主太牛逼了,在线想要 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>##### 括号(分组)- 提取数据区域( )```pythonimport retext = "楼主太牛逼了,在线想要 442662578@qq.com和xxxxx@live.com谢谢楼主,手机号也可15131255789,搞起来呀"data_list = re.findall("15131(2\d{5})", text)print(data_list) # ['255789']
import retext = "楼主太牛逼了,在线想要 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 retext = "楼主太牛逼了,在线想要 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’]
```pythonimport retext = "楼主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 retext = "啊442662578@qq.com我靠"email_list = re.findall("^\w+@\w+.\w+$", text, re.ASCII)print(email_list) # []================================================================================import retext = "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```pythonimport retext = "大小逗2B最逗3B欢乐"data = re.match("逗\dB", text)print(data) # None
import retext = "逗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,替换匹配成功的位置```pythonimport retext = "逗2B最逗3B欢乐"data = re.sub("\dB", "沙雕", text)print(data) # 逗沙雕最逗沙雕欢乐
import retext = "逗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) # [‘逗’, ‘最逗’, ‘欢乐’]
```pythonimport retext = "逗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())
```pythonimport retext = "逗2B最逗3B欢乐"data = re.finditer("(?P<xx>\dB)", text)# 命名分组 (?P<名称>正则)for item in data:print(item.groupdict())
```pythontext = "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 />```pythonimport copyv1 = ["武沛齐", "root", [44, 55]]print(id(v1)) # 140405837216896print(id(v1[2])) # 140405837214592v2 = copy.copy(v1)print(id(v2)) # 140405837214784print(id(v2[2])) # 140405837214592
深拷贝
- 不可变类型,不拷贝
特殊的元组: ```python import copy
v1 = “武沛齐” print(id(v1)) # 140188538697072
v2 = copy.deepcopy(v1) print(id(v2)) # 140188538697072
- 元组元素中无可变类型,不拷贝```pythonimport copyv1 = ("武沛齐", "root")print(id(v1)) # 140243298961984v2 = 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 />```pythonimport copyv1 = ["武沛齐", "root", [11, [44, 55], (11, 22), (11, [], 22), 33]]v2 = copy.deepcopy(v1)print(id(v1)) # 140391475456384print(id(v2)) # 140391475456640print(id(v1[2])) # 140352552779008print(id(v2[2])) # 140352552920448print(id(v1[2][1])) # 140642999940480print(id(v2[2][1])) # 140643000088832print(id(v1[2][2])) # 140467039914560print(id(v2[2][2])) # 140467039914560print(id(v1[2][3])) # 140675479841152print(id(v2[2][3])) # 140675480454784
import copyv1 = ["武沛齐", "root", [44, 55]]v2 = copy.deepcopy(v1)print(id(v1)) # 140405837216896print(id(v2)) # 140405837214784print(id(v1[2])) # 140563140392256print(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 reducereduce(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)
