Python基础08-名称空间与作用域

2022-09-26 11:34:03 浏览数 (1)

  • 函数对象
  • 函数嵌套
  • 名称空间与作用域
  • 闭包函数

-曾老湿, 江湖人称曾老大。


-多年互联网运维工作经验,曾负责过大规模集群架构自动化运维管理工作。 -擅长Web集群架构与自动化运维,曾负责国内某大型金融公司运维工作。 -devops项目经理兼DBA。 -开发过一套自动化运维平台(功能如下): 1)整合了各个公有云API,自主创建云主机。 2)ELK自动化收集日志功能。 3)Saltstack自动化运维统一配置管理工具。 4)Git、Jenkins自动化代码上线及自动化测试平台。 5)堡垒机,连接Linux、Windows平台及日志审计。 6)SQL执行及审批流程。 7)慢查询日志分析web界面。


函数对象


函数是第一类对象

指的是函数名指向的值(函数)可以被当做数据去使用

代码语言:javascript复制
def func(): #func=函数的内存地址
    print('from func')

print(func)

1、可以被引用

代码语言:javascript复制
def func(): #func=函数的内存地址
    print('from func')

print(func)


# 引用
f=func
print(f)
f()

2、可以当作参数传递

代码语言:javascript复制
age=18

def func(): #func=函数的内存地址
    print('from func')

print(func)

def bar(x):
    print(x)

bar(age)  #既然我可以往里传递一个age,同样我也可以把函数传递进去

#传递func函数
bar(func)

3、可以当做一个函数的返回值

代码语言:javascript复制
age=18

def func(): #func=函数的内存地址
    print('from func')


def bar(x):
    return x

res=bar(age)
print(res)

res=bar(func)
print(res)

4、可以当作容器类型的元素

代码语言:javascript复制
age=18

l=[age]
print(l)

def func(): #func=函数的内存地址
    print('from func')

l=[age,func,func()]

print(l)

# 结果
[18, <function func at 0x101dd2ea0>, None]

例子:

代码语言:javascript复制
def get():
    print('from get')

func_dic={
    'get':get
}

print(func_dic)
func_dic['get']()

LowB 购物车

代码语言:javascript复制
def login():
    print('sigin')

def register():
    print('register')

def shopping():
    print('shopping')

def pay():
    print('pay')

msg="""
0 退出
1 登录
2 注册
3 购物
4 支付
"""
while True:
    print(msg)
    choice=input('请输入您的操作:').strip()
    if choice == '0':break
    if choice == '1':
        login()
    elif choice == '2':
        register()
    elif choice == '3':
        shopping()
    elif choice == '4':
        pay()
    else:
        print('输入错误指令,请重新输入')
        
## 这个代码,如果购物车功能有100个,要写100个判断?

使用函数对象,第4个特性

利用该特性 优雅的取代多分支的if

代码语言:javascript复制
def login():
    print('sigin')

def register():
    print('register')

def shopping():
    print('shopping')

def pay():
    print('pay')

func_dic={
    '1':login,
    '2':register,
    '3':shopping,
    '4':pay
}

msg="""
0 退出
1 登录
2 注册
3 购物
4 支付
"""
while True:
    print(msg)
    choice=input('请输入您的操作:').strip()
    if choice == '0':break
    if choice in func_dic:
        func_dic[choice]()
    else:
        print('输入错误指令')

函数嵌套

函数的嵌套分为两大类: 1.函数的嵌套调用 在调用一个函数的过程中,函数内部代码有调用了其他函数

代码语言:javascript复制
def max(x,y):
    return x if x > y else y

def max4(a,b,c,d):
    res1=max(a,b)
    res2=max(res1,c)
    res3=max(res2,d)
    return res3
print(max4(1,2,3,4))

2.函数的嵌套定义 在一个函数内部,又定义了另外一个函数或多个函数

代码语言:javascript复制
def f1():
    def f2():
        def f3():
            print('from f3')
        f3()
    f2()

f1()
f3() #报错,为何?请看下一小节

函数在哪一级定义,就在哪一级调用


计算圆形

