【python学习笔记】函数和lambda

函数的定义

python中定义函数的代码如下:

def compareLength(str1,str2):
    print("比较",str1,"和",str2,"的长度")
    return len(str1)>=len(str2)

函数的调用

调用函数和其他语言没有区别。

print(compareLength("abc","ffff"))

为函数提供说明文档

python中可以通过help()函数或者__doc__属性来查看函数的说明文档,自定义函数的说明文档也需要自己去写。函数的说明文档通常位于函数的内部,所有代码的最前面。

def compareLength(str1,str2):
    '''
    compareLength函数用来比较两个字符串的长度。
    '''
    print("比较",str1,"和",str2,"的长度")
    return len(str1)>=len(str2)
help(compareLength)
print(compareLength.__doc__)

值传递和引用传递

python中的函数传参的时候,不可变的参数(如字符串、数字、元组)是通过值传递的方式,可变的参数(如列表、字典)是通过引用传递的方式。通过值传递的方式在函数中发生变化后不会改变调用函数时的实参的值。

位置参数

位置参数就是像java等语言调用函数时一样,实参的位置和数量都必须和形参相同。

关键字参数

使用关键字参数调用函数的语法如下:

print(compareLength(str2="abc",str1="ffff"))

可以看出,python除了可以用位置参数调用函数外,还可以通过关键字参数,无视形参的顺序进行调用。

默认参数

由于调用函数是不指定某个参数的值会造成异常,所以python在定义函数时可以给形参默认值,语法如下:

def compareLength(str1,str2 = "ffff"):
    '''
    compareLength函数用来比较两个字符串的长度。
    '''
    print("比较",str1,"和",str2,"的长度")
    return len(str1)>=len(str2)
print(compareLength("abc"))

python中提供了__defaults__属性来查看函数的默认值,返回结果是一个元组。

print(compareLength.__defaults__)

需要注意的是,当定义一个有默认值参数的函数时,有默认值的参数必须位于所有没默认值参数的后面。

可变参数

python中的可变参数有两种,可变参数的声明方式如下:

def some_func(a,*args, **kwargs):
    print(a)
    print(args)
    print(kwargs)

其中a为普通的参数,*args参数获取到的是一个元组,**kwargs参数获取到的试一个字典。调用该方法的时候,a的值不能省略,但是不定长参数可以省略。
还有一点需要注意的是,定义方法的时候,不定长参数必须在最后。

some_func(1,386,384,"test",姓名="张三",身高=176,体重="50kg")

None值

python中的None相当于java中的null。

函数返回值

python中的函数也是用return来返回值。

def sum(*a):
    s = 0
    for item in a:
        if isinstance(item,(int,float)):
            s += item
    return s
print(sum(1,2,3,4,"test"))

python中的函数可以返回多个值,使用,隔开,返回的结果是一个元组。

def test():
    return "res1","res2","res3"
print(test())

变量作用域

python的变量作用域跟java没有区别。使用globals()函数和locals()函数可以分别获得全局变量和当前作用域内的变量。返回值为一个字典。

def getParams():
    a = 10
    print(globals())
    print(locals())
getParams()

通过globals()和locals()返回的字典,可以直接用键来访问变量,globals()返回的字典可以修改变量的值,但是locals()只能访问。
vars(object)函数可以返回一个指定的objcet范围内所有的变量,如果不传入object,vars()和locals()的作用完全相同。python中用class来声明对象,现在还没有学到这里,先不赘述了。
当有局部变量和全局变量重名时,局部变量会遮蔽全局变量,像下面的方法在调用时会抛出UnboundLocalError异常。

g = 10
def test():
    print("-----",g)
    g = 20
test()

这种情况下可以通过global关键字,将函数中的变量g变成全局变量g,从打印结果可以看到,执行完方法之后,全局变量g的值发生了变化。

g = 10
def test():
    global g
    print("-----",g)
    g = 20
test()
print(g)

局部函数

python的局部函数就是在函数内部定义的函数,默认情况下,局部函数只能在其作用域内使用。这种最少有两层的函数也叫做嵌套函数。

def outdef():
    def indef():
        print("This is indef.")
    indef()
outdef()

可以通过将局部函数return的方式来扩大局部函数的作用域。

def outdef():
    def indef():
        print("This is indef.")
    return indef
#调用全局函数
newDef = outdef()
#调用全局函数中的局部函数
newDef()

如果局部函数中定义有和所在函数中变量同名的变量,也会发生遮蔽的问题。比如下面的函数,局部函数中的name遮蔽了outdef()中的name,所以在print(name)的时候会抛出未定义name的异常。

def outdef ():
    name = "所在函数中定义的 name 变量"
    def indef():
        print(name)
        name = "局部函数中定义的 name 变量"
    indef()
outdef()

这种情况可以用nonlocal关键字来解决,用法和global关键字一样。

def outdef ():
    name = "所在函数中定义的 name 变量"
    def indef():
        nonlocal name
        print(name)
        name = "局部函数中定义的 name 变量"
    indef()
outdef()

闭包函数

闭包函数由内部函数和外部函数组成,闭包的特点是外部函数返回的不是一个值,而是一个函数。

#闭包函数,其中 exponent 称为自由变量
def nth_power(exponent):
    def exponent_of(base):
        return base ** exponent
    return exponent_of # 返回值是 exponent_of 函数
square = nth_power(2) # 计算一个数的平方
cube = nth_power(3) # 计算一个数的立方
print(square(2))  # 计算 2 的平方
print(cube(2)) # 计算 2 的立方

