day14-函数

时间:2023-03-09 09:45:53
day14-函数

1、定义函数

一个函数就是封闭一个功能
def 函数名():
  函数代码
注意:函数名不要用默认的关键字。否则会将默认关键字函数覆盖掉。
命名规则与变量相同,使用字母、数字、下划线组成,不能以数字开关

2、调用函数

定义了函数之后,如果需要调用它,通过函数名()即可完成调用

3、函数说明文档

在 Python 里我们使用文档字符串(docstrings)来说明如何使用代码

 def printHello():
  "哈哈,我是函数的说明文档"
  print("hello world")

printHello()
hello world

help(printHello) #使用help(函数名)来显示说明文档

day14-函数

或通过__doc__来调用说明文档

 import math
def longest_side(a, b):
"""
Function to find the length of the longest side of a right triangle. :arg a: Side a of the triangle
:arg b: Side b of the triangle :return: Length of the longest side c as float
"""
return math.sqrt(a*a + b*b) if __name__ == '__main__':
print(longest_side.__doc__)
print(longest_side(4,5))

day14-函数

4、函数的返回值

如果一个函数有return,那么就意味着这个函数有一个返回值
函数在遇到return时就结束该函数,return如果后面还有代码则不执行
return在调用函数时返回函数运行结果的值

 def sum3Num(a,b,c):
sum = a+b+c
return sum #此时return的作用是把sum的结果返回 result = sum3Num(10,20,30)
print(result)

例子:计算1-用户指定数字的和

 #1.定义一个函数,计算1-用户指定数的和
numUser = int(input('请输入1-某个数的和:')) def sumUser(numUser):
i = 0
sum = 0
while i<=numUser:
sum+=i
i+=1
return sum result = sumUser(numUser)
print('-'*20)
print('结果是:%d'%result)
print('-'*20) 请输入1-某个数的和:1000
--------------------
结果是:500500
--------------------

5、函数返回多个值

如果return返回多个值,则会将多个值变成一个元组返回
有多种方法接收
1、可以定义多个变量,分别将多个变量的值返回,注意,当调用函数时,接收返回值的多个变量顺序需要与函数内的变量顺序一致,设计关系紧密,不推荐
2、可以定义一个元组、一个列表、一个字典来接收变量,再将这个元组、列表、字典当作一个整体返回
通过字典来接收变量的好处,是可以通过字典的key值来获取value值,无需关注变量顺序,推荐。

 def func():
name = input('请输入姓名:')
sex = input('请输入性别:')
age = input('请输入年龄:')
info = {'name':name, 'sex':sex, 'age':age}
return info my_info = func()
print(my_info)
print('姓名:{},性别:{},年龄:{}'.format(my_info.get('name'),my_info.get('sex'), my_info.get('age') ))

day14-函数

6、函数参数

定义时小括号中的参数,用来接收参数用的,称为“形参”,形参只在被调用时才分配内存,形参只在函数内有效,也可以说形参的作用域只在本函数内部。
调用时小括号中的参数,用来传递给函数用的,称为“实参”,实参可以是常量、变量、表达式、函数等。实参必须是确定的值,所有的数据类型都可以被当成参数传递给函数。

6.1、形参角度,分为位置参数、默认参数、不定长参数(万能参数)

6.1.1、位置参数
形参中的位置参数必须与调用函数时传入的实参位置一一对应

例子1:

 #1、定义一个函数,计算2个数的和,先定义2个形参
def add2Num(a,b):
sum = a+b
print('%s+%s=%s'%(a,b,sum))
#2、接收用户输入的2个数字,接收用户提交的实参
num1 = int(input('请输入第一个数字:'))
num2 = int(input('请输入第二个数字:'))
#3、调用函数
add2Num(num1,num2)
结果:
请输入第一个数字:123
请输入第二个数字:456
123+456=579

例子2:比大小

 def my_max(a,b):return a if a>=b else b s1 = my_max(300,200)
print(s1)

6.1.2、默认参数,也叫缺省参数

调用函数时,缺省参数的值如果没有传入,则被认为是默认值。如果缺省参数的值传入了,则会认为是指定的值。
缺省参数必须放在位置参数的后面,如果放在前面则会报错。
当有多个缺省参数时,如func(a,b=100,c=200),当调用函数时,可以指定变量的名称来改变缺省参数。

 def func(name, sex='male'):