代码语言:javascript复制
# 首先我们需要用到 π
from math import pi
print(pi)

def area(radius):
    return pi * (radius ** 2)

def perimeter(radius):
    return 2 * pi  * radius

如果这个写都和圆有关系,没必要定义两个函数

代码语言:javascript复制
from math import pi
# print(pi)



def circle(radius,action=0):
    """
    圆形相关运算
    :param radius: 半径
    :param action: 0 代表求面积,1代表求周长
    :return: 面积或者周长
    """

    def area(radius):
        return pi * (radius ** 2)

    def perimeter(radius):
        return 2 * pi  * radius

    if action == 0:
        res=area(radius)
    elif action == 1:
        res=perimeter(radius)

    return res

print(circle(10,0))

print(circle(10,1))

名称空间与作用域


什么是名称空间

名称空间(namespace):存放名字的地方,三种名称空间,(之前遗留的问题x=1,1存放于内存中,那名字x存放在哪里呢?名称空间正是存放名字x与1绑定关系的地方)

名称空间分为三大类 1.内置名称空间 2.全局名称空间 3.局部名称空间


名称空间的加载顺序

内置 -> 全局 -> 局部

python test.py 1、python解释器先启动,因而首先加载的是:内置名称空间

例:下面的几个功能,我们不需要定义就可以直接使用,因为python内置了

代码语言:javascript复制
len
max
print

2、执行test.py文件,然后以文件为基础,加载全局名称空间

例:下面名字中 x、y、z、b、foo都存在 全局名称空间中

代码语言:javascript复制
x=1
y=2
if x==1:
    z=3

while True:
    b=4
    break
    
def foo():
    m=3

3、在执行文件的过程中如果调用函数,则临时产生局部名称空间

例:foo内的m这个名字,一定是局部的。

代码语言:javascript复制
x=1
y=2
if x==1:
    z=3

while True:
    b=4
    break
    
def foo():
    m=3

生命周期

内置名称空间:在解释器启动时则生效,解释器关闭则失效 全局名称空间:在解释器执行python文件时则生效,文件执行完毕后则失效 局部名称空间:只在调用函数时临时产生该函数的局部名称空间,该函数调用完成之后则失效(但是局部名称空间不一定都会生效,如果函数没有被调用,则不生效)


名字的查找顺序

代码语言:javascript复制
局部名称空间--->全局名称空间--->内置名称空间

#需要注意的是:在全局无法查看局部的,在局部可以查看全局的,如下示例

# max=1
def f1():
    # max=2
    def f2():
        # max=3
        print(max)
    f2()
f1()
print(max)


# 例子1
x=111

def f1():
    x=222
    def f2():
        def f3():
            x=444
            print(x)
        x=333
        f3()
    f2()

f1()

结果 x=444

# 例子2
x=111

def f1():
    x=222
    def f2():
        def f3():
            #x=444
            print(x)
        x=333
        f3()
    f2()

f1()

结果 x=333

# 名字的查找顺序 ,在函数的定义阶段就已经固定死了,与函数的调用位置无关,也就是说,无论在任何地方调用函数,都必须回到当初定义函数的位置去确定名字的查找关系

## 例:下面代码,我们不能在outer函数外部直接调用inner,如何打破这个规则,直接在外部调用inner函数

x=111
def outer():
    def inner():
        print('from innser',x)

# 解决方案,我们把inner给return,返回inner函数的内存地址
x=111
def outer():
    def inner():
        print('from innser',x)
    return inner

f=outer()
f()

# 如此一来,我们就打破了函数的层级限制

# 那么问题来了,请问,下面代码,x是几?
x=111
def outer():
    def inner():
        print('from innser',x)
    return inner

f=outer()
x=222
f()

# 在一个函数内部,调用到另一个函数内部的函数
x=111
def outer():
    def inner():
        print('from innser',x)
    return inner

f=outer()

def func():
    f()
func()

# 问题又又来了,下面代码,x是几?
x=111
def outer():
    def inner():
        print('from innser',x)
    return inner

f=outer()

def func():
    x=333
    f()
func()


