Python函数(下)

python函数(下)

本文将链接上一章的python函数的参数继续讲下去

  • 函数的参数

  • 名称空间与作用域

  • global与nonlocal

  • 函数名的多种用法

  • 函数的参数

函数参数的概念

  1. 形式参数
    函数定义阶段括号内填写的参数
  2. 实际参数
    函数调用阶段括号内填写的参数

1. 位置参数

  1. 位置形参
    在定义函数时,在括号内从左到右依次填写的变量名
# 如何子代码只有一行或者很简短可以直接在冒号后面编写不用换行
def function(a, b, c):pass
	pass
  1. 位置实参
    在函数调用时,在括号内依次从左往右填写的参数值
function(1, 2, 3)

位置参数的注意事项


# 定义一个函数并且写上ab两个位置形参
def func1(a, b):
	pass(a, b)

1. func1(1, 2)  # 按照位置从左往右依次传值
2. func1(1)  # 少了一个传入的值,报错
3. func1(1, 2, 3)  # 多了一个值,还是报错
4. func1(b=1, a=2)  # 关键字参数,将1传给b,2传给a
5. func1(b=1, 2)  # 关键字参数一定要跟在位置参数后面否则会报错
6 func1(2, b=1)  # 这里关键字参数在位置参数后面就可以正常传入
7. func1(1, a=2, b=3)  # 同一个形参在调用的时候不能传入多个值

name = 'wesley'
pwd = 123

8. func1(name, pwd)
9. func1(a=name, b=pwd)
# 实参没有固定的定义,可以传入数据值,也可以传入绑定了数据值的变量名
"""
越短越简单的越靠前
越长越复杂的越靠后
但是遇到了下列的情况除外
	同一个形参在调用的时候不能多次赋值
"""
  • 默认参数

默认参数其实就是提前定义好的关键字形参,别名叫做默认参数,用户可以传值也可以不用传

"""
默认参数的定义也是遵循越短越简单越靠前
"""

def register(name, age, gender='male'):
	print(f"""
		-------user info--------
		name:{name}
		 age:{age}
	  gender:{gender}
		------------------------
		""")
register('wesley', 18,)
# 结果
	-------user info--------
	name:wesley
	age:18
	gender:male
	------------------------
register('andy', 18, 'female')
# 结果
	-------user info--------
	name:andy
	age:18
	gender:female
	------------------------
register('dev', 20, gender='female')
# 结果
	-------user info--------
	name:dev
	age:20
	gender:female
	------------------------
  • 可变长形参
# 定义一个函数,并写入可变长形参
def func1(*a):
	print(a)
func1()  # ()
func1(1)  # (1,)
func1(1,2)  # (1, 2)
"""
*号在形参中的作用是用于接收多余的位置参数,组织为元组赋值给*号后面的变量名
"""
def func2(b, *a):
    print(a, b)
# func2()  # 函数至少需要一个参数给到b
func2(1)  # () 1
func2(1, 2, 3, 4)  # (2, 3, 4) 1




# 定义一个函数 并写入**a可边长形参
def func3(**k):
	print(k)

# 不传值默认就是空字典
func3()  # {}
# 接受位置参数,并以字典的形式打印
func3(a=1)  # {'a': 1}
# 接受位置参数,并以字典的形式打印
func3(a=1, b=2, c=3)  # {'a': 1, 'b': 2, 'c': 3}

"""
如果函数中可变参数的前面出现了位置参数,就需要先给位置参数对应的数据值
**号在参数
用于接收多余的关键字参数,组织成字典的形式返回
"""

# 定义一个例子
# 定义一个函数并需要传䘝位置参数
def func5(n, *a, **k):
    print(a, k)


func5()  # 函数至少需要一个参数给到n
func5(1, 2, 3)  # (2, 3) {}
func5(111,a=1, b=2, c=3)  # () {'a': 1, 'b': 2, 'c': 3}
func5(n=111,a=1, b=2, c=3)  # () {'a': 1, 'b': 2, 'c': 3}
func5(a=1, b=2, c=3, n=111)  # () {'a': 1, 'b': 2, 'c': 3}
func5(1, 2, 3, a=1, b=2, c=3)  # (2, 3) {'a': 1, 'b': 2, 'c': 3}

# 在学习python中使用* 和**的使用率很高,在这里,python的默认函数中就有这种函数,比较建议的命名方式是python提供的
*args  # 用于接收多余的位置参数
**kwargs  # 用于接收关键字参数,不能够并以字典的形式返回

  • 可变长实参
- * 可变长实参

def index(a, b, c):
    print(a, b, c)

# 通过列表传值
l1 = [1, 2, 3]
index(*l1)
1 2 3

