一、初识函数定义和调用
def my_first_func():
"""这里是函数说明文档"""
print("my first function...")
my_first_func()
定义: def关键字开头,空格之后接函数名称和圆括号(),最后还有一个“:” …
函数文档: 每一个函数都应该对功能和参数进行相应的说明,应该写在函数下面第一行。以增强代码的可读性。通过函数特殊属性__doc__或者 help(函数名) 可以查看该函数的说明文档
调用: 就是函数名(),要记得加上括号。
二、函数的返回值
在python中使用关键字return来返回值,要研究返回值,分以下几个情况:没有返回值,返回一个值,返回多个值
-
没有返回值
不写return:默默返回一个None
只写return:也是返回None, 另外,程序一旦遇到return,会结束整个函数。
写 return None:和以上两种情况一样,但一般不这样写 -
返回一个值
return 后面写上要返回的内容即可。
# 函数定义
def mylen():
"""计算s1的长度"""
s1 = "hello world"
length = 0
for i in s1:
length += 1
return length
# 函数调用
str_len = mylen()
print('str_len:%s' % str_len)
注意:return和返回值之间要有空格,可以返回任意数据类型的值
- 返回多个值
def demo1():
'''返回多个值'''
return 1, 2, 3, 4
def demo2():
'''返回多个数据类型'''
return 1, ['jack', 520], 'hey'
ret1 = demo1()
print(ret1) # (1, 2, 3, 4)
ret2 = demo2()
print(ret2) # (1, ['jack', 520], 'hey')
返回的多个值会被组织成元祖被返回,也可用多个值来接受,但是注意返回几个数据,就用几个变量来接受,不然会报错。
def demo3():
'''返回多个数据类型'''
return 1, ['jack', 520], 'hey'
a, b, c = demo3()
print(a, b, c) # 1 ['jack', 520] hey
三、函数的参数
# 函数定义
# 函数定义
def mylen(s1):
"""计算s1的长度"""
length = 0
for i in s1:
length += 1
return length
# 函数调用
str_len = mylen("hello world")
print('str_len:%s' % str_len)
函数可以传递多个参数,用逗号隔开即可
- 实参与形参
1、 调用函数时传递的这个“hello world”被称为实际参数,应为这个是实际要交给函数的内容,简称实参
2、定义函数时的s1,只是一个名字,被称为形式参数,因为定义函数时它只是一个形式,表示这里有一个参数,简称形参
- 位置参数
站在实参角度
1.按照位置传值
def mymax(x,y):
#此时x=10,y=20
the_max = x if x > y else y
return the_max
ma = mymax(10,20)
print(ma)
2.按照关键字传值
def mymax(x,y):
#此时x = 20,y = 10
print(x,y)
the_max = x if x > y else y
return the_max
ma = mymax(y = 10,x = 20)
print(ma)
按照关键字传参
3.位置、关键字形式混着用
def mymax(x,y):
#此时x = 10,y = 20
print(x,y)
the_max = x if x > y else y
return the_max
ma = mymax(10,y = 20)
print(ma)
正确用法:
1.位置参数必须在关键字参数的前面
2.对于一个形参只能赋值一次
站在形参角度
位置参数必须传值
def mymax(x,y):
#此时x = 10,y = 20
print(x,y)
the_max = x if x > y else y
return the_max
#调用mymax不传递参数
ma = mymax()
print(ma)
#结果
TypeError: mymax() missing 2 required positional arguments: 'x' and 'y'
- 默认参数
1.正常使用
使用方法:将变化比较小的值设置为默认参数
2.默认参数的定义
def stu_info(name,sex = "male"):
"""打印学生信息函数,由于班中大部分学生都是男生,
所以设置默认参数sex的默认值为'male'
"""
print(name,sex)
stu_info('jacky')
stu_info('xiaohong','female')
3.参数陷阱:默认数据类型是一个可变数据类型
def jack(a, lis=[]):
lis.append(a)
return lis
print(jack(1))
print(jack(2))
print(jack(3))
运行结果:
[1]
[1, 2]
[1, 2, 3]
# 改进
def jack(a, lis=None):
if lis is None:
lis = []
lis.append(a)
return lis
# 陷进有时候用来简化代码,恰到好处
def jack(k,l = {}):
l[k] = 'v'
print(l)
jack(1)
jack(2)
jack(3)
运行结果:
{1: 'v'}
{1: 'v', 2: 'v'}
{1: 'v', 2: 'v', 3: 'v'}
summary:默认参数值只被赋值一次,如果默认参数的值是一个可变数据类型,那么每一次调用函数的时候如果不传参数,就共用这个数据类型的资源
- 动态参数
动态参数有两种:可以接受任意个参数
*args : 接收的是按照位置传参的值,组织成一个元组
**kwargs: 接受的是按照关键字传参的值,组织成一个字典
args必须在kwargs之前
按位置传值多余的参数都由args统一接收,保存成一个元祖的形式
def sum_num(*args): #站在形参的角度上,给变量加上*,就是组合所有传来的值。
sum = 0
for each_num in args:
sum += each_num
return sum
sum1 = sum_num(1, 2, 3, 4)
print(sum1) # 10
l = [4, 3, 2, 1]
sum2 = sum_num(*l) #站在实参的角度上,给一个序列加上*,就是将这个序列按照顺序打散
print(sum2) # 10
将多余的关键字参数都由kwargs统一接收,保存成一个字典的形式
def good_info(**kwargs): #形参角度,把键值对打包成一个字典
print(kwargs)
print(kwargs['name'], kwargs['price'])
good_info(name="computer", price=4000)
d1 = {'name': 'computer', 'price': 4000}
good_info(**d1) # 把一个字典解包传进去
# 运行结果
>>>{'name': 'computer', 'price': 4000}
>>>computer 4000
>>>{'name': 'computer', 'price': 4000}
>>>computer 4000
在函数内部使用 * 可以将传入的位置参数(被打包成一个元祖)解包成多个参数(实参传值时的模样)以方便使用
使用 ** 可以将传入的关键字参数(被打包成一个字典)解包成多个参数(多个键值对的感觉)
在装饰器时,需要把外部函数可变参数传入给内部时,把参数打散再传入
def good_info(*args, **kwargs):
print(args, type(args))
print(*args) # 把元祖拆开
print(kwargs, type(kwargs))
# print(**kwargs) # 打印这句将会报错
good_info("computer", "mouse", "phone", name="jack", sex="man")
# ('computer', 'mouse', 'phone') <class 'tuple'>
# computer mouse phone
# {'name': 'jack', 'sex': 'man'} <class 'dict'>
参数总结
只有调用函数的时候
按照位置传:直接写参数的值
按照关键字传:关键字 = 值
定义函数的时候:
位置参数 : 直接定义参数
默认参数,关键字参数 :参数名 = ‘默认的值’
动态参数 : 可以接受任意多个参数
参数名之前加*,习惯参数名args,
参数名之前加**,习惯参数名kwargs
顺序:位置参数,*args,默认参数,**kwargs
形参:
位置参数 : 必须传
*args :可以接收任意多个位置参数
默认参数 : 可以不传
**kwargs : 可以接收多个关键字参数
实参:按照位置传参,按照关键字传参
四、命名空间和作用域
命名空间一共分三种:
全局命名空间
局部命名空间
内置命名空间
*内置命名空间存放了python解释器为我们提供的名字,例如:print,input,list,len,…
加载顺序:内置命名空间(程序运行前加载)-> 全局命名空间(程序运行中,从上到下加载)-> 局部命名空间(函数被调用的时候加载)
取值:
在局部调用时:局部 -> 全局 -> 内置
在全局调用:全局->内置
作用域: 作用域就是作用范围,按照生效范围可以分为全局作用域和局部作用域
全局作用域:包含内置名称空间、全局名称空间,在整个文件的位置都能被引用,全局有效。
局部作用域:局部名称空间,只能在局部范围内生效。
globals() 和locals()的用法:
- globals() 函数会以字典类型返回当前位置的全部全局变量
- locals() 函数会以字典类型返回当前位置的全部局部变量。 对于函数, 方法, lambda 函式, 类, 以及实现了 call 方法的类实例, 它都返回 True。
def jack():
name = "jack_hh"
print(locals()) # 如果该代码放在全局范围内,那么会返回全局变量
print(globals()) # 在哪里调用结果都一样,返回全局变量
jack()
print(globals())
运行结果:
{'name': 'jack_hh'}
{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x000001BD1B7073C8>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': 'C:/python文件都在这里/python 全栈/day9-20/day10/day10.py', '__cached__': None, 'a': 1, 'jack': <function jack at 0x000001BD1B6BC268>}
{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x000001BD1B7073C8>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': 'C:/python文件都在这里/python 全栈/day9-20/day10/day10.py', '__cached__': None, 'a': 1, 'jack': <function jack at 0x000001BD1B6BC268>}
- global关键字:
在函数内不能修改全局的变量,如果这样做python会在函数内部生成一个和全局变量名字一样的全局变量,全局变量不受影响。
可以在局部内使用global关键字,来修改全局变量的值
a = 1
b = 1
def func():
global b
a = 2
b = 2
print("in func: a:%d b:%d" % (a, b))
func()
print("in outside: a:%d b:%d" % (a, b))
运行结果:
in func: a:2 b:2
in outside: a:1 b:2 # 只有global声明的变量b在函数中得到了修改
五、函数的嵌套和作用域链
- 函数的嵌套调用
def my_max2(x, y):
return x if x > y else y
def my_max4(a, b, c, d):
res1 = my_max2(a, b)
res2 = my_max2(res1, c)
res3 = my_max2(res2, d)
return res3
# my_max4(52,-2,123,0)
- 函数的嵌套的定义
- 函数的作用域链
# 内层函数可以使用外层函数变量
def f1():
a = 1
def f2():
print(a)
f2()
f1()
# 内层函数修改外层函数变量,将在内层函数中生成一个名字相同的变量,外层函数变量不受影响
def f1():
a = 1
def f2():
a = 2
f2()
print('a in f1 : ', a)
f1()
# a in f1 : 1
nonlocal 关键字用于在内层函数修改外层函数变量
1.nonlocal 只能用于局部变量,找上层中离当前函数最近一层的局部变量
2. 在内部函数声明nonlocal之前不能再出现同名变量
3. 声明了nonlocal的内部函数的变量修改会影响到 离当前函数最近一层的局部变量,对全局无效,对局部也只是对最近的一层有效
a = 0
def outer():
a = 1
def inner1():
a = 2
def inner2():
nonlocal a
a *= 10
print("in inner2 a:%d"%a)
inner2()
print("in inner1:a:%d"%a) # 外层函数inner1变量a被内层函数inner2函数修改
inner1()
print("outside a:%d"%a)
outer()
# 运行结果
outside a:0
in inner2 a:20
in inner1:a:20
六、函数名的本质
函数名的本质就是函数的内存地址,内存地址加()就是函数的调用
- 可以被引用
def func():
print("in func")
f = func
f()
- 可以被当做容器类型的元素
def func1():
print("in func1")
def func2():
print("in func2")
def func3():
print("in func3")
lis = [func1, func2, func3, ]
dict1 = {"f1": func1, "f2": func2, "f3": func3}
for each in lis:
each()
dict1["f2"]()
# in func1
# in func2
# in func3
# in func2
- 可以当做函数的参数和返回值
def wahaha(f):
f()
return f # 函数名可以作为返回值
def qqxing():
print("in qqxing")
func = wahaha(qqxing) # 函数名可以作为参数传入
func()
# result:
# in qqxing
# in qqxing
不懂? 那就把函数名当普通变量来用
第一类对象的概念:
第一类对象(first-class object)指
- 可在运行期创建
- 可用作函数参数或返回值
- 可存入变量的实体。
七、闭包
def func():
name = 'jacky'
def inner():
print(name)
闭包函数:内部函数包含对外部作用域而非全局作用域名字的引用,该内部函数称为闭包函数
闭包会一直存在内存当中,不会因为函数执行结束而被释放
由于作用域的关系,我们不能拿到函数内部的变量和内部定义的函数,这时,如果需要在函数外部使用函数内部变量,我们可以直接将变量返回, 那如果在需要用到函数内部定义的函数时,,我们就可以可以将函数的名字返回即可。
- 闭包函数的最常用的用法
def func():
name = 'jacky'
def inner():
print(name)
return inner
f = func()
f()
- 判断闭包函数的方法__closure__
# #输出的__closure__有cell元素 :是闭包函数
def func():
name = 'jacky'
def inner():
print(name)
print(inner.__closure__)
return inner
f = func()
f()
# #输出的__closure__为None :不是闭包函数
name = 'weige'
def func2():
def inner():
print(name)
print(inner.__closure__)
return inner
f2 = func2()
f2()
# C:\Users\Jacky\AppData\Local\Programs\Python\Python37\python.exe "C:/python文件都在这里day10.py"
# jacky
# (<cell at 0x0000026AB84F0108: function object at 0x0000026ABA3F1EA0>, <cell at 0x0000026ABA1C82E8: str object at 0x0000026AB85E73E8>)
# weige
# (<cell at 0x0000026ABA3ED0D8: function object at 0x0000026AC14236A8>,)
- 闭包嵌套
def func():
name = 'jacky'
def inner():
sex = "man"
def inner2():
print("hello, little %s" % "boy" if sex == "man" else "girl")
return inner2
return inner
f = func()
f()()
- 闭包获取网络的应用
from urllib.request import urlopen
# 多次调用get_message_func ()函数时,变量url以只需要被定义一次,相比于调用get_url函数而言,
# 省去了多次生成改变量之后又销毁
def get_url():
url = r"https://blog.csdn.net/qq_41823444/article/details/94393150"
def get_message():
return urlopen(url).read()
return get_message
get_message_func = get_url()
content = get_message_func()
print(content)