7.Python函数和lambda表达式

Python函数(函数定义、函数调用)用法

Python 中函数的应用非常广泛,比如 input() 、print()、range()、len() 函数等等,这些都是 Python 的内置函数,可以直接使用

除了可以直接使用的内置函数外,Python 还支持自定义函数,即将一段有规律的、可重复使用的代码定义成函数,从而达到一次编写、多次调用的目的

Python 提供了一个功能,即允许将常用的代码以固定的格式封装(包装)成一个独立的模块,只要知道这个模块的名字就可以重复使用它,这个模块就叫做函数(Function)

其实,函数的本质就是一段有特定功能、可以重复使用的代码

#自定义 len() 函数
def my_len(str):
    length = 0
    for c in str:
       length = length + 1
    return length
#调用自定义的 my_len() 函数
length = my_len("http://c.biancheng.net/python/")
print(length)
#再次调用 my_len() 函数
length = my_len("http://c.biancheng.net/shell/")
print(length)

运行结果:

30
29

和其他编程语言中函数相同的是,Python 函数支持接收多个( ≥0 )参数,不同之处在于,Python 函数还支持返回多个( ≥0 )值

Python函数的定义

定义函数,也就是创建一个函数,可以理解为创建一个具有某些用途的工具。定义函数需要用 def 关键字实现

def 函数名(参数列表):
    //实现特定功能的多行代码
    [return [返回值]]

用 [] 括起来的为可选择部分,即可以使用,也可以省略

  • 函数名:其实就是一个符合 Python 语法的标识符,但不建议使用 a、b、c 这类简单的标识符作为函数名,函数名最好能够体现出该函数的功能(如上面的 my_len,即表示自定义的 len() 函数)
  • 形参列表:设置该函数可以接收多少个参数,多个参数之间用逗号( , )分隔
  • [return [返回值] ]:整体作为函数的可选参参数,用于设置该函数的返回值。也就是说,一个函数,可以有返回值,也可以没有返回值,是否需要根据实际情况而定

注意,在创建函数时,即使函数不需要参数,也必须保留一对空的“()”,否则 Python 解释器将提示“invaild syntax”错误。另外,如果想定义一个没有任何功能的空函数,可以使用 pass 语句作为占位符

#定义个空函数,没有实际意义
def pass_dis():
    pass
#定义一个比较字符串大小的函数
def str_max(str1,str2):
    str = str1 if str1 > str2 else str2
    return str

虽然 Python 语言允许定义个空函数,但空函数本身并没有实际意义

函数中的 return 语句可以直接返回一个表达式的值

def str_max(str1,str2):
    return str1 if str1 > str2 else str2

Python函数的调用

调用函数也就是执行函数。如果把创建的函数理解为一个具有某种用途的工具,那么调用函数就相当于使用该工具

[返回值] = 函数名([形参值])

函数名即指的是要调用的函数的名称;形参值指的是当初创建函数时要求传入的各个形参的值。如果该函数有返回值,可以通过一个变量来接收该值,当然也可以不接收

需要注意的是,创建函数有多少个形参,那么调用时就需要传入多少个值,且顺序必须和创建函数时一致。即便该函数没有参数,函数名后的小括号也不能省略

调用上面创建的 pass_dis() 和 str_max() 函数

pass_dis()
strmax = str_max("http://c.biancheng.net/python","http://c.biancheng.net/shell");
print(strmax)

运行结果:

http://c.biancheng.net/shell

对于调用空函数来说,由于函数本身并不包含任何有价值的执行代码,也没有返回值,调用空函数不会有任何效果

对于上面程序中调用 str_max() 函数,由于当初定义该函数为其设置了 2 个参数,因此这里在调用该参数,就必须传入 2 个参数。同时,由于该函数内部还使用了 return 语句,因此可以使用 strmax 变量来接收该函数的返回值

为函数提供说明文档

通过调用 Python 的 help() 内置函数或者 __doc__ 属性,可以查看某个函数的使用说明文档。事实上,无论是 Python 提供的函数,还是自定义的函数,其说明文档都需要设计该函数的程序员自己编写

