函数的返回值和作用域

1、返回值

def guess(x):
    if x > 3:
        return "> 3"
    else:
        return "<= 3"
print(guess(10))

  1> Python 函数使用 return 语句返回 "返回值”
  2> 所有函数都有返回值,如果没有 return 语句,隐式调用 return None
  3> return 语句并不一定是函数的语句块的最后一条语句
  4> 一个函数可以存在多个 return 语句,但是只有一条可以被执行。如果没有一条 return 语句被执行到,隐式调用 return None
  5> 如果有必要,可以显示调用return None,可以简写为return
  6> 如果函数执行了 return 语句,函数就会返回,当前被执行的 return 语句之后的其它语句就不会被执行了
  7> 返回值的作用:结束函数调用、返回 "返回值”

2、能一次返回多个值嘛?

def showvalues():
return 1, 3, 5
print(showvalues()) # 返回 (1, 3, 5)

  函数不能同时返回多个值
  return 1, 3, 5 看似返回多个值,隐式的被 python 封装成了一个元组
  x, y, z = showvalues() 使用解构提取返回值更为方便

3、函数作用域**

3.1 作用域

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

x = 20
def fn():
x = 100 # x 的作用域:当前函数
fn()
print(x) # x = 20

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

3.2 作用域分类

  全局作用域:
    在整个程序运行环境中都可见
    全局作用域中的变量称为全局变量

  局部作用域:
    在函数、类等内部可见
    局部作用域中的变量称为局部变量,其使用范围不超过其所在局部作用域

# 局部变量
def fn1():
x = 1 # 局部作用域,x 为局部变量,使用范围在 fn1 内 def fn2():
print(x) # x 能打印吗?不能 print(x) # x 能打印吗?不能
# 全局变量
x = 5 # 全局变量,也在函数外定义
def foo():
print(x) # 可见吗?可以
foo()

  一般来讲外部作用域变量在函数内部可见,可以使用
  反过来,函数内部的局部变量,不能在函数外部看到

4、函数嵌套

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

def outer():
def inner():
print('inner')
print('outer')
inner()
outer()
inner() # 不可以

  内部函数 inner 不能在外部直接使用,会抛出 NameError 异常,因为它在函数外部不可见。
  其实,inner 不过就是一个标识符,就是一个函数 outer 内部定义的变量而已。

5、嵌套函数的作用域

def outer():
o = 65 # 局部变量、本地 local 变量、临时变量
def inner():
o = 97
print('inner', o)
print('outer 1 ', o)
inner()
print('outer 2 ', o)
outer() # 1:outer 1 65 2:inner 97 3:outer 2 65

  外层变量在内部作用域可见。
  内层作用域中,如果定义了和外层相同的变量,相当于在当前函数作用域中重新定义了一个新的变量,这个内层变量并不能覆盖掉外部作用域中的变量。

6、一个赋值语句的问题

x = 100
def fn():
y = x + 200
print(y)
fn()
x = 100
def fn():
x += 1 # 报错! 赋值即定义,即 x = x + 1 (局部变量 = 局部变量 + 1)!
print(x)
fn()
x = 100
def fn():
print(x) # 报错!该步执行不了!
x += 1 # 只要在该作用域内赋值定义('=')局部变量,在该作用域内的所有该变量都为局部变量!
print(x)
fn()

  能否解决呢?可以,使用 global 语句

x = 100
def fn():
global x # 声明全局变量
print(x) #
x += 1
print(x) #
fn()
print(x) #

  注意:全局变量一般情况不推荐修改,一旦在作用域中使用 global 声明全局变量,那么相当于在对全局变量赋值、定义。

  global 使用原则:

    1> 外部作用域变量会在内部作用域可见,但也不要在这个内部的局部作用域中直接使用,因为函数的目的就是为了封装,尽量与外界隔离。
    2> 如果函数需要使用外部全局变量,请尽量使用函数的形参定义,并在调用传实参解决。
    3> 一句话:不用 global,学习它就是为了深入理解变量作用域。

# 不建议直接传入全局变量!
y = [] def foo(): # x 就是标识符,就是变量,就是本地变量
y.append(1) foo()
foo()
print(y)
# 建议使用传参的方式,在函数内使用全局变量
y = [] def foo(x): # x 就是标识符,就是变量,就是本地变量
x.append(1) foo(y)
foo(y)
print(y)

7、闭包**

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

