Python入门篇-返回值和作用域

                                      作者:尹正杰

版权声明:原创作品,谢绝转载!否则将追究法律责任。

一.返回值

1>.返回值概述

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

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

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

一个函数可以存在多个return语句,但是只有一条可以被执行。如果没有一条return语句被执行到,隐式调用return None

如果有必要,可以显示调用return None,可以简写为return

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

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

2>.函数单个值

 #!/usr/bin/env python
#_*_coding:utf-8_*_
#@author :yinzhengjie
#blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/
#EMAIL:y1053419035@qq.com def show(x):
for i in range(x):
if i > 3:
return i
else:
print("{} is not greater than 3".format(i)) res1 = show(3) print("*" * 20 + "我是分隔符" + "*" * 20) res2 = show(5) print("res1 = {}".format(res1))
print("res2 = {}".format(res2)) #以上代码执行结果如下:
0 is not greater than 3
1 is not greater than 3
2 is not greater than 3
********************我是分隔符********************
0 is not greater than 3
1 is not greater than 3
2 is not greater than 3
3 is not greater than 3
res1 = None
res2 = 4

3>.返回多个值

#!/usr/bin/env python
#_*_coding:utf-8_*_
#@author :yinzhengjie
#blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/
#EMAIL:y1053419035@qq.com """
函数不能同时返回多个值,但是可用返回一个列表或者一个元组
""" """
是指明返回一个列表,是一个列表对象
"""
def returnList():
return [1,3,5] """
看似返回多个值,隐式的被python封装成了一个元组
"""
def returnTuple():
return [2,4,6] x,y,z = returnList() #使用解构提取更为方便 m,n,l = returnTuple() print(x,y,z,m,n,l) #以上代码执行结果如下:
1 3 5 2 4 6

二.作用域

1>.作用域概述

作用域:
一个标识符的可见范围,这就是标识符的作用域。一般常说的是变量的作用域 全局作用域
在整个程序运行环境中都可见 局部作用域
在函数、类等内部可见
局部变量使用范围不能超过其所在的局部作用域

2>.嵌套结构观察作用域变化

 #!/usr/bin/env python
#_*_coding:utf-8_*_
#@author :yinzhengjie
#blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/
#EMAIL:y1053419035@qq.com """
外层变量作用域在内层作用域可见
"""
def outer1():
o = 65
def inner():
print("inner {}".format(o))
print(chr(o))
print("outer {}".format(o))
inner() """
内层作用域inner中,如果定义了o=97,相当于当前作用域中重新定义了一个新的变量o,但是这个o并没有覆盖外层作用域outer中的o
"""
def outer2(): #
o = 65
def inner():
o = 97
print("inner {}".format(o))
print(chr(o))
print("outer {}".format(o))
inner() outer1() print("*" * 20 + "我是分隔符" + "*" * 20) outer2() #以上代码执行结果如下:
outer 65
inner 65
A
********************我是分隔符********************
outer 65
inner 97
a

3>.赋值定义注意事项

#!/usr/bin/env python
#_*_coding:utf-8_*_
#@author :yinzhengjie
#blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/
#EMAIL:y1053419035@qq.com x = 10 #全局作用域变量x def foo(): """
“y = x + 1”这一行代码引用了全局作用域的x变量。
"""
y = x + 10 """
这一行代码会抛异常:“UnboundLocalError: local variable 'x' referenced before assignment”。
原因就是赋值定义导致的,“x += 1”其实是“x = x + 1”,相当于在foo内部定义一个局部变量x,
那么foo内部所有x都是这个局部变量x了,但是这个x还没有完成赋值,就被右边拿来做加1操作了,因此才会抛出该异常
简单的说,就是在定义x变量之前我们已经对该变量做了引用导致的报错。如何解决这个问题?我们可以引入全局变量global.
"""
# x += 1 print(x,y) foo() #以上代码输出结果如下:
10 20

4>.在局部变量修改全局变量

#!/usr/bin/env python
#_*_coding:utf-8_*_
#@author :yinzhengjie
#blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/
#EMAIL:y1053419035@qq.com x = 5 """
使用global关键字的变量,将foo内的x声明为使用外部的全局作用域中定义的x
全局作用域中必须有x的定义,如果全局作用域中没有定义x变量,则会报错:“NameError: name 'x' is not defined”
"""
def foo():
global x
print("foo() : x = {}".format(x))
x += 10
print("foo() : x = {}".format(x)) print("调用foo()函数之前:x = {}".format(x)) foo() print("调用foo()函数之后:x = {}".format(x)) #以上代码输出结果如下:
调用foo()函数之前:x = 5
foo() : x = 5
foo() : x = 15
调用foo()函数之后:x = 15

5>. 在局部变量中声明全局变量

 #!/usr/bin/env python
#_*_coding:utf-8_*_
#@author :yinzhengjie
#blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/
#EMAIL:y1053419035@qq.com def foo():
global x #使用global关键字的变量,将foo内的x声明为使用外部的全局作用域中定义的x
x = 10 #x = 10 赋值即定义,在内部作用域为一个外部作用域的变量x赋值,不是在内部作用域定义一个新变量,所以x+=1不会报错。注意,这里x的作用域还是全局的
x += 20
print(x) foo() print(x) #注意,这个打印x变量一定要在先调用foo()函数,因为只有调用了foo()函数才会触发生成全局变量x,否则会抛异常:"NameError: name 'x' is not defined" #以上代码输出结果如下:
30
30

6>.global总结

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

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

7>.global使用原则

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

如果函数需要使用外部全局变量,请使用函数的形参传参解决

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

三.闭包 

1>.闭包概述

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

闭包:就是一个概念,出现在嵌套函数中,指的是内层函数引用到了外层函数的自由变量,就形成了闭包。很多语言都有这个概念,最熟悉就是JavaScript

2>.闭包函数案例(这是Python2中实现闭包的方式,Python3还可以使用nonlocal关键字

 #!/usr/bin/env python
#_*_coding:utf-8_*_
#@author :yinzhengjie
#blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/
#EMAIL:y1053419035@qq.com def counter():
c = [0]
def inc():
c[0] += 10 #注意,这行代码c[0] = c[0] + 10,这样操作并不会报错,因为并没有修改变量c,而是修改的c中的一个引用。
return c[0]
return inc #返回的是c变量的一个引用,即c[0] foo = counter() print(foo(),foo()) #每调用次都会对c[0] 对象加10,因此每次调用的结果不会发生改变 c = 100 #此处我们定了一个变量c,但对c[0]并不会产生印象哟,因此下面的操作依旧会对c[0]加10. print(foo()) #以上代码执行结果如下:
10 20
30

3>.nonlocal关键字

 #!/usr/bin/env python
#_*_coding:utf-8_*_
#@author :yinzhengjie
#blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/
#EMAIL:y1053419035@qq.com """
使用了nonlocal关键字,将变量标记为不在本地作用域定义,而在上级的某一级局部作用域中定义,但不能是全局作用域中定义
count 是外层函数的局部变量,被内部函数引用
内部函数使用nonlocal关键字声明count变量在上级作用域而非本地作用域中定义
""" def counter():
count = 0
def inc():
nonlocal count
count += 10
return count
return inc foo = counter() print(foo())
print(foo()) #以上代码执行结果如下:
10
20

四.默认值的作用域

1>.默认值的作用域

 #!/usr/bin/env python
#_*_coding:utf-8_*_
#@author :yinzhengjie
#blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/
#EMAIL:y1053419035@qq.com def foo(x=1):
return x def bar(y=[]):
y.append(100)
return y print("foo():{}, ID:{}\n".format(foo(),id(foo())))
print("foo():{}, ID:{}\n".format(foo(),id(foo()))) # print(x) #报错:NameError: name 'x' is not defined,即形参的作用域数据函数的局部变量 print("bar():{}, ID:{}\n".format(bar(),id(bar())))
print("bar():{}, ID:{}\n".format(bar(),id(bar()))) """
为什么第二次调用foo函数打印的是[100,100]?
因为函数也是对象,python把函数的默认值放在了属性中,这个属性就伴随着这个函数对象的整个生命周期
查看bar.__defaults__属性
"""
print("bar.__defaults__:{}".format(bar.__defaults__)) #以上代码执行结果如下:
foo():1, ID:1638886432 foo():1, ID:1638886432 bar():[100, 100], ID:42947656 bar():[100, 100, 100, 100], ID:42947656 bar.__defaults__:([100, 100, 100, 100],)

2>.非引用类型例子

 #!/usr/bin/env python
#_*_coding:utf-8_*_
#@author :yinzhengjie
#blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/
#EMAIL:y1053419035@qq.com def foo(w, u='yzj', *, z=123, zz=[456]):
u = 'jason'
z = 2019
zz.append(1)
print(w, u, z, zz) print(foo.__defaults__) #属性__defaults__中使用元组保存所有位置参数默认值,它不会因为在函数体内使用了它而发生改变 foo('node101.yinzhengjie.org.cn') print(foo.__kwdefaults__) #属性__kwdefaults__中使用字典保存所有keyword-only参数的默认值 #以上代码执行结果如下:
('yzj',)
node101.yinzhengjie.org.cn jason 2019 [456, 1]
{'z': 123, 'zz': [456, 1]}

3>.使用影子拷贝创建一个新的对象,永远不能改变传入的参数

 #!/usr/bin/env python
#_*_coding:utf-8_*_
#@author :yinzhengjie
#blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/
#EMAIL:y1053419035@qq.com """
使用影子拷贝创建一个新的对象,永远不能改变传入的参数
"""
def foo(x=[], y='jason', z=123):
x = x[:] # 影子拷贝
x.append(1) # x都是传入参数或者默认参数的副本,如果就想修改原参数,无能为力
print(x) foo()
print(foo.__defaults__)
foo()
print(foo.__defaults__)
foo([10])
print(foo.__defaults__)
foo([10,5])
print(foo.__defaults__) #以上代码执行结果如下:
[1]
([], 'jason', 123)
[1]
([], 'jason', 123)
[10, 1]
([], 'jason', 123)
[10, 5, 1]
([], 'jason', 123)

4>.使用不可变类型默认值

#!/usr/bin/env python
#_*_coding:utf-8_*_
#@author :yinzhengjie
#blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/
#EMAIL:y1053419035@qq.com """
通过值的判断就可以灵活的选择创建或者修改传入对象
这种方式灵活,应用广泛
很多函数的定义,都可以看到使用None这个不可变的值作为默认参数,可以说这是一种惯用法
"""
def foo(x=None, y='jason', z=2019):
if x is None:
x = []
x.append(1)
print(x) foo()
print(foo.__defaults__)
foo()
print(foo.__defaults__)
foo([10])
print(foo.__defaults__)
foo([10,5])
print(foo.__defaults__) #以上代码执行结果如下:
[1]
(None, 'jason', 2019)
[1]
(None, 'jason', 2019)
[10, 1]
(None, 'jason', 2019)
[10, 5, 1]
(None, 'jason', 2019)
#!/usr/bin/env python
#_*_conding:utf-8_*_
#@author :yinzhengjie
#blog:http://www.cnblogs.com/yinzhengjie def foo(x=1,y="jason",z={},*args,m=100,n,**kwargs):
print(x,y,z) #打印位置参数
print(m,n) #打印keyword-only参数
print(z.setdefault("Name","yinzhengjie")) #我们对z变量赋值 print("查看位置参数 : ",foo.__defaults__) print("查看keyword only参数 : ",foo.__kwdefaults__) foo(z={},n=200) #需要注意的是,这里我们给z传入了一个{}参数,并没有使用默认的"{}"参数哟!这一点大家不要搞混了 print("查看位置参数 : ",foo.__defaults__) print("查看keyword only参数 : ",foo.__kwdefaults__) foo(n=200) print("查看位置参数 : ",foo.__defaults__) #不难发现,由于我们没有给z传入变量,因此z使用了默认的"{}",而“foo.__defaults__”是不变的,但地址的引用对象里面的元素却发生了变化! print("查看keyword only参数 : ",foo.__kwdefaults__) #以上代码输出结果如下:
查看位置参数 : (1, 'jason', {})
查看keyword only参数 : {'m': 100}
1 jason {}
100 200
yinzhengjie
查看位置参数 : (1, 'jason', {})
查看keyword only参数 : {'m': 100}
1 jason {}
100 200
yinzhengjie
查看位置参数 : (1, 'jason', {'Name': 'yinzhengjie'})
查看keyword only参数 : {'m': 100}

新手必看案例(烧脑版)

五.变量名解析原则LEGB

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

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

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

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

所以一个名词的查找顺序就是LEGB 关于函数的执行流程,博主推荐链接:http://pythontutor.com/visualize.html#mode=edit

六.函数的销毁

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

  可以使用del语句删除函数,使其引用计数减1。

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

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

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

1>.全局函数销毁

 #!/usr/bin/env python
#_*_coding:utf-8_*_
#@author :yinzhengjie
#blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/
#EMAIL:y1053419035@qq.com """
全局函数销毁
重新定义同名函数
del 语句删除函数对象
程序结束时
""" def foo(x=[], y='jason', z=123):
x.append(100)
return x print(foo(), id(foo), foo.__defaults__) def foo(x=[], y='jason', z=123):
x.append(200)
return x print(foo(), id(foo), foo.__defaults__) del foo # print(foo(), id(foo), foo.__defaults__) #由于我们已经使用del关键字销毁了foo函数,执行该行代码会抛异常:"NameError: name 'foo' is not defined" #以上代码执行结果如下:
[100] 4136608 ([100], 'jason', 123)
[200] 35462136 ([200], 'jason', 123)

2>.局部函数销毁

 #!/usr/bin/env python
#_*_coding:utf-8_*_
#@author :yinzhengjie
#blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/
#EMAIL:y1053419035@qq.com """
局部函数销毁
重新在上级作用域定义同名函数
del 语句删除函数名称,函数对象的引用计数减1
上级作用域销毁时
"""
def foo(x=[], u='jason', z=2019):
x.append(1)
def inner(a=10):
pass
print(inner)
def inner(a=100):
print(x)
print(inner)
return inner bar = foo()
print(id(foo),id(bar), foo.__defaults__, bar.__defaults__)
del bar
# print(id(foo),id(bar), foo.__defaults__, bar.__defaults__) #由于我们已经使用del关键字销毁了bar函数,执行该行代码会抛异常:"NameError: name 'bar' is not defined" #以上代码执行结果如下:
<function foo.<locals>.inner at 0x0000000001EB1BF8>
<function foo.<locals>.inner at 0x0000000001EB1C80>
4923040 32185472 ([1], 'jason', 2019) (100,)

Python入门篇-返回值和作用域的更多相关文章

  1. Python函数的返回值和作用域

    函数的返回值和作用域 1.返回值 def guess(x):    if x > 3:        return "> 3"    else:        retu ...

  2. Python函数之返回值、作用域和局部变量

    一.函数返回值 说到返回值,相信大家肯定都认识,没错,就是return. 所谓返回值可以这样理解:函数外部的代码要想获取函数的执行结果,就可以在函数里用return语句把结果返回. 那具体怎么用呢?接 ...

  3. Python入门篇-文件操作

    Python入门篇-文件操作 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.文件IO常用操作 open:打开 read:读取 write:写入 close:关闭 readlin ...

  4. Python入门篇-高阶函数

    Python入门篇-高阶函数 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.高级函数  1>.First Class Object 函数在Python中是一等公民 函数也 ...

  5. Python入门篇-面向对象概述

    Python入门篇-面向对象概述 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.语言的分类 面向机器 抽象成机器指令,机器容易理解 代表:汇编语言 面向过程 做一件事情,排出个 ...

  6. Python入门篇-functools

    Python入门篇-functools 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.reduce方法 reduce方法,顾名思义就是减少 reduce(function,se ...

  7. Python入门篇-类型注解

    Python入门篇-类型注解 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.函数定义的弊端 1>.动态语言很灵活,但是这种特性也是弊端 Python是动态语言,变量随时可 ...

  8. Python入门篇-装饰器

    Python入门篇-装饰器 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.装饰器概述 装饰器(无参) 它是一个函数 函数作为它的形参 返回值也是一个函数 可以使用@functi ...

  9. Python入门篇-生成器函数

    Python入门篇-生成器函数 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.生成器概述 1>.生成器generator 生成器指的是生成器对象,可以由生成器表达式得到, ...

随机推荐

  1. Why Python's Integer Division Floors ---- python int(6/-132)时答案不一致,向下取整

    leetcode150题中有一个步骤: int(6/-132) == 0 or ==-1? 在自己本地python3环境跑是int(6/-132) =0,但是提交的时候确实-1. 查找相关资料解惑: ...

  2. 从原生Android 跳转到hbuilder项目

    原文地址:https://blog.csdn.net/pentablet/article/details/80277157 前段时间做项目,需要把别人做的hbuilder项目,添加到自己的app中,找 ...

  3. 快速修改Windows系统密码命令

    因现场需要,要对30多台虚拟机进行密码修改.正常修改方式为进入控制面板--用户账户--修改密码,输入原始密码.2遍新密码(一遍用于密码确认)完成密码修改. 这种方式操作较为繁琐,我们可以直接通过命令的 ...

  4. Kubernetes 使用 Weave Scope 监控集群(十七)

    目录 一.安装 二.使用 Scope 2.1.拓扑结构 2.2.在线操作 2.3.强大的搜索功能 创建 Kubernetes 集群并部署容器化应用只是第一步.一旦集群运行起来,我们需要确保一起正常,所 ...

  5. GraphQL&DSL&API网关

    车联网服务non-RESTful架构改造实践   导读 在构建面向企业项目.多端的内容聚合类在线服务API设计的过程中,由于其定制特点,采用常规的restful开发模式,通常会导致大量雷同API重复开 ...

  6. 【记录】【mysql】的REPLACE()用法

    操作前数据 操作 UPDATE `test_replace` SET PASSWORD ') WHERE id REPLACE(PASSWORD, '1', '77')意思就是password中的1替 ...

  7. [转帖]Beyond compare4密钥

    Beyond compare4密钥 https://blog.csdn.net/lemontree1945/article/details/92963423 学习一下 最近想破解水卡.... w4G- ...

  8. 题解 CF53E 【Dead Ends】

    题意: 给一个n(n<=10)个节点的无向图,图里面有m条边,以这m条边构建生成树,求所有生成树中只含有k个度数为1的点的方案数. 题解: 看见这个数量级就一定会想到状态压缩dp... 那让我们 ...

  9. day47——css介绍、语法结构、选择器、css权重

    day47 今日内容 css介绍 CSS(Cascading Style Sheet,层叠样式表)定义如何显示HTML元素,给HTML设置样式,让它更加美观. 语法结构 div{ color:gree ...

  10. java之mybatis之使用mybatis实现crud操作

    目录结构: 1.封装 mybatis 的工具类: MybatisUtil.java public class MybatisUtil { private static SqlSessionFactor ...