函数返回值,作用域

函数返回值

  • python函数使用return语句返回"返回值"
  • 所有函数都有返回值,如果没有return语句,隐式调用return None
  • 一个函数可以存在多条return语句,但是只有一条可以执行,没有则执行return None
  • 有时也可以显示调用return None 作为提醒
  • 如果执行了return语句,当前return语句之后的其他语句就不会被执行
  • 返回值的作用: 结束函数调用,返回返回值

    def showvalues():
        return 3,4,5
    showvalues()
返回(3,4,5)
  • 函数的定义,要求返回值只能是一个值
  • 隐式的被python封装成 一个 元组

函数的作用域

作用域

  • 定义:一个标识符的可见范围.一般常说的是变量的作用域

  • 函数是一个封装,每一个函数都会开辟一个作用域,函数中的变量会被限制在这个作用域中,函数外部对函数内的变量不可见.

  • 内部对外部不可见

作用域的分类

  • 全局作用域
    1. 在整个程序的运行过程中都可见
    2. 全局作用域的变量称为全局变量
  • 局部作用域
    1. 在函数,类内部可见
    2. 局部作用域中的变量称为局部变量,其范围不能超过其所在局部作用域
# 局部变量
def fn1():
    x = 1  # 局部作用域,x为局部变量,适用范围在fn1内
def fn2():
    print(x)     #x 不能打印,因为局部变量对外部不可见
print(x)#    也不能打印, 因为x 为局部变量,在fn1中可见  


#全局变量
x = 5 #全局变量,在函数外定义
def foo():
    print(x)  # x可以打印,因为x为全局变量,对函数内可见.
foo()
一般来讲,外部作用域的变量对函数内部可见,可以使用
反过来,函数内部的局部变量,不能在函数外部看到

函数的嵌套

在一个函数中定义另一个函数
def outer():
    def inner():
        print(inner)
    print(outer)
    inner()
outer()    ##可以使用
inner()    ## 不可以使用

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

嵌套函数的作用域

def outer1():
    o = 65   ##  == 0x41   A
    def inner():
        o = 97   ## 0x61  a
        print("inner {}".format(o)) ##使用的内部o= 97 inner 97 a      
        print(chr(o))
    
    inner()
    print("outer{}".format(o))   ##外部的o不受局部的o的影响, outer 65
outer1()
执行结果为   inner 97 a  
outer65
  • 外层变量在内部作用域可见
  • 相当于内部变量在当前的inner函数中重新定义了一个新的变量o,但是这个o并不会覆盖,也不会影响到外层函数中的变量o,对于inner函数来说,只能可见自己作用域中的定义的变量o
  • 总结
    1. 优先用内部的变量,记住赋值即定义
    2. 只要函数中出现了赋值语句,则这个被复制的变量的作用域就已经生成,在键入函数时就会被作用
x = 5 
def foo():
    x += 1  ##会报错 
foo()
  • 错误原因:
    因为x += 1 就相当于x = x + 1 ,虽然全局变量x能够对foo可见,但是赋值既定义,从x被赋值开始,局部 变量x的作用域就已经生成,foo函数内部只被局部变量x影响, 而此时执行过程中找那个,x还未被定义所 以会找不到x

  • 老师的回答:
    相当于在foo内部定义了一个局部变量x,那么foo内部所有的x都是这个局部变量x了
    x = x + 1 相当于使用了局部变量x ,但是这个x还没有完成赋值,就被右边拿来和加1操作了

global

x = 5 
def shownext():
    global x  #声明全局变量x
    x += 5       #此时的x已经被声明为全局变量
    return(x)    #返回的x是全局变量x  ,全局变量x的值 已经改变
shownext()

