第八章:函数与命名空间

函数的语法结构

def func(name, age=None):
    '''
    函数注释
    def 函数关键字
    func 函数名
    :return: 返回值
    :param name: 位置参数
    :param age: 关键字传参
    :return:返回值
    '''
    pass # pass 不做任何处理,此位置为函数体代码
    return # 返回值
name = 'ysg'
func(name) # 调用函数, 函数有参数则必须传参,关键字参数可以不传,因为已有指定的值了

函数的分类

1.空函数
	函数体代码为空 使用的pass或者...补全的
 	空函数主要用于项目前期的功能框架搭建
 	 def register():
        """注册功能"""
        pass
2.无参函数
	 定义函数的时候括号内没有参数
	 def index():
        print('from index function')
3.有参函数
	定义函数的时候括号内写参数 调用函数的时候括号传参数
 	def func(a):
    	  print(a)

函数的返回值

1.什么是返回值
	调用函数之后返回给调用者的结果
2.如何获取返回值
	变量名 赋值符号 函数的调用
 	res = func()  # 先执行 func 函数 然后将返回值赋值给变量 res
3.函数返回值的多种情况
	3.1.函数体代码中没有 return 关键字 默认返回 None
 	3.2.函数体代码有 return 如果后面没有写任何东西还是返回 None
 	3.3.函数体代码有 return 后面写什么就返回什么
 	3.4.函数体代码有 return 并且后面有多个数据值 则自动组织成元组返回
 	3.5.函数体代码遇到 return 会立刻结束

函数的参数

形参与实参

形式参数
在函数定义阶段括号内填写的参数 简称'形参'
实际参数
在函数调用阶段括号内填写的参数 简称'实参'

位置参数

站在实参的角度上
 按照位置传参
 按照关键字传参
 混着用可以:但是 必须先按照位置传参,再按照关键字传参数
 注意:不能给同一个变量传多个值

站在形参的角度上
 位置参数:必须传且有几个参数就传几个值,必须一一对应
 默认参数:可以不传,如果不传就是用默认的参数,如果传了就用所传的参数

#带默认参数的函数
def classmate(name, sex='男'):    # 参数中有俩个值:形参,默认参数性别为男
    print('%s: %s' % (name,sex))   # 适合于在输入某些值时,大多的值是相同的情况
classmate('诺娃', "女")
classmate('雷诺')
只有调用函数的时候
  按照位置传:直接写参数的值
  按照关键字:关键字 = 值

定义函数的时候:
 位置参数:直接定义参数
 默认参数,关键字参数:参数名 = '默认的值'
 动态参数:可以接受任意多个参数
 	参数名之前加 *,习惯参数名 args 
  参数名之前加 **,习惯参数名 kwargs 
顺序:位置参数,*args,默认参数,**kwargs 

动态参数

动态参数有两种:可以接受任意个参数
  1. *args 只接收位置参数的值,不能接收关键字传参的值
    组织成一个元组
  2. **kwargs 只能接收关键字传参的值
    组织成一个字典
  3. args 必须在 kwargs 之前

###动态参数
#求任意数的和
def sum(*args): #关键字“*”
    n=0
    for i in args:
        n+=i
    return n

print(sum(12,23,13))
print(sum(14,25,36,123))

#注意:**kwargs 只能接受关键字传参
def func(**kwargs):  #关键字“**”
    print(kwargs)

func(a = 1,b = 2,c = 3)
func(a = 1,b = 2)
func(a = 1)
func(1,2,c = 3)  #报错 **kwargs 只能接收关键字传参的值

默认参数的陷阱

def qqxing(f = []):
    f.append(1)
    print(f)
qqxing([])  # 表示使用了自定义参数 [1]
qqxing()    # 表示使用了默认参数 所以下面每次调用参数都在同一个列表中递增 [1, 1]
qqxing()    # [1, 1, 1]
qqxing()    # [1, 1, 1]


def qqxing2(k,f={}):
    f[k] = 'v'
    print(f)

qqxing2(1)  # {1: 'v'}
qqxing2(2)  # {1: 'v', 2: 'v'}
qqxing2(3)  # {{1: 'v', 2: 'v', 3: 'v'}

函数的多种用法

函数名可以用于:赋值、容器类型的元素、返回值、参数
函数名其实绑定的也是一块内存地址 只不过该地址里面存放的不是数据值而是一段代码 函数名加括号就会找到该代码并执行

#函数名的赋值与作为容器类型的元素使用
def func():
    print(123)