# 通过字符串传值
name = 'tom'
index(*name)
t o m

# 通过元组传值
t1 = (1, 2, 3)
index(*t1)
1 2 3


# 通过字典传值
d1 = {'name':'wesley', 'age':18, 'salary':10000}
index(*d1)
name age salary

# 注意如果是字典,只会传入键,这一点和for循环类似,这里也可以通过取值的方式将数据类型中的值取出再通过可变长实参赋予函数的形参

- ** 可变长实参
def userinfo(name, age):
    print(name, age)

# 使用字典传值
d1 = {'name':'wesley', 'age':18}
userinfo(**d1)
wesley 18

# *传入的是键,注意这里传入的是字典的值,除了使用字典以外这里还可以使用关键字参数传值

  • 命名关键字参数
'''形参必须按照关键字参数传值>>>:命名关键字参数'''


def index(name, *args, gender='male', **kwargs):
    print(name, args, gender, kwargs)

# index('wesley',1,2,3,4,a=1,b=2)
index('wesley', 1, 2, 3, 4, 'female', b=2)


  • 名称空间

名称空间就是存放变量名和函数名和数据值的绑定关系的地方,被称为名称空间

  • 内置名称空间
    内置名称空间是python解释器在启动时产生的,里面存放着我们使用的内置函数名称
    eg: len print input
    生命周期:
    内置名称空间在python解释器开启时产生,关闭的时候销毁
    作用域:
    内置名称空间在解释器级别全局有效

  • 全局名称空间
    全局名称空间是在我们run一个python文件中产生的,里面存放着定义的变量名类名
    函数名和他们的绑定关系
    生命周期:
    文件运行时产生,运行结束后销毁
    作用域:
    文件级别全局有效

  • 局部名称空间
    局部名称空间是我们在调用函数时,由函数体产生的变量名和绑定关系,局部命名空间将会在函数体代码运行结束后销毁
    生命周期:
    在函数体代码执行时产生,结束后销毁

    作用域:
    代码体内有效

  • 名称空间优先级
    1.当我们在局部名称空间中的时候
    局部名称空间 >>> 全局名称空间 >>> 内置名称空间
    2.当我们在全局名称空间中的时候
    全局名称空间 >>> 内置名称空间
    ps:其实名字的查找顺序是可以打破的

接下来通过下列的代码来画出优先级

下列这个图片是从内向外寻找的一个过程,具体优先级是什么需要看当前的位置


def mylen():
	print(len)

mylen()
<built-in function len>

  • 函数中global与nonlocal

先看代码,在运行代码的过程中解析
通过运行代码可以看出:
global关键字指定全局中的变量名可以在函数中重新赋值

global关键字

1.---code---
money = 666
def index():
    global money
    money = 900

# index()
print(money)
"""
在执行1部分代码时,没有调用函数这里money值为666
"""
2.---code---
money = 666
def index():
    global money
    money = 900

index()
print(money)
"""
在执行2部分代码时调用函数并指定global money,发现全局变量money被修改了
"""

nonlocal关键字

先看代码,在运行代码的过程中解析
通过运行代码可以看出:
nonlo关键字指定全局中的变量名可以在函数中重新赋值

1. ---code---
def index():
    name = 'wesley'
    def inner():
        # nonlocal name
        name = '111'
    inner()
    print(name)
index()

"""
这里可以看出在inner函数没有使用nonlocal name时,调用index()函数
时打印的wesley这个结果
"""
2. ---code---
def index():
    name = 'wesley'
    def inner():
        nonlocal name
        name = '111'
    inner()
    print(name)
index()

"""
通过2部分代码可以看出在inner函数中指定了nonlocal 这个关键字需要修改
的变量为name,所以下面的name=111相当于去上层的局部空间中将绑定关系
改为了111,所以这里打印的是111这个结果
"""
  • 函数名的多种用法

函数名绑定的是一块内存地址,该地址存放的就是函数体的代码,正常的使用函数名加括号就会找到该内存地址中存放的函数体代码并执行

  1. 函数名作为变量名赋值
def index():pass
res = index
print(res)

<function index at 0x10025b280>
"""
结论: 可以将函数名传给变量名使用,此时打印变量名就相当于打印函数名
     结果就是该函数的内存地址
"""
  1. 函数名可以当做其他函数的参数
def index():
    print('from index')

def func(a):
    print(a)
    a()
func(index)

<function index at 0x104f1f280>
from index
"""
1. 定义函数index() 功能打印from index
2. 定义函数func() 功能是打印传入的值
3. 调用func函数并将index函数名传入给a
4. func函数中的print打印a,就是打印index这个函数名的内存地址
5. a()就是调用了index函数并执行index函数的代码体
6. 最后index代码体执行结束打印from index
"""
  1. 函数名可以当做函数的返回值
