python基础六(函数基础及参数使用)

一、 函数定义

1、什么是函数
函数就相当于具备某一功能的工具
函数的使用必须遵循一个原则:
先定义
后调用
2、为何要用函数
代码冗余,程序的组织结构不清晰,可读性差
可维护性、扩展性差
3、如何用函数
# 定义函数
# 定义的语法
def 函数名(参数1,参数2,...):
    """文档描述"""
    函数体
    returndef: 定义函数的关键字;
函数名:函数名指向函数内存地址,是对函数体代码的引用。函数的命名应该反映出函数的功能;
括号:括号内定义参数,参数是可有可无的,且无需指定参数的类型;
冒号:括号后要加冒号,然后在下一行开始缩进编写函数体的代码;
"""文档描述""": 描述函数功能,参数介绍等信息的文档,非必要,但是建议加上,从而增强函数的可读性;
函数体:由语句和表达式组成;
return 值:定义函数的返回值,return是可有可无的。

二、 函数使用的原则:先定义,再调用

1、调用函数
函数的使用分为定义阶段与调用阶段,定义函数时只检测语法,不执行函数体代码,函数名加括号即函数调用,只有调用函数时才会执行函数体代码
定义阶段
函数的使用必须遵循’先定义,后调用’的原则。函数的定义就相当于事先将函数体代码保存起来,然后将内存地址赋值给函数名,
函数名就是对这段代码的引用,这和变量的定义是相似的。没有事先定义函数而直接调用,就相当于在引用一个不存在的’变量名’。
形式一:无参函数
定义函数发生的事情
1)、申请内存空间保存函数体代码
2)、将上述内存地址绑定函数名
3)、定义函数不会执行函数体代码,但是会检测函数体语法
调用函数发生的事情
1)、通过函数名找到函数的内存地址
2)、然后加括号就是触发函数代码的执行
def foo():
    print(foo)  # foo是函数的内存地址,<function foo at 0x000002B9614C7430>
    print('in the foo')
    bar()
    
def bar():
    print('in the bar')

# 调用阶段
foo()

# <function foo at 0x00000191556B9280>
# in the foo
# in the bar

形式二:有参函数
def fun1(x, y):
    print(x, y)


fun1(1, 2)


def fun2(a):
    print(a)


fun2('hello world')

# 1 2
# hello world
形式三:空函数,函数体代码为pass
def fun3(x, y):
    pass  # pass充当函数体“占位符”
# 三种形式的应用场景
# 函数类似于工厂,参数类似于原料,return产品,场景看你是否需要,pass,主要用于构思框架的时候用

三、 调用函数

1、语句的形式:只加括号调用函数
fun()
2、表达式形式:
def func1(x, y):
    res = x + y
    return res

res1 = func1(1, 2)
print(res1)
# 3、函数调用可以当做参数
res2 = func1(func1(1, 2), 10)
print(res2)

四、 函数返回值

return是函数结束的标志,既函数体代码一旦运行到return会立刻终止函数的运行,并且会将return后的值当作本次运行的结果返回
1、返回None:函数体内没有return
return
return None
2、返回一个值:return 值
3、返回多个值:用逗号分隔开多个值,会被return返回成元组
def func():
    return 1, 'an', [1, 23]

res = func()
print(func(), type(func()))

# (1, 'an', [1, 23]) <class 'tuple'>

五、 函数参数使用

一、形参与实参介绍
形参:在定义函数阶段定义的参数称之为形式参数,简称形参,相当于变量名
def func(x,y):
print(x,y)
实参:在调用函数阶段传入的值称之为实际参数,简称实参,相当于变量值
func(1,2)
实参和形参之间的关系:
在调用有参函数时,实参(值)会赋值给形参(变量名)。
在Python中,变量名与值只是单纯的绑定关系,而对于函数来说,这种绑定关系只在函数调用时生效,在调用结束后解除。

形参即在定义函数时,括号内声明的参数。形参本质就是一个变量名,用来接收外部传来的值。

实参即在调用函数时,括号内传入的值,值可以是常量、变量、表达式或三者的组合:
#1:实参是常量
res=my_min(1,2)

#2:实参是变量
a=1
b=2
res=my_min(a,b)

#3:实参是表达式
res=my_min(10*2,10*my_min(3,4))

#4:实参可以是常量、变量、表达式的任意组合
a=2
my_min(1,a,10*my_min(3,4))
二、形参与实参的具体使用
2.1 位置参数:在定义函数时,按照从左到右的顺序依次定义形参,称为位置形参,凡是按照这种形式定义的形参都必须被传值
特点:按照顺序与形参一一对应,实参个数比形参个数多一个不行,少一个也不行。
def register(name, age, sex):  # 定义位置形参:name,age,sex,三者都必须被传值
    print('Name:{} Age:{} Sex:{}'.format(name, age, sex))

