Python3入门与进阶笔记(五):函数

目录

简介    

拆装包

可变参数

默认参数+关键字参数

可变参数+关键字参数

返回值

全局变量 VS 局部变量


简介    

函数的参数列表可以有,也可以没有。

    return语句可以返回值也可以返回None,不写return返回None。

    返回多个变量时,可以在return后写上所有变量名,变量名之间用逗号隔开,此时return的类型是tuple,接受返回值时可以用多个变量(序列解包),之后就可以通过这些变量名访问,当然也可以用元祖的方式即下标访问,不过不建议这种做法。

拆装包

符号: *

  • 赋值
    • *只能出现在赋值符号的左侧,不能出现在右侧,表示将散列装包(列表,无论原先是啥数据结构,均无差别变成列表类型)
  • 函数
    • 实参,*表示对可迭代的对象进行拆包
    • 形参,看到*变量名,会进行装包操作

例如:

*_在赋值号左侧。首先将(1, 2, 3, 4, 5, 6)拆包变成散列1 2 3 4 5 6,然后第一个元素赋给a,即a=1,最后一个元素赋给c,即c=6,剩下的_前面有*,此时我们先给_一个容器(列表)用来装入剩下的散列,即将散列2 3 4 5,装入列表容器,所以_=[2, 3, 4, 5]。

*_在打印语句中,表示将可迭代对象拆包成散列。这边_是列表类型,是可迭代的,拆成散列2 3 4 5,再打印,对应函数传递实参。

至于type(*_)也是拆包,此时拆包后有四个元素,而type只能接受1个或3个参数,对应函数传参,实参。

a, *_, c = (1, 2, 3, 4, 5, 6) // *_ 中的_可以是其他,例如*x,_等价于x
print(a, c, _, *_)
try:
    print(type(_))
    print(type(*_))
except TypeError as e:
    print(e)
1 6 [2, 3, 4, 5] 2 3 4 5
<class 'list'>
type() takes 1 or 3 arguments

 从format以及print(*b)均可以看出拆包,拆完包后print可以直接打印出散列,format中的{},必须对应变量数+散列元素个数,即一个散列元素需要一个{}

a, *b, c = ['aa', 2, 'good', (4, 5, 6), 7]
# k = *['aa', 2, 'good', (4, 5, 6), 7] # 报错,can't use starred expression here
k = b
print("a: {}, b: {}, c: {}, b[0]: {}, b[1]: {}, b[2]: {}".format(a, b, c, *b))
print("a: {}, b: {}, b[0]: {}, c: {}".format(a, b, *b, c))
print("a: {}".format(a, c))
print(type(b), type(k), len(k))

print(*b)
print(*b[1:], k)
# print(*b[0]) # 报错:print() argument after * must be an iterable, not int,在print中是拆包,只有可迭代对象才可以拆包
# x, y, z = *b    # SyntaxError: can't use starred expression here,*不能出现在赋值号右侧
a: aa, b: [2, 'good', (4, 5, 6)], c: 7, b[0]: 2, b[1]: good, b[2]: (4, 5, 6)
a: aa, b: [2, 'good', (4, 5, 6)], b[0]: 2, c: good
a: aa
<class 'list'> <class 'list'> 3
2 good (4, 5, 6)
good (4, 5, 6) [2, 'good', (4, 5, 6)]
a, *b, c = 'hello'
print(a, b, c)
h ['e', 'l', 'l'] o

可变参数

*变量名

必须参数(下例中的name)必须放在可变参数(下例中的args)之前。解释一下执行过程,*args是可变参数的一种,当*变量名出现在实参中,会先进行拆包,,例如看到*l,拆成1 2 3的散列进行传参,传参后,看到形参*变量名,会进行装包,准备一个元祖,用来装传入的参数,即装包后args是元祖(1, 2, 3),若没有值传入,则args就是空元祖。

def my_print(name, *args):
    print(name)
    print(args, type(args))

my_print('my', 1, 2, 3)
print('='* 20)
my_print('my')
my_print('my', *l)
my
(1, 2, 3) <class 'tuple'>
====================
my
() <class 'tuple'>
my
(1, 2, 3) <class 'tuple'>

报错: my_print() missing 1 required keyword-only argument: 'name'

def my_print(*args, name):
    print(name)
    print(args, type(args))
try:
    my_print('my', 1, 2, 3)
except TypeError as e:
    print(e)
my_print() missing 1 required keyword-only argument: 'name'

默认参数+关键字参数

必须参数vs 默认参数:定义和调用时必须参数都必须在所有默认参数的前面,默认参数可以通过关键字给出,顺序无所谓。

关键字参数:可以在参数调用时明确指定实参赋值给哪个形参,也可以改变传参顺序,提升代码的可读性。

必须参数必须放在默认参数之前,由于有默认值,故可以传参,也可以不传,不传则是默认值。使用关键字参数传参,可无视传参的顺序。

可以无视传参顺序,也可以不传参。

def my_add(a, b=2, c=3):
    print('my_add: {}'.format(a + b + c))
my_add(1, c=2, b=9)
my_add(1, c=2)
my_add: 12
my_add: 5

 报错:不带默认值的参数,必须放在带默认值的参数的前面