print('姓名:%s,性别:%s'%(name,sex))
func('Tom')
func('Alice','female')
结果:
姓名:Tom,性别:male
姓名:Alice,性别:female

默认参数只会在函数命名阶段被赋值一次,后面再重新定义,不会改变默认参数的指,例如下面的例子

 res = 1
def foo(x, y=res):
print(x, y) res = 10
foo('a') #结果为:a,1

当函数被定义时,默认参数y的值为1,当后面再改变res的值为10时,不会改变函数默认参数y的值

6.1.3、不定长参数,也叫万能参数(*arge,**kwargs)

当在形参前面加上*号时,表示可以传入多个参数。
第1个参数赋值给第1个形参,后面所有的参数作为一个元组赋值给第2个形参

例子:计算所有数的和

 def func(*args):
result = 0
for i in args:
result += i
return result result = func(1,2,3,4,5)
print(result)
结果为15

当在形参前面加上**号时,表示可以传入1个字典。

 def test1(a, *args, **kwargs):
print(a)
print(args)
print(kwargs)
test1(1,2,3,[11,22,33],m=100,n=200)
结果为:
1 #第一个形参接收第一个实参
(2, 3, [11, 22, 33]) #args接收第二个非关键字的实参
{'m': 100, 'n': 200} #kwargs接收所有的关键字实参

6.2、实参角度,分为位置参数、关键字参娄、引入参数、强制关键字参数

实参中的位置参数必须与形参中的位置参数一一对应
实参中的关键字参数可以指定形参中的关键字进行传参

6.2.1、引用传参,实参中可以引用全局变量进行传参
例子1:

 a = 100
def func_1(a):
a +=1
return a
print(func_1(a))
print(a)
结果为:
101
100

例子2:

 lst = [11,22,33]
def func_1(a):
a +=a
return a
print(func_1(lst))
print(lst)

test2的结果为
用a+=a的结果是
[11,22,33,11,22,33]
[11,22,33,11,22,33]
因为传入的参数是可变参数,所以在函数内执行时将可变参数进行了修改,所以在函数内和函数外的结果都被修改了

用a=a+a的结果是
[11,22,33,11,22,33]
[11,22,33]
全局变量a的值没有被修改。
a+=a是在原变量的数据上进行修改,而a=a+a是先定义了一个变量a,然后再进行计算赋值

6.2.2、强制关键字参数
我们也能将函数的参数标记为只允许使用关键字参数。用户调用函数时将只能对每一个参数使用相应的关键字参数。

 def hello(*, name='user'):
print("Hello", name)
hello('Tom')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: hello() takes 0 positional arguments but 1 was given hello(name='Tom')
Hello Tom

位置参数,必须按顺序传入,传入的实参与形参一一对应
默认参数,必须放在位置参数后面
关键参数,同上,必须放在位置参数后面
非固定参数,*args=()以位置参数的形式传入,**kwargs{}以关键参数的形式传入
形参的顺序为位置参数 ---> *args ---> 默认参数 ---> **kwargs

 l1 = [1,2,3]
l2 = [11,22,33]
l3 = (55,66,77)
dic1 = {'name':'Tom', 'age':20}
dic2 = {'name1':'Mike', 'age1':22}
def func(*args, **kwargs):
return args,kwargs# print(func(*l1, *l2, *l3, **dic1, **dic2))
结果为:[1, 2, 3, 11, 22, 33, 55, 66, 77]

在函数定义时,*和**的作用是聚合,*将位置参数聚合成一个元组,**将关键字参数聚合成一个字典
在函数调用时,*和**的作用是打散,*将所有传入的元组或列表打散成单个字符串,**将所有传入的字典打散成一个字典,
所以如果要打散传入的多个字典时,各个字典的key不能重复,否则没办法打散成一个字典,因为key值重复

7、python中有3个名称空间:

1)全局名称空间:存储的是全局的变量与值的对应关系
2)临时(局部)名称空间:当函数被命名时全局名称空间会记录一个函数名与函数体的对应关系,但是函数不会执行。当函数被调用时,会在内存中临时开辟一个新的空间,此空间记录函数中的变量与值的对应关系,随着函数的结束,临时名称空间也会关闭
3)内置名称空间:存储内置函数,如len、print等