1.
def index():
    print('from index')

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

res = func()
print(res)
res()

from func
<function index at 0x10046b280>
from index


"""
1. 定义index函数功能是打印from index
2. 定义func函数功能是打印from func,并且将index函数名作为返回值
3. 调用func函数(调用结果打印from func)并将返回值赋值给res,当前返回值为index函数名
4. print打印res就相当于打印index函数名的内存地址
5. res()相当于使用index()调用index函数结果为from index
"""

2. 函数名本身作为本函数的返回值
def index():
    print('from index')

def func():
    print('from func')
    return func

res = index()
print(res)
res()

from index
None
Traceback (most recent call last):
  File "/Users/wesley/PycharmProjects/python-1/装饰器.py", line 49, in <module>
    res()
TypeError: 'NoneType' object is not callable

"""
1. 定义index函数功能为打印from index
2. 定义func函数功能为打印from func,并将自己的函数名本身作为返回值
3. 执行index函数打印from index,并将index的默认返回值None返回给res
4. 打印res,相当于打印index的返回值,当前为空
5. 使用res()调用,空返回值无法调用函数,导致报错
6. 此代码中的func函数无任何作用,因为和res无任何绑定关系
"""
  1. 函数名可以当做容器类型的数据(容器:可以存放多个数据的数据类型)项目演示如下:
# ATM项目框架
def register():
    print('注册功能')

def login():
    print('登录功能')

def withdraw():
    print('提现功能')

def transfer():
    print('转账功能')

def shopping():
    print('购物功能')

# 定以ATM功能字典
dict_atm = {
    '1':register,
    '2':login,
    '3':withdraw,
    '4':transfer,
    '5':shopping
            }
while True:
    print("""
    1.注册功能
    2.登录功能
    3.提现功能
    4.转账功能
    5.购物功能
    """)
    choice = input('请输入执行个功能: ').strip
    if choice in dict_atm:
        func_name = dict_atm.get(choice)
        func_name()
    else:
        print('此功能不存在')

  • 闭包函数

定义:
在函数内部的函数(函数嵌套)并且用到了外部的函数名称空间中的名字

  1. 被嵌套的函数(闭)
  2. 调用上层函数中的变量或值等(包)
  3. 闭包函数实际是另外一种给函数代码体传参的方式
  1. 下列代码就是一个非常简单的闭包
def index():
    name = 'wesley'
    def inner():
        print(name)

"""
1. inner函数时被index包含起来的函数,符合被包含
2. inner调用了上层函数的name变量,符合调用上层函数名称空间
"""
  1. 代码里缺什么变量名形参里就补什么变量名
def register(name, age):
    print(f"""
    姓名:{name}
    年龄:{age}
    """)
register('wesley', 18)

    姓名:wesley
    年龄:18
  1. 给函数体代码传参方式2:闭包函数
1. def oueter(name, age):
    def register():
    4.  print(f"""
        姓名:{name}
        年纪:{age}
        """)
    return register
2. res = oueter('wesley', 18)
3. res()
5. res = oueter('andy', 50)
6. res()

        姓名:wesley
        年纪:18
        
        姓名:andy
        年纪:50

"""
# 代码执行流程
1. 定义oueter函数,返回值为闭函数
2. 先执行赋值符号右边,向oueter传值,并将返回值register函数名返回给res
3. 使用闭函数返回值调用函数本身执行代码体
4. 打印名字和年龄
5. 6. 通过从新传值打印不同的信息,执行流程从夫1~4
"""

练习题

    
1.判断下列money的值是多少并说明理由 思考如何修改而不是新增绑定关系
	money = 100
	def index():
        money = 666	
 	print(money)
"""
1.函数并没有被调用,也就没有产生局部命名空间,所以函数中的money的值并不存在
2.在python文件中使用print 会直接先找全局命名空间的money,这里直接就找到了,值为100
"""
	money = 100
 	def func1():
        money = 666
        def func2():
            money = 888
        func2()
   print(money)
"""
1.和上一题一样,并没有调用函数,所以这里还是找全局,依旧是100,如果调用了func1函数,那向下看又调用了func2函数,func2的money = 888会被加载,局部命名空间产生,此时函数中的money就有了值,但是print和全局的money同级别所以还是100,除非在func2中加一个print,然后将文件级别的print换成func1(),这个时候才会到局部找到888
"""

posted @ 2025-03-13 13:38  樵夫-  阅读(20)  评论(0)    收藏  举报