def my_add(a=2, b, c=3):
    print('my_add: {}'.format(a + b + c))
my_add(1, 6, 8)
  File "<ipython-input-230-e2cf3aecc044>", line 1
    def my_add(a=2, b, c=3):
               ^
SyntaxError: non-default argument follows default argument

可变参数+关键字参数

def func(**kwargs),我们看到**变量名时,表明该函数只接受关键字传参,即key=value的形式,其他形式一律不接受。

我们可以看到func(a=1,b=2),是key=value的形式,解释一下,执行过程,**kwargs也是可变参数的一种,不同于*args,**kwargs必须用关键字传参,传参后会进行一个装包的操作。即程序看到**变量名,会准备一个字典,用来装传进来的参数。此处我们可以看到kwargs就是字典,{'a': 1, 'b': 2},键是key=value的key,key均变成了字符串的形式,值是value。

def func(**kwargs):
    print(kwargs)

func(a=1, b=2)
func()
{'a': 1, 'b': 2}
{}

我们知道字典也是key: value的形式,我们可以通过func(**字典名)来进行传参。解释一下执行过程。这边涉及到拆包和装包。当**字典名作为实参传递时会发生拆包,拆成函数需要的key=value的形式,当然这边和我们通常意义上的key=value意义不一样,当传参后,遇到**kwargs的形参会进行装包操作。将key=value的形式装包成字典。

d = {'a': 1, '009': 2}
func(**d)
{'a': 1, '009': 2}

通常:必须参数, *args, **kwargs 

返回值

可以返回多个值,返回多个值时,在return的时候,底层会准备一个元祖,将这些值装进元祖,作为一个整体返回,相当于装包。接受的时候可以只用一个变量接受,这时候接受的是元祖。若用多个变量接受,则会进行拆包,把值分别赋给每一个变量,但此时变量的个数必须等于返回元素的个数。

def func(a, b):
    return a, b, a + b
x = func(1, 2)
print(x, type(x))
a, b, c = func(1, 4)
print(a, b, c)
(1, 2, 3) <class 'tuple'>
1 4 5

全局变量 VS 局部变量

全局变量:
    声明在函数外层的是全局的,所有函数均可以访问
    若是不可变类型,在函数中修改需要添加global关键字
    若是可变类型,可以在函数中直接修改,无需添加global关键字
局部变量: 
    函数内部声明的变量,仅限于在函数内部使用
    当函数的局部变量与全局变量重名时优先使用局部变量

别给自己找麻烦,如果全局变量和局部变量重名,无法在函数内部既使用全局的又使用局部的。最好局部变量不和全局变量重名。

"""
全局变量:
    声明在函数外层的是全局的,所有函数均可以访问
    若是不可变类型,在函数中修改需要添加global关键字
    若是可变类型,可以在函数中直接修改,无需添加global关键字
局部变量: 
    函数内部声明的变量,仅限于在函数内部使用
    当函数的局部变量与全局变量重名时优先使用局部变量
"""
name = '月月' 
l = [1, 2, 3, 4]

def func():
    '''
    声明在函数外层的是全局的,所有函数均可以访问
    '''
    print('func函数: {}, {}'.format(name, l))

def func_incorrect():
    '''
    若是不可变类型,在函数中修改需要添加global关键字,
    name = '你好',这个是可以的,此时name指的是局部变量,而不是全局变量,即修改的时候,局部变量覆盖全局变量
    name += '你好',不可以,因为修改的时候,局部变量覆盖全局变量,但是又没有定义局部变量,程序认为未初始化就直接使用,故报错
    '''
    name += '你好' 
    l.append('l')
    print('func_incorrect函数: {}, {}'.format(name, l))

def func1_correct():
    '''
    若是不可变类型,在函数中修改需要添加global关键字,
    若是可变类型,可以在函数中直接修改,无需添加global关键字
    '''
    global name
    name += '你好' # 
    l.append('l')
    print('func1_correct函数: {}, {}'.format(name, l))

def func_local():
    """当函数的局部变量与全局变量重名时优先使用局部变量"""
    name = 'yueyue'
    name += '月月'
    print('func_local函数: {}, {}'.format(name, l))
    

try:
    func_incorrect()
except UnboundLocalError as e:
    print(e)

func()
func1_correct()
func()
func_local()
local variable 'name' referenced before assignment
func函数: 月月, [1, 2, 3, 4]
func1_correct函数: 月月你好, [1, 2, 3, 4, 'l']
func函数: 月月你好, [1, 2, 3, 4, 'l']
func_local函数: yueyue月月, [1, 2, 3, 4, 'l']
name = '月月' 
l = [1, 2, 3, 4]
def func_local_incorrect():
    '''
    别给自己找麻烦,如果全局变量和局部变量重名,无法在函数内部既使用全局的又使用局部的。
    最好局部变量不和全局变量重名。
    '''
    name = 'yueyue'
    name += '月月'
    global name
    print('func_local函数: {}, {}'.format(name, l))
    
func_local_incorrect()
  File "<ipython-input-19-cfc5811007d5>", line 6
    global name
    ^
SyntaxError: name 'name' is assigned to before global declaration

找到了一个简单明了的 https://zhuanlan.zhihu.com/p/678432234

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值