返回 10

  • 使用gloal关键字的变量,将foo内的x声明为使用外部的全局作用域中定义的x
  • 全局作用域中必须有x,如果没有,则会报错nameerror
  • 总结 : x +=1 是先引用后赋值,而python动态语言是赋值才算定义,才能用,
  • 解决办法
    1.在这条语句之前增加x = 0之类赋值语句,或者使用global,声明不在内部作用域,去使用全局作用域
    2.查找变量定义内部作用域使用x = 10之类的赋值语句会重新定义局部作用与的变量x,但是,一旦这个作用域中使用glabal声明x为全局的,那么x += 5相当于对全局作用域的变量x赋值
  • global使用原则
  1. 外部作用域的变量会在内部作用域可见,但也不要在这个内部的局部作用给予中直接使用,因为函数的目的就是封装,尽量与外界隔离
  2. 如果函数需要使用外部全局变量,请尽量使用函数的形参定义,并在调用传实参解决

闭包

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

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

def counter ():   #1定义函数,直接跳过   
   c = [0]                      #2 c={[0]  
   def inc():    #1同上        #2 内部的定义并没执行 过
       c[0] += 1                   
       return c[0]
   return inc         #2 counter的返回值是inc函数体 
foo = counter()      # 1函数counter的返回值给foo赋值,这时回看counter函数  .2 foo现在是函数体inc
print(foo(),foo())     #1 此时打印的是每次调用函数inc的返回值  第一次1,第二次2 
c = 100              #1 此时的c是全局变量c  而不是counter内部的c  局部变量c不会变
print(foo())          # 再次打印调用函数inc的返回值

代码分析:

  • foo 会执行counter函数并返回inc对应的函数对象,注意这个函数对象并不释放,因为有foo记着
  • c[0] += 1不会报错,c已经在counter函数中定义过了,而且inc中的使用方式是为c的元素修改至,而不是重新定义c的变量
  • 最后一行打印3
    因为c和counter中的c不一样,而inc引用的是自由变量正是counter中的变量c

nonlocal

  • 定义: 将变量标记为不在本地作用域定义,而是在上一级的某一集局部作用域中定义,但不能是全局作用域中的定义
def counter():
    count = 0
    
    def inc():
        nonlocal count #声明变量count不是本局变量,就会往上级非全局变量寻找变量count
        count += 1
        
        return count
        
    return inc
foo = counter()   # foo指向函数对象inc
print(foo(),foo())
  • count 是外层函数的局部变量,被内层函数引用
  • 内层函数使用nonlocal关键字声明count变量在上级的作用域而非本地作用域中的定义
  • 代码中内层函数引用外部局部作用域中的自由变量,形成闭包
  • 当在最外层局部作用域中使用nonlocal时,其无法引用全局变量,会报错

默认值的作用域

def foo (xyz=1):
    print(xyz)
foo()    #没有传参,则缺省值代替 xyz=1
foo()   #同样
print(xyz)   #xyz是foo函数的形参,作用域是函数体内
##输出   1  1   打印时会报错
def foo (xyz=[]):
    xyz.append(1)
    print(xyz)
foo()   # [1]   xyz是引用类型,引用xyz这个列表,函数体操作是对列表进行操作
foo()    # [1,1]
print(xyz)  #报错   xyz不在作用域内
  • 为什么第二次调用foo函数打印的是[1,1]?
    1.因为函数也是对象,每个函数定义被执行后,就会生成一个函数对象和函数名这个标志符关联
    2.python把函数的默认值放在了函数对象的属性中,这个属性就伴随这个函数对象的整个生命周期
  • __ defaults__
  • 查看foo.__defaults_属性,他是个元组
  • 函数地址没有变就说明foo这个函数对象没有变过,调用它,他的属性__defaults__中使用元祖保存默认值
  • xyz默认值是引用类型,引用类型的元素变动,并不是元组的变化

__defaults __

def foo (xyz=[], m=123,n='abc'):
    m = 456
    n = 'def'
    print(xyz)
print(foo.__defaults__)   # ([],123,'abc')
foo('magedu')               # magedu
print(foo.__defaults__)     #([],123,'abc')
  • 属性 __ 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__)  #  (123){'n':'abc' , 't':[1,2]}
foo('magedu')                               # magedu,456,'def',[1,2,300]
print(foo.__defaults__,foo.__kwdefaults__)  # (123){'n':'abc' , 't':[1,2,300]}
  • 属性__defaults__中使用元组保存所有位置参数的默认值
  • 属性__kwdefaults__中使用字典保存所有keyword-only参数的默认值
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值