函数的说明文档,本质就是一段字符串,只不过作为说明文档,字符串的放置位置是有讲究的,函数的说明文档通常位于函数内部、所有代码的最前面

#定义一个比较字符串大小的函数
def str_max(str1,str2):
    '''
    比较 2 个字符串的大小
    '''
    str = str1 if str1 > str2 else str2
    return str
help(str_max)
#print(str_max.__doc__)

运行结果:

Help on function str_max in module __main__:
str_max(str1, str2)
    比较 2 个字符串的大小

还可以使用 __doc__ 属性来获取 str_max() 函数的说明文档,即使用最后一行的输出语句

比较 2 个字符串的大小

Python函数值传递和引用传递(包括形式参数和实际参数的区别)

通常情况下,定义函数时都会选择有参数的函数形式,函数参数的作用是传递数据给函数,令其对接收的数据做具体的操作处理

在使用函数时,经常会用到形式参数(简称“形参”)和实际参数(简称“实参”),二者都叫参数,之间的区别是

  • 形式参数:在定义函数时,函数名后面括号中的参数就是形式参数
#定义函数时,这里的函数参数 obj 就是形式参数
def demo(obj):
    print(obj)
  • 实际参数:在调用函数时,函数名后面括号中的参数称为实际参数,也就是函数的调用者给函数的参数
a = "C语言中文网"
#调用已经定义好的 demo 函数,此时传入的函数参数 a 就是实际参数
demo(a)

实参和形参的区别,就如同剧本选主角,剧本中的角色相当于形参,而演角色的演员就相当于实参

Python 中,根据实际参数的类型不同,函数参数的传递方式可分为 2 种,分别为值传递和引用(地址)传递:

  • 值传递:适用于实参类型为不可变类型(字符串、数字、元组)
  • 引用(地址)传递:适用于实参类型为可变类型(列表,字典)

值传递和引用传递的区别是,函数参数进行值传递后,若形参的值发生改变,不会影响实参的值;而函数参数继续引用传递后,改变形参的值,实参的值也会一同改变

def demo(obj) :
    obj += obj
    print("形参值为:",obj)
print("-------值传递-----")
a = "C语言中文网"
print("a的值为:",a)
demo(a)
print("实参值为:",a)
print("-----引用传递-----")
a = [1,2,3]
print("a的值为:",a)
demo(a)
print("实参值为:",a)

运行结果:

-------值传递-----
a的值为: C语言中文网
形参值为: C语言中文网C语言中文网
实参值为: C语言中文网
-----引用传递-----
a的值为: [1, 2, 3]
形参值为: [1, 2, 3, 1, 2, 3]
实参值为: [1, 2, 3, 1, 2, 3]

在执行值传递时,改变形式参数的值,实际参数并不会发生改变;而在进行引用传递时,改变形式参数的值,实际参数也会发生同样的改变

Python位置参数

位置参数,有时也称必备参数,指的是必须按照正确的顺序将实际参数传到函数中,换句话说,调用函数时传入实际参数的数量和位置都必须和定义函数时保持一致

实参和形参数量必须一致

在调用函数,指定的实际参数的数量,必须和形式参数的数量一致(传多传少都不行),否则 Python 解释器会抛出 TypeError 异常,并提示缺少必要的位置参数

def girth(width , height):
    return 2 * (width + height)
#调用函数时,必须传递 2 个参数,否则会引发错误
print(girth(3))

运行结果:

Traceback (most recent call last):
  File "C:\Users\mengma\Desktop\1.py", line 4, in <module>
    print(girth(3))
TypeError: girth() missing 1 required positional argument: 'height'
def girth(width , height):
    return 2 * (width + height)
#调用函数时,必须传递 2 个参数,否则会引发错误
print(girth(3,2,4))

运行结果:

Traceback (most recent call last):
  File "C:\Users\mengma\Desktop\1.py", line 4, in <module>
    print(girth(3,2,4))
