我是靠谱客的博主 优美故事,最近开发中收集的这篇文章主要介绍python返回上一条语句_Python 函数之二(返回值、作用域、LEGB、销毁),觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

一、函数返回值

先看几个例子

# return 语句之后可以执行么?

def showplus(x):

print(x)

return x + 1

print('~~end~~')

showplus(5)

# 多条 return 语句都会执行么?

def showplus(x):

print(x)

return x + 1

return x + 2

showplus(5)

# 下例多个 return 可执行么?

def guess(x):

if x > 3:

return "> 3"

else:

return "<= 3"

print(guess(10))

# 下面函数执行的结果是什么

def fn(x):

for i in range(x):

if i > 3:

return i

else:

print("{} is not greater than 3".format(x))

print(fn(5)) # 打印 4

print(fn(3)) # 打印 None

总结

Python 函数使用 return 语句返回 “返回值”

所有函数都有返回值,若没有 return 语句,隐式调用 return None

return 语句并不一定是函数的语句块的最后一条语句

一个函数可存在多个 return 语句,但只有一条可被执行,若没有一条 returm 语句被执行,隐式调用 return None

若有必要,可显示调用 return None,可简写为 return

若函数执行了 return 语句,函数就会返回,当前被执行的 return 语句之后的其他语句就不会被执行了

返回值的作用:结束函数调用、返回 “返回值”

能够一次返回多个值么?

def showvalues():

return 1, 3, 5

showvalues()

函数不能同时返回多个值

return 1, 3, 5 看似返回多个值,隐式的被 python 封装成了一个 元组

x, y, z = showlist() 使用解构提取返回值更为方便

二、函数 作用域

2.1 作用域

一个标识符的可见范围,这就是标识符的作用域,一般常说的是变量的作用域

def foo():

x = 100

print(x) # 可以访问到么?

上例中 x 不可被访问到,会抛出异常 NameError: name 'y' is not defined,原因在于函数是一个封装,它会开辟一个 作用域,x 变量被限制在这个作用域中,所以在函数外部 x 变量 不可见

注意:每一个函数都会开辟一个作用域

2.2 作用域分类

全局作用域

在整个程序运行环境中都可见

全局作用域中的变量称为全局变量

局部作用域

在函数、类等内部可见

局部作用域中的变量称为局部变量,其使用范围不能超过其所在局部作用域

# 局部变量

def fn1():

x = 1 # 局部作用域,x 为局部变量,使用范围在 fn1 内

def fn2():

print(x)

print(x)

# 全局变量

x = 5 # 全局变量,也在函数外定义

def foo():

print(x)

foo()

一般来讲外部作用域变量在函数内部可见,可以使用

反过来,函数内部的局部变量,不能在函数外部看到

2.3 函数嵌套

在一个函数中定义了另一个函数

def outer():

def inner():

print("inner")

print("outer")

inner()

outer() # 可行么?

inner() # 可行么?

内部函数 inner 不能在外部直接使用,会抛 NameError 异常,因为它在函数外部不可见

其实, inner 不过就是一个标识符,就是一个函数 outer 内部定义的变量而已

嵌套结构作用域

对比下面嵌套结构,代码执行的效果

def outer1():

o = 65

def inner():

print("inner {}".format(o))

print(chr(o))

inner()

print("outer {}".format(o))

outer1()

def outer2():

o = 65

def inner():

o = 97

print("inner {}".format(o))

print(chr(o))

inner()

print("outer {}".format(o))

outer2()

从执行的结果来看

外层变量在内部作用域可见

内层作用域 inner 中,若定义了 o = 97,相当于在当前函数 inner 作用域中 重新定义了一个新的变量 o,但 这个 o 并不能覆盖外部作用域 outer2 中的变量 o,只不过对于 inner 函数来说,其只能可见自己的作用域中定义的变量 o 了

2.4 一个复制语句的问题

再看下例

f977bde2a4b1

示例.png

仔细观察函数 2 返回的错误指向 x += 1,原因是什么呢?

