【函数】0二、函数进阶


1、函数的返回值
python

In [4]: def add(x, y):
   ...:     return x+y
   ...: 

In [5]: add(1, 2)
Out[5]: 3


In [8]: def add(x, y):
   ...:     return x+y
   ...:     print(x+y)
   ...:     

In [9]: add(1, 2)
Out[9]: 3

In [16]: def add(x, y):
    ...:     return "abc", x+y
    ...:     return x+y
    ...:     

In [17]: add(1, 2)
Out[17]: ('abc', 3)

  
In [18]: def add(x, y):
    ...:     return
    ...: 
    ...:     

In [19]: add(1, 2)

In [20]: a = add(1, 2)

In [21]: a

In [22]: type(a)
Out[22]: NoneType


In [25]: def add(x, y):
    ...:     return None
    ...: 

In [26]: add(1, 2)

In [27]: type(add(1, 2))
Out[27]: NoneType


关键字:returnbash

     return只能出如今函数中,能够返回任何对象,能够做为元祖变相的返回多个值闭包

     全部函数都有返回值,若是没定义return则默认返回值Noneapp

     return语句除了返回值以外还会结束函数ide

    1个函数能够有多个return语句,但只会执行一个
函数


2、做用域
ui

一、函数嵌套
spa

  函数能够嵌套定义
code

In [28]: def outter():
    ...:     def inner():
    ...:         print('inner')
    ...:     print('outter')
    ...:     inner()
    ...:     

In [29]: outter()
outter
inner


二、做用域orm

   做用域是一个变量的可见范围

   函数内部是一个局部做用域,外面叫全局做用域

   不一样做用域变量不可见,可是上级做用域的变量对下级只读可见

In [32]: x = 1

In [33]: def inc():
    ...:     x += 1   # x此时是局部变量,不能直接使用全局做用域的变量
    ...:     

In [34]: inc()
---------------------------------------------------------------------------
UnboundLocalError                         Traceback (most recent call last)
<ipython-input-34-ae671e6b904f> in <module>()
----> 1 inc()

<ipython-input-33-661b9217054c> in inc()
      1 def inc():
----> 2     x += 1
      3 

UnboundLocalError: local variable 'x' referenced before assignment


In [40]: x = 1

In [41]: def fn():
    ...:     print(x)   
    ...:     

In [42]: fn()    # 为何这里能打印出来,不抛出错误呢
1


变量的做用域为定义此变量的做用域:

In [43]: def fn():
    ...:     name = "xxj"
    ...:     print(name)
    ...:     

In [44]: fn()
xxj

In [45]: name
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
<ipython-input-45-18697449d7c4> in <module>()
----> 1 name

NameError: name 'name' is not defined


上级做用域对下级做用域是只读可见的

In [46]: def fn():
    ...:     xx = 1
    ...:     print(xx)
    ...:     def inner():
    ...:         print(xx)
    ...:     inner()
    ...:     

In [47]: fn()
1
1


In [48]: def fn():
    ...:     xx = 1
    ...:     print(xx)
    ...:     def inner():
    ...:         xx = 2
    ...:         print(xx)
    ...:     inner()
    ...:     print(xx)
    ...:     

In [49]: fn()
1
2
1


global关键字能且只能引用全局做用域中的变量;引用后的变量能读写

若是全局做用域有此变量名,则引用,

没有则须要定义,定义后此做用域和全局做用域中可见;不定义则会报错,

In [53]: xx = 1

In [54]: def fn():
    ...:     global xx    # global关键字能显式的提高1个变量的做用域
    ...:     xx += 1
    ...:     

In [55]: fn()

In [56]: xx
Out[56]: 2

In [57]: fn()

In [58]: xx
Out[58]: 3



In [68]: xxj
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
<ipython-input-68-bc382da45e82> in <module>()
----> 1 xxj

NameError: name 'xxj' is not defined

In [69]: def fn():
    ...:     global xxj  # 若是此变量没有定义,则此提高变量做用域没有意义 
    ...:     
    ...:     

