七、学习py:函数
人嘛了,这篇写到一半关机忘记保存了。
就简略写啦o(╥﹏╥)o
1.初识函数
def dhk():
print("joker!")
#定义函数
dhk()#执行函数
1.1扩充功能,发送邮件
import smtplib
from email.mime.text import MIMEText
from email.utils import formataddr
def send_email():
# 1.邮件内容配置
# 邮件文本
msg = MIMEText("越江才学会用py发送邮件。", "html", "utf-8")
# 邮件上显示的发信人
msg['From'] = formataddr(["越江", "自己的邮箱"])
# 邮件上显示的主题
msg['Subject'] = "猪哥好"
# 2.发送邮件
server = smtplib.SMTP_SSL("smtp.qq.com#发信服务器")
server.login("#自己的邮箱", "#授权码")
server.sendmail("#自己的邮箱", "#对面的邮箱", msg.as_string())
server.quit
send_email()#执行函数
2.参数
在上述发邮件功能中,如果要对多人进行发送操作,一个一个发送略显复杂,所以可以将对方的邮件设置为参数,这样就能极大的提高代码的利用率。
def dhk(a):#这里的a为形式参数
print("a")
dhk(joker)#传入之后就是实际参数
2.1动态参数
2.1.1*(只能通过地址传递)
def func(*args):
print(args)
func(1,12,123,123,321)
#输出(1, 12, 123, 123, 321),元组类型
2.1.2**(只能通过名称传递)
def func(**kwargs):
print(kwargs)
func(n1 = "dhk",n2 = 'joker')#{'n1': 'dhk', 'n2': 'joker'}
#输出字典
2.1.3*&**
def func(*args,**kwargs):
print(args,kwargs)
func(123,n1="nihao")#(123,) {'n1': 'nihao'}
func(123)#func(123)
2.1.4知识补充
在定义函数时可以用**和 *,其实在执行函数时,也可是使用。
形参固定,实参用**和 *
def func(a1,a2):
print(a1,a2)
func(11,22)#11 22
func(*[11,22])#11 22
func(a1=1,a2=2)#1 2
func(**{"a1":1,"a2":2})#1 2
形参中用了* 和 ,实参也用了 和 *
2.2默认参数
def func(a,b=123):#这里的b就是默认参数,可以输入可以不输入,如果没有输入就是默认值。
print(a,b)
2.2.1参数的默认值【面试题】有坑
这个知识点在面试题中出现的概率比较高,但真正实际开发中用的比较少。
def func(a1,a2=18):
print(a1,a2)
原理:python在创建函数(未执行)时,如果发现函数的参数中有默认值,则在函数内部会创建一块区域并维护这个默认值。
执行函数未传值时,则让a2指向函数维护的那个值的地址。
func("dhk")
执行函数传值时,则让a2指向新传入的值的地址
func("adhk",123)
在特定情况【默认参数的值是可变类型(列表,字典,集合)】&【函数内部会修改这个值】下,参数的默认值 有坑!超级大坑!!!!
小坑
#在内存中会维护一块区域,维护[],而这个地址在每次函数执行的时候不会重新创建,在加载的时候就创建好了。所以每一次执行的时候都是使用的默认值的维护地址,就导致了下面的情况!!!
def func(a1,a2=[]):
a2.append(666)
print(a1,a2,id(a2))
func(123)#123 [666] 2791287502016
func(321)#321 [666, 666] 2791287502016
func(33,[77,66])#33 [77, 66, 666] 2791287504768
func(300)#300 [666, 666, 666] 2791287502016
大坑
def func(a1,a2=[1,2]):
a2.append(a1)
return(a2)
v1 = func(10)
print(v1)#[1, 2, 10]
v2 = func(20)
print(v2)#[1, 2, 10, 20]
v3 = func(30,[11,22])
print(v3)#[11, 22, 30]
v4 = func(40)
print(v4)#[1, 2, 10, 20, 40]
深坑
#在这里由于先执行的函数,后输出的内容。但是v124都指向的是同一个内存地址,内存地址又发生了一系列的变化,所以就变成了这样。
def func(a1,a2=[1,2]):
a2.append(a1)
return(a2)
v1 = func(10)
v2 = func(20)
v3 = func(30,[11,22])
v4 = func(40)
print(v1)#[1, 2, 10, 20, 40]
print(v2)#[1, 2, 10, 20, 40]
print(v3)#[11, 22, 30]
print(v4)#[1, 2, 10, 20, 40]
2.5知识补充
1.**必须放在 *的后面
2.参数和动态参数混合时,动态参数只能放在最后。
3.默认值参数和动态参数同时存在。
2.6参数的补充!
2.6.1参数内存地址相关【面试题】
在开始将参数的内存地址相关之前,我们来学习一个技能:
查看某个值在内存中的地址。
v1 = "dhk"
addr = id(v1)
print(addr)#2780843422256
使用案例,如果两个东西(有些东西是两个东西但是内容是一样的)指向的是同一个地址,那他们就是一个东西。(听君一席话,如听一席话)
注意:函数执行传参时,传递的是内存地址。
v1 = "dhk"
addr = id(v1)
print(addr,id(addr))#1971302903920 1971302979760 内存地址是一样的
2.6.2内存地址的利用
def func(data):
data.append(666)
data_list = [11,22,33]
func(data_list)
print(data_list)#[11, 22, 33, 666]
#在这个函数内部修改的是data,但是由于data和data_list的内存地址是一样的,所以修改了data的内容其实也就修改了,data_list。
#但是能修改的必须是可以修改的类型
3.函数返回值
def func(a,b):
return (a+b)
print(func(a=1,b=123))#124
注意:函数在碰到return的时候就会立即停止
3.1函数的返回值是内存地址
def func():
data = [11,22,33]
print(id(data))#2831053370624
return data
v1 = func()
print(id(v1))#2831053370624
但是在第二遍执行这个函数的时候,内存地址是会变的,因为在函数执行完之后,其中参数是会被抹去的,重新执行函数又会重新创建一个新的内存地址。
def func():
data = [11,22,33]
return data
v1 = func()
print(id(v1))#1658473428224
v2 = func()
print(id(v2))#1658473713088
4.函数和函数名
函数名其实就是一个变量,这个变量只不过指代的函数而已。
注意:函数必须先定义才能被调用。(解释型语言)(如果是编译型语言就不用。)
4.1函数做元素
既然函数就相当于是一个变量,那么在列表等元素中是否可以把函数当做元素呢?
def func():
return(123)
data_list = ["dhk","func",func,func()]
print(data_list[0])#dhk
print(data_list[1])#func
print(data_list[2])#<function func at 0x0000023E7A43F040>
print(data_list[3])#123
注意:函数同时也可被哈希,所以函数名同志也可以当做集合的元素、字典的键。
掌握这个只是之后,对后续的开发有很大的帮助。
案例1:要开发一个类似微信的功能。
def send_message():
'''发送消息'''
pass
def send_image():
'''发送图片'''
pass
def send_file():
'''发送文件'''
pass
#把函数名和其对应的数字存入字典中。
func_dict = {
"1":send_message,#因为input获取的是字符串,所以这里也要是字符串
"2":send_image,
"3":send_file}
print("欢迎使用越江系统")
print("请选择:1.发送消息2.发送图片3.发送文件")
choice = input("请输入选择的序号")
func = func_dict.get(choice)
if not func:#如果键不存在返回none,加个not 就是True
print("输入错误")
else:
#执行函数
func()
案例2:某个特定情况需要连续执行发消息,发图片,发文件
def send_message():
'''发送消息'''
pass
def send_image():
'''发送图片'''
pass
def send_file():
'''发送文件'''
pass
#把函数名存入列表中
func_List = [send_message,send_image,send_file]
for func in func_List:#使用for循环
func()
4.2函数名赋值
如果我们将函数名重新赋值,那么函数名不在指代函数,而是指代你赋值的那个东西。
def func():
print(123)
func()
func = 1
print(func)
注意:由于函数名被重新定义之后,就会变成新定义的值,所以大家在自定义函数的时候,不要与python内置的函数重合。
4.3函数名做参数和返回值
1.参数
def plus(num):
return num+100
def handler(func):
res = func(10)
msg = f"执行func,并且获取到的结果为:{res}"
print(msg)
headler(plus)
2.返回值
def plus(num):
return num+100
def handler():
print("执行handler")
return plus
result = handler()
data = result(20)
print(data)
5.作用域
作用域,可以理解为一块空间,这块空间的数据是可以共享的。通俗点来说,作用域就类似于一个房子,房子中的东西归里面所有人共享,其他房子的人无法获取。
5.1函数为作用域
python以函数为作用域,所以在函数内创建的所有数据,可以在次函数中被使用,无法在其他函数中被使用。
def func():
age = "dhk"
print(name)
def func2():
age = 123
print(age)
func()
func2()
5.2全局和局部
一般来说全局变量的变量名都是大写。
在外面定义的变量为全局变量,一般在函数里面定义的变量为局部变量。
在局部作用域调用变量的时候,首先会在自己的作用域寻找变量,如果找不到才会在上一级作用域中寻找变量。
实例1:在局部作用于中读取全局作用域的变量。
NAME = "dhk"
AGE = 123
def func():
asd = "dsa"
AGE = "nihao"
print(asd)
print(AGE)
print(NAME)
func()
print(AGE)
'''
输出
dsa
nihao
dhk
123
'''
5.3global关键字
在局部作用于默认对全局变量只能进行:读取和修改内部元素(可变类型),无法对全局变量进行重新赋值。
但是如果想要在局部作用域中对全局变量重新赋值,则可以基于global关键字实现:
dhk = "joker"
def func():
global dhk#将全局变量引入局部作用域
dhk = "动肝"
func()
print(dhk)#动肝
6.函数的嵌套
python中以函数为作用域,在作用域中定义的相关数据只能被当前作用于或子作用域使用。
6.1函数在作用域中
其实,函数也是定义在作用域中的数据,在执行函数的时候,也同样遵循:优先在自己的作用域中寻找,没有则向上寻找
6.2函数定义的位置
def func():
print("外面")
def inside():
def func():
print("里面")
func()
func()#外面
inside()#里面
其实,大多数情况下我们都会将函数定义在全局,不会嵌套定义函数。不过,当我们定义一个函数去实现某功能,想要将内部功能拆分成N个函数,又担心这n个函数放在全局会与其他函数冲突时(尤其是多人协同开发),可以选择使用函数的嵌套。
6.3嵌套引发的作用域问题
基于内存和执行过程分析作用域
name = "dhk"
def run():
name = "joker"
def inner():
print(name)
inner()
run()#执行函数就创建了一个作用域.
#输出joker
7.闭包
简而言之就是讲数据封装在一个包中,使用时再去里面取。(本质上,闭包是基于函数嵌套搞出来一个中特殊嵌套)
闭包应用场景1:封装数据防止污染全局
name = "dhk"
def func(age):
print(name,age)
def func2():
print(name,age)
def func3:
print(name,age)
闭包应用场景2:封装数据到一个包里,使用时再取
def task(arg):
def inner():
print(arg)
return inner
v1 = task(11)#创建了一个空间储存了arg,这个时候的v1就是inner
v2 = task(22)
v1()#11
v2()#22
def task(arg):
def inner():
print(arg)
return inner
inner_list = []
for val in [11,22,33]:
inner_list.append(task(val))#虽然返回的都是inner但是每一个inner的包都是不一样的
inner_list[0]()#11
inner_list[1]()#22
inner_list[2]()#33
8.装饰器
现在给你一个函数,在不修改函数源码的前提下,实现在函数执行前和执行后分别输入before和after
def func():
print("我是一个func函数")
value = (11,22,33,44)
return value
result = func()
print(result)
8.1第一回合
def func():
print("我是一个func函数")
value = (11,22,33,44)
return value
#创建了一个闭包,维护了origin内存地址
def outer(origin):
def inner():
print("before")
res = origin()#调用原来func的函数
print("after")
return res
return inner
func = outer(func)#在这里func被重新定义为inner函数,原来的func则被封存在origin的内存地址中
result = func()
print(result)
'''
输出
before
我是一个func函数
after
(11, 22, 33, 44)
'''
8.2第二回合
python中支持特殊语法,在某个函数上方使用:
@函数名
def xxx():
pass
#python内部会自动执行 函数名(xxx),执行完之后,在将结果赋值给xxx。
#xxx = 函数名(xxx)
def outer(origin):
def inner():
print("before")
res = origin()
print("after")
return res
return inner
@outer #func = outer(func)
def func():
print("我是一个func函数")
value = (11,22,33,44)
return value
reslut = func()
print(reslut)
'''
before
我是一个func函数
after
(11, 22, 33, 44)
'''
8.3第三回合
更改需求,现在有三个fanc函数,func1,func2,func3,都在前面和后面分别输出before 和after
def outer(origin):
def inner():
print("before")
res = origin()
print("after")
return res
return inner
@outer
def func1():
print("我是一个func1函数")
value = (11,22,33,44)
return value
@outer
def func2():
print("我是一个func2函数")
value = (11,22,33,44)
return value
@outer
def func3():
print("我是一个func3函数")
value = (11,22,33,44)
return value
8.4支持优化n个参数
如果函数存在参数
def outer(origin):
def inner(*args,**kwargs):
print("before")
res = origin(*args,**kwargs)
print("after")
return res
return inner
@outer #func = outer(func)
def func(a1):
print("我是一个func函数")
print(a1)
value = (11,22,33,44)
return value
reslut = func(123)
print(reslut)
'''
before
我是一个func函数
123
after
(11, 22, 33, 44)
'''
总结
装饰器实例
def outer(origin):
def inner(*args,**kwargs):
#执行前
res = origin(*args,**kwargs)
#执行后
return res
return inner
@outer #func = outer(func)
def func(a1):
pass
func()
在不修改原函数内部和调用方式的情况下,使用装饰器。
9.匿名函数
匿名函数,是基于lambda表达式实现定义一个可以没有名字的函数,例如:
data_list = [lambda x:x+100,lambda x:x+110,lambda]
print(data_list[0])
9.1定义
基于lambda定义的函数格式为:lambda 参数 :函数体
参数,支持任意参数
lambda x:函数体
lambda x1,x2:函数体
lambda *args,**kwargs:函数体
函数体,只能支持单行代码
lambda xx:x + 100
返回值,默认将函数体单行代码执行的结果返回给执行函数的地方。
func = lambda x:x+100
v1 = func()
print(v1)#110
10.生成器
生成器是由函数+yield关键字创造出来的写法,在特定情况下,用它可以帮助我们节省内存。
def func():
print(111)
yield 1#可以防止任何类型的数据,出现yield关键字之后,这个函数就变成了生成器函数。
执行生成器函数的时候,函数体默认不会被执行;返回的是一个生成器对象。
下面结合案例说明生成器函数的作用
def func():
print("1")
yield 123
print("2")
yield 234
print("3")
yiled 345
v1 = func()#执行生成器函数时,函数体默认不会被执行,返回的是一个生成器对象。
print(v1)
n1 = next(v1)#在next中执行生成器对象,进入生成器函数并执行其中的代码
print(n1)
n1 = next(v1)
print(n1)
n1 = next(v1)
print(n1)
'''
<generator object func at 0x000001F71903CD60>
1 执行第一次的结果
123返回第一个关键词后面的内容
2 执行第二次的结果
234 返回第二个关键词的内容
3 执行第三次的结果
345 返回第三个关键词的内容
'''
10.1应用场景
假设要让你生成300w个随即的四位数,并打印出来。
1.在内存中一次创建300w个。
2.动态创建,用一个创建一个。
import random
val = random.range(1000,9999)
print(val)
import random
data_list = []
for item in range(3000000):
val = random.randint(1000,9999)
data_list.append(val)
#在使用时,从列表中取即可。
而使用生成器的话
import random
def num(max_count):
counter = 0
while counter < max_count:
yield random.randint(1000,9999)
counter += 1
data_list = num(3000000)
n1 = next(data_list)
n2 = next(data_list)#一边使用,一边生成,节省了大量的内存。
所以在以后遇到,先生成再用,一边生成再用的时候,要想到生成器。
10.2基于for循环
data = func()
for item in data:
print(item)
11.推导式
推导式,是python中提供的一个非常方便的功能,可以让我们通过一行代码实现创建,list,dict,tuple,set的同时初始化一些值。
列表
num_list = [i for i in range(10)]#创建了零到九的一个列表
print(num_list)
#[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
字典
num_list = {i:i for in range(10)}
num_list2 = {i:(i,11) for i in range(10)}
推导式的一个拓展(除去末尾的mp4)
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。后续可能会有评论区,不过也可以在github联系我。