# python 2 实现闭包
def counter():
c = [0]
def inc():
c[0] += 1 # 是赋值即定义嘛?不是!是修改值
return c[0]
return inc # 返回标识符,即函数对象 m = counter()
m() # 调用函数 inc(),但是 c 消亡了嘛?没有,内层函数没有消亡,c 不消亡(闭包)
m()
m()
print(m())
# 不推荐使用 global !
def counter():
global c
c = 0
def inc():
global c
c += 1 # 不是闭包!
return c
return inc m = counter()
m()
m()
m()
print(m())
# 推荐使用 nonlocal,python 3 实现闭包
def counter():
c = 0
def inc():
nonlocal c # 非当前函数的本地变量,当前函数之外的任意层函数的变量,绝非 global
c += 1 # 是闭包吗?是!
return c
return inc m = counter()
m()
m()
m()
print(m())

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

8、默认值的作用域

def foo(x=1):
x += 1
print(x)
foo() #
foo() # def bar(x=[]): # x = [],引用类型
x.append(1) # [1]
print(x)
bar() # [1]
bar() # [1, 1]

  为什么上列 bar 函数第二次调用打印的是 [1, 1]?
    因为函数也是对象,每个函数定义被执行后,就生成了一个函数对象和函数名这个标识符关联。
    python 把函数的默认值放在了函数对象的属性中,这个属性就伴随着这个函数对象的整个生命周期。

# 查看 foo.__defaults__ 属性,它是个元组
def bar(x=[]):
x.append(1)
print(x)
print(bar.__defaults__)
bar() # [1]
print(bar.__defaults__)
bar() # [1, 1]
print(bar.__defaults__) # 执行结果:
([],)
[1]
([1],)
[1, 1]
([1, 1],) # 元组不变,记录的是地址,引用类型变化
def foo(x, m=123, n='abc'):
m=456
n='def'
print(x)
print(foo.__defaults__) # (123, 'abc')
foo('yang')
print(foo.__defaults__) # (123, 'abc')
def foo(x, m=123, *, n='abc', t=[1,2]):
m=456
n='def'
t.append(12)
#t[:].append(12) # t[:],全新复制一个列表,避免引用计数
print(x, m, n, t) print(foo.__defaults__, foo.__kwdefaults__) #(123,) {'n': 'abc', 't': [1, 2]}
foo('yang')
print(foo.__defaults__, foo.__kwdefaults__) #(123,) {'n': 'abc', 't': [1, 2, 12]}
def x(a=[]):
a = a + [5] # 加法的本质:返回新列表、新地址;赋值即定义
print(x.__defaults__) # ([],)
x()
x()
print(x.__defaults__) # ([],) def y(a=[]):
a += [5] # += 即 extend => a.extend([5])
print(y.__defaults__) # ([],)
y()
y()
print(y.__defaults__) # ([5, 5],) # 列表的 + 和 += 的区别:
# + 表示两个列表合并并返回一个全新的列表。
# += 表示,就地修改前一个列表,在其后追加后一个列表,就是 extend 方法。
# 例:
l1 = [1, 2]
l2 = [3, 4]
l3 = l1 + l2
print(id(l1), id(l2), id(l3))
l3 += l2 # 就地修改
print(id(l3)) 执行结果:
2279905055304 2279905055816 2279906334216
2279906334216

9、变量名解析原则 LEGB**

  Local,本地作用域、局部作用域的 local 命名空间,函数调用时创建,调用结束消亡。
  Enclosing,Python2.2 时引入了 嵌套函数,实现了闭包,这个就是嵌套函数的外部函数的命名空间
  Global,全局作用域,即一个模块的命名空间。模块被 import 时创建, 解释器退出时消亡。
  Build-in,内置模块的命名空间,生命周期从 python 解释器启动时创建到解释器退出时消亡。例如 print(open),print 和 open 都是内置的变量。

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

  

10、函数的销毁

  定义一个函数就是生成一个函数对象,函数名指向的就是函数对象。
  可以使用del语句删除函数,使其引用计数减 1。
  可以使用同名标识符覆盖原有定义,本质上也是使其引用计数减 1。
  Python程序结束时,所有对象销毁。
  函数也是对象,也不例外,是否销毁,还是看引用计数是否减为 0。