x = 5

def foo():

x += 1

foo() # 报错如下

f977bde2a4b1

示例.png

原因分析

x += 1 其实是 x = x + 1

相当于在 foo 内部定义了一个局部变量 x,那么 foo 内部所有 x 都是这个局部变量 x 了

x = x + 1 相当于使用了局部变量 x,但是这个 x 还没有完成赋值,就被右边拿来做加 1 操作了

如何解决这个常见问题?

2.5 global 语句

x = 5

def foo():

global x # 全局变量

x += 1

print(x)

foo()

使用 global 关键字的变量,将 foo 内的 x 声明为使用 外部的全局作用域 中定义的 x

全局作用域中必须有 x 的定义

若全局作用域中没有 x 定义会怎样?

注意,下面实验若在 ipython、jupyter 中做,上下文运行环境中有可能有 x 的定义,稍微不注意,就测试不出效果

# 有错么?

def foo():

global x

x += 1

print(x)

foo()

# 有错么?

def foo():

global x

x = 10

x += 1

print(x)

foo()

print(x) # 可以么?

使用 global 关键字定义的变量,虽然在 foo 函数中声明,但是这将告诉当前 foo 函数作用域,这个 x 变量将使用外部全局作用域中的 x

即使实在 foo 中又写了 x=10,也不会在 foo 这个局部作用域中定义局部变量 x 了

使用了 global,foo 中的 x 不再是局部变量了,它是全局变量

总结

x+=1 这种是特殊形式产生的错误的原因?先应用后赋值,而 Python 动态语言是赋值才算定义,才能被引用。解决办法,在这条语句前增加 x=0 之类的赋值语句,或使用 global 告诉内部作用域,去全局作用域查找变量定义

内部作用域使用 x = 10 之类的赋值语句会重新定义局部作用域使用的变量 x,但是,一旦这个作用域宠使用 global 声明 x 为全局的,那么 x=5 相当于在为全局作用域的变量 x 赋值

global 使用原则

外部作用域变量会在内部作用域可见,但也不要在这个内部的局部作用域中直接使用,因为函数的目的就是为了封装,尽量与外界隔离

若函数需要使用外部全局变量,请尽量使用函数的形参定义,并在调用传实参解决

一句话:不用 global。学些它就是为了深入理解变量作用域

2.6 闭包

自由变量:未在本地作用域中定义的变量,例如定义在内层函数外的外层函数的作用域中的变量

闭包:就是一个概念,出现在嵌套函数中,至的是 内层函数引用到了外层函数的自由变量,就形成了闭包。

def counter():

c = [0]

def inc():

c[0] += 1

return c[0]

return inc

foo = counter()

print(foo(), foo())

c = 100

print(foo())

上例代码分析

第七行会执行 counter() 并 返回 inc 对应的函数对象,注意这个函数对象并不释放,因为有 foo 记住

第四行会报错么?

不会报错,因为修改的是 counter() 的本地变量 c 中 index 为 0 的元素的值,而不是重新定义 c 变量

第八行打印什么结果?

打印 1 2

第十行打印什么结果?

打印 3

第九行的 c 和 counter 中的 c 不一样,而 inc() 引用的是自由变量 counter() 中的变量 c

这是 Python 2 中实现闭包的方式,Python 3 还可使用 nonlocal 关键字

def counter():

count = 0

def inc():

count += 1

return count

return inc

foo = counter()

print(foo(), foo())

上例一定出错,使用 global 可解决

def counter():

global count

count = 0

def inc():

global count

count += 1

return count

return inc

foo = counter()

print(foo(), foo())

上例使用 global 解决,这是全局变量的实现,而不是闭包了

若对这个普通变量使用闭包,Python 3 中可使用 nonlocal 关键字

2.7 nonlocal 语句

nonlocal:将变量标记为不在本地作用域定义,而是在 上级的某一级局部作用域 中定义,但 不能是全局作用域 中定义

def counter():