In [70]: fn()

In [71]: xxj
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
<ipython-input-71-bc382da45e82> in <module>()
----> 1 xxj

NameError: name 'xxj' is not defined


  除非你清楚的知道global会带来什么,而且明确的知道没有global不行的话,不然不要用global;就是不建议使用global关键字


三、默认参数的做用域

函数也是对象,参数是函数对象的属性,因此函数参数的做用域伴随函数整个生命周期

In [92]: def fn(xy=[]):
    ...:     xy.append(1)
    ...:     print(xy)
    ...:     

In [93]: fn()
[1]

In [94]: fn()
[1, 1]

In [95]: fn()
[1, 1, 1]

In [96]: fn.__defaults__     # 函数默认参数的值保存在函数__defaults__属性中
Out[96]: ([1, 1, 1],)

In [98]: fn()
[1, 1, 1, 1]

In [100]: fn.__defaults__
Out[100]: ([1, 1, 1, 1],)

  当使用可变类型数据做为默认参数默认值时,须要特别注意。

In [105]: def fn(x=0, y=0):
     ...:     x = 1  # 赋值即定义
     ...:     y = 2
     ...:     print(x)
     ...:     print(y)
     ...:     

In [106]: fn.__defaults__
Out[106]: (0, 0)

In [107]: fn()
1
2

In [108]: fn.__defaults__
Out[108]: (0, 0)

解决方案:

  使用不可变类型做为默认值

  函数体内不改变默认值

In [109]: def fn(lst=None):     # 若是传入的参数是非None,那么改变了传入参数;
     ...:     if lst is None:
     ...:         lst = []
              else:
                  lst = lst[:]
     ...:     lst.append(3)
     ...:     print(lst)
     ...:     

In [110]: fn.__defaults__
Out[110]: (None,)

In [111]: fn()
[3]

In [112]: fn.__defaults__
Out[112]: (None,)


In [113]: def fn(lst=[]):
     ...:     lst = lst[:]    # 浅拷贝
     ...:     lst.append(2)   # 不管如何不修改传入参数
     ...:     print(lst)
     ...:     

In [114]: fn.__defaults__
Out[114]: ([],)

In [115]: fn()
[2]

In [116]: fn.__defaults__
Out[116]: ([],)

  一般若是使用一个可变参数做为默认参数时,会使用None来代替


四、命名空间与LEGB

 1)命名空间 

       理解Python的LEGB原则是理解Python命名空间的关键,而理解Python的命名空间又是理解Python中许多语法规定的关键。因此,Python的LEGB原则就成为Python中一个很是核心的内容

白话一点讲:命名空间是对变量名的分组划分
       不一样组的相同名称的变量视为两个独立的变量,所以隶属于不一样分组(即命名空间)的变量名能够重复。
命名空间能够存在多个,使用命名空间,表示在该命名空间中查找当前名称。


       命名空间表示变量的可见范围,一个变量名能够定义在多个不一样的命名空间,相互之间并不冲突,但同一个命名空间中不能有两个相同的变量名。

好比:两个叫“张三”的学生能够同时存在于班级A和班级B中,若是两个张三都是一个班级,那么带来的麻烦复杂不少了,在Python中你不能这么干。

      在Python中用字典来表示一个命名空间,命名空间中保存了变量(名字)和对象的映射关系,在Python中命名空间出如今哪些地方呢?有函数范围内的命名空间(local),有模块范围内的命名空间(global),有python内建的命名空间(built-in),还有类对象的全部属性组成的命名空间

Python一切皆对象,因此在Python中变量名是字符串对象

例如:

1
In [25]: a=10

      表示创建字符串对象aNumber对象10之间的对应关系。因为这是一种映射关系,因此,可使用键-值的形式来表示,即{name : object}
前面已经说过,命名空间是对变量名的分组划分,因此,Python的命名空间就是对许多键-值对的分组划分,即,键值对的集合,所以:

Python的命名空间是一个字典,字典内保存了变量名称与对象之间的映射关系


 2)命名空间的生命周期

       全部的命名空间都是有生命周期的,对于python内建的命名空间,python解析器启动时建立,一直保留直至直python解析器退出时才消亡。而对于函数的local命名空间是在函数每次被调用的时候建立,调用完成函数返回时消亡,而对于模块的global命名空间是在该模块被import的时候建立,解析器退出时消亡。


 3)做用域

  一个做用域是指一段程序的正文区域,能够是一个函数或一段代码。

 一个变量的做用域是指该变量的有效范围。Python的做用域是静态做用域,由于它是由代码中得位置决定的,而命名空间就是做用域的动态表现。

函数定义了本地做用域,而模块定义了全局做用域:

       每一个模块都是一个全局做用域,所以,全局做用域的范围仅限于单个程序文件

       每次对函数的调用都会建立一个新的本地做用域,赋值的变量除非声明为全局变量,不然均为本地变量

       全部的变量名均可以概括为本地,全局或内置的(由__builtin__模块提供)

      

 4)LEGB原则

LEGB含义解释:
       L-Local(function);函数内的名字空间
       E-Enclosing function locals;外部嵌套函数的名字空间(例如closure)
       G-Global(module);函数定义所在模块(文件)的名字空间
       B-Builtin(Python);Python内置模块的名字空间,
builtin做用域,对应builtin命名空间,python内部定义的最顶层的做用域,在这个做用域里面定义了各类内建函数:open、range、xrange、list等等      

 前面讲到,Python的命名空间是一个字典,字典内保存了变量名与对象之间的映射关系

所以,查找变量名就是在命名空间字典中查找键-值对
Python有多个命名空间,所以,须要有规则来规定,按照怎样的顺序来查找命名空间LEGB就是用来规定命名空间查找顺序的规则。

  LEGB规定了查找一个名称的顺序为:local-->enclosing function locals-->global-->builtin


3、闭包

当一个函数结束了,函数的内部部分变量引用还存在,这就叫闭包

外层函数主要为内层函数提供环境

定义在外层函数内,却由内层函数引用的变量,在外层函数返回时,若是外层函数返回的值是内层函数,再次调用内层函数时,会记忆下内层函数调用的外层函数的变量。


python的闭包可使用可变容器实现,这也是python2惟一的方式

In [91]: def counter():
    ...:     c = [0]
    ...:     def inc():
    ...:         c[0] += 1
    ...:         return c
    ...:     return inc
    ...: 

In [92]: type(counter)
Out[92]: function

In [93]: type(counter())
Out[93]: function

In [94]: type(counter()())
Out[94]: list

In [95]: f = counter()

In [96]: f()
Out[96]: [1]

In [97]: f()
Out[97]: [2]

In [98]: f()
Out[98]: [3]


nonlocal关键字:

  nonlocal关键字能且只能引用外部函数做用域中已存在的变量(不能在本身的做用域中定义);引用后的变量能读写

In [102]: def counter():
     ...:     x = 0
     ...:     def inc():
     ...:         nonlocal x
     ...:         x += 1
     ...:         return x
     ...:     return inc
     ...: 

In [103]: f = counter()

In [104]: f()
Out[104]: 1

In [105]: f()
Out[105]: 2

In [106]: f()
Out[106]: 3


4、递归函数

    函数体内调用自身的函数       

    递归函数须要有合适的退出条件,不然就成了死循环; 递归须要边界条件,递归前进段和递归返回段 

    在python中为了保护解释器,递归深度最大为1000

    python中应尽可能避免递归,效率低(能转化为迭代尽可能转化为迭代) 

In [2]: def fib(n):
   ...:     if n == 0:
   ...:         return 1
   ...:     if n == 1:
   ...:         return 1
   ...:     return fib(n-1) + fib(n-2)
   ...: 

In [3]: fib(5)
Out[3]: 8

In [4]: fib(6)
Out[4]: 13
相关文章
相关标签/搜索