TypeError: girth() takes 2 positional arguments but 3 were given

实参和形参位置必须一致

在调用函数时,传入实际参数的位置必须和形式参数位置一一对应,否则会产生以下 2 种结果:

  • 抛出 TypeError 异常

当实际参数类型和形式参数类型不一致,并且在函数种,这两种类型之间不能正常转换,此时就会抛出 TypeError 异常

def area(height,width):
    return height*width/2
print(area("C语言中文网",3))

运行结果:

Traceback (most recent call last):
  File "C:\Users\mengma\Desktop\1.py", line 3, in <module>
    print(area("C语言中文网",3))
  File "C:\Users\mengma\Desktop\1.py", line 2, in area
    return height*width/2
TypeError: unsupported operand type(s) for /: 'str' and 'int'

以上显示的异常信息,就是因为字符串类型和整形数值做除法运算

  • 产生的结果和预期不符

调用函数时,如果指定的实际参数和形式参数的位置不一致,但它们的数据类型相同,那么程序将不会抛出异常,只不过导致运行结果和预期不符

def area(upper_base,lower_bottom,height):
    return (upper_base+lower_bottom)*height/2
print("正确结果为:",area(4,3,5))
print("错误结果为:",area(4,5,3))

运行结果:

正确结果为: 17.5
错误结果为: 13.5

Python函数关键字参数及用法

关键字参数是指使用形式参数的名字来确定输入的参数值。通过此方式指定函数实参时,不再需要与形参的位置完全一致,只要将参数名写正确即可

Python 函数的参数名应该具有更好的语义,这样程序可以立刻明确传入函数的每个参数的含义

def dis_str(str1,str2):
    print("str1:",str1)
    print("str2:",str2)
#位置参数
dis_str("http://c.biancheng.net/python/","http://c.biancheng.net/shell/")
#关键字参数
dis_str("http://c.biancheng.net/python/",str2="http://c.biancheng.net/shell/")
dis_str(str2="http://c.biancheng.net/python/",str1="http://c.biancheng.net/shell/")

 运行结果:

str1: http://c.biancheng.net/python/
str2: http://c.biancheng.net/shell/
str1: http://c.biancheng.net/python/
str2: http://c.biancheng.net/shell/
str1: http://c.biancheng.net/shell/
str2: http://c.biancheng.net/python/

在调用有参函数时,既可以根据位置参数来调用,也可以使用关键字参数来调用。在使用关键字参数调用时,可以任意调换参数传参的位置

还可以使用位置参数和关键字参数混合传参的方式。但需要注意,混合传参时关键字参数必须位于所有的位置参数之后

# 位置参数必须放在关键字参数之前,下面代码错误
dis_str(str1="http://c.biancheng.net/python/","http://c.biancheng.net/shell/")

运行结果:

SyntaxError: positional argument follows keyword argument

Python函数默认参数设置

在调用函数时如果不指定某个参数,Python 解释器会抛出异常。为了解决这个问题,Python 允许为参数设置默认值,即在定义函数时,直接给形式参数指定一个默认值。这样的话,即便调用函数时没有给拥有默认值的形参传递参数,该参数可以直接使用定义函数时设置的默认值

def 函数名(...,形参名,形参名=默认值):
    代码块

注意,在使用此格式定义函数时,指定有默认值的形式参数必须在所有没默认值参数的最后,否则会产生语法错误

#str1没有默认参数,str2有默认参数
def dis_str(str1,str2 = "http://c.biancheng.net/python/"):
    print("str1:",str1)
    print("str2:",str2)
dis_str("http://c.biancheng.net/shell/")
dis_str("http://c.biancheng.net/java/","http://c.biancheng.net/golang/")

运行结果:

str1: http://c.biancheng.net/shell/
str2: http://c.biancheng.net/python/
str1: http://c.biancheng.net/java/
str2: http://c.biancheng.net/golang/

Pyhton 中,可以使用“函数名.__defaults__”查看函数的默认值参数的当前值,其返回值是一个元组

