Python学习week5-函数返回值与作用域

1、函数的返回值

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

# 函数不能同时返回多个值,return 1,3,5 表示返回了一 个元组(1,3,5) # 返回值被python隐式封装成了一个元组;

def showlist():
    return 1,3,5

res=showlist()=(1,3,5)

x,y,z=showlist=(1,3,5) # 使用解构可以取值方便

# 函数的返回值可以使任意数据类型,但是能且只能返回一个;

2、函数的嵌套

# 函数嵌套:在一个函数内部定义了另外一个函数;

# 内部函数对于外部不可见,不能直接对其调用,否则会抛异常;

def outer():
    def inner():
        print('from inner')
    print('from outer')
    inner()

outer()
'''
==>
from outer
from inner
'''
inner() # NameError: name 'inner' is not defined

3、作用域

3.1、 变量的作用域

  作用域其实指的是变量的作用范围;变量并不是在哪个位置都可以访问的,访问权限取决于这个变量是在哪赋值的;也就是在哪个作用域内;

  通常来说,在编程语言中,变量的作用域从代码的结构形式来看的,有块级,函数,类,模块,包等由小到大;

  python中没有块级作用域,也就是类似if-else-if,for语句,with上下文管理,他们不存在作用域关系;

3.2、python的作用域分层

  • L(Local)  局部作用域
  • E(Enclosing)嵌套作用域
  • G(Global)    全局作用域
  • B(Build-in)   内建作用域

  查找原则:L==>E==>G==>E

3.3、作用域关系实例解析

(1)下面代码运行结果:
  x=5   def foo()     x+=1     print(x)   foo()
#代码分析:
def foo():
    x+=1   
    '''
    x+=1 ==> x=x+1 python赋值即定义,x=x+1 相当于从新定义一个变量x,
    然后拿到一个未赋值的变量x,(这里的x指的是等式右边的x)给(等式左边的x)x赋值,
    相当于: 
      x
      x=x+1
    '''
    print(x) # UnboundLocalError: local variable 'x' referenced before assignment

'''
函数在定义阶段并没有报错,因为在定义阶段只检查语法;
''' 
foo()
(2)下面代码运行结果?
  name ='jack'   def f1():   print(name)   def f2():   name = 'eric'   f1()   f2()
# 代码分析:
常规错误理解:在f2内部定义一个变量name,然后调用f1(),执行打印print(name),这个name按照LEGB的查找关系应该找f2()内部的name,所以打印'eric';
关键点函数的作用域关系在函数的定义阶段就已经形成,跟调用函数的位置无关;上面代码执行f1(),f1内部没有name变量,则向全局查找name,所有打印'jack'
总结:
作用域关系,在函数定义时候就已经固定,与调用位置无关,在调用函数时候,必须一定要回到函数原来定义的位置去找作用域关系;

4、闭包函数

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

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

# 闭包作用?

  一般情况下,如果一个函数调用结束,函数内部的所有东西都会释放,还给内存,函数的局部变量消失;

  但是闭包是一种特殊情况,如果外部函数在调用结束后发现自己的临时变量会被内部函数引用,就会把这个临时变量绑定给内部函数,然后再结束;

# 理解闭包函数
# 1、简单闭包函数
def counter():
    c = [0]
    def inc():
        c[0] += 1  #应用的是自由变量正式counter的变量c
        return c[0]
    print(c[0])
    return inc

#counter()()
foo = counter()    # counter()调用结束将,变量c=[0]并不销毁;
print(foo(),foo())   #调用的是inner()
print(foo())

# 2、分析函数打印结果
def count():
    fs=[]
    for i in range(1,4):  # for循环3次相当于在count函数内定义了3次函数f 
        def f():
            return i*i
        fs.append(f)
    return fs

f1,f2,f3=count() # count函数调用结束,for循环在调用结束后 变量i=3 最后内存中i记录的值为3;

print(f1()) # 调用f1()相当于调用f(),return i*i 所有返回9
print(f2())
print(f3())

