python函数的参数、作用域、匿名函数

概述

什么是函数?

函数就是一段根据逻辑封装好的代码块,通过调用的方式达到代码复用的目的.

语法

使用关键字def 就可以实现函数的定义.

def function():
    some code

在该表达式中,function被称为函数名,当然函数名是可以随便自定义的.只要符合python中对象的命名规则即可.

既不能以数字或者非'_'以外的符号开头,同时一般情况下,保持小写.

调用函数就可以得到特定逻辑代码的返回值.我们使用函数名后面加上()来实现函数调用.

function()

函数都有返回值.

如果一个函数没有没有指定的特定的返回值,就是返回一个None.或者说没有用return关键字的函数,都是返回None.

如果需要返回特定的值或者对象,就要使用return关键字.当然,也可以return一个None.

def func():
    result=2+2
 
def other_func():
    return  'I\'m a function.'


result=func()
other_result=other_func()

print(result)
print(other_result)

#输出:
None
I'm a function.

参数 

函数是对特定的逻辑代码进行封装,要实现特定的逻辑,我们可以为函数传递参数.

例如,设计一个计算两个数之和的函数.

def add(a,b):
    return a+b

result=add(10,1999)
print(result)

#输出:
2009

其中,a和b就是add函数的两个参数,如果在调用函数时,不传递参数,就是抛出缺少参数的错误.

但传入了不正确类型的参数,也会抛出错误.比如传入一个数字和一个字母,很明显是无法相加的.

add(10)
TypeError: add() missing 1 required positional argument: 'b'.


add(10,'a')
TypeError: unsupported operand type(s) for +: 'int' and 'str'

但是,我们发现传入两个字母,或者两个列表该函数也是可以正常运行的.

charactor=add('a','b')
araay=add([1,2],[3,4])

print(charactor)
print(araay)

#输出:
ab
[1, 2, 3, 4]

其实这就是python中一切皆对象的思想.

只要我们传入的两个对象,只要可以支持'+'运算,就可以得到相应的结果.

参数的作用就是将函数执行逻辑代码时需要的变量传递给函数.

在python中,参数有四种类型.这并不是从功能上区分,而是在参数传递方式上.

参数类型描述
位置参数如果不输入参数名称,则必须按照指定顺序输入参数;若输出参数名称,则可以不按照指定顺序输入参数
默认参数在设置参数时已设定默认值,故输入参数时可忽略,但若要重新配置该参数值,则按照位置参数规则执行
可变参数使用*,则连续输入多个的个体参数,会被自动封装为tuple,但输入类容器结构数据时,在前类容器前加*进行拆包
关键字参数使用**,可以输入任意的key=value键值对形式的参数,会自动封装为dict,但若输入dict为参数,则需要在dict加**进行拆包
  • 位置参数 

位置参数是有序的.

def compute(a,b,c):
    return a-b+c

print(compute(a=5,c=2,c=1))
print(compute(5,1,2))

#输出:
6
6

上面的例子中,第一次计算中,虽然传入的数值的顺序与参数不一致,但指明了参数与数值的对应关系.

而在第二次计算中,并没有指明对应关系,却依然得到了正确的结果.

这就是位置参数的有序性的作用.python解释器会将输入的顺序与参数的顺序自动匹配.

  • 默认参数 

假设我们要设计一个计算数值平方的函数.

def square(a,num):
    return a**num

print(square(2,2))#输出:4

每次计算调用该函数时都需要输出两个参数,感觉有点麻烦.此时,就可以使用默认参数.

def square(a,num=2):
    return a**num

print(square(2))#输出:4

这样一来,每次调用就只用输出一个参数即可.很明显,默认参数就是为特定的参数设置默认值.

那这个函数能不能计算立方呢?

def square(a,num=2):
    return a**num

print(square(2,num=3))#输出:8
print(square(3))#输出:9

 只需要手动设置num参数即可.而且之后并不影响原来的默认值.

这就是默认参数的作用.

需要注意的是:有默认值的参数只能放到最后.

因为参数的有序的,在不指定对应关系的情况下,输入的参数值会进行自动匹配.

如果默认参数不在最后的,在不指定对应关系的情况下传入的参数可能会覆盖掉默认参数.

不过在python3.6版本中,解释器会自动检查,如果默认参数不在最后会报错.

def sum_square(a,num=2,b):
    return (a+b)**num

SyntaxError: non-default argument follows default argument
  • 可变参数 

在介绍可变参数之前,先来介绍一下'拆包'.

什么叫'拆包'?

举个例子,现有包含3个元素的一个列表,我们要将三个元素分别赋值给三个变量,怎么做呢?

常规的做法:

L=[1,'abc',2]

a=L[0]
b=L[1]
c=L[2]

print(a,b,c)

#输出:1 abc 2

现在用'拆包'的方式在实现:

L=[1,'abc',2]
a,b,c=L
print(a,b,c)
#输出:1 abc 2

这就是拆包.拆包不仅对列表管用,对元素,集合,甚至是字典也一样起作用(仅对主键).

不仅可以'全拆',也可以不'全拆'.

L=[1,'abc',2]
a,*b=L
print(a,b)#输出:1 ['abc', 2]
*a,b=L
print(a,b)#输出:[1, 'abc'] 2

'*'号的使用就是关键.

有了拆包打底,再来说可变参数.

假设,要设计一个计算一个列表中所有元素之和的函数.

def add(L):
    return sum(L)

print(add([1,2,3]))#输出:6

但我们传入的不是列表时,将会报错:

def add(L):
    return sum(L)

print(add(1,2,3))

TypeError: add() takes 1 positional argument but 3 were given

此时我们结合一下拆包的'*'号.该参数变成可变参数.

def add(*L):
    return sum(L)