个人感觉闭包函数是为了让程序更简洁,把一个参数很多的函数封装起来,减少多次调用时需要输入的参数。
闭包函数比普通函数多了一个 __closure__属性,该属性记录着自由变量的地址。当闭包函数被调用时,系统就会根据该地址找到对应的自由变量,完成整体的函数调用。

print(square.__closure__)
print(cube.__closure__)

lambda表达式

lambda表达式又称匿名函数,在python中通常来表示仅有一行表达式的函数。如果一个函数的函数体只有一行表达式,则该函数就可以用lambda表达式代替。
lambda表达式的完整格式为:name = lambda [list] : 表达式,其中lambda关键字是必须的,list为可选参数,等同于参数列表,name为该表达式的名称。例如下面的函数:

def add(x, y):
    return x+ y
print(add(3,4))

#改为lambda表达式的写法可以写为:

add = lambda x,y:x+y
print(add(3,4))

eval()和exec()

eval() 函数的语法为:eval(expression, globals=None, locals=None, /),exec() 函数的语法为exec(expression, globals=None, locals=None, /)。两个函数除了函数名不同,语法完全相同,功能也相似,都是执行一段字符串形式的python代码,相当于一个代码解释器。二者的不同之处在于,eval()函数执行完要返回结果,而exec()函数执行完不返回结果。

eval()和exec()函数语法中的expression是一个字符串,代表要执行的语句。该语句受后面额globals和locals限制,只有在globals字典和locals字典作用域内的函数和变量才能被执行。

globals参数管控的是一个全局的命名空间,即 expression 可以使用全局命名空间中的函数。如果只提供了globals参数,而没有提供自定义的 builtins,则系统会将当前环境中的 __builtins__复制到自己提供的globals中,然后才会进行计算;如果连globals都没有提供,则使用python的全局命名空间。

locals参数管控的试一个局部命名空间,和globals类似,当它和globals有重复或者冲突时,以locals为准。如果没有提供locals,则默认为globals。

其中__builtins__ 是 Python 的内建模块,平时使用的 int、str、abs 都在这个模块中。通过 print(dic[“builtins”]) 语句可以查看 builtins 所对应的 value。
下面这个例子可以说明globals作用域的作用。

dic={} #定义一个字典
dic['b'] = 3 #在 dic 中加一条元素,key 为 b
print (dic.keys()) #先将 dic 的 key 打印出来,有一个元素 b
exec("a = 4", dic) #在 exec 执行的语句后面跟一个作用域 dic
print(dic.keys()) #exec 后,dic 的 key 多了一个a和__builtins__

locals作用域的用法如下:

a=10
b=20
c=30
g={'a':6, 'b':8}
t={'b':100, 'c':10}
print(eval('a+b+c', g, t))

可以看到打印的结果为116,即是使用了locals作用域中的b和c,由于locals作用域中没有a的值,使用了globals作用域中的a。如果globals作用域中也没有a,则会抛出异常,因为传入了globals和locals作用域之后,eval()和exec()方法只能使用作用域内的参数。

我个人一直觉得evel()这种函数太过危险,很容易被利用来执行攻击语句,python中的globals和locals作用域可以在一定程度上解决这个问题。

函数式编程

函数式编程是一种编程思想,主要思想是把运算过程尽量写成一系列嵌套的函数调用,函数式编程的思想中,函数可以接收函数作为参数和返回值。上文提到的嵌套函数、lambda表达式都属于函数式编程的思想。

上面我们说了将函数作为返回值的闭包函数,函数式编程中还有一种将函数作为参数的方式,叫做高阶函数。例如下面的写法就是一个高阶函数:

def cubeAbs(x,f):
    return f(x)*f(x)*f(x)
print(cubeAbs(-2,abs))

abs()是python中内置的求绝对值的函数,上面的cubeAbs()函数即是求x的立方的绝对值。

除了自己定义的高阶函数,python中也内置了一些高阶函数,比如map(),reduce(),filter(),sorted()等。

map()函数有两个参数,第一个是函数,第二个是可迭代对象,比如下面的例子:

f = lambda x:-x
m = map(f,[1,2,3,4,5])
print(list(m))

也可以直接写成下面这样:

m = map(lambda x:-x,[1,2,3,4,5])
print(list(m))

map()函数返回的是一个映射后的新list,而reduce()函数返回值是一个值,示例如下:

from functools import reduce
l = ["l","i","s","t"]
print(reduce(lambda x,y:x+y,l))

需要注意的是,reduce()函数需要从functools模块中导入。
上面的函数执行顺序为,第一次循环的时候,x为l,y为i;第二次循环的时候,x为li,y为s;以此类推,直到最后一个数据。

filter()函数的返回值和map()一样。是一个可迭代对象,该函数的作用是根据传入的条件进行筛选,只保留符合条件的数据。

l = [1,2,3,4,5]
print(list(filter(lambda x:x>3,l)))

sorted函数在流程控制一文里说过了,这里就不再赘述了。

函数注解

函数注解是python3中提供的对函数的参数和返回值进行说明的信息,语法如下:

def f(a:str,b:int=1)->str:
    return a
#打印结果为{'a': <class 'str'>, 'b': <class 'int'>, 'return': <class 'str'>}
print(f.__annotations__)

有一说一,函数式编程写出来确实是挺好看,自从用了jdk8的流式处理之后我就不能自拔了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值