# 3、将上面函数稍作修改
def count1():
fs=[]
for i in range(1,4):
def f():
return i*i
fs.append(f())
return fs
print(count1())
f1,f2,f3=count()
print(f1,f2,f3)
# 上面代码在for循环定义f函数后直接对f函数进行调用,然后将返回值i*i 添加到列表fs,由于i的取值是发生在for循环一次之后所以对应的i取值为1,2,3

5、nonlocal关键字

#  使用了nonlocal关键字的变量 ,将变量标记为不在本地作用域定义,而在上级的某一级局部作用域中定义,但不能是在全局作用域中定义;

def bar():
    x=0
    def foo():
        nonlocal x  # 将x标记为非本地变量
        x+=1    # x=x+1  此时x向外层函数找,x=0,x=x+1 ,形成闭包;
        return x
    return foo

res=bar()()
print(res)

6、默认值的作用域

  • 函数也是对象,python 把函数的默认值放在了属性中,这个属性就伴随着这个函数对象的整个生命周期; 
# 参数为引用类型实例
def
foo(l:list=[]): l.append(1) return l print(foo(),id(foo)) print(foo.__defaults__) print('#'*30) print(foo(),id(foo)) print(foo.__defaults__) ''' [1] 1966940858160 ([1],) ############################## [1, 1] 1966940858160 ([1, 1],) foo地址并没有变,就是说foo这个对象没有变,调用它,它的属性__defaults__ 中使用元组保存默认值;
l是列表,__defaults__ 中元组记录的是l引用地址,列表元素的增加,列表的地址不会发生改变;
'''

# 参数为非引用类型
  def foo(w,u='abc',z=123):
    u = 'xyz'
    z = 789
    print(w,u,z)
  print(foo.__defaults__)
  foo('test')
  print(foo.__defaults__)
  ''’
  运行结果:
  ('abc', 123)
  test xyz 789
  ('abc', 123)
  '''
  分析:属性__defaults__中使用元组保存所有位置参数默认值,它不会因为在函数体内使用了它而发生改变;
  
  # 带keyword-only参数
  def foo(w,u='abc',*,z=123,zz=[456]):
    u = 'xyz'
    z = 789
    zz.append(1)
    print(w,u,z,zz)
  print(foo.__defaults__)
  foo('tes')
  print(foo.__kwdefaults__)
  '''
  运行结果:
  ('abc',)
  tes xyz 789 [456, 1]
  {'z': 123, 'zz': [456, 1]}

  属性__kwdefaults__ 中使用字典保存所有keyword-only 参数的默认值;
  使用可变类型作为形参默认值的时候,就可能修改这个默认值;
  '''
  • 使用可变类型作为形参默认值的时候,就可能修改这个默认值;

(1)函数体内,不改变默认值;

def foo(xyz=[],u='abc',z=123):
    xyz = xyz[:]     #影子拷贝
    xyz.append(1)
    print(xyz)
print(foo.__defaults__)
foo()
print(foo.__defaults__)
foo([10])    
foo()
print(foo.__defaults__)
foo([10,5])
print(foo.__defaults__)
'''
运行结果:
([], 'abc', 123)
[1]
([], 'abc', 123)
[10, 1]
[1]
([], 'abc', 123)
[10, 5, 1]
([], 'abc', 123)
'''
# 函数中的xyz 都是传入参数或者默认参数的副本,如果就想修改原参数,不可以;

(2)使用不可变类型的默认值;

def foo(xyz=None,u='abc',z=123):
    if xyz is None:
        xyz = []
    xyz.append(1)
    print(xyz)
print(foo.__defaults__)
foo()
foo()
print(foo.__defaults__)
foo([10])
print(foo.__defaults__)
foo([10,5])
print(foo.__defaults__)

'''
(None, 'abc', 123)
[1]
[1] (None, 'abc', 123) [10, 1] (None, 'abc', 123) [10, 5, 1] (None, 'abc', 123)
''' # (1)如果使用缺省值None就创建一个列表 # (2)如果传入一个列表,就修改这个列表
原文地址:https://www.cnblogs.com/soulgou123/p/9570045.html