register('lq', 18, 'male')  # TypeError:缺少3个位置参数
2.2 关键字参数:在调用函数时,实参可以是key=value的形式,称为关键字参数,凡是按照这种形式定义的实参,
可以完全不按照从左到右的顺序定义,但仍能为指定的形参赋值。指名道姓给某个形参传值,可以不参照顺序。
register(sex='male',name='lili',age=18)
需要注意在调用函数时:
实参也可以是按位置或按关键字的混合使用,但必须保证关键字参数在位置参数后面,且不可以对一个形参重复赋值
register('lili', sex='male', age=18)  # 正确使用
register(name='lili', 18, sex='male')  # SyntaxError:关键字参数name=‘lili’在位置参数18之前
register('lili', sex='male', age=18, name='jack')  # TypeError:形参name被重复赋值
2.3 默认参数:在定义函数时,就已经为形参赋值,这类形参称之为默认参数,当函数有多个参数时,
需要将值经常改变的参数定义成位置参数,而将值改变较少的参数定义成默认参数。
特点:在定义阶段就已经被赋值,意味着在调用阶段可以不用为其赋值
def register(name, age, sex='male'):  # 默认sex的值为male
    print('Name:%s Age:%s Sex:%s' % (name, age, sex))

register('tom', 17)  # 大多数情况,无需为sex传值,默认为male
register('Lili', 18, 'female')  # 少数情况,可以为sex传值female
需要注意:
默认参数必须在位置参数之后
默认参数的值仅在函数定义阶段被赋值一次,准确的说被赋予的是值的内存地址
范例1
m=1
def func(x,y=m):
    print(x,y)

m=223222
func(2)
范例2
m = ['a', ]
def func(x, y=m):  # y--->['a',]的内存地址
    print(x, y)
m.append('b')
func(2)

# 2 ['a', 'b']
虽然默认值可以被指定为任意数据类型,但是不推荐使用可变类型
函数最理想的状态:函数的调用只跟函数本身有关系,不受外界代码的影响

def fun(x, y, z, l=[]):
    l.append(x)
    l.append(y)
    l.append(z)
    print(l)
l = ['a', 'b']
fun(1, 2, 3)
print(l)

# [1, 2, 3]
# ['a', 'b']

def fund(x,y,z,l=['a','b']):
    l.append(x)
    l.append(y)
    l.append(z)
    print(l)
fund(1,2,3)
fund(4,5,6)

# ['a', 'b', 1, 2, 3]
# ['a', 'b', 1, 2, 3, 4, 5, 6]

# 推荐使用形式:
def fune(x,y,z,l=None):
    if l is None:
        l=[]
    l.append(x)
    l.append(y)
    l.append(z)
    print(l)
fune(1,2,3)
fune(4,5,6)

# [1, 2, 3]
# [4, 5, 6]
2.4 可变长度的位置参数(*与**的用法):参数的长度可变指的是在调用函数时,实参的个数可以不固定,而在调用函数时,
实参的定义无非是按位置或者按关键字两种形式,这就要求形参提供两种解决方案来分别处理两种形式的可变长度的参数
(实参用来为形参赋值的,所以对应着,针对溢出的实参必须有对应的形参来接受)
2.4.1 可变长度的位置参数:如果在最后一个形参名前加号,那么在调用函数时,溢出的位置实参,都会被接收,
以元组的形式保存下来赋值给该形参。*后跟的可以实任意的名,但约定俗成应该是args.
(1)
def func(x, y, *args):
    print(x)
    print(y)
    print(args)
func(1, 2, 3, 4, 5, 6) 
# 1
# 2
# (3, 4, 5, 6)
# 例:求多个值得和
def func(*args):
    res = 0
    for item in args:
        res += item
    return res
res = func(1, 2, 3, 4, 5, )
print(res)    # 15

(2)*可以用在实参中,实参中带*,先*后的值打散成位置实参,打散就是一个循环遍历取值。

# 例:
def func(x,y,z):
    print(x,y,z)
func(*[1,2,3])     # 1 2 3

(3)形参与实参中都带*

def func(x, y, *args):
    print(x)
    print(y)
    print(args)