# 问题又又又来了,下面代码,x是几?
x=111
def outer():
    def inner():
        print('from innser',x)
    return inner

f=outer()

def func():
    x=333
    f()
func()
x=444

func()

作用域

1.作用域即范围 - 全局范围(内置名称空间与全局名称空间属于该范围):全局存活,全局有效 - 局部范围(局部名称空间属于该范围):临时存活,局部有效

2.作用域关系是在函数定义阶段就已经固定的,与函数的调用位置无关,如下:

代码语言:javascript复制
x=1
def f1():
    def f2():
        print(x)
    return f2
x=100
def f3(func):
    x=2
    func()
x=10000
f3(f1())

3.查看作用域:globals(),locals()

代码语言:javascript复制
LEGB 代表名字查找顺序: locals -> enclosing function -> globals -> __builtins__
locals 是函数内的名字空间,包括局部变量和形参
enclosing 外部嵌套函数的名字空间(闭包中常见)
globals 全局变量,函数定义所在模块的名字空间
builtins 内置模块的名字空间

# 下面代码x=1
x=1
def foo():
    x=2

foo()
print(x)

# 如果我使用global
x=1
def foo():
    global x
    x=2

foo()
print(x)

闭包函数


什么是闭包函数

闭:指的是闭包函数是一个定义在一个函数内部的函数 包:该内部去函数包含对外层函数作用域名字的引用

需要结合函数对象的概念,将闭包函数返回到全局作用域去使用,从而打破函数的层级限制

代码语言:javascript复制
def outter():
    x=111
    def inner():
        print(x)
    return inner

f=outter()  #f=outer内部的inner函数

f()

模拟浏览器访问页面

代码语言:javascript复制
# 安装requests模块
MacBook-pro:~ driverzeng$ pip3 install requests
代码语言:javascript复制
# 爬百度
import requests

response=requests.get('https://www.baidu.com')

if response.status_code == 200:
    print(response.text.encode('utf-8'))

# 如果我现在还要爬京东呢?再复制一遍?
import requests

response=requests.get('https://www.baidu.com')

if response.status_code == 200:
    print(response.text.encode('utf-8'))

response=requests.get('https://www.jd.com')

if response.status_code == 200:
    print(response.text.encode('utf-8'))
    
# 太智障了吧,所以我们使用函数
import requests

def get(url):
    response=requests.get(url)

    if response.status_code == 200:
        print(response.text.encode('utf-8'))

get('http://www.baidu.com')
get('http://www.baidu.com')
get('http://www.jd.com')


# 高端解决方案,闭包
import requests

def outter(url):
    # url='https://www.baidu.com'
    def get():
        response = requests.get(url)
        if response.status_code == 200:
            print(response.text.encode('utf-8'))
    return get

baidu=outter('http://www.baidu.com')
baidu()
baidu()
baidu()
baidu()

jd=outter('http://www.jd.com')
jd()
jd()
jd()
jd()

但是我们每次都还要传递参数,有没有什么方法不用传参?默认参数,全局变量都麻烦


为何要用闭包函数

所以此时我们要用到 闭包函数,闭包函数提供了 一种为函数体传值的解决方案。

代码语言:javascript复制
#内部函数包含对外部作用域而非全局作用域的引用

#提示:之前我们都是通过参数将外部的值传给函数,闭包提供了另外一种思路,包起来喽,包起呦,包起来哇

        def counter():
            n=0
            def incr():
                nonlocal n
                x=n
                n =1
                return x
            return incr

        c=counter()
        print(c())
        print(c())
        print(c())
        print(c.__closure__[0].cell_contents) #查看闭包的元素

如何使用闭包函数

代码语言:javascript复制
#闭包的意义:返回的函数对象,不仅仅是一个函数对象,在该函数外还包裹了一层作用域,这使得,该函数无论在何处调用,优先使用自己外层包裹的作用域
#应用领域:延迟计算(原来我们是传参,现在我们是包起来)
    from urllib.request import urlopen

    def index(url):
        def get():
            return urlopen(url).read()
        return get

    baidu=index('http://www.baidu.com')
    print(baidu().decode('utf-8'))

0 人点赞