函数

引言

我们在写代码时会发现有的由几个字符组成的东西会带着一些功能,可以很好地帮我们编写代码,这个由几个字符组成的东西就是一个函数

有一个列表a = [1, 2, 3, 4, 5]
当我们需要统计列表内数据值的个数时我们首先想到的会用len(a),如果不用len那就需要我们自己编写代码了
count = 0
for i in a:
    count += 1
print(count)
不用len的情况下统计一次,我们可以编写代码,当我们需要统计几十上百次的时候,如果每次都要编写代码那就太繁琐了
这种时候我们就可以把这段代码写成一个函数,以后每次使用的时候直接调用该函数就好了
def my_len():
    for i in a:
    	count += 1
	print(count)
这就是我们定义了一个函数my_len(),以后我们需要统计列表内数据值的个数的时候用my_len()就行了
换一种说法就是函数是工具,定义函数的过程就是在打造工具,完成以后想什么时候用就什么时候用

函数的语法结构

def 函数名(参数):
    '''函数注释'''
    函数体代码
    return 返回值
1.def
    定义函数的关键字,见到这个就要知道是在定义函数
2.函数名
    命名规则等同于变量名
3.参数
    可有可无,主要看我们在定义函数时要不要让外界的数据传进来
4.函数注释
    关于该函数的解释说明
5.函数体代码
    是整个函数的核心,该函数执行什么功能由函数体代码决定
6.return
    调用函数之后返回给使用者的数据,可有可无

函数的定义与调用

1.函数在定义阶段只检测语法不检测的代码
    在定义函数时,函数体代码的正确与否不影响函数的定义
2.函数在调用阶段才会执行函数体代码
3.函数要先定义后调用
    程序运行代码的原则是同级之间从上往下依次执行,如果先调用,程序无法识别
4.函数的定义必须使用关键字def

函数的分类

1.空函数
    函数体代码中没有具体的代码,以pass或者...补全的
    空函数主要用于项目前期的功能框架搭建
    def a():
        '''功能'''
        pass
2.无参函数
    定义函数的时候括号内没有参数
    def a():
        print('hello world')
3.有参函数
    定义函数的时候括号内有参数,调用函数的时候括号内传参数
    def a(b):
        print(b)
        
    a(111)  # 111

函数返回值

1.什么是返回值
    返回值就是我们调用函数之后返回给我们的结果
2.获取返回值
    变量名 赋值符号 调用的函数
3.函数返回值的多种情况
    1.函数体代码中没有return,默认返回None
    2.函数体代码中有return但是后面没有跟任何东西,那么返回值还是None
    3.函数体代码中有return且后面跟东西了,那么跟的什么就返回什么
    4.return后面有多个数据值时,则自动组成元组返回
    5.函数体代码遇到return会立刻结束,return后面的函数体代码不再执行

函数的参数

函数的参数分为两种,一种是形式参数,一种是实际参数
形式参数
    在函数定义阶段括号内填写的参数
实际参数
    在函数调用阶段括号内填写的参数
二者关系
    形参类似于变量名,在函数定义阶段命名
    实参类似于数据值,在函数调用阶段与形参临时绑定,函数体代码运行结束后断开绑定

1.位置参数

说明

位置参数分为位置形参和位置实参
位置形参
    函数定义阶段括号内从左往右依次填写的变量名
    def func(a, b, c):pass
位置实参
    函数调用阶段括号内从左往右依次填写的数据值
    func(1, 2, 3)

使用

1.函数调用时括号内填写的数据值需要与函数定义时填写的变量名一一对应,不能多也不能少
2.实参没有固定的含义可以传数据值,也可以传绑定了数据值的变量名
eg:
    name = 'zyg'
    pwd = 111
    age = 18
    def func(a, b, c):
        print(a, b, c)
    func(1, 2, 3)  # 1, 2, 3
    func(1, 2)  # 报错
    func(1, 2, 3, 4)  # 报错
    func(name, pwd, age)  # zyg, 111, 18

2.关键字参数

说明

关键字形参
    别名叫默认参数,定义函数时就已经给了数据值,用户可以传也可以不传
    传了,会用新的数据值替换已经给了的数据值;不传,则直接用已经给了的数据值
关键字实参
    调用函数时指定数据值给哪个变量名

用法

1.关键字传参一定要跟在位置传参的后面,否则会报错
2.同一个形参在调用的时候不能多次赋值
eg:
    def func(a=1, b):  # 报错  函数定义不能让位置参数在关键字参数后面
    def func(a, b):
        print(a, b)
    func(a=1, b=2)  # 1,2
    func(b=1, a=2)  # 2,1  顺序颠倒也可以
    func(a=1, 2)  # 报错  关键字传参要跟在位置传参的后面
    func(1, a=2, b=3)  # 报错  同一个形参在调用的时候不能多次赋值
    
    def func(a, b, c=6):
        print(a, b, c)
    func(1, 2)  # 1, 2, 6  有默认参数时传参时可以不传
    func(1, 2, 3)  # 1, 2, 3  有默认参数时,函数调用传参了那么就是穿的参数
    func(1, 2, c=8)  # 1, 2, 8  默认参数也可以用关键字传参