Python函数的返回值和作用域的更多相关文章

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

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

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

    Python入门篇-返回值和作用域 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.返回值 1>.返回值概述 Python函数使用return语句返回“返回值” 所有函数都 ...

  3. 『无为则无心』Python函数 — 27、Python函数的返回值

    目录 1.返回值概念 2.return关键字的作用 3.返回值可以返回的数据类型 4.函数如何返回多个值 5.fn5 和 fn5()的区别 6.总结: 1.返回值概念 例如:我们去超市购物,比如买饮料 ...

  4. Python 函数作为返回值

    函数作为返回值高阶函数除了可以接收函数作为参数外,还可以把函数作为结果值返回. def lazy_sum(*args): def sum(): ax=0 for n in args: ax = ax ...

  5. 第八天pyhton3 函数的返回值、作用域

    返回值 pthon函数使用return语句返回"返回值": 所有函数都有返回值,如果没有return语句,隐式调用return None: return 语句并不一定是函数的语句块 ...

  6. python函数的返回值 讲解

    我们一起来聊聊python函数返回值的特殊情况,之前我也碰到过类似方面的问题,到后来查阅了一些资料后,发现原来是这样. 首先,写函数的时候,一定要写函数的文档,这样方便我们识别函数是做什么的.我记得很 ...

  7. python函数的返回值

    返回值:return1.没有返回值    #不写return    #只写return:结束一个函数    #return None2.有一个返回值    #可以返回任何数据类型    #只要返回就可 ...

  8. Python 基础之返回值与函数使用与局部变量和全局变量locals() 和 globals()

    一.函数的返回值 return return: 自定义返回值,返回到哪里? 返回到函数的[调用处]1.return 后面可以跟上六个标准数据类型,除此之外,可以跟上 类对象,函数,如果不写return ...

  9. Python的函数式编程-传入函数、排序算法、函数作为返回值、匿名函数、偏函数、装饰器

    函数是Python内建支持的一种封装,我们通过把大段代码拆成函数,通过一层一层的函数调用,就可以把复杂任务分解成简单的任务,这种分解可以称之为面向过程的程序设计.函数就是面向过程的程序设计的基本单元. ...

随机推荐

  1. [dfs] 2019牛客暑期多校训练营(第十场) Coffee Chicken

    题目地址: https://ac.nowcoder.com/acm/contest/890/B   时间限制:C/C++ 1秒,其他语言2秒空间限制:C/C++ 524288K,其他语言1048576 ...

  2. 扩展KMP(记录学习过程)

    首先看了几篇博客,发现还得耐下心来理解,动手画图 ,才能理解俩字符串还有数组之间神奇的关系 这篇博客图不错:https://segmentfault.com/a/1190000008663857 /* ...

  3. coding++ :在引入的css或者js文件后面加参数的作用

    前沿: 有些小伙伴们在页面(F12)直接对 JS.CSS 文件进行编辑.或者打断点调试的时候 可能会发现 所有的操作都不生效,为什么? 原因可能存在以下情况 有时候可能会遇到js或者css文件引用后传 ...

  4. JavaScript实现图结构

    JavaScript实现图结构 一.图论 1.1.图的简介 什么是图? 图结构是一种与树结构有些相似的数据结构: 图论是数学的一个分支,并且,在数学中,树是图的一种: 图论以图为研究对象,研究顶点和边 ...

  5. CVE-2020-2551复现过程

    项目地址 cnsimo/CVE-2020-2551 CVE-2020-2551 weblogic iiop 反序列化漏洞 该项目的搭建脚本在10.3.6版本经过测试,12版本未测试. 环境 kali+ ...

  6. Spring如何解决循环依赖,你真的懂了?

    导读 前几天发表的文章SpringBoot多数据源动态切换和SpringBoot整合多数据源的巨坑中,提到了一个坑就是动态数据源添加@Primary接口就会造成循环依赖异常,如下图: 这个就是典型的构 ...

  7. Window.requestAnimationFrame()动画更新

    概述 Window.requestAnimationFrame()方法告诉浏览器你希望执行动画,并且再下一次重绘之前要求浏览器调用一个特定的函数去更新动画.该方法把一个回调函数作为参数,该回调函数会在 ...

  8. Jmeter 压力测试笔记(2)--问题定位

    事情已经出了,是该想办法解决的时候了. 经过运维和DBA定位: 数据库读写分离中,读库延时超过了30秒,导致所有请求都压在主库.另外所有数据库都连接数都被占满,但活跃请求数量缺不多. 数据库16K的连 ...

  9. sqlchemy查询的其他操作

    sqlalchemy的数据查询排序 1 .正序排序:session.query(model).order_by(model.attr).all() session.query(model).order ...

  10. 机器学习4- 多元线性回归+Python实现

    目录 1 多元线性回归 2 多元线性回归的Python实现 2.1 手动实现 2.1.1 导入必要模块 2.1.2 加载数据 2.1.3 计算系数 2.1.4 预测 2.2 使用 sklearn 1 ...