第五章 函数
5.1 函数的本质及应用场景
截至目前:面向过程编程(可读性差/可重用性差)
对于函数编程:
- 本质:将N行代码拿到别处并给他起一个名字,以后通过名字就可以找到这段代码并执行
- 应用场景:
- 代码重复执行
- 代码特别多超过一屏,可以选择通过函数进行代码的分割
# 面向过程编程
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()
#函数式编程
#先不考虑是否能执行,单纯为了比较函数式编程与面向过程编程的区别
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()
5.2 函数定义
5.2.1 基本定义函数
-
基本形式
def 函数名(): #函数的定义 代码 #函数内容 函数名() #函数的执行
-
注意
- 函数如果不被调用,则内部代码永远不会被执行
- len(计算长度)是python内部写好的一个函数
5.2.2 参数(个数不限制)
5.2.2.1 基本参数知识
def get_list_date(aaa): #aaa:形式参数(形参) 任意个数
v = [11,22,33,44]
print(v[aaa])
get_list_date(1) #1:实际参数(实参) 任意类型
-
发送邮件问题
# 假如:管理员/业务员/老板用的是同一个邮箱。 def send_email(to): 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(["导演", to]) msg['Subject'] = "情爱的导演" server = smtplib.SMTP("smtp.163.com", 25) server.login("15776556369@163.com", "qq1105400511") server.sendmail('15776556369@163.com', [to, ], msg.as_string()) server.quit() def send_email(to): template = "要给%s发送邮件" %(to,) print(template) user_input = input('请输入角色:') if user_input == '管理员': send_email('xxxx@qq.com') elif user_input == '业务员': send_email('xxxxo@qq.com') elif user_input == '老板': send_email('xoxox@qq.com')
5.2.2.2 位置传参
- 传参:调用函数并传入参数
- 要求:严格按照位置进行传参
- 位置参数:positional argument
- 基本形式
def func(a,b):
print(a,b)
func(11,22) #位置都是一一对应的,a = 11,b = 22
5.2.2.3 关键字传参
-
关键字参数:keyword argument
-
基本形式
def func(a,b): print(a,b) func(b = 11,a = 22) #按照关键字进行传参,可以交换位置
-
注意:open(打开文件)是python内部写好的一个函数,运用的就是关键字传参
-
关键字传参和位置传参可以混合使用
- 位置参数必须在关键字参数的前面
- 位置参数的个数 + 关键字参数的个数 = 总参数个数
- 一个参数不能传多次
5.2.2.4 默认参数:可传可不传 (可变数据类型有一个陷阱)
-
基本形式
def func(a,b = 9): #b就是默认参数,默认值为9 #func函数接受两个参数,调用函数进行传值时,默认参数可传可不传,不传则使用默认值,传则使用传的值 print(a,b) func(123) #a = 123,b使用默认值,即b = 9 func(11,22) #a = 11,b = 22
-
对于默认值,如果是可变类型(对于函数的默认值慎用可变类型)
# 如果要想给value设置默认是空列表 # 不推荐(坑) def func(data,value=[]): pass # 推荐 def func(data,value=None): if not value: value = []
-
面试题
def func(a,b=[]): b.append(a) return b l1 = func(1) l2 = func(2,[11,22]) l3 = func(3) # [1,3] [11,22,2] [1,3] #注意这里是最后打印的l1,l3,公用的同一个内存地址都是[1,3]。 print(l1,l2,l3) ################## def func(a,b=[]): b.append(a) print(b) l1 = func(1) l2 = func(2,[11,22]) l3 = func(3) # [1] [11,22,2] [1,3] #注意这里是立即打印的 print(l1,l2,l3)
5.2.2.5 万能参数(*args / **kwargs)
-
*args
- 可以接受任意个数的位置参数,并将参数转化为元组(注意实参里有*和没有的区别)
- 只能使用位置传参
- 基本形式
def func(*args): print(args) func(1,2) #args = (1,2) func((11,22,33)) #args = ((11,22,33),) func(*(11,22,33)) #args = (11,22,33) 循环元组里的元素,加入到元组中 func(*[11,22,33,44]) #args = (11,22,33,44) 循环列表里的元素,加入到元组中 #注意实参里有*和没有的区别 #特殊情况: def func(a,*args,b): #a只能使用位置参数,b只能使用关键字参数 print(a,args,b) func(1,2,3,4,b = 5) #a = 1,args = (2,3,4),b = 5
-
**kwargs
- 可以接受任意个数的关键字参数,并将参数转化为字典(注意实参里有**和没有的区别)
- 只能使用关键字传参
- 基本形式
def func(**kwargs): print(kwargs) func(k1 = 1,k2 = 'alex') #kwargs = {'k1':1,'k2':'alex'} func(**{'k1':'v1','k2':'v2'}) #kwargs = {'k1':'v1','k2':'v2'} #注意实参里有**和没有的区别
-
综合运用(*args和**kwargs一起使用)
def func(*args,**kwargs):
print(args,kwargs)
func(1,2,3,4,k1 = 1,k2 = 2) #args = (1,2,3,4),kwargs = {'k1':1,'k2':2}
func(*[1,2,3],**{'k1':'v1','k2':'v2'}) #args = (1,2,3),kwargs = {'k1':'v1','k2':'v2'}
-
参数相关重点
-
定义函数
#情况一: def func(a,b): pass #情况二: def func(a,b = None): pass #情况三: def func(*args,**kwargs): pass
-
调用函数:位置参数 > 关键字参数
-
-
练习题
# 1. 请写一个函数,函数计算列表 info = [11,22,33,44,55] 中所有元素的和。
def get_sum():
info = [11,22,33,44,55]
data = 0
for item in info:
data += item
print(data)
get_sum()
# 2. 请写一个函数,函数计算列表中所有元素的和。
def get_list_sum(a1):
data = 0
for item in a1:
data += item
print(data)
get_list_sum([11,22,33])
get_list_sum([99,77,66])
v1 = [8712,123,123]
get_list_sum(v1)
# 3. 请写一个函数,函数将两个列表拼接起来。
def join_list(a1,a2):
result = []
result.extend(a1)
result.extend(a2)
print(result)
join_list([11,22,33],[55,66,77]
# 4. 计算一个列表的长度
def my_len(arg):
count = 0
for item in arg:
count += 1
print(count)
v = [11,22,33]
my_len(v)
len(v)
# 5. 发邮件的示例
def send_email(role,to):
template = "要给%s%s发送邮件" %(role,to,)
print(template)
user_input = input('请输入角色:')
if user_input == '管理员':
send_email('管理员','xxxx@qq.com')
elif user_input == '业务员':
send_email('业务员','xxxxo@qq.com')
elif user_input == '老板':
send_email('老板','xoxox@qq.com')
5.2.3 返回值(return)
- 作用
- 返回值
- 终止函数的执行
- 基本形式
def func(arg):
代码 #函数内容
return 9 #返回值为9,默认:return None
val = func('ads') #设置一个变量接收返回值
print(val) #9
#特殊情况:
def func():
return 2,3,'alex' #返回是一个元组
val = func()
print(val) #(2,3,'alex')
- 返回值相关重点
- 函数没有返回值,默认返回:None
- 函数内部执行过程中一旦遇到return就终止
- return可以返回任意类型
# 练习题:
# 1. 写函数,计算一个列表中有多少个数字,打印: 列表中有%s个数字。
# 提示:type('x') == int 判断是否是数字。
# 方式一:
def get_list_counter1(data_list):
count = 0
for item in data_list:
if type(item) == int:
count += 1
msg = "列表中有%s个数字" %(count,)
print(msg)
get_list_counter1([1,22,3,'alex',8])
# 方式二:
def get_list_counter2(data_list):
count = 0
for item in data_list:
if type(item) == int:
count += 1
return count
v = get_list_counter1([1,22,3,'alex',8])
msg = "列表中有%s个数字" %(v,)
print(msg)
# 2. 写函数,计算一个列表中偶数索引位置的数据构造成另外一个列表,并返回。
# 方式一:
def get_data_list1(arg):
v = arg[::2]
return v
data = get_data_list1([11,22,33,44,55,66])
# 方式二:
def get_data_list2(arg):
v = []
for i in range(0,len(arg)):
if i % 2 == 0:
v.append(arg[i])
return v
data = get_data_list2([11,22,33,44,55,66])
# 3. 读取文件,将文件的内容构造成指定格式的数据,并返回。
"""
a.log文件
alex|123|18
eric|uiuf|19
...
目标结构:
a. ["alex|123|18","eric|uiuf|19"] 并返回。
b. [['alex','123','18'],['eric','uiuf','19']]
c. [
{'name':'alex','pwd':'123','age':'18'},
{'name':'eric','pwd':'uiuf','age':'19'},
]
"""
def a():
info = []
with open('a.log',mode='r',encoding='utf-8') as f:
for line in f:
line = line.strip()
info.append(line)
return info
def b():
info = []
with open('a.log',mode='r',encoding='utf-8') as f:
for line in f:
line = line.strip()
v = line.split('|')
info.append(v)
return info
def c():
info = []
with open('a.log',mode='r',encoding='utf-8') as f:
for line in f:
line = line.strip()
list = line.split('|')
v = {}
v['name'] = list[0]
v['pwd'] = list[1]
v['age'] = list[2]
info.append(v)
return info
date1 = a()
date2 = b()
date3 = c()
print(date1,date2,date3)
-
数据类型中的方法有没有返回值
-
无返回值
#示例: v = [11,22,33] v.append(99)
-
仅有返回值:
#示例一: v = "alex" result = v.split('l') #示例二: v = {'k1':'v2'} result1 = v.get('k1') result2 = v.keys()
-
有返回+修改数据
#示例: v = [11,22,33] result = v.pop() # 将被删除的数据赋值给result
-
常用需记住的
- str
- strip,返回字符串
- split,返回列表
- replace,返回字符串
- join,返回字符串
- list
- append,无
- insert,无
- pop,返回要删除的数据
- remove,无
- find/index,返回索引的位置的数据
- dict
- get,返回索引键的值
- keys,返回字典的所有键
- values,返回字典的所有值
- items,返回字典所有的键值对
- str
-
5.2.4 总结
- 函数的基本结构:
#情况一
def f1():
函数内容
f1()
#情况二
def f2(a):
函数内容
f2(1)
#情况三
def f3():
函数内容
return
v1 = f3()
#情况四
def f4(a1,a2):
函数内容
return a1+a2
v2 = f4(1,7)
- 函数内部的数据是否会混乱?
- 函数内部执行相互之间不会混乱
- 执行完毕 + 内部元素不被其他人使用 => 销毁
5.3 函数小高级
5.3.1 函数名可以当作变量来使用
#示例一:
def func():
print(123)
func_list = [func, func, func]
# func_list[0]()
# func_list[1]()
# func_list[2]()
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)
5.3.2 函数可以当作参数进行传递
#示例:
def func(arg):
v1 = arg()
print(v1)
def show():
print(666)
result = func(show)
print(result)
#面试题:多个函数的调用
def func():
print('话费查询')
def bar():
print('语音沟通')
def base():
print('业务办理')
def show():
print('流量查询')
def test():
print('人工服务')
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('输入错误')
5.3.3 函数可以当做字典的value
def fun1():
pass
def fun2():
pass
def fun3():
pass
def fun4():
pass
info = {
'f1':fun1,
'f2':fun2,
'f3':fun3,
'f4':fun4,
}
choice = input('<<')
func_name = info.get(choice)
if func_name: #如果有key
func_name()
else:
print('...')
5.3.4 匿名函数: lambda表达式
-
作用:用于表达简单的函数
-
基本形式:
#三元运算,为了解决简单的if else的情况 if 1 == 1: a = 123 else: a = 456 #用三元运算表示: a = 123 if 1 == 1 else 456 #lambda表达式,为了解决简单函数的情况 def func(a1,a2): return a1 + a2 #用lambda表达式表示: func = lambda a1,a2: a1+a2 #隐含了return
-
重点:
- lambda表达式只能用一行表示函数
- 用lambda表达式表示函数时,无法设置新变量,只能用参数作变量
- 列表所有方法基本上都是返回None,字符串的所有方法基本上都是返回新值
-
练习题
# 练习题1 USER_LIST = [] def func0(x): v = USER_LIST.append(x) return v result = func0('alex') print(result) #None # 练习题2 def func0(x): v = x.strip() return v result = func0(' alex ') print(result) #'alex' # 练习题3 USER_LIST = [] func1 = lambda x: USER_LIST.append(x) v1 = func1('alex') print(v1) #None print(USER_LIST) #['alex'] # 练习题4 func1 = lambda x: x.split('l') v1 = func1('alex') print(v1) #['a','ex'] # 练习题5 func_list = [lambda x:x.strip(), lambda y:y+199,lambda x,y:x+y] v1 = func_list[0]('alex ') print(v1) #'alex' v2 = func_list[1](100) print(v2) #299 v3 = func_list[2](1,2) print(v3) #3
5.4 函数中高级
5.4.1 函数可以做返回值
#示例:
def func():
print(123)
def bar():
return func
v = bar()
v()
5.4.2 闭包
- 概念:为函数创建一块区域并为其维护自己数据,以后执行方便调用
- 应用场景:
- 装饰器
- SQLAlchemy源码
#示例:
name = 'oldboy'
def bar(name):
def inner():
print(name)
return inner
v1 = bar('alex') # { name=alex, inner } # 闭包,为函数创建一块区域(内部变量供自己使用),为他以后执行提供数据。
v2 = bar('eric') # { name=eric, inner }
v1()
v2()
#区分:
# 不是闭包 没有使用name
def func1(name):
def inner():
return 123
return inner
# 是闭包:封装值 + 内层函数需要使用。
def func2(name):
def inner():
print(name)
return 123
return inner
#练习题:
#第一题
name = 'alex'
def base():
print(name)
def func():
name = 'eric'
base()
func()
# 第二题
name = 'alex'
def func():
name = 'eric'
def base():
print(name)
base()
func()
# 第三题
name = 'alex'
def func():
name = 'eric'
def base():
print(name)
return base
base = func()
base()
#注意:函数在何时被谁创建?
#示例:
info = []
def func():
print(item)
for item in range(10):
info.append(func)
info[0]()
#面试题
info = []
def func(i):
def inner():
print(i)
return inner
for item in range(10):
info.append(func(item))
info[0]()
info[1]()
info[4]()
5.4.3 高阶函数
- 把函数当作参数传递
- 把函数当作返回值
- 注意:对函数进行赋值
5.5 内置函数
函数分为自定义函数和内置函数
python内置函数分类:
5.5.1 强制转换
- int() / str() / bool() / list() / tuple() / dict() / set()
5.5.2 输入输出
- input() / print()
5.5.3 数学相关
-
abs():计算绝对值
-
sum():求和
-
float():转化为浮点型(小数)
v = 55 v1 = float(55) print(v1) #55.0
- 补充:int(55.5) #只保留整数:55
-
max():找到最大值
-
min():找到最小值
-
divmod():取两数相除的商和余数
#示例: a,b = divmod(1001,5) print(a,b) ##a=200,b=1
-
注意:divmod在分页展示时用得比较多
# 练习题 请通过分页对数据进行展示 """ 要求: 每页显示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: start = (pager-1) * per_page_count end = pager * per_page_count data = USER_LIST[start:end] for item in data: print(item)
-
-
pow():指数
v = pow(2,3) #相当于2**3 print(v) # 8
-
round():保留几位小数,默认保留整数,还会四舍五入
v = round(1.127,2) #第二个数代表保留几位小数,默认是None print(v) # 1.13 四舍五入
5.5.4 进制转换相关
-
bin():将十进制转换成二进制
-
oct():将十进制转换成八进制
-
int():将其他进制转换成十进制 #八进制和18进制之间不能直接转换,可以先转成10进制在进行转换
- 默认:base = 10
# 二进制转化成十进制 v1 = '0b1101' result = int(v1,base=2) #base=2说明读取的是二进制 print(result) # 八进制转化成十进制 v1 = '0o1101' result = int(v1,base=8) print(result) # 十六进制转化成十进制 v1 = '0x1101' result = int(v1,base=16) print(result)
-
hex():将十进制转换成十六进制
#1字节等于8位
# IP: 192.168.12.79 -> 001010010 . 001010010 . 001010010 . 001010010
# 请将 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))
#面试题:
# 请将 ip = "192.168.12.79" 中的每个十进制数转换成二进制:
# 0010100100001010010001010010001010010 -> 十进制的值。
# 3232238671
ip = '192.168.12.79'
v = ip.split('.')
info = []
for i in v:
date = str(bin(int(i)))
if len(date) > 10:
date = date[-8:]
else:
count = 10 - len(date)
date = date.replace('0b','0'*count)
info.append(date)
val = "".join(info)
a = int(val,base=2)
print(a)
5.5.5 编码相关
-
chr():将十进制数字转换成 unicode 编码中的对应字符串 (unicode包括ascci)
-
ord():根据字符在 unicode 编码中找到其对应的十进制
#应用:生成随机验证码 import random # 导入一个模块 def get_random_code(length=6): data = [] for i in range(length): v = random.randint(65,90) data.append(chr(v)) return ''.join(data) code = get_random_code() print(code)
5.5.6 内置函数高级
-
map(函数,可迭代对象):一起执行
- 循环每个元素(第二个参数),然后让每个元素执行函数(第一个参数),将每个函数执行的结果保存到新的列表中,并返回
#示例: v1 = [11,22,33,44] result = map(lambda x:x+100,v1) print(list(result)) #python3中为节省内容要强制转换下list
-
filter(函数,可迭代对象):进行筛选
#示例: v1 = [11,22,33,'asd',44,'xf'] def func(x): if type(x) == int: return True return False result = filter(func,v1) print(list(result)) # 用lambda表达式: 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))
-
map / filter(python2与python3的区别九)
- python2:返回列表,直接创建值,可以通过索引取值
- python3:返回迭代器,不直接创建值,通过循环,边循环边创建
-
reduce(函数,可迭代对象):得到结果
import functools v1 = ['wo','hao','e'] def func(x,y): return x+y result = functools.reduce(func,v1) print(result) # 用lambda表达式: result = functools.reduce(lambda x,y:x+y,v1) print(result)
5.5.7 类相关
-
type():查看数据类型
class Foo: pass obj = Foo() if type(obj) == Foo: print('obj是Foo类的对象')
-
issubclass(类,类或其基类):判断前一个类是否是后一个类或其基类的子类
- 结果是布尔类型,是 -> True,否 -> False
class Base: pass class Base1(Base): pass class Foo(Base1): pass class Bar: pass print(issubclass(Bar,Base)) print(issubclass(Foo,Base))
-
isinstance(对象,类或其基类):判断一个对象是否是一个类或其基类的实例(对象)
- 结果是布尔类型,是 -> True,否 -> False
- 注意:判断一个对象是否是一个类的实例(对象),一定要用type,不要用isinstance
class Base(object): pass class Foo(Base): pass obj = Foo() print(isinstance(obj,Foo)) # 判断obj是否是Foo类或其基类的实例(对象) print(isinstance(obj,Base)) # 判断obj是否是Foo类或其基类的实例(对象)
-
super().方法名():根据self对象所属的类的继承关系,按照顺序依次找方法并执行(找到第一个为止)
class Base(object): def func(self): super().func() # 根据self对象所属类的继承关系,按照顺序挨个找func方法并执行(找到第一个为止) print('base.func') class Bar(object): def func(self): print('bar.func') class Foo(Base,Bar): # Foo -> Base -> Bar pass obj = Foo() obj.func()
5.5.8 其他
- len() :计算长度
- open() :打开文件
- range()
- id() :查看内存地址
5.7 作用域
5.7.1 分类
-
全局作用域:在python中,相当于一个py文件
- 全局作用域中的变量称为全局变量
- 全局变量必须全用大写
-
局部作用域:在python中,相当于一个函数
- 局部作用域中的变量称为局部变量
- 局部变量则用小写
USER_LIST = [11,22,3] #全局变量 def func(): name = 'asdf' #局部变量 USER_LIST.append(12) USER_LIST.append(name) func() print(USER_LIST)
5.7.2 递归
-
函数自己调用自己。(效率低)
def func(): print(1) func() func()
# 递归的返回值 def func(a): if a == 5: return 100000 result = func(a+1) + 10 v = func(1) #此时只有a=5的时候会有返回值 name = 'alex' def func(): def inner(): print(name) return inner v =func()
5.7.3 总结
-
在python中,一个函数就是一个作用域
-
作用域中查找数据规则:
- 优先在自己的作用域里找数据
- 自己没有就去 '父级' 中找,没有就去 '父级' 的 '父级' 中找,直到找至全局作用域
- 如果最后全局作用域中也没有,就会报错
- 寻找时注意:父级作用域中的值到底是什么
#示例一: x = 10 def func(): x = 9 print(x) def x1(): x = 8 print(x) func() # 9 #示例二: x = 10 def func(): x = 9 print(x) def x1(): x = 8 print(x) x1() func() # 9 8 #示例三: x = 10 def func(): x = 9 print(x) def x1(): x = 8 print(x) print(x) x1() func() # 9 9 8 #示例四: x = 10 def func(): x = 9 print(x) def x1(): x = 8 print(x) x1() print(x) func() # 9 8 9 #示例五: x = 10 def func(): x = 9 print(x) def x1(): print(x) x1() print(x) func() # 9 9 9 #示例六: x = 10 def func(): x = 8 print(x) def x1(): print(x) x = 9 x1() x = 10 print(x) func() # 8 9 10 #示例七: x = 10 def func(): x = 8 print(x) def x1(): print(x) x1() x = 9 x1() x = 10 print(x) func() # 8 8 9 10
-
对于子作用域:
-
只能找到 '父级' 中的值,默认无法重新为 '父级' 的变量进行赋值
-
如果非要对变量进行重新赋值,使用 global / nonlocal 进行强制赋值
- global:直接找到全局的变量,再进行重新赋值,其他的不更改
- nonlocal:只找到 '父级' 的变量,进行重新赋值,其他的不更改
- 建议:一般不要使用,以免造成代码混乱
#global示例: name = 'oldboy' def func(): name = 'alex' def inner(): global name #直接找到全局的name name = 'abc' #再进行重新赋值 inner() print(name) #'alex' func() print(name) #'abc' #nonlocal示例: name = "oldboy" def func(): name = 'alex' def inner(): nonlocal name #找到上一级的name name = 'abc' #再进行重新赋值 inner() print(name) #'abc' func() print(name) #"oldboy"
-
5.8 推导式
5.8.1 列表推导式
# 目的:方便的生成一个列表
# 格式:
v1 = [i for i in 可迭代对象 ]
v2 = [i for i in 可迭代对象 if 条件 ] #条件为true才进行append
# 示例:
v1 = [ i for i in 'alex' ]
v2 = [i+100 for i in range(10)]
v3 = [99 if i>5 else 66 for i in range(10)]
def func():
return 100
v4 = [func for i in range(10)]
v5 = [lambda : 100 for i in range(10)]
result = v5[9]()
v6 = [lambda :i for i in range(10)]
result = v6[5]()
# 新浪微博面试题
v7 = [lambda x:x*i for i in range(10)]
# 1.请问 v7 是什么? 列表中9个函数名
# 2.请问 v7[0](2) 的结果是什么? 18
# 面试题
def num():
return [lambda x:i*x for i in range(4)] #lambda 就是一个函数,不被调用,内部代码不会执行
# num() -> [函数,函数,函数,函数]
print([ m(2) for m in num() ]) # [6,6,6,6]
5.8.2 集合推导式
# 目的:方便的生成一个集合
# 格式:
v1 = { i for i in 'alex' }
5.8.3 字典推导式
# 目的:方便的生成一个字典
# 格式:
v1 = { 'k'+str(i):i for i in range(10)}
5.8.4 生成器推导式
# 列表推导式,立即循环创建所有元素
"""
def func():
result = []
for i in range(10):
result.append(i)
return result
v1 = func()
"""
v1 = [i for i in range(10)]
print(v1)
# 生成器推导式,创建了一个生成器,内部循环为执行
"""
def func():
for i in range(10):
yield i
v2 = func()
"""
v2 = (i for i in range(10))
for item in v2:
print(item)
# 面试题:请比较 [i for i in range(10)] 和 (i for i in range(10)) 的区别?
5.9 装饰器
5.8.1 目的
- 在不改变原函数内部代码的基础上,在函数执行之前和之后自动执行某个功能
5.8.2 应用场景
- 想要为函数扩展功能时,可以选择用装饰器
5.8.3 基本装饰器
-
基本格式:
def func(arg): def inner(): v = arg() return v return inner # 重点: # 第一步:执行func函数并将下面的函数参数传递,相当于:func(index) # 第二步:将func的返回值重新赋值给下面的函数名。 index = func(index) @func def index(): print(123) return 666 print(index)
-
总结
- 编写格式:
def 外层函数(参数): def 内层函数(*args,**kwargs) return 参数(*args,**kwargs) return 内层函数
- 应用格式:
@外层函数 def index(): #要装饰的函数 pass index()
# 装饰器的编写 def x(func): def y(): # 前 ret = func() # 后 return ret return y # 装饰器的应用 @x def index(): return 10 #这里的返回值 就是装饰器里的ret 接受的返回值 # 执行函数,自动触发装饰器了 v = index() print(v)
-
示例:
def func(arg): def inner(): print('before') v = arg() print('after') return v return inner def index(): print('123') return '666' # 示例一 v1 = index() # 执行index函数,打印123并返回666赋值给v1. # 示例二 v2 = func(index) # v2是inner函数,arg=index函数 index = 666 v3 = v2() # 示例三 v4 = func(index) index = v4 # index ==> inner index() # 示例四 index = func(index) index()
5.8.4 装饰器建议的写法
def wrapper(f):
def inner(*args,**kwargs):
data = f(*args,**kwargs)
return data #此时返回的是原函数的666
return inner
@wrapper
def func():
print('123')
return 666
ret = func() #这里接受666
print(ret)
#123
#666
5.8.5 带参数的装饰器
-
应用场景:flask框架 / django缓存 / 写装饰器实现被装饰的函数要执行N次
# 第一步:执行 ret = xxx(index) # 第二步:将返回值赋值给 index = ret @xxx def index(): pass # 第一步:执行 v1 = uuu(9) # 第二步:ret = v1(index) # 第三步:index = ret @uuu(9) 相当于先执行函数unn() def index(): pass
-
区别:
# 普通装饰器 def wrapper(func): def inner(*args,**kwargs): data = func(*args,**kwargs) # 执行原函数并获取返回值 return data return inner @wrapper def index(): pass # 带参数装饰器 def x(counter): #最外层又加了一个装饰器 def wrapper(func): def inner(*args,**kwargs): data = func(*args,**kwargs) # 执行原函数并获取返回值 return data return inner return wrapper @x(9) def index(): pass
-
练习题
# 习题一: # 写一个带参数的装饰器,实现:参数是多少,被装饰的函数就要执行多少次,把每次结果添加到列表中,最终返回列表。 def xxx(counter): def wrapper(func): def inner(*args,**kwargs): v = [] for i in range(counter): data = func(*args,**kwargs) # 执行原函数并获取返回值 v.append(data) return v return inner return wrapper @xxx(5) def index(): return 8 v = index() print(v) # 习题二: # 写一个带参数的装饰器,实现:参数是多少,被装饰的函数就要执行多少次,并返回最后一次执行的结果【面试题】 def xxx(counter): def wrapper(func): def inner(*args,**kwargs): for i in range(counter): data = func(*args,**kwargs) # 执行原函数并获取返回值 return data return inner return wrapper @xxx(5) def index(): return 8 v = index() print(v) # 习题三: # 写一个带参数的装饰器,实现:参数是多少,被装饰的函数就要执行多少次,并返回执行结果中最大的值。 def xxx(counter): def wrapper(func): def inner(*args,**kwargs): value = 0 for i in range(counter): data = func(*args,**kwargs) # 执行原函数并获取返回值 if data > value: value = data return value return inner return wrapper @xxx(5) def index(): return 8 v = index() print(v)
5.10 迭代器
5.10.1 基本知识
-
用途:对 某种对象(str/list/tuple/dict/set类创建的对象-可迭代对象)中的元素进行逐一获取
-
表象:具有
__next__
方法且每次调用都获取可迭代对象中的元素(从前到后一个一个获取) -
示例:
- 列表转换成迭代器:
- v1 = iter([11,22,33,44])
v1 = [11,22,33,44].__iter__()
- 迭代器想要获取每个值:反复调用
val = v1.__next__()
v1 = [11,22,33,44] # 列表转换成迭代器 v2 = iter(v1) # 迭代器获取每个值 result1 = v2.__next__() print(result1) result2 = v2.__next__() print(result2) result3 = v2.__next__() print(result3) result4 = v2.__next__() print(result4) result5 = v2.__next__() print(result5) # 报错:Stoplteration 表示已经迭代结束
- 列表转换成迭代器:
-
for循环:运用了迭代器
v1 = [11,22,33,44] # 1.内部会将v1转换成迭代器 # 2.内部反复执行 迭代器.__next__() # 3.取完不报错 for item in v1: print(item)
5.10.2 可迭代对象
-
表象:可以被for循环,内部有__iter__方法的对象就可以称为是可迭代对象。
-
如何让一个对象变成可迭代对象?
- 在类中实现
__iter__
方法且返回一个迭代器(生成器)
# 示例一: class Foo: def __iter__(self): return iter([1,2,3,4]) obj = Foo() # 示例二: class Foo: def __iter__(self): yield 1 yield 2 yield 3 obj = Foo()
- 在类中实现
-
注意:只要能被for循环,就是去看他内部的
__iter__
方法
5.11 生成器
5.11.1 基本知识
-
可以理解为:函数的变异、特殊的迭代器、特殊的可迭代对象
-
生成器的作用:
- 生成数据
- 迭代
-
示例:
# 生成器函数(内部是否包含yield) def func(): print('F1') yield 1 print('F2') yield 2 print('F3') yield 100 print('F4') # 函数内部代码不会执行,返回一个 生成器对象 。 v1 = func() # 生成器是可以被for循环,一旦开始循环那么函数内部代码就会开始执行。 for item in v1: print(item)
5.11.2 关键字
-
yield
- 用途:判断函数是否是生成器函数
-
yield from
- 用途:从当前生成器函数跳到其他生成器函数中,执行结束后再回原函数继续执行下面代码
def base(): yield 88 yield 99 def func(): yield 1 yield 2 yield from base() # 跳到base函数 yield 3 result = func() for item in result: print(item) # 1 2 88 99 3
5.11.3 总结
-
重点:
- 函数中如果存在yield,那么该函数就是一个生成器函数
- 调用生成器函数会返回一个生成器
- 生成器只有被for循环时,生成器函数内部的代码才会执行,每次循环都会获取yield返回的值
-
建议:
-
生成器函数中一般不要有return
-
如果需要终止生成器函数中的循环,可以用return
def func(): count = 1 while True: yield count count += 1 if count == 100: return val = func() for item in val: print(item)
-
-
生成器示例:读取大文件内容
def func(): # 分批去读取文件中的内容,将文件的内容返回给调用者。 cursor = 0 while True: f = open('db', 'r', encoding='utf-8') # 通过网络连接上redis # 代指 redis[0:10] f.seek(cursor) data_list =[] for i in range(10): line = f.readline() if not line: return data_list.append(line) cursor = f.tell() f.close() # 关闭与redis的连接 for row in data_list: yield row for item in func(): print(item)
5.12 总结
-
迭代器,对可迭代对象中的元素进行逐一获取,迭代器对象的内部有一个next放法。用于一个个的获取数据
-
可迭代对象,可以被for循环,且此类对象中都有iter方法且要返回一个迭代器(或生成器都可以)
-
生成器,函数内部有yiled则就是生成器函数,调用函数则返回一个生成器,循环生成器时,函数内部代码才会执行
生成器即有iter方法,也有next方法,所以说生成器是特殊的可迭代对象和特殊的迭代器