python基础之函数-下

上两篇记录了函数的基本定义和参数的使用(查看上篇请点击这里),本篇侧重于描述函数的变量作用域和迭代器、装饰器的使用,话不多说,开始吧!

一、变量作用域

python变量大致有内置变量、全局变量、闭包变量、局部变量等,访问的顺序为局部变量——闭包变量——全局变量——内置变量。

1.1 内置变量

python解释器里内置了一些常量和函数,叫做内置变量或内置常量,如list、int等。可以在python IDLE中输入:dir(__builtins__) 查看,如下图:

在这里插入图片描述

1.2 全局变量

全局变量,顾名思义,即在全局中都可使用的变量,如在整个文件、整个代码块、或函数中都可以使用。如:

num = 20
def func():
    print(num)
func()

此时的num即为全局变量,可以在函数func()中使用。
注意:如果在函数内试图去修改全局变量的话,python会自动创建一个新的局部变量代替,名字和全局变量相同。但两个变量实际上是两个互不影响的变量,存储空间不同。python会自动屏蔽新创建的局部变量而保护全局变量。所以不要轻易的在函数中修改全局变量,可以访问。全局变量的使用要谨慎。 如:

name = "jsonwong"
def func():
    new_name = name[:5]
    return new_name
print(func())
print(name)

输出结果为:

jsonw
jsonwong

1.3 闭包变量

在说明闭包变量前,需解释一下什么是闭包。百度百科对闭包的解释是:“
闭包:从表现形式上定义为,如果在一个内部函数里,对外部作用域(但不是在全局作用域)的变量进行引用,那么内部函数就会被认为是闭包。”

闭包的定义
1. 闭包是嵌套在函数中的函数。
2. 闭包必须是内层函数对外层函数的变量(非全局变量)的引用。
闭包的作用:
保存局部信息不被销毁,保证数据的安全性。
闭包的应用:
1,保证数据的安全。
2,装饰器的本质。
如:

def make_average():
    l1 = [1000, 1000, 1000, 2000]
    def average(price):
        l1.append(price)
        total = sum(l1)
        return total/len(l1)
    return average
avg = make_average()
print(type(avg))
print(avg(1000))

输出为:

<class 'function'>
1200.0

此时函数make_average中 l1 即为闭包变量。

1.4 局部变量

局部变量:在函数里定义的参数以及变量都为局部变量,在函数外不可使用。如1.3中 total 即为局部变量,注意局部变量只能在函数内部使用,函数外不可以使用!如:

def func(num):
    s = 0
    print(s*num)
func(5)

此时的变量s即为局部变量。但如果在函数外使用局部变量,就会报错,如:

def func(num):
    s = 0
    print(s*num)
func(5)
print(s)

提示报错:

NameError: name 's' is not defined

正确的做法是让局部变量变为全局变量,在变量前加globle关键字,如:

def func(num):
    global s
    s = 0
    print(s*num)
func(5)
print(s)

输出:

0
0

局部变量能够访问闭包变量,但不能对其进行修改!

二、迭代器

2.1 迭代器

有一些Python对象,我们可以从中按一定次序提取出其中的元素。这些对象称之为可迭代对象。比如,字符串、列表、元组、集合、字典都是可迭代对象。其实for循环的本质就是迭代器循环。
迭代器优点:
    1.提供了一种通用不依赖索引的迭代取值方式
    2.同一时刻在内存中只存在一个值,更节省内存
迭代器缺点:
    1.取值不如按照索引的方式灵活,不能取指定的某一个值,只能往后取,不能往前去取
    2.无法预测迭代器的长度

2.2 生成器

生成器是创建迭代器的一种简便的方法。生成器是一个特殊的函数。但凡函数内包含yield关键字,调用函数不会执行函数体代码,会得到一个返回值,该返回值就是生成器对象。如:

def func():
    print(1)
    yield 1
    print(2)
    yield 2
g = func()
print(g)

此时调用函数func()不会执行函数体代码,而是得到一个返回值,就是生成器对象,结果如下:

<generator object func at 0x000001BFF82E64F8>

如果使用next则会触发函数的执行,直到碰到一个yield停下来,将yield之后的值做为next的结果返回,如:

def func():
    print(1)
    yield 1
    print(2)
    yield 2
g = func()
result_1 = next(g)
print(result_1)
result_2 = next(g)
print(result_2)

输出:

1
1
2
2

注意:1、生成器保存的是算法,而列表保存内容是在内存中,列表不占优势,耗内存,生成器节约内存开销。
2、yield 对比 return:相同点,都可以返回值,值得类型与个数没有限制。
不同点:yield 可以返回多次值,而return只能返回一次值函数就会结束。

2.3 判断迭代器和生成器

from collections.abc import Iterable
from inspect import isgeneratorfunction

def func():
    print(1)
    yield 1
    print(2)
    yield 2
gs = (x for x in range(1, 10))

print(isinstance(gs, Iterable))
print(isgeneratorfunction(func))

输出:

True
True

三、装饰器

3.1 普通装饰器

装饰器如同其名,是在不改变某些函数原有功能的基础上增加新的功能
作用: 程序代码运行时,动态增加功能。
本质: 就是一个闭包,返回了函数的高阶函数。
好处遵序开闭原则,对修改关闭,对扩展开放。
假如现在有两个函数demo()和func(),我需要在func中使用demo同样的功能,那么只能这样:

def demo():
    print("demo func")
def func():
    print("demo func")
    print("func")

现在为了减少代码,我们可以这样:

def demo(func):
    print("demo func")
    func()

def y():
    print("func")

demo(y)

但原来func()变为了demo(y)破坏了原有的代码逻辑,但装饰器可以解决这一问题,装饰器用@+装饰函数来对要装饰的函数进行装饰。

def demo(y):
    print("demo func")
    y()
@demo
def func():
    print("func")

现在只需要调用func()即可了。

3.2 带参数的装饰器

带参数的装饰器为装饰器的编写和使用提供了更大的灵活性。
注意:
装饰器函数在被装饰函数定义好后立即执行。

def demo(func):
    print("我是装饰器")
    def inner(age):
        if age <= 10:
            mystr = "children"
        elif age <= 20:
            mystr = "midman"
        else:
            mystr = "oldman"
        return func(mystr)
    return inner

@demo
def y(p):
    print("你是一个这样的", p)
y(14)

四、参考文章:

https://blog.csdn.net/love_Aym/article/details/76470475
https://www.cnblogs.com/changyifei-8/p/11064445.html
https://www.cnblogs.com/louyefeng/p/9430415.html
https://www.cnblogs.com/guodengjian/articles/9134944.html

猜你喜欢

转载自blog.csdn.net/JaysonWong/article/details/105269965