3.可变长参数

说明

可变长实参
    字面意思接受的数据值的数量是可变的
可变长实参
    传递的数据值的数量是可变的

用法

可变长形参
    1.定义函数时如需要接受多个位置参数可在变量名前面加*,接受的位置参数组织成元组赋值给*号后面的变量名
    def func(*a):pass
    2.定义函数时如需要接受多个关键字参数可在变量名前面加**,接受的关键字参数组织成字典赋值给**后面的变量名
    def func(**a):pass
    3.
eg:
    def func1(*a):
        print(a)
    func1()  # ()  使用可变长形参时可以不传参数,结果为空元组
    func1(1, 2, 3)  # (1, 2, 3)  传递的参数自动组织成元组的形式赋值给*号后面的变量名
    
    def func2(a, *b):
        print(a, b)
    func2()  # 报错  函数至少需要一个参数给到a
    func2(1)  # 1, ()
    func2(1, 2, 3, 4)  # 1, (2, 3, 4)
    
**的形参用法与*差不多
    def func3(**a):
        print(a)
    func3()  # {}
    func3(b=1,c=2)  # {'b': 1, 'c': 2}
    
    def func4(a, **b):
        print(a, b)
    func4()  # 报错  函数至少需要一个参数给到a
    func4(1)  # 1 {}
    func4(1, c=2, d=3)  # 1 {'c': 2, 'd': 3}
    func4(a=1, c=2, d=3)  # 1 {'c': 2, 'd': 3}
    
由于*和**在函数的形参中使用频率很高 后面跟的变量名推荐使用
    *args
    **kwargs

可变长实参
    1.调用函数时如需要传递列表或者元组内的数据值时在列表或元组的变量名前面加*
      把列表中的数据一个一个拿出来然后按照位置参数一次性传递给函数
    2.调用函数时如需要传递字典内的键值对则在字典的变量名前面加**
      把字典中的键值对打散成关键字参数的形式传给函数
eg:
    def index(a, b, c):
     	print(a, b, c)
    l1 = [11, 22, 33]
    t1 = (33, 22, 11)
    s1 = 'tom'
    se = {123, 321, 222}
    d1 = {'username': 'jason', 'pwd': 123, 'age': 18}
    index(*l1)  # (11, 22, 33)
    index(*t1)  # (33, 22, 11)
    index(*s1)  # ('t', 'o', 'm')
    index(*se)  # (123, 321, 222)
    index(*d1)  # ('username', 'pwd', 'age')
    
    def index(username, pwd, age):
        print(username, pwd, age)
    d1 = {'username': 'jason', 'pwd': 123, 'age': 18}
    index(**d1)  # (username = 'jason', pwd = 123, age = 18)
函数定义与调用时各种参数的排序方法
   越短/越简单的越靠前
   越长/越复杂的越靠后

名称空间与作用域

名称空间

名称空间我们可以简单理解为存储变量名的地方(实际是存储变量名与数据值绑定关系的地方)

名称空间分为三种:内置名称空间、全局名称空间、局部名称空间

1.内置名称空间
    解释器运行自动产生
    存放解释器自带的变量名
2.全局名称空间
    py文件运行产生产生的空间
    存放除了函数体代码(和类体代码)中的所有自己定义的变量名
3.局部名称空间
    函数体代码运行(或者是类体代码运行)产生的空间
    存放函数体代码(或者是类体代码)中生成的变量名

名称空间存活周期

内置名称空间
    python解释器运行自动创建,关闭则销毁
全局名称空间
    py文件执行时创建,运行结束销毁
局部名称空间
    函数体代码运行创建,运行结束销毁

名称空间作用域

内置名称空间
    在解释器环境下都有效
全局名称空间
    在一个py文件下有效
局部名称空间
    函数体代码内有效

名称的查找顺序

查找顺序

查找名称时需要先搞清楚该名称在哪个名称空间中
1.在局部名称空间中
    查找顺序:局部名称空间>>>全局名称空间>>>内置名称空间
2.在全局名称空间中
    查找顺序:全局名称空间>>>内置名称空间
正常情况下查找名称的查找顺序是从小到大,不能从大到小

查找案例

1.相互独立的局部名称空间正常情况下不能够互相访问
    def func1():
        a = 1
        print(b)    
    def func():
        b = 2
        print(a)
    func1()  # 报错
    func2()  # 报错
    
2.局部名称空间嵌套
    先从自己所在的名称空间查找,然后由内而外依次查找
    a = '捉迷藏'
def func():
    a = '找到小明了'
    def func1():
        a = '找到小红了'
        def func2():
            a = '不想找了'
            def func3():
                a = '找不到了,不找了'
                print(a)
            func3()  # 第四个执行
        func2()  # 第三个执行
    func1()  # 第二个执行
func()  # 第一个执行
# 找不到了,不找了
'''
在上述代码中打印的a优先在func3()的函数体代码中查找,找不到去func2()的函数体代码中找,接着是func1(),func()
  全局名称空间,乃至内置名称空间
  ''' 

posted on 2023-04-05 20:18  zyg111  阅读(55)  评论(0)    收藏  举报