func(1, 2, *[3, 4, 5])  # [3,4,5]打散成三个元素,三个元素以元组存
# 1
# 2
# (3, 4, 5)
func(1, 2, [3, 4, 5])  # [3,4,5]一个元素,按元组存
# 1
# 2
# ([3, 4, 5],)
func(*'hello world')  # 打散成单个元素,以元组存
# h
# e
# ('l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd')
2.4.2 可变长度的关键字参数
如果在最后一个形参名前加**,那么在调用函数时,溢出的关键字参数,都会被接收,以字典的形式保存下来赋值给该形参
**后跟的可以实任意的名,但约定俗成应该是kargs。
(1)
def func(x, y, **kwargs):
    print(x)
    print(y)
    print(kwargs)
func(1, 2, a=3, c=4, d=5)
# 1
# 2
# {'a': 3, 'c': 4, 'd': 5}

(2)  **可以用在实参中(**后跟的只能是字典),实参中带**,先**后的值打散成关键字实参

def func(x, y, z):
    print(x, y, z)
func(**{'x': 3, 'y': 4, 'z': 5})
func(*{'x': 3, 'y': 4, 'z': 5})  # 循环遍历取了key
# 3 4 5
# x y z

(3) 形参与实参中都带*

def func(x, y, **kwargs):
    print(x, y, kwargs)
func(1, 2, **{'a': 3, 'b': 4, 'c': 5})
# 1 2 {'a': 3, 'b': 4, 'c': 5}
2.5 命名关键字参数
命名关键字参数:在定义函数时,*后定义的参数,如下所示sex,age,称之为命名关键字参数
想要限定函数的调用者必须以key=value的形式传值,Python3提供了专门的语法:
需要在定义形参时,用作为一个分隔符号,*号之后的形参称为命名关键字参数。
对于这类参数,在函数调用时,必须按照key=value的形式为其传值,且必须被传值
def register(x, y, *, sex, age):
    print(x, y, sex, age)
register(1, 2, sex='male', age=18)  # 正确使用
# 1 2 male 18
register(1, 2, 'male', '18')  # TypeError:未使用关键字的形式为sex和age传值
2.6 组合使用(*args必须在**kwargs之前)
综上所述所有参数可任意组合使用,但定义顺序必须是:位置参数、默认参数、*args、命名关键字参数、**kwargs。
可变参数*args与关键字参数**kwargs通常是组合在一起使用的,如果一个函数的形参为*args与**kwargs,
那么代表该函数可以接收任何形式、任意长度的参数。 
def func(*args, **kwargs):
    pass
# 例:
def func(x, y, z):
    print(x, y, z)
def wrapper(*args, **kwargs):
    func(*args, **kwargs)
wrapper(1, z=3, y=2)
# 1 2 3
'''
按照上述写法,在为函数wrapper传参时,其实遵循的是函数func的参数规则,调用函数wrapper的过程分析如下:
位置实参1被接收,以元组的形式保存下来,赋值给args,即args=(1,),关键字实参z=3,y=2被**kwargs接收,
以字典的形式保存下来,赋值给kwargs,即kwargs={'y': 2, 'z': 3}执行func(args,kwargs),即func((1,),**{'y': 2, 'z': 3}),
等同于func(1,z=3,y=2)提示: *args、**kwargs中的args和kwargs被替换成其他名字并无语法错误,但使用args、kwargs是约定俗成的。
'''

六、总结

0、引用传递
    python中所有值得传递,传递的都是不是值本身,而是值得引用,既内存地址
1、函数分为两大类,分别是什么?二者在使用时有何区别
    内置函数
    自定义函数
2、什么是形参、什么是实参?形参与实参之间的关系是什么?
    定义阶段和调用阶段
3、简述两只形参的区别:位置形参、默认形参
    默认形参就在在定义阶段已给出值
    ps:函数最理想的状态是函数的运行不受外界代码的干扰
    def func(x,y,z=None):
        if z is None:
            z=[]
        print(z)
4、简述两只实参的区别:位置实参、关键字实参
    调用阶段func(1,2,a=3,b=3)
5、简述*和**在形参与实参中的区别(解决调用时,实参的个数与形参的个数不一样的问题)
    def func(*args,**kwargs):
        pass
    func(1,2,3,4,a=5,b=6)

    实参带*和**,就是打散,for循环的一个取值;形参中的*和**就汇总成元组和字典。

6、解释下述形式,既函数wrapper的参数特点是什么
    def index(x,y,z):
        print(x,y,z)

    def wrapper(*args,**kwargs) #形参汇总,args=(1,2),kwargs={'a':3,'b':4},
        index(*args,**kwargs)   #调用,实参打散,index(*(1,2),**{'a':3,'b':4})----->index(1,2,a=3,b=4)

    wrapper(1,2,a=3,b=4)

 



posted @ 2023-04-04 12:51  coder雪山  阅读(66)  评论(0编辑  收藏  举报