【Python学习教程】Python函数和lambda表达式_6,2024年五面蚂蚁

先自我介绍一下,小编浙江大学毕业,去过华为、字节跳动等大厂,目前阿里P7

深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Python全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
img
img



既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上Python知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

如果你需要这些资料,可以添加V获取:vip1024c (备注Python)
img

正文

正确结果为: 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/

可以看到,在调用有参函数时,既可以根据位置参数来调用,也可以使用关键字参数(程序中第 8 行)来调用。在使用关键字参数调用时,可以任意调换参数传参的位置。

当然,还可以像第 7 行代码这样,使用位置参数和关键字参数混合传参的方式。但需要注意,混合传参时关键字参数必须位于所有的位置参数之后。也就是说,如下代码是错误的:

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

Python 解释器会报如下错误:

SyntaxError: positional argument follows keyword argument

Python函数默认参数设置(超级详细)

我们知道,在调用函数时如果不指定某个参数,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/

上面程序中,dis_str() 函数有 2 个参数,其中第 2 个设有默认参数。这意味着,在调用 dis_str() 函数时,我们可以仅传入 1 个参数,此时该参数会传给 str1 参数,而 str2 会使用默认的参数,如程序中第 6 行代码所示。

当然在调用 dis_str() 函数时,也可以给所有的参数传值(如第 7 行代码所示),这时即便 str2 有默认值,它也会优先使用传递给它的新值。

同时,结合关键字参数,以下 3 种调用 dis_str() 函数的方式也是可以的:

dis_str(str1 = "http://c.biancheng.net/shell/")
dis_str("http://c.biancheng.net/java/",str2 = "http://c.biancheng.net/golang/")
dis_str(str1 = "http://c.biancheng.net/java/",str2 = "http://c.biancheng.net/golang/")

再次强调,当定义一个有默认值参数的函数时,有默认值的参数必须位于所有没默认值参数的后面。因此,下面例子中定义的函数是不正确的:

#语法错误
def dis\_str(str1="http://c.biancheng.net/python/",str2,str3):
    pass

显然,str1 设有默认值,而 str2 和 str3 没有默认值,因此 str1 必须位于 str2 和 str3 之后。

有读者可能会问,对于自己自定义的函数,可以轻易知道哪个参数有默认值,但如果使用 Python 提供的内置函数,又或者其它第三方提供的函数,怎么知道哪些参数有默认值呢?

Pyhton 中,可以使用“函数名.defaults”查看函数的默认值参数的当前值,其返回值是一个元组。以本节中的 dis_str() 函数为例,在其基础上,执行如下代码:

print(dis_str.__defaults__)

程序执行结果为:

(‘http://c.biancheng.net/python/’,)

Python函数可变参数(*args,**kwargs)详解

Python 在定义函数时也可以使用可变参数,即允许定义参数个数可变的函数。这样当调用该函数时,可以向其传入任意多个参数。

可变参数,又称不定长参数,即传入函数中的实际参数可以是任意多个。Python 定义可变参数,主要有以下 2 种形式。

1) 可变参数:形参前添加一个 ‘*’

此种形式的语法格式如下所示:

*args

args 表示创建一个名为 args 的空元组,该元组可接受任意多个外界传入的非关键字实参。

下面程序演示了如何定义一个参数可变的函数:

# 定义了支持参数收集的函数
def dis\_str(home, \*str) :
    print(home)
    # 输出str元组中的元素
    print("str=",str)
    for s in str :
        print(s)
#可传入任何多个参数
dis_str("http://c.biancheng.net","http://c.biancheng.net/python/","http://c.biancheng.net/shell/")

程序执行结果为:

http://c.biancheng.net
str= (‘http://c.biancheng.net/python/’, ‘http://c.biancheng.net/shell/’)
http://c.biancheng.net/python/
http://c.biancheng.net/shell/

上面程序中,dis_str() 函数的最后一个参数就是 str 元组,这样在调用该函数时,除了前面位置参数接收对应位置的实参外,其它非关键字参数都会由 str 元组接收。

当然,可变参数并不一定必须为最后一个函数参数,例如修改 dis_str() 函数为:

# 定义了支持参数收集的函数
def dis\_str(\*str,home) :
    print(home)
    # 输出str元组中的元素
    print("str=",str)
    for s in str :
        print(s)
dis_str("http://c.biancheng.net","http://c.biancheng.net/python/",home="http://c.biancheng.net/shell/")

可以看到,str 可变参数作为 dis_str() 函数的第一个参数。但需要注意的是,在调用该函数时,必须以关键字参数的形式给普通参数传值,否则 Python 解释器会把所有参数都优先传给可变参数,如果普通参数没有默认值,就会报错。

也就是说,下面代码调用上面的 dia_str() 函数,是不对的:

dis_str("http://c.biancheng.net","http://c.biancheng.net/python/","http://c.biancheng.net/shell/")

Python 解释器会提供如下报错信息:

TypeError: dis_str() missing 1 required keyword-only argument: ‘home’

翻译过来就是我们没有给 home 参数传值。当然,如果 home 参数有默认参数,则此调用方式是可行的。

2) 可变参数:形参前添加两个’*’

这种形式的语法格式如下:

**kwargs

**kwargs 表示创建一个名为 kwargs 的空字典,该字典可以接收任意多个以关键字参数赋值的实际参数。

例如如下代码:

# 定义了支持参数收集的函数
def dis\_str(home,\*str,\*\*course) :
    print(home)
    print(str)
    print(course)
#调用函数
dis_str("C语言中文网",\
        "http://c.biancheng.net",\
        "http://c.biancheng.net/python/",\
        shell教程="http://c.biancheng.net/shell/",\
        go教程="http://c.biancheng.net/golang/",\
        java教程="http://c.biancheng.net/java/")

程序输出结果为:

C语言中文网
(‘http://c.biancheng.net’, ‘http://c.biancheng.net/python/’)
{‘shell教程’: ‘http://c.biancheng.net/shell/’, ‘go教程’: ‘http://c.biancheng.net/golang/’, ‘java教程’: ‘http://c.biancheng.net/java/’}

上面程序在调用 dis_str() 函数时,第 1 个参数传递给 home 参数,第 2、3 个非关键字参数传递给 str 元组,最后 2 个关键字参数将由 course 字典接收。

注意,*args 可变参数的值默认是空元组,**kwargs 可变参数的值默认是空字典。因此,在调用具有可变参数的函数时,不一定非要给它们传值。以调用 dis_str(home, *str, **course) 为例,下面的调用方式也是正确的:

dis_str(home="http://c.biancheng.net/shell/")

程序执行结果为:

http://c.biancheng.net/shell/
()
{}

Python逆向参数收集详解(进阶必读)

前面章节中介绍了,Python 支持定义具有可变参数的函数,即该函数可以接收任意多个参数,其中非关键字参数会集中存储到元组参数(*args)中,而关键字参数则集中存储到字典参数(**kwargs)中,这个过程可称为参数收集。

不仅如此,Python 还支持逆向参数收集,即直接将列表、元组、字典作为函数参数,Python 会将其进行拆分,把其中存储的元素按照次序分给函数中的各个形参。

在以逆向参数收集的方式向函数参数传值时,Pyhon 语法规定,当传入列表或元组时,其名称前要带一个 * 号,当传入字典时,其名称前要带有 2 个 * 号。

举个例子:

def dis\_str(name,add) :
    print("name:",name)
    print("add",add)
data = ["Python教程","http://c.biancheng.net/python/"]
#使用逆向参数收集方式传值
dis_str(\*data)

程序执行结果为:

name: Python教程
add http://c.biancheng.net/python/

再举个例子:

def dis\_str(name,add) :
    print("name:",name)
    print("add:",add)
data = {'name':"Python教程",'add':"http://c.biancheng.net/python/"}
#使用逆向参数收集方式传值
dis_str(\*\*data)

程序执行结果为:

name: Python教程
add: http://c.biancheng.net/python/

此外,以逆向参数收集的方式,还可以给拥有可变参数的函数传参,例如:

def dis\_str(name,\*add) :
    print("name:",name)
    print("add:",add)
data = ["http://c.biancheng.net/python/",\
        "http://c.biancheng.net/shell/",\
        "http://c.biancheng.net/golang/"]
#使用逆向参数收集方式传值
dis_str("Python教程",\*data)

程序执行结果为:

name: Python教程
add: (‘http://c.biancheng.net/python/’, ‘http://c.biancheng.net/shell/’, ‘http://c.biancheng.net/golang/’)

上面程序中,也同样可以用逆向参数收集的方式给 name 参数传值,只需要将 “python教程” 放到 data 列表中第一个位置即可。也就是说,上面程序中,以下面代码调用 dis_str() 函数的方式也是可行的:

data = ["Python教程",\
        "http://c.biancheng.net/python/",\
        "http://c.biancheng.net/shell/",\
        "http://c.biancheng.net/golang/"]
#使用逆向参数收集方式传值
dis_str(\*data)

执行此程序,会发现其输出结果和上面一致。

再次强调,如果使用逆向参数收集的方式,必须注意 * 号的添加。以逆向收集列表为例,如果传参时其列表名前不带 * 号,则 Python 解释器会将整个列表作为参数传递给一个参数。例如:

def dis\_str(name,\*add) :
    print("name:",name)
    print("add:",add)
data = ["Python教程",\
        "http://c.biancheng.net/python/",\
        "http://c.biancheng.net/shell/",\
        "http://c.biancheng.net/golang/"]
dis_str(data)

程序执行结果为:

name: [‘Python教程’, ‘http://c.biancheng.net/python/’, ‘http://c.biancheng.net/shell/’, ‘http://c.biancheng.net/golang/’]
add: ()

Python None(空值)及用法

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

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

None is []
False
None is “”
False

None 有自己的数据类型,我们可以在 IDLE 中使用 type() 函数查看它的类型,执行代码如下:

type(None)
<class ‘NoneType’>

可以看到,它属于 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 语句的语法格式如下:

return [返回值]

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

【例 1】

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

运行结果为:

7
7

本例中,add() 函数既可以用来计算两个数的和,也可以连接两个字符串,它会返回计算的结果。

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

【例 2】

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

运行结果为:

True
False

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

以上实例中,我们通过 return 语句,都仅返回了一个值,但其实通过 return 语句,可以返回多个值,读者可以阅读《Python函数返回多个值》一节做详细了解。

Python函数返回多个值的方法(入门必读)

通常情况下,一个函数只有一个返回值,实际上 Python 也是如此,只不过 Python 函数能以返回列表或者元组的方式,将要返回的多个值保存到序列中,从而间接实现返回多个值的目的。

因此,实现 Python 函数返回多个值,有以下 2 种方式:

  1. 在函数中,提前将要返回的多个值存储到一个列表或元组中,然后函数返回该列表或元组;
  2. 函数直接返回多个值,之间用逗号( , )分隔,Python 会自动将多个值封装到一个元组中,其返回值仍是一个元组。

下面程序演示了以上 2 种实现方法:

def retu\_list() :
    add = ["http://c.biancheng.net/python/",\
            "http://c.biancheng.net/shell/",\
            "http://c.biancheng.net/golang/"]
    return add
def retu\_tuple() :
    return "http://c.biancheng.net/python/",\
           "http://c.biancheng.net/golang/",\
           "http://c.biancheng.net/golang/"
print("retu\_list = ",retu_list())
print("retu\_tuple = ",retu_tuple())

程序执行结果为:

retu_list = [‘http://c.biancheng.net/python/’, ‘http://c.biancheng.net/shell/’, ‘http://c.biancheng.net/golang/’]
retu_tuple = (‘http://c.biancheng.net/python/’, ‘http://c.biancheng.net/golang/’, ‘http://c.biancheng.net/golang/’)

在此基础上,我们可以利用 Python 提供的序列解包功能,之间使用对应数量的变量,直接接收函数返回列表或元组中的多个值。这里以 retu_list() 为例:

def retu\_list() :
    add = ["http://c.biancheng.net/python/",\
            "http://c.biancheng.net/shell/",\
            "http://c.biancheng.net/golang/"]
    return add
pythonadd,shelladd,golangadd = retu_list()
print("pythonadd=",pythonadd)
print("shelladd=",shelladd)
print("golangadd=",golangadd)

程序执行结果为:

pythonadd= http://c.biancheng.net/python/
shelladd= http://c.biancheng.net/shell/
golangadd= http://c.biancheng.net/golang/

Python函数递归(带实例演示)

一个函数在它的函数体内调用它自身称为递归调用,这种函数称为递归函数。执行递归函数将反复调用其自身,每调用一次就进入新的一层,当最内层的函数执行完毕后,再一层一层地由里到外退出。

递归函数不是 Python 语言的专利,C/C++、Java、C#、JavaScript、PHP 等其他编程语言也都支持递归函数。

下面我们通过一个实例,看看递归函数到底是如何运作的。

有这样一个数学题。己知有一个数列:f(0) = 1,f(1) = 4,f(n + 2) = 2*f(n+ 1) +f(n),其中 n 是大于 0 的整数,求 f(10) 的值。这道题可以使用递归来求得。下面程序将定义一个 fn() 函数,用于计算 f(10) 的值。

def fn(n) :
    if n == 0 :
        return 1
    elif n == 1 :
        return 4
    else :
        # 函数中调用它自身,就是函数递归
        return 2 \* fn(n - 1) + fn(n - 2)
# 输出fn(10)的结果
print("fn(10)的结果是:", fn(10))

在上面的 fn() 函数体中再次调用了 fn() 函数,这就是函数递归。注意在 fn() 函数体中调用 fn 的形式:

return 2 * fn(n - 1) + fn(n - 2)

对于 fn(10),即等于 2fn(9)+fn(8),其中 fn(9) 又等于 2fn(8)+fn(7)……依此类推,最终会计算到 fn(2) 等于 2*fn(1)+fn(0),即 fn(2) 是可计算的,这样递归带来的隐式循环就有结束的时候,然后一路反算回去,最后就可以得到 fn(10) 的值。

仔细看上面递归的过程,当一个函数不断地调用它自身时,必须在某个时刻函数的返回值是确定的,即不再调用它自身:否则,这种递归就变成了无穷递归,类似于死循环。因此,在定义递归函数时有一条最重要的规定: 递归一定要向已知方向进行。

例如,如果把上面数学题改为如此。己知有一个数列:f(20)=1,f(21)=4,f(n + 2)=2*f(n+1)+f(n),其中 n 是大于 0 的整数,求 f(10) 的值。那么 f(10) 的函数体应该改为如下形式:

def fn(n) :
    if n == 20 :
        return 1
    elif n == 21 :
        return 4
    else :
        # 函数中调用它自身,就是函数递归
        return fn(n + 2) - 2\*fn(n + 1)

从上面的 fn() 函数来看,当程序要计算 fn(10) 的值时,fn(10) 等于 fn(12)-2fn(11),而 fn(11) 等于 fn(13)-2fn(12)……依此类推,直到 fn(19) 等于 fn(21)-2fn(20),此时就可以得到 fn(19) 的值,然后依次反算到 fn(10) 的值。这就是递归的重要规则:对于求 fn(10) 而言,如果 fn(0) 和 fn(1) 是已知的,则应该采用 fn(n)=2fn(n-1)+fn(n-2) 的形式递归,因为小的一端已知;如果 fn(20) 和 fn(21) 是已知的,则应该采用 fn(n)=fn(n+2)-2*fn(n+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
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
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 关键字修饰变量名时,不能直接给变量赋初值,否则会引发语法错误。

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

在一些特定场景中,我们可能需要获取某个作用域内(全局范围内或者局部范围内)所有的变量,Python 提供了以下 3 种方式:

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教程

显然,locals() 返回的局部变量组成的字典,可以用来访问变量,但无法修改变量的值。

3) vars(object)

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

由于目前读者还未学习 Python 类和对象,因此初学者可先跳过该函数的学习,等学完 Python 类和对象之后,再回过头来学习该函数。

举个例子:

 #全局变量
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如何在函数中使用同名的全局变量?

Python局部变量和全局变量》一节中提到,全局变量可以在程序中任何位置被访问甚至修改,但是,当函数中定义了和全局变量同名的局部变量时,那么在当前函数中,无论是访问还是修改该同名变量,操作的都是局部变量,而不再是全局变量。

当函数内部的局部变量和函数外部的全局变量同名时,在函数内部,局部变量会“遮蔽”同名的全局变量。

有读者可能并不能完全理解上面这段话,没关系,这里举个实例:

name = "Python教程"
def demo ():
    #访问全局变量
    print(name)
demo()

程序执行结果为:

Python教程

上面程序中,第 4 行直接访问 name 变量,这是允许的。在上面程序的基础上,在函数内部添加一行代码,如下所示:

name = "Python教程"
def demo ():
    #访问全局变量
    print(name)
    name = "shell教程"
demo()

执行此程序,Python 解释器报如下错误:

UnboundLocalError: local variable ‘name’ referenced before assignment

该错误直译过来的意思是:所访问的 name 变量还未定义。这是什么原因呢?就是我们添加第 5 行代码导致的。

Python 语法规定,在函数内部对不存在的变量赋值时,默认就是重新定义新的局部变量。上面程序中,第 5 行就定义了一个新的 name 局部变量,由于该局部变量名和全局变量名 name 同名,局部 name 变量就会“遮蔽”全局 name 变量,再加上局部变量 name 在 print(name) 后才被初始化,违反了“先定义后使用”的原则,因此程序会报错。

那么,如果就是想在函数中访问甚至修改被“遮蔽”的变量,怎么办呢?可以采取以下 2 中方法:

  1. 直接访问被遮蔽的全局变量。如果希望程序依然能访问 name 全局变量,且在函数中可重新定义 name 局部变量,也就是在函数中可以访问被遮蔽的全局变量,此时可通过 globals() 函数来实现,将上面程序改为如下形式即可:
name = "Python教程"
def demo ():
    #通过 globals() 函数访问甚至修改全局变量
    print(globals()['name'])
    globals()['name']="Java教程"
    #定义局部变量
    name = "shell教程"
demo()
print(name)

程序执行结果为:

Python教程
Java教程
2. 在函数中声明全局变量。为了避免在函数中对全局变量赋值(不是重新定义局部变量),可使用 global 语句来声明全局变量。因此,可将程序改为如下形式:

name = "Python教程"
def demo ():
    global name
    #访问全局name变量
    print(name)
    #修改全局name变量的值
    name = "shell教程"
demo()
print(name)

程序执行结果为:

Python教程
shell教程

增加了“global name”声明之后,程序会把 name 变量当成全局变量,这意味着 demo() 函数后面对 name 赋值的语句只是对全局变量赋值,而不是重新定义局部变量。

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

通过前面的学习我们知道,Python 函数内部可以定义变量,这样就产生了局部变量,有读者可能会问,Python 函数内部能定义函数吗?答案是肯定的。Python 支持在函数内部定义函数,此类函数又称为局部函数。

那么,局部函数有哪些特征,在使用时需要注意什么呢?接下来就给读者详细介绍 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()

程序执行结果为:

调用局部函数

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

以上面程序中的 outdef() 和 indef() 为例,如果 outdef() 不将 indef 作为返回值,则 indef() 只能在 outdef() 函数内部使用;反之,则 indef() 函数既可以在 outdef() 函数内部使用,也可以在 outdef() 函数的作用域,也就是全局范围内使用。

有关函数返回函数,更详细的讲解,可阅读《Python函数高级方法》一节。

另外值得一提的是,如果局部函数中定义有和所在函数中变量同名的变量,也会发生“遮蔽”的问题。例如:

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

执行此程序,Python 解释器会报如下错误:

UnboundLocalError: local variable ‘name’ referenced before assignment

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

由于这里的 name 变量也是局部变量,因此前面章节讲解的 globals() 函数或者 globals 关键字,并不适用于解决此问题。这里可以使用 Python 提供的 nonlocal 关键字。

例如,修改上面程序为:

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

程序执行结果为:

所在函数中定义的 name 变量

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

对于定义一个简单的函数,Python 还提供了另外一种方法,即使用本节介绍的 lambda 表达式。

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

lambda 表达式的语法格式如下:

name = lambda [list] : 表达式

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

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

def name(list):
    return 表达式
name(list)

显然,使用普通方法定义此函数,需要 3 行代码,而使用 lambda 表达式仅需 1 行。

举个例子,如果设计一个求 2 个数之和的函数,使用普通函数的方式,定义如下:

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

程序执行结果为:

7

由于上面程序中,add() 函数内部仅有 1 行表达式,因此该函数可以直接用 lambda 表达式表示:

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

程序输出结果为:

7

可以这样理解 lambda 表达式,其就是简单函数(函数体仅是单行的表达式)的简写版本。相比函数,lamba 表达式具有以下 2 个优势:

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

Python函数式编程(map()、filter()和reduce())详解

所谓函数式编程,是指代码中每一块都是不可变的,都由纯函数的形式组成。这里的纯函数,是指函数本身相互独立、互不影响,对于相同的输入,总会有相同的输出。

除此之外,函数式编程还具有一个特点,即允许把函数本身作为参数传入另一个函数,还允许返回一个函数。

例如,想让列表中的元素值都变为原来的两倍,可以使用如下函数实现:

def multiply\_2(list):
    for index in range(0, len(list)):
        list[index] \*= 2
    return list

需要注意的是,这段代码不是一个纯函数的形式,因为列表中元素的值被改变了,如果多次调用 multiply_2() 函数,那么每次得到的结果都不一样。

而要想让 multiply_2() 成为一个纯函数的形式,就得重新创建一个新的列表并返回,也就是写成下面这种形式:

def multiply\_2\_pure(list):
    new_list = []
    for item in list:
        new_list.append(item \* 2)
    return new_list

函数式编程的优点,主要在于其纯函数和不可变的特性使程序更加健壮,易于调试和测试;缺点主要在于限制多,难写。

注意,纯粹的函数式编程语言(比如 Scala),其编写的函数中是没有变量的,因此可以保证,只要输入是确定的,输出就是确定的;而允许使用变量的程序设计语言,由于函数内部的变量状态不确定,同样的输入,可能得到不同的输出。

Python 允许使用变量,所以它并不是一门纯函数式编程语言。Python 仅对函数式编程提供了部分支持,主要包括 map()、filter() 和 reduce() 这 3 个函数,它们通常都结合 lambda 匿名函数一起使用。接下来就对这 3 个函数的用法做逐一介绍。

Python map()函数

map() 函数的基本语法格式如下:

map(function, iterable)

其中,function 参数表示要传入一个函数,其可以是内置函数、自定义函数或者 lambda 匿名函数;iterable 表示一个或多个可迭代对象,可以是列表、字符串等。

map() 函数的功能是对可迭代对象中的每个元素,都调用指定的函数,并返回一个 map 对象。

注意,该函数返回的是一个 map 对象,不能直接输出,可以通过 for 循环或者 list() 函数来显示。

【例 1】还是对列表中的每个元素乘以 2。

listDemo = [1, 2, 3, 4, 5]
new_list = map(lambda x: x \* 2, listDemo)
print(list(new_list))

运行结果为:

[2, 4, 6, 8, 10]

【例 2】map() 函数可传入多个可迭代对象作为参数。

listDemo1 = [1, 2, 3, 4, 5]
listDemo2 = [3, 4, 5, 6, 7]
new_list = map(lambda x,y: x + y, listDemo1,listDemo2)
print(list(new_list))

运行结果为:

[4, 6, 8, 10, 12]

注意,由于 map() 函数是直接由用 C 语言写的,运行时不需要通过 Python 解释器间接调用,并且内部做了诸多优化,所以相比其他方法,此方法的运行效率最高。

Python filter()函数

filter()函数的基本语法格式如下:

filter(function, iterable)

此格式中,funcition 参数表示要传入一个函数,iterable 表示一个可迭代对象。

filter() 函数的功能是对 iterable 中的每个元素,都使用 function 函数判断,并返回 True 或者 False,最后将返回 True 的元素组成一个新的可遍历的集合。

【例 3】返回一个列表中的所有偶数。

listDemo = [1, 2, 3, 4, 5]
new_list = filter(lambda x: x % 2 == 0, listDemo)
print(list(new_list))

运行结果为:

[2, 4]

最后

🍅 硬核资料:关注即可领取PPT模板、简历模板、行业经典书籍PDF。
🍅 技术互助:技术群大佬指点迷津,你的问题可能不是问题,求资源在群里喊一声。
🍅 面试题库:由技术群里的小伙伴们共同投稿,热乎的大厂面试真题,持续更新中。
🍅 知识体系:含编程语言、算法、大数据生态圈组件(Mysql、Hive、Spark、Flink)、数据仓库、Python、前端等等。

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip1024c (备注python)
img

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
是一个 map 对象,不能直接输出,可以通过 for 循环或者 list() 函数来显示。

【例 1】还是对列表中的每个元素乘以 2。

listDemo = [1, 2, 3, 4, 5]
new_list = map(lambda x: x \* 2, listDemo)
print(list(new_list))

运行结果为:

[2, 4, 6, 8, 10]

【例 2】map() 函数可传入多个可迭代对象作为参数。

listDemo1 = [1, 2, 3, 4, 5]
listDemo2 = [3, 4, 5, 6, 7]
new_list = map(lambda x,y: x + y, listDemo1,listDemo2)
print(list(new_list))

运行结果为:

[4, 6, 8, 10, 12]

注意,由于 map() 函数是直接由用 C 语言写的,运行时不需要通过 Python 解释器间接调用,并且内部做了诸多优化,所以相比其他方法,此方法的运行效率最高。

Python filter()函数

filter()函数的基本语法格式如下:

filter(function, iterable)

此格式中,funcition 参数表示要传入一个函数,iterable 表示一个可迭代对象。

filter() 函数的功能是对 iterable 中的每个元素,都使用 function 函数判断,并返回 True 或者 False,最后将返回 True 的元素组成一个新的可遍历的集合。

【例 3】返回一个列表中的所有偶数。

listDemo = [1, 2, 3, 4, 5]
new_list = filter(lambda x: x % 2 == 0, listDemo)
print(list(new_list))

运行结果为:

[2, 4]

最后

🍅 硬核资料:关注即可领取PPT模板、简历模板、行业经典书籍PDF。
🍅 技术互助:技术群大佬指点迷津,你的问题可能不是问题,求资源在群里喊一声。
🍅 面试题库:由技术群里的小伙伴们共同投稿,热乎的大厂面试真题,持续更新中。
🍅 知识体系:含编程语言、算法、大数据生态圈组件(Mysql、Hive、Spark、Flink)、数据仓库、Python、前端等等。

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip1024c (备注python)
[外链图片转存中…(img-s2gaKg4t-1713428154380)]

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值