函数的返回值和作用域

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. 【2019HDU多校】第九场1006/HDU6685-Rikka with Coin——位运算打表

    题目链接 题目大意 使用10.20.50.100元面额的硬币能分别组成题目给出的面额,需要最少的硬币个数 分析 一开始队友想用一堆if-else解决问题,然后WA了无数发-- 我想到了一种比较简单的打 ...

  2. 99%的程序员都在用Lombok,原理竟然这么简单?我也手撸了一个!|建议收藏!!!

    罗曼罗兰说过:世界上只有一种英雄主义,就是看清生活的真相之后依然热爱生活. 对于 Lombok 我相信大部分人都不陌生,但对于它的实现原理以及缺点却鲜为人知,而本文将会从 Lombok 的原理出发,手 ...

  3. 听说用 Lombok 可以早点下班?

    听说隔壁用 Lombok 的六点就下班了,我也想六点下班! 好的,那么这篇文章就介绍下什么是 Lombok,Lombok 做了什么以及 Lombok 是怎么做的? 在介绍之前,先通过是否使用 Lomb ...

  4. linux bash吧,还有啥Bourne Again Shell

    linux bash吧,还有啥Bourne Again Shell bash吧,还有啥Bourne Again Shell 头部要写#!/bin/bash set -x #open script de ...

  5. 面试刷题29:mysql事务隔离实现原理?

    mysql的事务是innodb存储引擎独有的,myisam存储引擎不支持事务. 事务最经典的例子就是转账了,事务要保证的是一组数据库的操作要么全部成功,要么全部失败.是为了保证高并发场景下数据的正确性 ...

  6. 倒计时器CountDownLatch

    1.背景: countDownLatch是在java1.5被引入,跟它一起被引入的工具类还有CyclicBarrier.Semaphore.concurrentHashMap和BlockingQueu ...

  7. Nginx是什么东东?

    Nginx的产生 没有听过Nginx?那么一定听过它的"同行"Apache吧!Nginx同Apache一样都是一种WEB服务器.基于REST架构风格,以统一资源描述符(Unifor ...

  8. C# Threading.Timer 为什么一会儿自己停了

    这两天做一个socket通信的Demo,用定时器启动client端去连接server端,出现一个状况,连接几次后定时器就停了. 下面就是会造成终止的代码: public class Client { ...

  9. CI / CD /CD 持续集成 持续交付 持续部署

    CI / CD /CD 持续集成 持续交付 持续部署 CI CD 是啥?干了啥? CI continuous integration 持续集成 CD continuous delivery 持续交付 ...

  10. 并查集例题01. 种类并查集(poj1733)

    题目: http://poj.org/problem?id=1733 题意: 输入n表示有一个长度为n的0,1字符串, m表示接下来有m行输入, 接下来的m行输入中x, y, even表示第x到第y个 ...