# func()     # 函数名就是内存地址
func2 = func  # 函数名可以用于赋值
func2()      # 所以结果为 123

l = [func,func2]    # 函数名可以作为容器类型的元素 如下面 for 循环
for i in l:
    i()
# 函数名作为返回值与参数
def func():
    print(123)

def func2(f):
    f()
    return f  # 函数名可以作为函数的返回值
func2(func)   # 函数名可以作为函数的参数
func3 = func2(func)  # 用于接收返回值
func3()

闭包函数

闭包常用形式:接收返回值,在一个函数的内部去使用它外部的变量
定义:

1. 定义在函数内部的函数
2. 用到外部函数名称空间中的名字
# 采用接收返回值的形式,来代替 globals 的使用
def outer():
    a = 1 
    def inner():
        print(a)
    return inner
inn = outer()
inn()

# 在这里 a = 1 不会因为 outer() 函数的结束而消失,因为 inn() 后面可能会用到,所以才不会消失
# 使用闭包的好处在于 我保护了 a = 1 这个变量,它既不是全局变量,我又可以在使用它时,去使用它
# 延长了 a = 1 的声命周期 节省了创建和删除变量 a = 1 的时间

命名空间

三种命名空间

内置命名空间 —— python 解释器
  就是 python 解释器一启动就可以使用的名字存储在内置命名空间中
  内置的名字在启动解释器的时候被加载进内存里
全局命名空间 —— 我们写的代码但不是函数中的代码
  是在程序从上到下被执行的过程中依次加载进内存的
  放置了我们设置的所有变量名和函数名
局部命名空间 —— 函数
  就是函数内部定义的名字
  当调用函数时 才会产生这个名称空间 随着函数执行的结束 这个命名空间随之消失

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

局部命名空间的名字 不可以在全局空间中被调用

def func():
    a = 1
func()
print(a) #报错——NameError: name 'a' is not defined

命名空间的使用原则

​ 1、在正常情况下,直接使用内置的名字
​ 2、当我们在全局定义了和内置命名空间中同名的名字时,会使用全局的名字
​ 3、在嵌套中,当自己的这一级中有所需的名字时,就不去找上一级找了
​ 4、在嵌套中,如果自己没有 就找上一级要 上一级没有再找上一级 如果内置的名字空间也没有 就报错
​ 5、多个函数应该拥有多个独立的局部名字空间,不互相共享

名称空间存活周期及作用范围(域)

globals()   查看全局作用域中的名字
locals()    查看当前作用域中的名字
global      把全局变量拿到局部来用
nonlocal    把离他最近的一层的变量拿过来, 不会找全局
存活周期
	内置名称空间
  		python解释器启动则创建 关闭则销毁
 	全局名称空间
    	py文件执行则创建 运行结束则销毁
 	局部名称空间
    	函数体代码运行创建 函数体代码结束则销毁(类暂且不考虑)
作用域
	内置名称空间
    	解释器级别的全局有效
 	全局名称空间
    	py文件级别的全局有效
 	局部名称空间
    	函数体代码内有效

例子:

globals() 与 locals()

a = 100     # 全局变量
def run():  # 全局变量
    a = 50      # 按就近原则, 局部变量
    print(a)     # 局部变量
    print('这是一个局部变量') # 局部变量
    print(globals())  # {'a': 100 ...}    globals() 获取到全局作用域(内置,全局)中的所有名字
    print(locals())  # {'a': 50}          locals()  查看当前作用域中的所有名字

if __name__ == '__main__':
    run()

global 和 nonlocal

#global 声明过得变量,让在函数外部的 print() 可以进行打印
a = 1
def func():
    global a   # 使用 global 进行声明
    a = 2
func()
print(a)       # 输出结果为 2


b=1
def func():
    # global b
    b = 2
func()
print(b)  # 此时结果还是 1,因为在内部的任何操作 不影响外部的

# nonlocal 找局部作用域中, 离他最近的那个变量引入进来
a = 10
def func():
    a = 40
    def func1():
        a = 50
        def func2():  # nonlocal a 只要是在局部中就可以引进来
            nonlocal a  # 找局部作用域中, 离他最近的那个变量引入进来,没找到 则报错(不常用)
            a = 20  # 把上面的 a = 40 进行赋值,所以上面的a = 20
            print(a)  # 20
        func2()
        print(a) # 20
    func1()
    print(a)    # 40
    
if __name__ == '__main__':
    func()

globals 函数的安全性