print(dis_str.__defaults__)

运行结果:

('http://c.biancheng.net/python/',)

Python None(空值)及用法

在 Python 中,有一个特殊的常量 None(N 必须大写)。和 False 不同,它不表示 0,也不表示空字符串,而表示没有值,也就是空值

这里的空值并不代表空对象,即 None 和 []、“” 不同

>>> None is []
False
>>> None is ""
False

None 有自己的数据类型,可以在 IDLE 中使用 type() 函数查看它的类型

>>> type(None)
<class 'NoneType'>

需要注意的是,None 是 NoneType 数据类型的唯一值(其他编程语言可能称这个值为 null、nil 或 undefined),也就是说,不能再创建其它 NoneType 类型的变量,但是可以将 None 赋值给任何变量。如果希望变量中存储的东西不与任何其它值混淆,就可以使用 None

除此之外,None 常用于 assert、判断以及函数无返回值的情况。举个例子,使用 print() 函数输出数据,其实该函数的返回值就是 None。因为它的功能是在屏幕上显示文本,根本不需要返回任何值,所以 print() 就返回 None

>>> spam = print('Hello!')
Hello!
>>> None == spam
True

对于所有没有 return 语句的函数定义,Python 都会在末尾加上 return None,使用不带值的 return 语句(也就是只有 return 关键字本身),那么就返回 None

Python return函数返回值

Python中,用 def 语句创建函数时,可以用 return 语句指定应该返回的值,该返回值可以是任意类型。需要注意的是,return 语句在同一函数中可以出现多次,但只要有一个得到执行,就会直接结束函数的执行

return [返回值]

返回值参数可以指定,也可以省略不写(将返回空值 None)

def add(a,b):
    c = a + b
    return c
#函数赋值给变量
c = add(3,4)
print(c)
#函数返回值作为其他函数的实际参数
print(add(3,4))

运行结果:

7
7

通过 return 语句指定返回值后,在调用函数时,既可以将该函数赋值给一个变量,用变量保存函数的返回值,也可以将函数再作为某个函数的实际参数

def isGreater0(x):
    if x > 0:
        return True
    else:
        return False
print(isGreater0(5))
print(isGreater0(0))

运行结果:

True
False

函数中可以同时包含多个 return 语句,但需要注意的是,最终真正执行的做多只有 1 个,且一旦执行,函数运行会立即结束

Python变量作用域(全局变量和局部变量)

所谓作用域(Scope),就是变量的有效范围,就是变量可以在哪个范围以内使用。有些变量可以在整段代码的任意位置使用,有些变量只能在函数内部使用,有些变量只能在 for 循环内部使用

变量的作用域由变量的定义位置决定,在不同位置定义的变量,它的作用域是不一样的

Python局部变量

在函数内部定义的变量,它的作用域也仅限于函数内部,出了函数就不能使用了,将这样的变量称为局部变量(Local Variable)

当函数被执行时,Python 会为其分配一块临时的存储空间,所有在函数内部定义的变量,都会存储在这块空间中。而在函数执行完毕后,这块临时存储空间随即会被释放并回收,该空间中存储的变量自然也就无法再被使用

def demo():
    add = "http://c.biancheng.net/python/"
    print("函数内部 add =",add)
demo()
print("函数外部 add =",add)

运行结果:

函数内部 add = http://c.biancheng.net/python/
Traceback (most recent call last):
  File "C:\Users\mengma\Desktop\file.py", line 6, in <module>
    print("函数外部 add =",add)
NameError: name 'add' is not defined

如果试图在函数外部访问其内部定义的变量,Python 解释器会报 NameError 错误,并提示没有定义要访问的变量

函数的参数也属于局部变量,只能在函数内部使用

def demo(name,add):
    print("函数内部 name =",name)
    print("函数内部 add =",add)
demo("Python教程","http://c.biancheng.net/python/")
print("函数外部 name =",name)
print("函数外部 add =",add)

运行结果:

函数内部 name = Python教程
函数内部 add = http://c.biancheng.net/python/
Traceback (most recent call last):
  File "C:\Users\mengma\Desktop\file.py", line 7, in <module>
    print("函数外部 name =",name)
NameError: name 'name' is not defined

由于 Python 解释器是逐行运行程序代码,由此这里仅提示“name 没有定义”,实际上在函数外部访问 add 变量也会报同样的错误

Python全局变量 

除了在函数内部定义变量,Python 还允许在所有函数的外部定义变量,这样的变量称为全局变量(Global Variable)

和局部变量不同,全局变量的默认作用域是整个程序,即全局变量既可以在各个函数的外部使用,也可以在各函数内部使用

定义全局变量的方式有以下 2 种:

  • 在函数体外定义的变量,一定是全局变量
add = "http://c.biancheng.net/shell/"
def text():
    print("函数体内访问:",add)
text()
print('函数体外访问:',add)

运行结果:

函数体内访问: http://c.biancheng.net/shell/
函数体外访问: http://c.biancheng.net/shell/
  • 在函数体内定义全局变量。即使用 global 关键字对变量进行修饰后,该变量就会变为全局变量
def text():
    global add
    add= "http://c.biancheng.net/java/"
    print("函数体内访问:",add)
text()
print('函数体外访问:',add)

运行结果:

函数体内访问: http://c.biancheng.net/java/
函数体外访问: http://c.biancheng.net/java/

注意,在使用 global 关键字修饰变量名时,不能直接给变量赋初值,否则会引发语法错误

获取指定作用域范围中的变量

1) globals()函数

globals() 函数为 Python 的内置函数,它可以返回一个包含全局范围内所有变量的字典,该字典中的每个键值对,键为变量名,值为该变量的值

#全局变量
Pyname = "Python教程"
Pyadd = "http://c.biancheng.net/python/"
def text():
    #局部变量
    Shename = "shell教程"
    Sheadd= "http://c.biancheng.net/shell/"
print(globals())

运行结果:

{ ...... , 'Pyname': 'Python教程', 'Pyadd': 'http://c.biancheng.net/python/', ......}

注意,globals() 函数返回的字典中,会默认包含有很多变量,这些都是 Python 主程序内置的

通过调用 globals() 函数,可以得到一个包含所有全局变量的字典。并且,通过该字典,还可以访问指定变量,甚至如果需要,还可以修改它的值

print(globals()['Pyname'])
globals()['Pyname'] = "Python入门教程"
print(Pyname)

运行结果:

Python教程
Python入门教程

2) locals()函数

locals() 函数也是 Python 内置函数之一,通过调用该函数,可以得到一个包含当前作用域内所有变量的字典。这里所谓的“当前作用域”指的是,在函数内部调用 locals() 函数,会获得包含所有局部变量的字典;而在全局范文内调用 locals() 函数,其功能和 globals() 函数相同

#全局变量
Pyname = "Python教程"
Pyadd = "http://c.biancheng.net/python/"
def text():
    #局部变量
    Shename = "shell教程"
    Sheadd= "http://c.biancheng.net/shell/"
    print("函数内部的 locals:")
    print(locals())
text()
print("函数外部的 locals:")
print(locals())

运行结果:

函数内部的 locals:
{'Sheadd': 'http://c.biancheng.net/shell/', 'Shename': 'shell教程'}
函数外部的 locals:
{...... , 'Pyname': 'Python教程', 'Pyadd': 'http://c.biancheng.net/python/', ...... }

当使用 locals() 函数获取所有全局变量时,和 globals() 函数一样,其返回的字典中会默认包含有很多变量,这些都是 Python 主程序内置的

当使用 locals() 函数获得所有局部变量组成的字典时,可以向 globals() 函数那样,通过指定键访问对应的变量值,但无法对变量值做修改

#全局变量
Pyname = "Python教程"
Pyadd = "http://c.biancheng.net/python/"
def text():
    #局部变量
    Shename = "shell教程"
    Sheadd= "http://c.biancheng.net/shell/"
    print(locals()['Shename'])
    locals()['Shename'] = "shell入门教程"
    print(Shename)
