python-函数
第六章 函数
v = 前面 if 条件 else 后面
print(v)
if 条件:
v = '前面'
else:
v = '后面'
# 让用户输入值,如果值是整数,则转换成整数,否则赋值位None
data = input('>>>')
value = int(data) if data.isdecimal() else None
# **注意**:先做出来,再思考如何简化。
面向过程编程
-
可读性差,可重用用性差
# 面向过程编程
user_input = input('请输入角色:')
if user_input == '管理员':
import smtplib
from email.mime.text import MIMEText
from email.utils import formataddr
msg = MIMEText('管理员,我想演男一号,你想怎么着都行。', 'plain', 'utf-8')
msg['From'] = formataddr(["李邵奇", '15776556369@163.com'])
msg['To'] = formataddr(["管理员", '344522251@qq.com'])
msg['Subject'] = "情爱的导演"
server = smtplib.SMTP("smtp.163.com", 25)
server.login("15776556369@163.com", "qq1105400511")
server.sendmail('15776556369@163.com', ['管理员', ], msg.as_string())
server.quit()
elif user_input == '业务员':
import smtplib
from email.mime.text import MIMEText
from email.utils import formataddr
msg = MIMEText('业务员,我想演男一号,你想怎么着都行。', 'plain', 'utf-8')
msg['From'] = formataddr(["李邵奇", '15776556369@163.com'])
msg['To'] = formataddr(["业务员", '业务员'])
msg['Subject'] = "情爱的导演"
server = smtplib.SMTP("smtp.163.com", 25)
server.login("15776556369@163.com", "qq1105400511")
server.sendmail('15776556369@163.com', ['业务员', ], msg.as_string())
server.quit()
elif user_input == '老板':
import smtplib
from email.mime.text import MIMEText
from email.utils import formataddr
msg = MIMEText('老板,我想演男一号,你想怎么着都行。', 'plain', 'utf-8')
msg['From'] = formataddr(["李邵奇", '15776556369@163.com'])
msg['To'] = formataddr(["老板", '老板邮箱'])
msg['Subject'] = "情爱的导演"
server = smtplib.SMTP("smtp.163.com", 25)
server.login("15776556369@163.com", "qq1105400511")
server.sendmail('15776556369@163.com', ['老板邮箱', ], msg.as_string())
server.quit()
6.1 函数式编程
-
定义:函数是以功能为导向,一个函数就是一个功能。
函数的特点:
1、定义函数可以将功能代码进行封装
2、便于对该功能进行复用
3、函数只有被调用才会被执行
4、函数的出现提高了代码的复用性
-
命名:和变量的命名一样
def send_email():
import smtplib
from email.mime.text import MIMEText
from email.utils import formataddr
msg = MIMEText('老板,我想演男一号,你想怎么着都行。', 'plain', 'utf-8')
msg['From'] = formataddr(["李邵奇", '15776556369@163.com'])
msg['To'] = formataddr(["老板", '老板邮箱'])
msg['Subject'] = "情爱的导演"
server = smtplib.SMTP("smtp.163.com", 25)
server.login("15776556369@163.com", "qq1105400511")
server.sendmail('15776556369@163.com', ['老板邮箱', ], msg.as_string())
server.quit()
user_input = input('请输入角色:')
if user_input == '管理员':
send_email()
elif user_input == '业务员':
send_email()
elif user_input == '老板':
send_email()
6.2 函数的基本结构
-
结构:def 关键字,定义函数,函数名(): 缩进,函数体,尽量不要出现print
def 函数名():
# 函数内容
pass
# 函数的执行
函数名()
# 调用函数:函数名()
v = [11,22,33,44]
def my_len(a):
count = 0
for i in range(len(a)):
count += 1
print(count)
my_len(v)
# 注意:函数不被调用,则内部代码永远不会被执行。
-
函数的返回值
v = [11,22,33,44]
def my_len(a):
count = 0
for i in range(len(a)):
count += 1
return count
val = my_len(v)
print(val)
# return 将数据返回给函数的调用者,执行者:函数名()
-
return :默认返回None
def meet():
print('打开tantan')
print('左滑一下')
return
print('右滑一下')
print('找美女')
print('悄悄话....')
print('约....走起...')
meet()
# return: 在函数中遇到return直接结束执行函数。
-
return 返回单个值【是数据什么类型就返回什么数据类型】
def func():
print(123)
return 1 # int类型
print(func())
-
return 返回多个值【是以元组的形式返回给调用者】
def func():
print(123
return 1, '妹子', [1.2,3]
print(func())
# (1, '妹子', [1.2, 3])
6.3 函数的参数
-
函数的传参
def get_list_first_data(aaa): # aaa叫形式参数(形参)
v = [11,22,33,44]
print(v[aaa])
get_list_first_data(1) # 2/2/1调用函数时传递叫:实际参数(实参)
get_list_first_data(2)
get_list_first_data(3)
get_list_first_data(0)
6.3.1实参角度
-
位置参数【从左至右,一 一对应】
def meet(sex,age,skill):
print('打开tantan')
print('进行筛选:性别:%s,年龄:%s,%s' %(sex,age,skill))
print('左滑一下')
print('右滑一下')
print('找美女')
print('悄悄话....')
print('约....走起...')
meet('女',25,'python技术好的',)
-
关键字参数【一 一对应】
def meet(sex,age,skill,hight,weight,):
print('打开tantan')
print('进行筛选:性别:%s,年龄:%s,技术:%s,身高:%s,体重%s' %(sex,age,skill,hight,weight))
print('左滑一下')
print('右滑一下')
print('找美女')
print('悄悄话....')
print('约....走起...')
meet(age=25,weight=100,hight=174,skill='python技术好的',sex='女') #关键字参数
-
混合参数
# 混合参数
# 位置参数一定要在关键字参数的前面。
def meet(sex,age,skill,hight,weight,):
print('打开tantan')
print('进行筛选:性别:%s,年龄:%s,技术:%s,身高:%s,体重%s' %(sex,age,skill,hight,weight))
print('左滑一下')
print('右滑一下')
print('找美女')
print('悄悄话....')
print('约....走起...')
return '筛选结果:性别:%s,体重%s' %(sex,weight)
print(meet('女',25,weight=100,hight=174,skill='python技术好的'))
6.3.2形参角度
-
位置参数
def meet(sex,age,skill):
print('打开tantan')
print('进行筛选:性别:%s,年龄:%s,%s' %(sex,age,skill))
print('左滑一下')
print('右滑一下')
print('找美女')
print('悄悄话....')
print('约....走起...')
meet('女',25,'python技术好的',)
-
默认参数
# 默认参数设置的意义:普遍经常使用的。
def meet(age,skill='python技术好的',sex='女',):
print('打开tantan')
print('进行筛选:性别:%s,年龄:%s,技能:%s' %(sex,age,skill))
print('左滑一下')
print('右滑一下')
print('找美女')
print('悄悄话....')
print('约....走起...')
# meet(25,'python技术好的',)
meet(25,'运维技术好的','男') # 如果传参就把默认参数覆盖
-
万能参数:如果一个参数设置了动态参数,那么他可以接受所有的位置参数,以及关键字参数,这样就会大大提升函数拓展性,针对于实参参数较多的情况下,解决了一一对应的麻烦。
# 当我们要传的参数过多时,,就需一种形参,可以接受所有的参数。【万能参数】
# 万能参数:*args,约定俗成:args
# 函数定义时:* 代表聚合。它将接收的所有的位置参数聚合成一个元组,并赋值给args
def eat(*args):
print(args)
print('我请你吃:%s,%s,%s,%s,%s,%s' % args)
eat('蒸羊羔','蒸熊掌','蒸鹿邑','烧花鸭','烧雏鸡','烧子鹅')
-
练习题
# 写一个函数:计算你传入函数的所有的数字的和
def func(*args):
count = 0
for i in args:
coount += 1
return count
print(func(1,2,3,4,5,6))
# **kwargs
# 定义函数时:**将接收的所有的的关键字参数聚合到一个字典,将这个字典赋值给了kwargs。
def func(**kwargs):
print(kwargs)
print(func(name='alex',age=73,sex='ladyboy'))
# 万能参数:*args **kwargs
def func(*args,**kwargs):
print(args)
print(kwargs)
func()
# * **在函数的调用时,*代表打散。** 代表只能打散字典
def func(*args,**kwargs):
print(args) # (1,2,3,22,33)
print(kwargs)
# func(*[1,2,3],*[22,33]) # func(1,2,3,22,33)
# func(*'fjskdfsa',*'fkjdsal') # func(1,2,3,22,33)
func(**{'name': '太白'},**{'age': 18}) #func(name='太白',age='18')
-
形参角度的参数的顺序
# *argsd 位置?
def func(a,b,*args,sex= '男'):
print(a,b)
print(sex)
print(args)
func(1,2,3,4,5,6,7,sex='女')
# **kwargs位置?
def func(a,b,*args,sex= '男',**kwargs,):
print(a,b)
print(sex)
print(args)
print(kwargs)
func(1,2,3,4,5,6,7,sex='女',name='Alex',age=80)
# 形参的仅限关键字参数
def func(a,b,*args,sex= '男',c,**kwargs,):
print(a,b)
print(sex)
print(args)
print(c)
print(kwargs)
func(1,2,3,4,5,6,7,sex='女',name='Alex',age=80,c='666')
# 形参角度最终的顺序:位置参数,*args,默认参数,仅限关键字参数,**kwargs【默认参数和仅限关键字参数武所谓前后】
# 这个仅限关键字参数从名字定义就可以看出他只能通过关键字参数传参,其实可以把它当成不设置默认值的默认参数而且必须要传参数,不传就报错。
# 练习题
def foo(a,b,*args,c,sex=None,**kwargs):
print(a,b)
print(c)
print(sex)
print(args)
print(kwargs)
# foo(1,2,3,4,c=6)
# foo(1,2,sex='男',name='alex',hobby='old_woman')
# foo(1,2,3,4,name='alex',sex='男')
# foo(1,2,c=18)
# foo(2, 3, [1, 2, 3],c=13,hobby='喝茶')
# foo(*[1, 2, 3, 4],**{'name':'太白','c':12,'sex':'女'})
-
默认参数如果是可变数据类型【陷阱】
def func(name,alist = [])
alist.append(name)
return alist
ret1 = func('alex')
print(ret1,id(ret1)) # ['alex']
ret2 = func('太白金星')
print(ret2,id(ret2)) # ['alex','太白金星']
# 如果你的默认参数指向的是可变的数据类型,那么你无论调用多少次这个默认参数,都是同一个。
# 函数的定义相当于一次类型构造,默认值只在此时解析一次。
# 而函数调用时不会重新执行默认参数的构造。所以,如果使用了字典,列表这样的可变类型。
# 而又要在函数体内修改它,可能会出现意想不到的效果.
def func(a,alist=[]):
alist.append(a)
return alist
print(func(10)) # [10]
print(func(20,[])) # [20]
print(func(100)) # [10,100]
def func(a,list=[]):
list.append(a)
return list
ret1 = func(10)
ret2 = func(20,[])
ret3 = func(100)
print(ret1) # [10,100]
print(ret2) # [20]
print(ret3) # [10,100]
# print(ret1,ret2,ret3)
6.4.1 命名空间
-
在 python代码运行的时候遇到函数时,会开辟一块内存【空间】,存放变量与值的关系,我们把'存放变量与之的关系'的空间叫------命名空间。
-
代码在运行伊始,创建的存储‘变量名与值的关系’的空间叫做:全局命名空间
-
在函数的运行中开辟的临时的空间叫做局部命名空间也叫做临时名称空间【执行函数时】
-
其实python还有一个空间叫做内置名称空间:内置名称空间存放的就是一些内置函数等拿来即用的特殊的变量:input,print,list等等
# 加载顺序:
# 内置名称空间 ---> 全局名称空间 ----> 局部名称空间(函数执行时)
# def func():
# pass
# func()
# a = 5
# print(666)
# 取值顺序(就近原则) 单向不可逆【LEBG】
# LEGB原则
# input = '太白金星'
def func():
# input = 'alex'
print(input)
# func()
# (从局部找时)局部名称空间 ---> 全局名称空间 ---> 内置名称名称空间
# input = '太白金星'
# def func():
# input = 'alex'
#
# func()
# print(input)
6.4.2 作用域
-
局部作用域不能改变全局变量
# count = 1
# # def func():
# # count += 2
# # print(count)
# # func() # local variable 'count' referenced before assignment
**局部作用域不能改变全局作用域的变量,当python解释器读取到局部作用域时,发现了你对一个变量进行修改的操作,
解释器会认为你在局部已经定义过这个局部变量了,他就从局部找这个局部变量,找不到就报错了**
# 使用可以,不能改变
# def func():
# count = 1
# def inner():
# print(count)
# inner()
# func()
def func():
count = 1
def inner():
count += 1
print(count)
inner()
func()
-
局部作用域的坑
count = 1
def func():
count += 1
print(count) # 局部变量不可以修改全局变量,会报错
func()
# 在函数中,如果你定义了一个变量,但是在定义这个变量之前对其引用了,那么解释器认为:语法问题。
# 你应该在使用之前先定义。
count = 1
def func():
print(count) # 没有定义局部变量,报错
count = 3
func()
-
全局作用域: 全局命名空间 + 内置命名空间
-
局部作⽤域: 局部命名空间
-
global【限于字符串,数字】第一个功能:在局部作用域中可以更改全局作用域的变量。
count = 1
def search():
global count
count = 2
search()
print(count)
利用global在局部作用域也可以声明一个全局变量。
# 声明一个全局变量
def func():
global a
a = 3
func()
print(a)
# 修改一个全局变量
count = 1
def func():
# print(count) 会报错:python解释器会认为你定义了一个局部变量,找不到就报错
global count
count += 1
print(count)
func()
print(count)
-
nonlocal
1,不能操作全局变量。
2,在局部作用域中,对父级作用域(或者更外层作用域非全局作用域)的变量进行引用和修改,并且引用的哪层,从那层及以下此变量全部发生改变。
def add_b():
b = 42
def do_global():
b = 10
print(b)
def dd_nonlocal():
nonlocal b
b = b + 20
print(b)
dd_nonlocal()
print(b)
do_global()
print(b)
add_b()
10
30
30
42
6.4.3 函数的嵌套【高阶函数】
# 例1:
def func1():
print('in func1')
print(3)
def func2():
print('in func2')
print(4)
func1()
print(1)
func2()
print(2)
# in func1 3 1 in func2 4 2
# 例2:
def func1():
print('in func1')
print(3)
def func2():
print('in func2')
func1()
print(4)
print(1)
func2()
print(2)
# 例3:
def fun2():
print(2)
def fun3():
print(6)
print(4)
fun3()
print(8)
print(3)
fun2()
print(5)
-
内置函数:globals,locals
"""
本文件:研究内置函数:globals locals
"""
a = 1
b = 2
def func():
name = 'alex'
age = 73
print(globals()) # 返回的是字典:字典里面的键值对:全局作用域的所有内容。{'a':1,'b':2}
print(locals()) # 返回的是字典:字典里面的键值对:当前作用域的所有的内容。{'name':'alex'}
print(globals()) # 返回的是字典:字典里面的键值对:全局作用域的所有内容。{'a':1,'b':2}
print(locals()) # # 返回的是字典:字典里面的键值对:当前作用域的所有的内容。{'a':1,'b':2}
func()
6. 5 函数名的运用
-
函数名指向的是函数的内存地址。【函数名 + ()就可以执行函数】
def func():
print(666)
func()
print(func,type(func)) # <function func at 0x000002245E181F28> <class 'function'>
-
函数可以当变量来使用
def func():
print(123)
v1 = func
func() # 123
v1() # 123
-
函数可以当容器数据类型的元素
def func():
print(666)
func_list = [func,func,func]
for item in func_list:
v = item()
print(v)
def func():
print(123)
def bar():
print(666)
info = {'k1':func,'k2':bar}
info['k1']()
info['k2']()
# 容易混淆
def func():
return 123
func_list1 = [func,func,func]
func_list2 = [func(),func(),func()]
print(func_list1)
print(func_list2)
info = {
'k1':func,
'k2':func(),
}
print(info)
-
函数可以当参数进行传递
def func(arg):
print(arg) # 函数地址
func(1) # 1
func([1, 2, 3, 4]) # [1,2,3,4]
def show():
return 999
func(show)
-
函数可以当返回值
def func():
print('in func')
def func1(x):
print('in func1')
return x
ret = func1(func)
ret()
-
函数名是一个特殊的变量,他除了具有变量的功能,还有最主要一个特点就是加上() 就执行,其实他还有一个学名叫*第一类对象。
-
面试题
def func():
print('花费查询')
def bar():
print('语音沟通')
def base():
print('xxx')
def show():
print('xxx')
def test():
print('xxx')
info = {
'f1': func,
'f2': bar,
'f3':base,
'f4':show,
'f5':test
}
choice = input('请选择要选择功能:')
function_name = info.get(choice)
if function_name:
function_name()
else:
print('输入错误')
6 .6.1迭代器
-
可迭代对象
对象:python中一切皆对象,一个可以循环的实实在在的值,对象。
可迭代:更新迭代。重复的,循环的一个过程。更新迭代每次都有新的内容,
可以进行循环更新的一个实实在在值。
-
专业角度:内部含有
'__iter__()'方法的对象,可迭代的 。 -
学过的有 :str, list, tuple ,dict, set, range都是可迭代对象。
-
-
获取对象的所有方法并以字符串返回的表现形式:dir()
-
判断一个对象是否是可迭代对象
s1 = 'fjdskl'
l1 = [1,2,3]
# print(dir(s1))
print(dir((l1)))
print('__iter__' in dir(s1))
print('__iter__' in dir(range(10)))
-
优点
-
存储的数据直接能显示,比较直观。
-
拥有的方法比较多,操作比较方便。
-
-
缺点
-
占用内存
-
可迭代对象是不可以直接迭代取值的(除去用索引,切片以及Key),但是转化成迭代器就可以了,迭代器是利用
__next__()进行取值:。
-
-
迭代器【文件句柄是迭代器】
定义:字面意思:更新迭代,器:工具:可更新迭代的工具。
专业角度:内部含有'__iter__'方法并含有'__next__'方法的对象就是迭代器
-
判断一个对象是否是迭代器
with open('文件1',encoding='utf-8',mode='w') as f1:
print(('__iter__' in dir(f1)) and ('__next__' in dir(f1)))
print('__iter__' in dir(f1) and '__next__' in dir(f1))
-
迭代器取值
# 有几个元素就next()几次
s1 = 'fjdag'
obj = iter(s1) # s1.__iter__()
# print(obj)
# print(next(obj)) # print(obj.__next__())
# print(next(obj)) # print(obj.__next__())
# print(next(obj)) # print(obj.__next__())
# print(next(obj)) # print(obj.__next__())
# print(next(obj)) # print(obj.__next__())
# print(next(obj)) # print(obj.__next__())
# l1 = [11,22,33,44,55,66]
# obj = iter(l1)
# print(next(obj))
# print(next(obj))
# print(next(obj))
# print(next(obj))
# print(next(obj))
# print(next(obj))
-
可迭代对象如何转化成迭代器
l1 = [11,22,33,44,55,66,77,88,99,1111,1133,15652]
iter(l1)
v = l1.__iter__()
-
while循环模拟for循环机制
l1 = [11,22,33,44,55,66,77,88,99,1111,1133,15652]
obj = iter(l1) # 将可迭代对象转化成迭代器
while 1:
try:
print(next(obj))
except StopIteration:
break
l1 = [11,22,33,44,55,66,77,88,99,1111,1133,15652]
obj = iter(l1)
for i in range(4): # next方法会记录迭代器的取值位置
print(next(obj))
for i in range(6):
print(next(obj))
-
优点
-
节省内存
-
惰性机制:next一次只取一个值。迭代是数据处理的基石。扫描内存中放不下的数据集时,我们要找到一种惰性获取数据项的方式,即按需一次获取一个数据项。这就是迭代器模式。
-
-
缺点:
-
速度慢。
-
不走回头路
-
可迭代对象:1,可迭代对象是一个操作方法比较多,比较直观,存储数据相对少(几百万个对象,8G内存是可以承受的)的一个数据集。
2.当你侧重于对数据可以灵活处理,并内存空间足够,可以将数据设置为可迭代对象。
迭代器:1.是一个非常节省内存,可以记录取值位置,可以直接通过循环+next方法取值,但是不直观,操作方法比较单一的数据集
l1 = [11,22,33,44,55,66,77,88,99,1111,1133,15652]
obj = iter(l1)
for i in range(4):
print(next(obj))
for i in range(6): # 会记录取值位置,可迭代对象不可以
print(next(obj))
2.当你的数据了过大,大到足以撑爆你内存或者你要以节省内存为首选因素时,就可以将数据集设置为迭代器。
6.6.2 生成器 【列表推导式和生成器表达式】
-
生成器:python社区,生成器与迭代器看成是一种。生成器的本质就是迭代器。唯一的区别:生成器是我们自己用python代码构建的数据。迭代器都是提供的,或者转化来的。
-
获取生成器的三种方式
-
生成器函数
-
生成器表达式
-
python内部提供的
-
-
-
生成器函数
函数
def func():
print(111)
print(222)
return 3
ret = func()
print(ret)
生成器函数
def func():
print(111)
print(222)
yield 3
a = 1
b = 2
c = a + b
print(c)
yield 4
ret = func()
# print(ret)
print(next(ret))
print(next(ret))
print(next(ret))
一个next 对应一个yield
-
函数中如果存在yield,那么该函数就是一个生成器函数,调用生成器函数会返回一个生成器,生成器只有被for循环或者执行
__next__方法时,生成器函数内部的代码才会执行,每次循环或__next__方法都会获取yield返回的值。
def func():
count = 1
while True:
yield count
count += 1
if count == 100:
return
val = func()
for item in val:
print(item)
-
yield 和return的区别
return:函数中只存在一个return,函数遇到rturn就结束,并可以选择性的给函数的执行者返回值。
yield:只要函数中有yield那么它就是一个生成器函数,一个生成器函数中可以存在多个yield,yield不会结束生成器函数,一个yield对应一个next。*
-
yield from【将一个可迭代对象,变成一个生成器】
def func():
l1 = [1, 2, 3, 4, 5]
yield l1
ret = func()
print(next(ret)) # 每执行一个next就返回一个yield值
def func():
l1 = [1, 2, 3, 4, 5]
yield from l1
'''
yield 1
yield 2
yield 3
yield 4
yield 5
'''
将l1这个列表变成了迭代器返回。
ret = func()
print(next(ret))
print(next(ret))
print(next(ret))
print(next(ret))
print(next(ret))
# def func():
# lst1 = ['卫龙', '老冰棍', '北冰洋', '牛羊配']
# lst2 = ['馒头', '花卷', '豆包', '大饼']
# yield from lst1
# yield from lst2
# g = func()
# for i in range(8):
# print(next(g))
-
列表推导式
-
用一行代码构建一个比较复杂有规律的列表
-
循环模式:[变量(加工后的变量) for 变量 in iterable]
val = [i for i in range(1,11)]
print(val)
# 从python1期到python100期写入列表lst
print([f'python{i}期' for i in range(1,101)])
-
筛选模式:[变量(加工后的变量) for 变量 in iterable if 条件]
将100以内的所有偶数添加到列表中
val = [i for i in range(2,100,2)]
print(val)
val = [i for i in range(1,100) if i % 2 == 0]
print(val)
-
列表推导式
-
缺点:列表推导式只能构建比较复杂并有规律的列表
-
超过三层才能构建的,不建议用
-
查找错误(debug模式)不行
-
-
优点:一行构建,简单
-
练习题
# 含有两个'e'的所有的人名全部大写留下来
names = [['Tom', 'Billy', 'Jefferson', 'Andrew', 'Wesley', 'Steven', 'Joe'],
['Alice', 'Jill', 'Ana', 'Wendy', 'Jennifer', 'Sherry', 'Eva']]
l1 = []
for i in names:
for name in i:
if name.count('e') == 2:
l1.append(name)
print(l1)
# 列表推到式
print([name.upper() for i in names for name in i if name.count('e') == 2])
-
生成器表达式
# 与列表推导式的写法几乎一模一样,也有筛选模式,循环模式,多层循环构建。写法上只有一个不同:
# [] 换成 ().
print([i for i in range(1,11)]) # 列表推到式
print((i for i in range(1,11))) # 生成器推导式
obj = (i for i in range(1,11))
print(next(obj))
print(next(obj))
print(next(obj))
print(next(obj))
print(next(obj))
for i in obj:
print(i)
-
列表推导式与生成器表达式区别
写法:[] ()
列表:iterable:可迭代的 生成器:iterator:迭代器 最终的区别
#字典推导式(了解)
lst1 = ['jay', 'jj', 'meet']
lst2 = ['周杰伦','林俊杰','元宝']
dic = { lst2[i]: lst1[i] for i in range(len(lst1))}
print(dic)
# 集合推导式(了解)
print({i for i in range(1,11)}
-
练习题
g=(i for i in range(4))
for i in [1,10]:
g=(i+j for j in g)
print(list(g))
解释一:
生成器只会在被需要的时候才会执行代码,但这里还需要另一个知识前提,那就是推导式中的变量是临时变量,不会影响到其他变量的,简单来看个例子:
x = 5
a = (x for x in range(3))
print(list(a)) # [0, 1, 2]
print(x) # 5
不记得是那个python版本处理了这个问题了,在一些比较旧的版本里是没有变量保护机制的,至少我用的是 3.6 版本有。同理,来看下这个和题目中比较接近的:
g=(i for i in range(4))
i = 8
print(list(g))
你猜,这里会打印什么,是 [0, 1, 2, 3] 。生成器中的 i 是受保护的,因此与外部变量 i 无关,它的取值就一定是 0123。
再看一个:
a = 1
g=(a + i for i in range(4))
a = 5
print(list(g))
这里 i 是受保护的,而 a 并没有,由于后续 a 的值是 5,所以打印语句中生成器应该是 <gen 5 + 0, 5 + 1, ...> 的存在。
好,终于能谈谈题目中的代码了。 第一行的 g=(i for i in range(4)) 中,i 是受保护的,所以它的迭代永远都是 0~3
for i in [1,10]:
g=(i+j for j in g)
这里的 j 也属于受保护的,在第一次循环中,它的值就是 g 初始时的产出 0~3。而这里的 i 不受保护,只是进行变量绑定,在生成器生成数据时才获取其值。
第一次循环后 g 的值:
<gen i+0, i+1, i+2, i+3>
第二次循环, 推导式中j就是生产的值,虽然此时 i=10 ,但i是后续绑定的,所以出生产为 i+0, i+1, i+2,i+3 第二次循环后 g 的值:
<gen i+i+0, i+i+1, i+i+2, i+i+3>
最后打印语句时,i的值是循环体最后一次的值(10),所以打印输出 20, 21, 22, 23
顺便对楼上的:
g=(i for i in range(4))
for i in [1,10]: # 列表不是 range
g=(i+j for j in g)
for i in g:
print(i) #输出会是20,41,84,171
做个解释。
在第二个循环体开始前,g 生成器是 <gen i+i+0, i+i+1, ..., i+i+3>。
for i in g 表明,从 g 生成的数据赋值给 i 。从生成器获得 i+i+0 ,要生成数据了,i得代入此时的值了,此时是什么值呢?没错, i 是上一次循环体最后一个值(10)所以生成 20,将 20 赋值给 i;
第二次循环在从生成器中获取了 i+i+1 ,此时 i 是 20(上一次循环给赋值的,难点在这),所以生成是 41,再次赋值给 i;
第三次循环,取得 i+i+2 此时 i 的值是 41, 所以得到的生成值是 84, 再次赋值给 i;
...等
解释二:
生成器的特点是并没有立即执行,而是记住'生产方式',等被调用时再执行. 在您的这个例子中: g=(i for i in range(4))#此时,如果被list调用,g为会是[0,1,2,3],但没有被调用,只是生成器 以下循环中: for i in [1,10]:
g=(i+j for j in g)
并不是说,第一次循环i为1时,g就应该为[1,2,3,4],其实g并没有被调用,所有并没有执行,只是记住生成器的值为i+j而已 第二循环时,i为10,同样也没有执行,也仅仅是记住i+j而已 当被print(list(g))命令调用执行时,循环中的变量i的值,已经是10了. 所以最终的g中的每一值,是执行连续执行两次i+j,既i+(i+j) 所以print(list(g))的输出是[20, 21, 22, 23]
还有更神奇的,同样是你的代码: g=(i for i in range(4)) for i in [1,10]:
g=(i+j for j in g)
for i in g:
print(i)#输出会是20,41,84,171
输出结果和list(g)又不一样,是不是更奇怪? 但是如果改一下循环变量名称: for k in g:
print(k)#输出20, 21, 22, 23
输出结果就和list(g)一致了.为什么了?
还是因为生产器的'记住生产方式,而没有被立即执行'的原因. 在生成器g中,变量i是一直存在,并没有被释放和回收的,再使用变量i去循环g,i的值就产生混乱了,第一个循环时i还是10,所以第一个值是20,此时,i值已经被赋成了20,所以第二次循环再执行i+(i+j)时,就得到41,同理第三次循环执行i+(i+j)时,i已经是41了,第四次.......所以最终输出20,41,84,171
将循环的变量i换为k后,变量名不再重复,赋值也就不再混乱了,从而和list(g)的结果一致了
6.6.3 匿名函数【lambda表达式】
-
匿名函数:就就是一句话函数,比较简单的函数。
def func(a,b):
return a + b
# 构建匿名函数
func1 = lambda a,b: a + b
print(func1(1,2))
-
练习题
# 接收一个可切片的数据,返回索引为0与2的对应的元素(元组形式)
func = lambda a:(a[0], a[2])
print(func([1,2,3,4]))
# 写匿名函数:接收两个int参数,将较大的数据返回。
def func(a,b):
return a if a > b else b
lambda a,b: a if a > b else b
-
此函数不是没有名字,他是有名字的,他的名字就是你给其设置的变量,比如func.
-
lambda 是定义匿名函数的关键字,相当于函数的def.
-
lambda 后面直接加形参,形参加多少都可以,只要用逗号隔开就行。
-
返回值在冒号之后设置,返回值和正常的函数一样,可以是任意数据类型。
-
匿名函数不管多复杂.只能写一行.且逻辑结束后直接返回数据
01
lambda表达式
python书写简单,功能强大, 迅速发展成为 AI ,深度学习的主要语言。介绍Python中的lambda表达式,注意到,它只是一个表达式,不是语句啊。
lambda的语法规则: 一个或多个参数以冒号终止输入参数,这些参数可以理解为有名函数原型的输入参数,以后是一个表达式,相当于有名函数的函数体部分。lambda的函数体部分,是作为返回值输出部分。
02
场景1:map 和 reduce
Python 中的 map 和 reduce,再结合 lambda表达式的话,使用起来会更加高效,如下所示:
def sum(mylist):
return reduce(lambda x, y: 2x + 1y, mylist)
可以看到两个输入参数 x, y , 返回为 2*x + y ,通过这样对 mylist 的元素完成化简操作。
03
场景2: lambda 和 list 结合使用
这个场景在面试中经常被问到,如下所示:
li = [lambda :x for x in range(10)]
得到一个 lambda 的list,打印 li
NO!是 9
lambda表达式不会形成对函数体内变量的记忆,只记录最后一个状态。
那么如果lambda的入参中带有x, 会得到我们想要的结果0 吗?
li = [lambda x :x for x in range(10)]
res = li
print(res)
输出:0
6.7 内置函数
-
chr,将十进制数字转换成unicode编码中的对应字符串。
v = chr(99)
print(v) # c
-
ord,根据字符在unicode编码中找到其对应的十进制
num = ord('中')
print(num)
-
应用
import random
def get_random_code(length=4):
data = []
for i in range(length):
v = random.randint(65,90) # 65~90是ascii码对应的字母
data.append(chr(v))
return ''.join(data)
code = get_random_code()
print(code)
import random # 导入一个模块
v= random.randint(起始,终止) # 得到一个随机数
6.7.1 高级一点的内置函数【重点】
-
map【循环每个元素:函数的第二个参数】然后让每个元素执行函数【map的第一个参数】,将每个函数执行的结果保存到新的列表中,并返回,返回的是可迭代对象。
语法:
map(function, iterable)
-
map,操作sequence【序列】中的每个元素,调用函数将执行的结果保存到新的列表中并返回,返回的是可迭代对象。map(func,iterable:可迭代的)
-
注意:python3中map()返回的是一个object,因为map()转变成了迭代器来节约空间,返回的是可迭代对象。
v1 = [11,22,33,44]
result = map(lambda x:x+100,v1)
print(list(result)) # 特殊
# map 列表推导式的循环模式
# l1 = [1, 4, 9, 16, 25]
# print([i**2 for i in range(1,6)]) # 返回的是列表
# ret = map(lambda x: x**2,range(1,6)) # 返回的是迭代器
# print(ret)
# print(list(ret))
-
filter()函数用于过滤序列,过滤掉不符合条件的元素,返回由符合条件元素组成的新列表。
-
接收两个参数,第一个为函数,第二个为序列,序列的每个元素作为参数传递给函数进行判断,返回True或False,将返回True的元素放到新列表中。
语法:
filter(function, iterable)
参数:
-
function:判断函数
-
iterable:可迭代对象
v1 = [11,22,33,'asd',44,'xf']
def func(x):
if type(x) == int:
return True
return False
result = filter(func,v1) # [11,]
print(list(result))
result = filter(lambda x: True if type(x) == int else False ,v1)
print(list(result))
result = filter(lambda x: type(x) == int ,v1)
print(list(result)) # 返回的是迭代器
l1 = [2, 3, 4, 1, 6, 7, 8]
print([i for i in l1 if i > 3]) # 返回的是列表
-
reduce【合并】求积累运算
函数将一个数据集合(链表,元组等)中的所有数据进行下列操作:用传给 reduce 中的函数 function(有两个参数)先对集合中的第 1、2 个元素进行操作,得到的结果再与第三个数据用 function 函数运算,最后得到一个结果。
语法
reduce() 函数语法:
reduce(function, iterable[, initializer])
参数
-
function -- 函数,有两个参数
-
iterable -- 可迭代对象
-
initializer -- 可选,初始参数
返回值
返回函数计算结果。
-
import functools
v1 = ['wo','hao','e']
def func(x,y):
return x+y
result = functools.reduce(func,v1)
print(result)
result = functools.reduce(lambda x,y:x+y,v1)
print(result)
6.7.2 常用的内置函数
-
其他
len() open() range() id() type()
-
eval:剥去字符串外衣运算里面的代码,有返回值
s1 = '1 + 3'
print(eval(s1))
# 网络传输str input 输入的时候,spl注入等等不要用eval
-
exec :与eval几乎一样,代码流
msg = """
for i in range(10):
print(i)
"""
print(msg)
exec(msg)
-
hash:哈希值
print(hash('hjsdhfjskjd'))
-
callable:判断一个对象是否可被调用【重要】
def func():
pass
func
print(callable(func))
s = 'hdhfjlkajhf'
print(callable(s))
-
complex:【返回一个值为real+imag*j的复数,或者把字符串或数字转化为复数】
print(complex(1,2)) # (1+2j) 默认函数返回复数0j
-
bytes【字节】
s1 = '太白'
b = s1.encode('utf-8') # 编码为十六进制的格式
print(b)
b = bytes(s1,encoding='utf-8')
print(b)
-
ord:输入字符找该字符编码的位置
ascii Unicode
print(ord('a')) #
print(ord('中')) # 20013 Unicode
-
chr:输入位置数字找出期对应的字符
print(chr(97))
print(chr(20013)) # Unicode
-
repr:返回一个对象的string形式(原形必露)
s1 = '存龙'
print(s1)
print(repr(s1)) # '存龙'
-
all:可迭代对象中,全是True才是True
l1 = [1, 2, '太白', True, [1,2,3], '']
print(all(l1))
-
any:可迭代对象中,有一个True,就式True
l1 = [ 0, '太白', False, [], '',()]
print(any(l1))
-
reversed: 返回的是一个翻转的迭代器【重要】
l1 = [i for i in range(10)]
l1.reverse() # 列表的方法
print(l1)
l1 = [i for i in range(10)]
obj = reversed(l1)
print(l1)
print(list(obj))
-
zip:拉链方法*
-
用于将可迭代对象作为参数,将对象对应的元素打包成一个个元组,然后返回有这些元组组成的列表。
-
如果可迭代对象的元素不一致,则返回列表长度与最短对象一致
l1 = [1, 2, 3, 4, 5]
tu1 = ('太白', 'b哥', '德刚')
s1 = 'abcd'
obj = zip(l1,tu1,s1)
print(obj)
for i in obj:
print(i)
l1 = [1, 2, 3, 4, 5]
tu1 = ('太白', 'b哥', '德刚')
s1 = 'abcd'
obj = zip(l1,tu1,s1)
print(list(obj))
-
sorted: 可以加key【重要】
l1 = [22, 33, 1, 2, 8, 7,6,5]
l2 = sorted(l1) # 返回的是一个列表
print(l1)
print(l2)
l2 = [('大壮', 76), ('雪飞', 70), ('纳钦', 94), ('张珵', 98), ('b哥',96)]
print(sorted(l2))
print(sorted(l2,key= lambda x:x[1])) # 返回的是一个列表,默认从低到高
print(sorted(l2,key= lambda x:x[1],reverse=True)) # 返回的是一个列表,默认从高到低
-
输入输出
print()
print(self, *args, sep=' ', end='\n', file=None)
sep:设置分割符
end:默认换行
input()
-
强制转换
str() list() dict() tuple() int() set() bool()
-
数学相关
-
abs():绝对值
v = abs(-1)
print(v)
# 以绝对值的方式取最小值
l1 = [33, 2, 3, 54, 7, -1, -9]
print(min(l1))
l2 = []
func = lambda a: abs(a)
for i in l1:
l2.append(func(i))
print(min(l2))
-
round:保留浮点数小数的位数
print(round(3.141592653, 2)) # 3.14
-
float,转换成浮点型(小数)
v = 55
v1 = float(55)
print(v1)
-
max:找最大值【重要】
v = [1,2,311,21,3,]
result = max(v)
print(result)
-
min:找最小值【重要】
v = [1,2,311,21,3,]
result = min(v)
print(result)
l1 = [-1,2,3,4,5,-9]
def func1(a):
return abs(a)
print(min(l1,key=func1))
# 凡是可以加key的:它会自动的将可迭代对象中的每个元素按照顺序传入key对应的函数中,
# 以返回值比较大小。
dic = {'a': 3, 'b': 2, 'c': 1}
# 求出值最小的键
# print(min(dic)) # min默认会按照字典的键去比较大小。
def func(args):
# '''
# 第一次:
# args: 'a' 返回值:dic['a'] 记录:3
# 第二次:
# args : 'b' 返回值:dic['b'] 记录:2
# 第三次:
# args: 'c' 返回值:dic['c'] 记录:1
# '''
return dic[args]
print(min(dic,key=lambda args: dic[args]))
func = lambda a:dic[a]
print(min(dic,key=lambda a: dic[a]))
l2 = [('太白',18), ('alex', 73), ('wusir', 35), ('口天吴', 41)]
print(min(l2))
print(min(l2,key=lambda x:x[1]))
-
sum:求和
v = [1,2,311,21,3,]
result = sum(v)
print(result)
-
divmod:两数相除的商和余数
a,b = divmod(1001,5)
print(a,b)
-
应用
# 练习题
"""
要求:
每页显示10条数据
让用户输入要查看的页面:页码
"""
USER_LIST = []
for i in range(1,836):
temp = {'name':'你少妻-%s' %i,'email':'123%s@qq.com' %i }
USER_LIST.append(temp)
# 数据总条数
total_count = len(USER_LIST)
# 每页显示10条
per_page_count= 10
# 总页码数
max_page_num,a = divmod(total_count,per_page_count)
if a>0:
max_page_num += 1
while True:
pager = int(input('要查看第几页:'))
if pager < 1 or pager > max_page_num:
print('页码不合法,必须是 1 ~ %s' % max_page_num )
else:
"""
# 第1页:USER_LIST[0:10] -> 0123456789
# 第2页:USER_LIST[10:20]
# 第3页:USER_LIST[20:30]
...
"""
start = (pager-1) * per_page_count
end = pager * per_page_count
data = USER_LIST[start:end]
for i- tem in data:
print(item)
-
pow:求
x**y次幂(三个参数为x**y的结果对z取余)
print(pow(2,3)) **
print(pow(2,3,3)) # 2**3 % 3
-
bin:将十进制转换成二进制
num = 13
v1 = bin(num)
print(v1)
-
oct:将十进制转换成八进制
num = 8
v1 = oct(8)
print(v1)
-
int:将其他进制转换成十进制【可以取整】
# 二进制转化成十进制
v1 = '0b1101'
result = int(v1,base=2)
print(result)
# 八进制转化成十进制
v1 = '0o1101'
result = int(v1,base=8)
print(result)
# 十六进制转化成十进制
v1 = '0x1101'
result = int(v1,base=16)
print(result)
-
hex:将十进制转换成十六进制
num = 16
v1 = hex(num)
print(v1)
-
面试题
# 1字节等于8位
# IP: 192.168.12.79 -> 001010010 . 001010010 . 001010010 . 001010010
# 1. 请将 ip = "192.168.12.79" 中的每个十进制数转换成二进制并通过,连接起来生成一个新的字符串。
ip = "192.168.12.79"
ip_list = ip.split('.') # ['192','168','12','79']
result = []
for item in ip_list:
result.append(bin(int(item)))
print(','.join(result))
# 2. 请将 ip = "192.168.12.79" 中的每个十进制数转换成二进制:
# 0010100100001010010001010010001010010 -> 十进制的值。
# 3232238671
ip = '192.168.14.200'
ip_list = ip.split('.')
result = []
for i in ip_list:
item = bin(int(i))
new_item = item[2:]
if len(new_item) < 8:
new_item = (8 - len(new_item)) * '0' + new_item
result.append(new_item)
info = ''.join(result)
print(int(info, base=2))
ip = '192.168.14.200'
ip_list = ip.split('.')
num = 0
number = 0
for i in ip_list:
num += int(i) * 256**(len(ip_list) - 1 - number)
number += 1
print(num)
6.8 闭包
# 封闭的东西: 保证数据的安全。
# 方案一:
# l1 = [] # 全局变量 数据不安全
# li = [] # 如果写错了。会影响全局
# def make_averager(new_value):
# l1.append(new_value)
# total = sum(l1)
# averager = total/len(l1)
# return averager
# print(make_averager(100000))
# print(make_averager(110000))
# # 很多代码.....
# l1.append(666)
# print(make_averager(120000))
# print(make_averager(90000))
# 方案二: 数据安全,l1不能是全局变量。
# 每次执行的时候,l1列表都会重新赋值成[]
def make_averager(new_value):
l1 = []
l1.append(new_value)
total = sum(l1)
averager = total / len(l1)
return averager
print(make_averager(100000))
print(make_averager(110000))
# 很多代码.....
print(make_averager(120000))
print(make_averager(90000))
# 方案三: 闭包
#
def make_averager():
l1 = []
def averager(new_value):
l1.append(new_value)
print(l1)
total = sum(l1)
return total/len(l1)
return averager
avg = make_averager() # averager
print(avg(100000))
print(avg(110000))
print(avg(120000))
print(avg(190000))
def func():
return 666
ret = func()
print(globals())
# 闭包: 多用于面试题: 什么是闭包? 闭包有什么作用。
# 1,闭包只能存在嵌套函数中。
# 2, 内层函数对外层函数非全局变量的引用(使用),就会形成闭包。
# 被引用的非全局变量也称作自由变量,这个自由变量会与内层函数产生一个绑定关系,
# 自由变量不会再内存中消失。
# 闭包的作用:保证数据的安全。
# 如何判断一个嵌套函数是不是闭包
# 1,闭包只能存在嵌套函数中。
# 2, 内层函数对外层函数非全局变量的引用(使用),就会形成闭包。
# 例一:
def wrapper():
a = 1
def inner():
print(a)
return inner
ret = wrapper()
# # 例二:
a = 2 #
def wrapper():
def inner():
print(a)
return inner
ret = wrapper()
# # 例三:
# 也是闭包!
def wrapper(a,b):
def inner():
print(