Python基础-函数进阶

Python之函数进阶

1.1 命名空间

执行以下代码,在函数外面引用函数里面的变量,会发现执行报错了

def func1():
    m = 1
    print(m)
print(m)  #这行报的错

报错了:
NameError: name 'm' is not defined  # m未定义

Python执行代码原理回顾

  从Python解释器开始执行代码之后,就在内存中开辟一个空间,每当遇到一个变量的时候,就把变量名和值之间对应的关系记录下来。但是当遇到函数定义的时候,解释器只是象征性的将函数名读入内存,表示已经知道这个函数的存在了。至于函数内部的变量和逻辑,解释器根本不关心。

  等执行到函数调用语句的时候,Python解释器会在新开辟一块内存来存储这个函数里面的内容。这个时候,才关注函数里面有哪些变量。而函数中的变量会存储咋新开辟出来的内存中,函数中的变量只能在函数内部使用,并且会随着函数的执行完毕,这块内存中的内容也会被清空。

  存放变量名与值的关系的空间,就叫做---命名空间。

  代码在运行伊始,创建的存储‘变量名与值的关系’的空间,叫做全局命名空间

在函数的运行中开辟的临时的空间,叫做局部命名空间

1.2 命名空间和作用域

命名空间一共分为三种:

  全局命名空间

  局部命名空间

  内置命名空间

* 内置命名空间中存放了python为我们提供的默认的方法,比如input、len、str等等

1.2.1 命名空间的加载顺序

内置命名空间(程序运行前加载) --> 全局命名空间(程序运行中,从上到下加载) -->局部命名空间(程序运行中,调用函数时才加载)

1.2.2 命名空间的取值顺序

  在局部调用:局部命名空间 --> 全局命名空间 --> 内置命名空间

  在全局调用: 全局命名空间 --> 内置命名空间  (无法调用到局部命名空间)

综上所诉,在寻找变量时,从小范围,一层一层到大范围去寻找

1.2.3 作用域

作用域就是作用范围,按照生效范围可以分为全局作用域局部作用域

全局作用域:

  包含内置命名空间、全局命名空间,在整个文件的任意位置都能被引用,全局有效。

局部作用域:

  局部命名空间,只能在局部范围内生效

1.2.4 global nonlocal

执行以下代码会报错,为什么呢?

count = 1
def func1():
    count = count + 1
    print(count)
func1()

因为count是全局变量,在函数中不能直接对全局变量进行更改

global

name = 'wusir'
def func1():
    global name
    name = 'alex'
    return
func1()
print(name)
# # global 1,在函数中声明一个全局变量
# # 2,在函数中更改一个全局变量

nonlocal

def func1():
    name1 = 'alex'
    print('+',name1)
    def inner():
        nonlocal name1
        name1= 'wusir'
        print('*',name1)
        def inner1():
            pass
    inner()
    print('%',name1)
func1()

nonlocal 在子函数中声明、更改一个外层的局部变量

 

1.3 函数名的操作

1.3.1 函数名的赋值

# 函数名可以相互赋值
def func1():
    print(666)

f1 = func1
f1()

1.3.2 函数名做参数

# 函数名可以当成其他函数的参数
def func1():
    print(666)

def func2(argv):
    argv()
    print(777)

func2(func1)

1.3.3 函数名可以当成容器类数据类型的参数

# 函数名当做数据类型的参数
def func1():
    print(666)
def func2():
    print(777)
def func3():
    print(888)

l1 = [func1, func2, func3]
for i in l1:
    i()

1.3.4 函数名当做函数的返回值

def func1():
    print(666)

def func2(argv):
    print(777)
    return argv

ret = func2(func1)
ret()

 

1.4 闭包

闭包的简单定义:内层函数对外层函数非全局变量的引用,叫做闭包。

闭包的好处:python的一个机制,如果python检测到闭包,它的局部作用域是不会随着函数的结束而结束的。

判断一个函数是否闭包的方法:

def wrapper():
    name1 = '老男孩'
    def inner():
        print(name1)
    inner()
    print(inner.__closure__)  # cell
wrapper()
###输出结果
老男孩
(<cell at 0x101a950a8: str object at 0x1040e58d0>,)   #检测结果是 闭包
# name1 是外层函数wrapper中的变量,是局部变量。

name1 = '老男孩'
def wrapper():
    def inner():
        print(name1)
    inner()
    print(inner.__closure__)  # None
wrapper()
### 输出结果
老男孩
None    ##检测结果 不是闭包
# name1 是全局变量。

name = 'alex'
def wrapper(argv):
    def inner():
        print(argv)
    inner()
    print(inner.__closure__)  # cell
wrapper(name)
### 输出结果
alex
(<cell at 0x101a950a8: str object at 0x101eb9618>,)
上述函数也是闭包 为什么呢?
因为:
函数中argv接收的names的变量,等于是在函数中重新定义了一个变量:args='alex' ,仍旧是一个局部变量,所有仍旧形成了闭包

闭包的用处:  

  python的一个机制,如果python检测到闭包,它的局部作用域是不会随着函数的结束而结束的。

    因为有以上机制,如果你需要执行爬虫,不会每次都新开内存。第一次运行爬虫后,会把爬取的内容计入内存。下次爬取的时候,因为局部作用域没有关闭,所以可以直接读取到内存中的内容。

闭包的另一大用处:装饰器!

 

1.5 装饰器

1.5.1 认识装饰器

写学习写一个最简单的装饰器

## 简单的装饰器
def timer(f1):
    def inner():
        sta_time = time.time()
        f1()
        end_time = time.time()
        print('代码执行效率为%s' %(end_time - sta_time))
    return inner

@timer # <==> func1 = timer(func1)
def func1():
    print("晚上回去吃烧烤")
    time.sleep(0.3)

@timer
def func2():
    print('晚上喝啤酒')
    time.sleep(0.5)


func1()
func2()

装饰器的作用:在不改变原函数及原函数的调用的情况下,为原函数增加一些额外的功能,如打印日志、执行时间、登录认证。

1.5.2 带参数的装饰器

# 带函数的装饰器
def timer(f1):
    def inner(*args, **kwargs):
        sta_time = time.time()
        f1(*args, **kwargs)
        end_time = time.time()
        print('代码执行效率为%s' % (end_time - sta_time))

    return inner

@timer  #func3 = timer(func3)
def func3(a, b):
    print(a, b)
    time.sleep(0.2)

func3('', '鸿')

 

 

 

 

 

posted on 2018-04-18 16:05  鸿飞漫天  阅读(195)  评论(0编辑  收藏  举报

导航