text()

运行结果:

shell教程
shell教程

3) vars(object)

vars() 函数也是 Python 内置函数,其功能是返回一个指定 object 对象范围内所有变量组成的字典。如果不传入object 参数,vars() 和 locals() 的作用完全相同

 #全局变量
Pyname = "Python教程"
Pyadd = "http://c.biancheng.net/python/"
class Demo:
    name = "Python 教程"
    add = "http://c.biancheng.net/python/"
print("有 object:")
print(vars(Demo))
print("无 object:")
print(vars())

运行结果:

有 object:
{...... , 'name': 'Python 教程', 'add': 'http://c.biancheng.net/python/', ......}
无 object:
{...... , 'Pyname': 'Python教程', 'Pyadd': 'http://c.biancheng.net/python/', ...... }

Python局部函数及用法(包含nonlocal关键字)

Python 支持在函数内部定义函数,此类函数又称为局部函数

和局部变量一样,默认情况下局部函数只能在其所在函数的作用域内使用

#全局函数
def outdef ():
    #局部函数
    def indef():
        print("http://c.biancheng.net/python/")
    #调用局部函数
    indef()
#调用全局函数
outdef()

运行结果:

http://c.biancheng.net/python/

如同全局函数返回其局部变量,就可以扩大该变量的作用域一样,通过将局部函数作为所在函数的返回值,也可以扩大局部函数的使用范围

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

运行结果:

调用局部函数

如果所在函数没有返回局部函数,则局部函数的可用范围仅限于所在函数内部;反之,如果所在函数将局部函数作为返回值,则局部函数的作用域就会扩大,既可以在所在函数内部使用,也可以在所在函数的作用域中使用

如果局部函数中定义有和所在函数中变量同名的变量,也会发生“遮蔽”的问题

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

运行结果:

UnboundLocalError: local variable 'name' referenced before assignment

此错误直译过来的意思是“局部变量 name 还没定义就使用”。导致该错误的原因就在于,局部函数 indef() 中定义的 name 变量遮蔽了所在函数 outdef() 中定义的 name 变量。再加上,indef() 函数中 name 变量的定义位于 print() 输出语句之后,导致 print(name) 语句在执行时找不到定义的 name 变量,因此程序报错

可以使用 Python 提供的 nonlocal 关键字

#全局函数
def outdef ():
    name = "所在函数中定义的 name 变量"
    #局部函数
    def indef():
        nonlocal name
        print(name)
        #修改name变量的值
        name = "局部函数中定义的 name 变量"
    indef()
#调用全局函数
outdef()

运行结果:

所在函数中定义的 name 变量

Python闭包

闭包,又称闭包函数或者闭合函数,其实和嵌套函数类似,不同之处在于,闭包中外部函数返回的不是一个具体的值,而是一个函数。一般情况下,返回的函数会赋值给一个变量,这个变量可以在后面被继续执行调用

#闭包函数,其中 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 的立方

运行结果:

4
8

外部函数 nth_power() 的返回值是函数 exponent_of(),而不是一个具体的数值

在执行完 square = nth_power(2) 和 cube = nth_power(3) 后,外部函数 nth_power() 的参数 exponent 会和内部函数 exponent_of 一起赋值给 squre 和 cube,这样在之后调用 square(2) 或者 cube(2) 时,程序就能顺利地输出结果,而不会报错说参数 exponent 没有定义

使用闭包,可以让程序变得更简洁易读

和缩减嵌套函数的优点类似,函数开头需要做一些额外工作,当需要多次调用该函数时,如果将那些额外工作的代码放在外部函数,就可以减少多次调用导致的不必要开销,提高程序的运行效率

Python闭包的__closure__属性

闭包比普通的函数多了一个 __closure__ 属性,该属性记录着自由变量的地址。当闭包被调用时,系统就会根据该地址找到对应的自由变量,完成整体的函数调用

