python 函数之作用域、闭包

一、查看全局变量和局部变量

在Python中,可以使用内置函数locals()globals()查看函数的名称空间

locals()函数返回当前局部作用域的名称空间,包括函数内部定义的变量、函数和其他对象。

globals()函数返回全局作用域的名称空间,包括全局变量、函数和其他对象。

1、globals ()

{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x7fea80168710>, '__spec__': None, '__annotations__': \
{}, '__builtins__': <module 'builtins' (built-in)>, '__file__': '/Users/sanpangdan/Desktop/python_fullstack/随机抽人.py', '__cached__': None, 'openpyxl': <module 'openpyxl' from \
'/Users/sanpangdan/Library/Python/3.6/lib/python/site-packages/openpyxl/__init__.py'>, 'random': <module 'random' from '/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/random.py'>,\
'workbook': <openpyxl.workbook.workbook.Workbook object at 0x7fea80c02978>, 'sheet': <Worksheet "Sheet1">, 'i': 27, 'j': 2, 'x': '钱家'}

2、locals()

{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x7f7a50168710>, '__spec__': None, '__annotations__': \
{}, '__builtins__': <module 'builtins' (built-in)>, '__file__': '/Users/sanpangdan/Desktop/python_fullstack/随机抽人.py', '__cached__': None, 'openpyxl': <module 'openpyxl' from \
'/Users/sanpangdan/Library/Python/3.6/lib/python/site-packages/openpyxl/__init__.py'>, 'random': <module 'random' from '/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/random.py'>, \
'workbook': <openpyxl.workbook.workbook.Workbook object at 0x7f7a50c02978>, 'sheet': <Worksheet "Sheet1">, 'i': 5, 'j': 2, 'x': ''}

二、名称空间的作用域

1、作用域:变量能够作用的范围

内置的名称空间

在程序的任何阶段任何位置都可以使用(全局有效)

全局的名称空间

在程序的任何阶段任何位置都可以使用(全局有效)

局部的名称空间

在函数内部有效(局部有效)

名称空间的加载顺序如下:

  1. 内置名称空间(Built-in Namespace):这是Python解释器启动时自动加载的名称空间,包含了内置函数、异常和其他对象。这些名称可以直接在任何地方使用,无需导入任何模块。

  2. 全局名称空间(Global Namespace):全局名称空间包含在整个程序中定义的全局变量、函数和类。全局名称空间在程序启动时创建,并在程序结束时销毁。它的加载顺序是从上到下逐行执行源代码,遇到定义语句时,将相应的名称添加到全局名称空间中。

  3. 模块名称空间(Module Namespace):每个导入的模块都有自己的模块名称空间,用于隔离和组织模块内的对象。当导入模块时,解释器会执行模块的代码,并创建一个模块对象,其中包含了模块的名称空间。模块的加载顺序是从上到下逐行执行模块的源代码。

  4. 局部名称空间(Local Namespace):局部名称空间是在函数或方法被调用时动态创建的。每当函数或方法被调用时,都会创建一个新的局部名称空间,用于存储局部变量、参数和内部函数。局部名称空间的加载顺序是在函数或方法被调用时创建,并在函数或方法执行结束时销毁。

查找名字的顺序是按照以下规则进行:

  1. 当前名称空间:首先搜索当前名称空间,包括局部名称空间、类名称空间或全局名称空间,具体取决于当前代码所在的上下文。

  2. 作用域链(Scope Chain):如果名称在当前名称空间中找不到,Python会继续搜索包含当前名称空间的外部作用域,直到找到名称或搜索到最外层的全局名称空间。

  3. 全局名称空间:如果名称在局部名称空间和外部作用域中都找不到,Python会在全局名称空间中搜索名称。

  4. 内置名称空间:如果名称在全局名称空间中也找不到,Python会在内置名称空间中搜索名称。

这种搜索变量的过程被称为LEGB规则(Local - Enclosing - Global - Built-in)

  • L:局部命名空间(Local Namespace)
  • E:嵌套命名空间(Enclosing Namespace),比如一个函数中嵌套的函数
  • G:全局命名空间(Global Namespace)
  • B:内置命名空间(Built-in Namespace)