在内存中的加载顺序是内置名称空间--->全局名称空间--->函数执行时:局部名称空间

8、作用域

1)全局作用域:内置名称空间、全局名称空间
2)局部作用域:临时(局部)名称空间
3)取值顺序:函数执行时:临时(局部)名称空间 ---> 全局名称空间 ---> 内置名称空间

取值顺序满足就近原则,也可以叫做LEGB原则,表示的是Local -> Enclosed -> Global -> Built-in
Local 本函数或者类方法内部的变量。
Enclosed 本函数的父级函数内的变量。
Global 全局作用域内的变量。
Built-in Python内置函数。

9、全局变量

在全局定义的变量,就是全局变量,全局变量在全局生效,也在所有函数内生效
注意:全局变量可以在任意一个函数内调用,但是在函数内不能直接修改全局变量的值,如果一定要在函数内修改全局变量的话,需要在函数内声明变量为全局变量,并且其他函数在调用这个全局变量时,也会使用修改后的变量值。

10、局部变量

在函数内部定义的变量,就是局部变量,局部变量只在函数内生效,外部变量默认不能被函数内的局部变量修改,但是局部变量可以引用外面的全局变量
如果想要在函数内部修改外面的全局变量a,需要在函数内部声明一个全局变量 global a,但是建议不要这么做
但是函数内部是可以修改函数外面的列表、字典、实例,因为修改列表内容时是引用了列表的内存地址,而列表内的每个元素又都有自己的内存地址,所以可以修改列表中元素的值

 a = 100
def func():
global a
a+=1
return a
print(func())
print(a)
结果为:
101
101

例子1:

 def change():
name = 'Tom'
print(name)
name = 'Mike'
change()
print(name)

#结果发现在调用函数时,函数内部的变量值被修改了,但是函数外的变量值并没有受到影响
#当全局变量和局部变量都定义了某个变量时,那么函数内执行局部变量。
Tom
Mike

例子2:

 def change():
name.append('Jack')
print(name)
name = ['Mike','Tom']
change()
print(name)

结果发现函数外的列表值也被修改了
['Mike', 'Tom', 'Jack']
['Mike', 'Tom', 'Jack']

函数引用可变类型与不可变类型
可变类型,值可以改变,例如:
列表 list
字典 dict

不可变类型,值不可以改变,例如:
数值类型 int,long,bool,float
字符串 str
元组 tuple

当变量值为可变类型时,如果变量值发生改变,引用的id是不会发生改变的
当变量值为不可变类型时,如果变量值发生改变,则引用的id也会发生改变

为了防止全局变量和局部变量的名字相同,可以在定义全局变量名字前加g_,即全局变量的名字都定义为g_变量名字。

11、nonlocal

1、nonlocal不能引用和操作全局变量

 a = 2
def func():
nonlocal a
print(a)
func()
报错SyntaxError: no binding for nonlocal 'a' found

2、在局部作用域中,对父级作用域(或者更外层作用域非全局作用域)的变量进行引用和修改,并且引用的哪层,从那层及以下此变量全部发生改变。
子名称空间只能引用父名称空间的变量,但是不能修改。

 def func1():
a = 1
def inner():
nonlocal a
a += 1
print('外层函数的a原来为',a)
inner()
print('外层函数的a被内层inner()操作之后的值为',a)
func1()
结果为:
外层函数的a原来为 1
外层函数的a被内层inner()操作之后的值为 2

12、函数名可以作为函数的参数,函数的返回值,和容器内的一个元素用来循环

12.1、函数作为参数和返回值

 def func1():
  print(666) def func2(x):
  print(888)
  return x ret = func2(func1)
ret()

结果:

888
666

func1函数作为一个参数传递给func2,func2函数内又返回了func1函数,执行ret()其实就是执行func1()

12.2、循环执行函数

 def func1():
print('')
def func2():
print('')
def func3():
print('')
def func4():
print('') L1 = [func1, func2, func3, func4]
for i in L1:
i()
结果为:

12.3、实现增删改查可以用下面这样的方法:

 def func1():
print('增加功能')
def func2():
print('删除功能')
def func3():
print('修改功能')
def func4():
print('查询功能')
dic = {
1:func1,
2:func2,
3:func3,
4:func4
}
while 1:
choice = int(input('请输入数字:'))
dic[choice]()