def nth_power(exponent):
    def exponent_of(base):
        return base ** exponent
    return exponent_of
square = nth_power(2)
#查看 __closure__ 的值
print(square.__closure__)

运行结果:

(<cell at 0x0000014454DFA948: int object at 0x00000000513CC6D0>,)

显示的内容是一个 int 整数类型,这就是 square 中自由变量 exponent 的初始值。还可以看到,__closure__ 属性的类型是一个元组,这表明闭包可以支持多个自由变量的形式

Python lambda表达式(匿名函数)及用法

lambda 表达式,又称匿名函数,常用来表示内部仅包含 1 行表达式的函数。如果一个函数的函数体仅有 1 行表达式,则该函数就可以用 lambda 表达式来代替

name = lambda [list] : 表达式

定义 lambda 表达式,必须使用 lambda 关键字;[list] 作为可选参数,等同于定义函数是指定的参数列表;value 为该表达式的名称

该语法格式转换成普通函数的形式,如下所示

def name(list):
    return 表达式
name(list)
def add(x, y):
    return x+ y
print(add(3,4))

运行结果:

7

该函数可以直接用 lambda 表达式表示

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

运行结果:

7

lambda 表达式,其就是简单函数(函数体仅是单行的表达式)的简写版本

  • 对于单行函数,使用 lambda 表达式可以省去定义函数的过程,让代码更加简洁
  • 对于不需要多次复用的函数,使用 lambda 表达式可以在用完之后立即释放,提高程序执行的性能

Python eval()和exec()函数

eval() 和 exec() 函数都属于 Python 的内置函数

eval() 和 exec() 函数的功能是相似的,都可以执行一个字符串形式的 Python 代码(代码以字符串的形式提供),相当于一个 Python 的解释器。二者不同之处在于,eval() 执行完要返回结果,而 exec() 执行完不返回结果

eval()和exec()的用法

eval(expression, globals=None, locals=None, /)
exec(expression, globals=None, locals=None, /)
  • 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

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 多了一个

 运行结果:

dict_keys(['b'])
dict_keys(['b', '__builtins__', 'a'])        

exec() 之前 dic 中的 key 只有一个 b。执行完 exec() 之后,系统在 dic 中生成了两个新的 key,分别是 a 和 __builtins__。其中,a 为执行语句生成的变量,系统将其放到指定的作用域字典里;__builtins__ 是系统加入的内置 key

a=10
b=20
c=30
g={'a':6, 'b':8} #定义一个字典
t={'b':100, 'c':10} #定义一个字典
print(eval('a+b+c', g, t)) #定义一个字典 116

运行结果:

116

exec()和eval()的区别

eval() 执行完会返回结果,而 exec() 执行完不返回结果

a = 1
exec("a = 2") #相当于直接执行 a=2
print(a)
a = exec("2+3") #相当于直接执行 2+3,但是并没有返回值,a 应为 None
print(a)
a = eval('2+3') #执行 2+3,并把结果返回给 a
print(a)

运行结果:

2
None
5

exec() 中适合放置运行后没有结果的语句,而 eval() 中适合放置有结果返回的语句

如果 eval() 里放置一个没有结果返回的语句

a= eval("a = 2")

这时 Python 解释器会报 SyntaxError 错误,提示 eval() 中不识别等号语法

eval() 和 exec() 函数的应用场景

TensorFlow 框架中的静态图就是类似这个原理实现的:

  • TensorFlow 中先将张量定义在一个静态图里,这就相当将键值对添加到字典里一样
  • TensorFlow 中通过 session 和张量的 eval() 函数来进行具体值的运算,就当于使用 eval() 函数进行具体值的运算一样

在使用 eval() 或是 exec() 来处理请求代码时,函数 eval() 和 exec() 常常会被黑客利用,成为可以执行系统级命令的入口点,进而来攻击网站。解决方法是:通过设置其命名空间里的可执行函数,来限制 eval() 和 exec() 的执行范围

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值