2、global和nonlocals关键字

global的使用:单个函数调用函数之外全局变量场景

在函数的内部不能直接修改外部的变量

  •  在函数的内部修改外部的不可变的数据类型(如字符串)需要使用global关键字声明
  •  在函数的内部修改外部的可变类型的数据(如列表)不需要使用global关键字的声明

nonlocal的使用:函数嵌套函数,内层函数调用外层函数场景

  • 在函数内部的局部修改外部的局部的不可变类型的数据使用关键字nonlocal关键字声明
  • 在函数内部的局部修改外部的局部的可变类型的数据不需要使用关键字nonlocal声明

三、函数对象:函数名

函数名的使用有四种用法

1. 函数名可以当成变量名来使用

函数名对应的就是一个内存地址

def index(): # index它是属于全局的
    print('index')

# """函数名就是函数的内存地址"""

print(index)

index() # 直接调用函数
a = index
a() # index()

2、函数名也可以当成函数的实参

def index():
    print('from index')

def func(func_name):
    print('from func')
    func_name() # 相当于是index()

func(index)

3. 函数名也可以当成函数的返回值

def func():
    print('from func')
# return index() # None
    return index
res=func() # res是函数的内存地址
res() # index()
print(res)

4、函数名也可以当成容器类型的元素

def index():
    print('from index')

# l = [1, 2, 3, index()]
l = [1, 2, 3, index] # [1, 2, 3, <function index at 0x0000021226FCE310>]
print(l)
l[3]() # 调用index函数

四、函数的嵌套使用

1、函数之间互相调用,函数里面调用函数
2、常用场景
定义一个函数,功能是比较两个数的大小

def my_max(a, b):
    if a > b:
        return a
    return b

# res = my_max(1, 10)
# print(res)

# 2. 定义一个函数,比较4个数的大小
def many_max(a, b, c, d):
    res = my_max(a, b)  # res是a和b中的最大的
    res1 = my_max(res, c)  # res1就是abc中的最大值
    res2 = my_max(res1, d)  # res2就是abcd中的最大值
    print(res2)
    return res2

ret=many_max(1, 2, 3, 4)
print(ret)

3、闭包函数

什么是闭包函数? (闭包函数是一个整体,只有同时满足闭和包的条件才算)
闭:闭就是函数内部定义函数,至少两层函数

def outer():
  def inner():
  pass

包:内部的函数使用外部函数名称空间中的名字

闭包函数使用场景?

闭包函数其实是给函数传参的第二种方式!

一次传参多次调用

(技巧:内层函数 return  内层函数的函数名

def outer(a, b):
    def my_max():
        if a > b:
            return a  # 不同分支的 return
        return b

    return my_max

res = outer(2, 20)  # outer 函数不会直接执行my_max(my_max没有被调用),而是返回return my_max的函数名
print(res)  # my_max
print(res())  # my_max()
res1 = outer(3, 30)
print(res1)
print(res1())

五、多分支问题优化

if elif  分支多了会影响代码的可读性和维护性。当有太多的分支时,代码会变得复杂,难以理解和调试。使用太多的分支还可能导致性能下降,因为每个分支都需要进行条件判断。

关键点:将每个小功能定义成函数,将可选序号和函数名组成字典,序号为 k,函数名为 v。

def register():
    print('register')

def login():
    print('login')

def transfer():
    print('login')

def withdraw():
    print('login')

。。。

func_dict = {
    '1': register,
    '2': login,
    '3': transfer,
    '4': withdraw,
    '5': shopping,
    '6': shopping1
}
while True:
    print("""
        1. 注册功能
        2. 登录功能
        3. 转账功能
        4. 提现功能
        5. 购物功能
    """)
    choice = input('请输入你的选择:').strip()
    if choice in func_dict:
        # func_name = func_dict.get(choice)
        # func_name()
        func_dict.get(choice)()

 

  

 

posted @ 2023-05-29 15:54  凡人半睁眼  阅读(55)  评论(0编辑  收藏  举报