count = 0

def inc():

nonlocal count # 声明变量 count 不是本地变量

count += 1

return count

return inc

foo = counter()

print(foo(), foo())

count 是外层函数的局部变量,被内部函数引用

内部函数使用 nonlocal 关键字声明 count 变量在上级作用域而非本地作用域中定义

代码中内层函数应用外部局部作用域中的自由变量,形成闭包

f977bde2a4b1

示例.png

上例是错误的,nonlocal 声明变量 a 不在当前作用域,但是往外就是全局作用域了,所以报错

三、默认值的作用域

f977bde2a4b1

示例.png

f977bde2a4b1

示例.png

为何第二次调用 foo() 打印的值 [1, 1]

因为函数也是对象,每个函数定义被执行后,就生成一个函数对象和函数名这个标识符关联

Python 把函数的默认值放在了函数对象的属性中,这个属性就伴随着这个函数对象的整个生命周期

查看 foo.__defaults__ 属性为元祖

def foo(xyz=[], m=123, n='abc'):

xyz.append(1)

print(xyz)

print(id(foo), foo.__defaults__)

foo()

print(id(foo), foo.__defaults__)

foo()

print(id(foo), foo.__defaults__)

函数地址并没有变,就是说 foo() 这个函数对象没有变过,调用它,他的属性 __defaults__ 中使用元祖保存默认值 xyz 的默认值为引用类型,引用类型的元素变动,并不是元祖的变化

# 非引用类型缺省值

def foo(xyz, m=123, n='abc'):

m = 456

n = 'def'

print(xyz)

print(foo.__defaults__)

foo('rookie')

print(foo.__defaults__)

属性 __defaults__ 中使用元祖保存所有位置参数默认值,不会因为在函数体内改变了局部变量(形参)的值而发生改变

# keyword-only 参数的缺省值

def foo(xyz, m=123, *, n='abc', t=[1, 2]):

m = 456

n = 'def'

t.append(300)

print(xyz, m, n, t)

print(foo.__defaults__, foo.__kwdefaults__)

foo('rookie')

print(foo.__defaults__, foo.__kwdefaults__)

属性 __defaults__ 中使用元组保存所有位置参数默认值

属性 __kwdefaults__ 中使用字典保存所有 keyword-only 参数的默认值

def x(a=[]):

a += [5]

print(x.__defaults__)

x()

x()

print(x.__defaults__)

def y(a=[]):

a = a = [5]

print(y.__defaults__)

y()

y()

print(y.__defaults__)

输出结果不一致

+ 表示两个列表合并并返回一个新列表

+= 表示就地修改一个列表,在其后追加一个列表。就是 extend 方法

四、变量名解析原则 LEGB

Local,本地作用域、局部作用域的 local 命名空间。函数调用时创建,调用结束消亡

Enclosing,Python 2.2 时引入了嵌套函数,实现了闭包,这个就是嵌套函数的外部函数的命名空间

Global,全局作用域,即一个模块的命名空间。模块被 import 时创建,解释器退出时消亡

Build-in,内置模块的命名空间,生命周期从 Python 解释器启动时创建到解释器退出时消亡。例如 print(open),print 和 open 都是内置的变量

所以一个名词的查找顺序就是 LEGB

f977bde2a4b1

示例.png

五、函数的销毁

定义一个函数就是生成一个函数对象,函数名指向的就是函数对象

可使用 del 语句删除函数,使其引用计数减一

可使用同名标识符覆盖原有定义,本质上也是使其引用计数减一

Python 程序结束时,所有对象销毁

函数也是对象,也不例外,是否销毁,还是看引用计数是否减为 0

最后

以上就是优美故事为你收集整理的python返回上一条语句_Python 函数之二(返回值、作用域、LEGB、销毁)的全部内容,希望文章能够帮你解决python返回上一条语句_Python 函数之二(返回值、作用域、LEGB、销毁)所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(37)

评论列表共有 0 条评论

立即
投稿
返回
顶部