关于默认参数的陷阱问题:

见如下代码:

def func(m=[]):#默认参数的值是可变类型
    m.append(1)
    print(m)
func()
func()
func()

#控制台输出如下:

[1]
[1, 1]
[1, 1, 1]

即如果默认参数的值是一个可变数据类型,那么每一次调用函数的时候,如果不传值就共用这个数据类型的资源。

 

 

函数的命名空间

1:内置命名空间

就是python解释器一启动就可以使用的名字,存储在内置命名空间中

内置的名字在启动解释器的时候被加载进内存里

 

2:全局命名空间

是在程序从上到下被执行的过程中依次加载进内存的,放置了我们设置的所有变量名和函数名

 

3:局部命名空间

就是函数内部定义的名字

当调用函数的时候才会产生这个名称空间,随着函数执行的结束,这个命名空间就又消失了。

 

在局部:可以使用全局,内置命名空间中的名字

在全局:可以使用内置命名空间中的名字,不能使用局部命名空间中的名字

在内置:不能使用全局,局部命名空间中的名字

多个函数应该拥有多个独立的命名空间,不互相共享

 

作用域:

全局作用域----作用在全局----内置和全局命名空间中的名字都属于全局作用域

局部作用域----作用在局部----函数(局部命名空间中的名字属于局部作用域)

 

对于不可变数据类型,在局部可以查看全局作用域中变量,但是不能直接修改,如果想要修改,需要在局部的一开始添加global声明,如果在一个局部(函数)内声明了一个global变量,那么这个变量在局部的所有操作将对全局的变量有效。

 例如如下代码:

 

a=1#声明了一个全局变量a,值为1
def func():
    a=2#在局部(函数)改变a的值
func()#调用这个函数
print(a)#由输出结果可知局部的改变并没有影响到全局变量

#控制台输出
1

 

再看下面代码

a=1
def func():
    global a#在上面代码的基础上,添加global声明
    a=2
func()
print(a)#由输出结果可知,在局部声明了global后,局部的变化影响到了全局变量


#控制台输出
2

 

函数的嵌套定义:

内部函数可以使用外部函数的变量(不可变类型),但也不能直接修改,想要修改,需要在内部函数的变量前添加nonlocal声明

def outer():#定义一个外部函数
    a=1#在外部函数定义变量a
    def inner():#定义一个内部函数
        b=2
        print(a)
        print('inner')
        def inner2():#定义一个内部函数
            print(a,b)#可以查看a,b的值,但是不能直接修改
            print('inner2')
        inner2()
    inner()

outer()

#控制台输出
1
inner
1 2
inner2

如果企图在内部函数修改变量a

def outer():
    a=1
    def inner():
        b=2
        print(a)
        print('inner')
        def inner2():
            a+=1#企图在内部函数修改a
            print(a,b)
            print('inner2')
        inner2()
    inner()

outer()

#控制台输出:(报错)

UnboundLocalError: local variable 'a' referenced before assignment

在内部函数为变量a添加nonlocal声明后:

def outer():
    a=1
    def inner():
        b=2
        print(a)
        print('inner')
        def inner2():
            nonlocal a#添加nonlocal声明,nonlocal只能用于局部变量,只会找上层中离当前函数最近一层的局部变量,如果最外层函数也没有这个变量,就会报错,不会去全局命名空间找,声明了nonlocal的内部函数的变量修改会影响到离当前函数最近一层的局部变量
            a+=1
            print(a,b)
            print('inner2')
        inner2()
    inner()

outer()

输出为:

1
inner
2 2#可见a的值变化
inner2

 

函数名的本质:

函数名本质上就是函数的内存地址。

1.可以被引用(可以赋值)

def func():
    print(123)
func2=func#函数名可以赋值
func2()
#控制台输出:123

2.可以被当作容器类型的元素(容器类型即列表,元组,集合这些)

def func():
    print(123)
func2=func
l=[func,func2]#函数名可以作为容器类型的元素
print(l)
for i in l:
    i()

#控制台输出
[<function func at 0x000001B98ED85950>, <function func at 0x000001B98ED85950>]
123
123

3.函数名可以作为函数的参数和返回值

#函数名作为函数的参数
def func():
    print(123)

def wahaha(f):
    f()

wahaha(func)#函数名可以作为函数的参数

#控制台输出
123
#函数名作为函数的返回值
def func():
    print(123)

def wahaha(f):
    f()
    return f#函数名可以作为函数的返回值

qqxing=wahaha(func)
qqxing()

#控制台输出
123
123

所以函数是第一类对象,什么是第一类对象?

第一类对象(first-class object)指
1.可在运行期创建
2.可用作函数参数或返回值

3.可存入变量的实体。

 

不明白?那就记住一句话,就当普通变量使用

 

闭包:

内部函数包含对外部作用域而非全局作用域的名字的引用,该内部函数成为闭包函数。

def outer():
    a=1
    def inner():#这个函数就是一个闭包函数
        print(a)#如果没有这句,这个函数就不是一个闭包函数,因为内部函数没有引用外部函数的变量
outer()

想要知道一个内部函数是不是闭包函数,需要使用__closure__方法。

def outer():
    a=1
    def inner():
        print(a)
    print(inner.__closure__)#注意没有__closure__括号
outer()

#控制台输出
(<cell at 0x000001D98A4EC078: int object at 0x00000000584F22D0>,)
有cell 什么什么的,就说明是一个闭包函数

 

我们都知道如果想要在函数外部使用函数内部的变量,只需返回这个变量,同理,如果我们想在函数外部使用函数内部嵌套的函数,只需将内部函数返回即可,这就是闭包最常用的方法

def outer():
    a=1
    def inner():
        print(a)
    return inner#返回inner()函数的地址
inn=outer()#执行outer()函数,返回inner()函数的地址赋给inn变量
inn()#加上括号表示执行这个函数,inn变量里面是inner函数的地址

#控制台输出
1

闭包的好处:

若不使用闭包,则内部函数想要在外部调用必须如下操作:

def outer():
    a=1
    def inner():
        print(a)
    inner()#必须写上这句
outer()#然后再外部执行外部函数

这样每次调用outer()函数都会在内存创建a变量,结束调用后又删除a变量,浪费了很多时间,若使用闭包,当执行inn=outer()时,内部函数的地址就被inn变量保存,当结束调用后python解释器会认为变量a还有用,所以不会删除,这样以后每次要使用内部函数时,只要写inn()就可以,节省了时间。

 

posted on 2018-07-23 15:26  行走的cd  阅读(142)  评论(0编辑  收藏  举报