print(add(1,2,3))#输出:6
print(add(*[1,2,3]))#输出:6

很明显,我们在定义函数的参数时,在参数前面加上了一个'*'号.

第一次我们是将所有要计算的元素单个传入,

第二次是将其封装成列表,但在其前面加上了一个*号 

两次不同类型输入的计算都可以正常运行.

解释器会自动检测传入参数的类型,将其封包或者拆包,这就是可变参数的作用.

  • 关键字参数 

首先声明一点的是:关键字参数值针对字典.

直接上例子:

def say(name,age,gender):
    print('我是%s,年龄%s,性别%s'%(name,age,gender))

say('小明',12,'男')#输出:我是小明,年龄12,性别男
say(**{'name':'大明','age':14,'gender':'女'})#输出:我是小明,年龄12,性别男

除了正常的传入参数外,

我们使用了一个主键与参数名称对应的字典,并在其前面使用了'**'

很明显,解释器是可以识别的.

再看一个例子:

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

func(a=1)#输出:{'a': 1}
func(**{'b':2})#输出:{'b': 2}

这次我们在函数定义的参数前面加上了'**'.

同时,我们以'x=y'键值对形式传入的参数,被自动解析为了字典.

而我们在把字典作为参数传入时,在其前面加上了'**'后,也正常的解析了.

最后总结下:

在使用**kwargs可变参数后,

所以我们以单个'x=y'键值对形式的参数在函数内部,都会被自动封装成字典.

而且我们在把主键与参数名称对应的字典作为参数传递时,在其前面加上'**',同样字典也可以被自动解析.

作用域 

简单点说,作用域是指变量的有效访问范围.

函数的变量并不是在哪个位置都可以访问的,变量的作用域决定了在该变量可以被访问的权限和范围.

比如说,一个函数中定义的变量尽在该函数中有效,一旦该函数结束,则该变量失效.

在python中,有四个类型的变量作用范围,分别是:

中文名英文名举例说明
局部Local函数中定义的变量仅仅在函数范围内有效
局部上层(外部)Encllosing函数嵌套函数,外层函数中定义的变量相当于内层函数的外部变量
全局Golbal整个解释器范围内
内建Build-in通过魔术方法可设置可访问的属性

使用代码对作用域范围做个演示:

build_in=int(10)#内建作用域

num=5#全局

def func():
    x=1#局部的外部或上层
    def inside_func():
        y=2#局部
        pass
    pass

变量查找是有顺序的,按照L –> E –> G –>B 的规则查找.

既,先在局部查找,再到局部的上层查找,再到全局查找,最后在内建范围查找.

除了查找顺序,还有访问权限的限制.访问权限指的是对变量数据的查询和修改权限.

在自身的本作用域范围内,对象的操作具有所有的绝对权限.

但一旦跨越的变量的作用域范围层级,就有相应的权限限制以保障变量的安全性.

因为变量作用域仅在当前逻辑代码块有效,一旦该逻辑块代码执行完毕,则该变量被释放,既失效.

所以,不在上层作用域访问下层作用域的问题,也无法访问.

那对于下层作用域访问上层作用域有什么使用规则呢?

在介绍规则之前,还需要回顾下可变对象和不可变对象.

list、dict、set是可变对象,而int,str是不可变对象.

原则上,下层作用域对上层作用域中的变量仅有查询权,不能对数据进行修改.

num=10
List=[1,2]

def inquiry():
    new_num=num+1
    print(num,List)

inquiry()#打印: 10 [1,2]

def modify_num():
    b=num+1
    print(b)

modify_num()#报错:UnboundLocalError: local variable 'num' referenced before assignment

def modify_list():
    List=List.append(100)
    print(List)

modify_list()#报错:UnboundLocalError: local variable 'List' referenced before assignment

函数嵌套函数的作用域特殊,不可变对象,同样只有查询权,但对可变对象,则下层作用域可以修改上层作用域.

def out_func():
    List=[1,2]
    num=10
    def iner_func():
        List.append(100)
        print(num)
    iner_func()
    print(List)

out_func()

#输出:
10
[1, 2, 100]

有时确实会有在下层作用域中修改上层作用域中对象的需求.

所以,python提供了两个关键字来绝对此需求.globalnonlocal .

nonlocal用于声明在函数嵌套函数的情况下,下层作用域使用并修改上层作用域.

global用于在函数中声明调用并可以修改全局变量.

def outer():
    num=100
    def iner():
        nonlocal num
        num=num-99
        print(num)
    iner()
    print(num)

outer()#输出: 1  1


string='abc'

def func():
    global string
    string=string+'efg'
    print(string)

func()#输出:abcefg

虽然使用global和nonlocal关键字可以变量作用域的范围,但建议还是要合理的规划好变量的作用域.

下层或者说内层对上层或者说外层变量的修改,很容易出现错误.

需要说明的是,该作用域概念仅仅对针对函数.在面向对象的类的中,又会有其特殊的作用域. 

匿名函数

函数定义都有特定的原则,但有时某些函数使用范围很有限,使用频率也很低,甚至只使用一次.

此时,再按照规范来定义一个函数,就比较麻烦.

因为担心与其他函数名冲突,还的费尽心思去想函数名.

这时,匿名函数就派上了用场.

  • 语法

使用lambad关键字来定义一个匿名函数.

匿名函数只允许有一行代码的表达式,无法进行换行.

匿名函数执行完毕就立即释放内存空间,而不像其他函数一样存留在内存中.

lambda x:x**2

关键字lambad之后的是该匿名函数需要的参数

冒号":"后是表达式,匿名函数返回或者输出的结果就是该表达式执行的结果.

匿名函数一般都使用在高阶函数或者面对对象的过程中.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值