你一定会觉得 globals 函数在很多时候很好用(我也是这么觉得着)
可以 globals 这个函数涉及到代码的安全性 不推荐使用
可以通过传参和接收返回值 来完成原本使用 global 完成的事情

# 通过传参和接收返回值来时实现调用全局变量
a = 1
def func():
    global a
    a = 3

func()
print(a)  # 3

# 改为:
a = 2
def func(a):
    a += 1
    return a

print(func(a)) # 此时返回 3

名字的查找顺序

涉及到名字的查找 一定要先搞明白自己在哪个空间
1.当我们在局部名称空间中的时候
	局部名称空间 >>> 全局名称空间 >>> 内置名称空间
2.当我们在全局名称空间中的时候
	全局名称空间 >>> 内置名称空间
ps:其实名字的查找顺序是可以打破的 

查找顺序案例

1.相互独立的局部名称空间默认不能够互相访问
	 def func1():
    	name = 'jason'
    	print(age)

    def func2():
       age = 18
       print(name)
2.局部名称空间嵌套
	先从自己的局部名称空间查找 之后由内而外依次查找
"""
函数体代码中名字的查找顺序在函数定义阶段就已经固定死了
	x = '干饭了'
    def func1():
        x = 1
        def func2():
            x = 2
            def func3():
                print(x)
                x = 3
            func3()
        func2()

    func1()
"""

作业

1.使用函数将员工管理系统和文件进阶注册登录封装
2.判断下列 money 的值是多少并说明理由,思考如何修改而不是新增绑定关系
	money = 100
	def index():
        money = 666	
 	print(money)
    
   

	money = 100
 	def func1():
        money = 666
        def func2():
            money = 888
        func2()
   print(money)

解答

# 1.使用函数将员工管理系统和文件进阶注册登录封装
# 员工管理系统
def del_user(dit):
    print('删除用户名')
    del_name = input('需要删除的用户名:').strip()
    dit.pop(del_name)
    print('{} 已删除'.format(del_name))

def see_all(dit):
    print('查看所有用户名')
    print('  '.join(dit.keys()))

def add_user(dit):
    print('添加用户')
    name = input('用户名:').strip()
    pawd = input('密码:').strip()
    if name in dit.keys():
        print('用户名重复')
    else:
        dit[name] = pawd
        print('添加成功')

def run():
    info = '''
    输入1 执行添加用户名功能
    输入2 执行查看所有用户名功能
    输入3 执行删除指定用户名功能
        '''
    dit = {}
    while 1:
        print(info)
        num = int(input('请输入序号:').strip())
        if num == 1:
            add_user(dit)
        elif num == 2:
            see_all(dit)
        elif num == 3:
            del_user(dit)
        else:
            print('暂无该功能')
        exit = input('是否重新选择(N/Y):').strip().upper()
        if exit == 'Y':
            pass
        else:
            print('退出系统')
            break

if __name__ == '__main__':
    run()

# 文件进阶注册登录
def sign_in():
    print('开始登录')
    with open(r'F:\上海_脱产\userinfo.txt', 'r', encoding='utf-8') as fa:
        total = {num: line.split('|') for num, line in enumerate(fa.read().splitlines()) if num > 0}
    name = input('请输入用户名:').strip()
    pawd = input('请输入密码:').strip()
    for val in total.values():
        if name == val[0] and pawd == val[1]:
            info = '''
                        登录成功!
                        用户名:{}
                        年龄:{}
                        工作:{}
                        '''.format(val[0], val[2], val[3])
            print(info)

def logon():
    with open(r'F:\上海_脱产\userinfo.txt', 'a', encoding='utf-8') as fa:
        name = input('请输入用户名:').strip()
        pawd = input('请输入密码:').strip()
        age = input('请输入年龄:').strip()
        job = input('请输入工作:').strip()
        line = '{}|{}|{}|{}\n'.format(name, pawd, age, job)
        fa.write(line)


def run():
    while 1:
        info = '''
            1.注册功能
            2.登录功能
            '''
        print(info)
        num = input('请输入功能序号(q:退出):').strip().lower()
        if num == 'q': break
        if num == '1':
            logon()
        elif num == '2':
            sign_in()
        else:
            print('无此功能')


if __name__ == '__main__':
    run()
# 2.判断下列 money 的值是多少并说明理由,思考如何修改而不是新增绑定关系
money = 100
def index():
    money = 666
print(money)    # 100


money = 100
def func1():
    money = 666
    def func2():
        money = 888
    func2()

print(money)    # 100
posted @ 2022-10-10 23:33  亦双弓  阅读(446)  评论(0)    收藏  举报