• 函数基础


    一、 函数的定义

    1、1 函数体系

    接下来,我们将按照这个函数体系给大家详细的介绍函数:

    • 什么是函数?

    • 为什么要用函数?

    • 函数的分类:内置函数与自定义函数

    • 如何自定义函数

      • 语法

      • 定义有参数函数,及有参函数的应用场景

      • 定义无参数函数,及无参函数的应用场景

      • 定义空函数,及空函数的应用场景

    • 调用函数

      • 如何调用函数

      • 函数的返回值

      • 函数参数的应用:形参和实参,位置形参,位置实参,关键字实参,默认形参,*args,**kwargs

    • 高阶函数(函数对象)

    • 函数嵌套

    • 作用域与名称空间

    • 装饰器

    • 迭代器与生成器及协程函数

    • 三元运算,列表解析、生成器表达式

    • 函数的递归调用

    • 内置函数

    • 面向过程编程与函数式编程

    如果现在有一个需求需要实现用户登录注册的功能,我们该怎么实现呢?

    # 注册
    username = input('username: ').strip()
    pwd = input('password: ').strip()

    with open('38a.txt', 'a', encoding='utf8') as fa:
       fa.write(f"{username}:{pwd} ")
       fa.flush()
    # 登录
    inp_username = input('username: ').strip()
    inp_pwd = input('password: ').strip()

    with open('38a.txt', 'rt', encoding='utf8') as fr:
       for user_info in fr:
           user_info = user_info.strip(' ')
           user_info_list = user_info.split(':')
           if inp_username == user_info_list[0] and inp_pwd == user_info_list[1]:
               print('login successful')
               break
       else:
           print('failed')

    1、2 什么是函数?

    假设现在你是下水道工,如果你事先准备好你的工具箱,等你接到修理下水道的工作的时候,你直接把你的工具箱拿过去直接使用就行了,而不需要临时准备锤子啥的。

    在程序中,函数就是具备某一功能的工具,事先将工具准备好就是函数的定义,遇到应用场景拿来就用就是函数的调用,所以需要注意的是:

    函数基础-水道工.jpg?x-oss-process=style/watermark

    1、3 为何用函数

    如果不使用函数,写程序时将会遇到这三个问题:

    1. 程序冗长

    2. 程序的扩展性差

    3. 程序的可读性差

    1、4 如何用函数

    先定义函数,后调用。

    • 定义函数:

    def 函数名(param1、param2……):
      """
      函数功能的描述信息
      :param1:描述
      :param2:描述
      :return:返回值
      """
      code 1
      code 2
      code 3
      ...

      return 返回值
    • 调用函数

    函数名(param1、param2……)

    1.4.1 注册功能函数

    # 注册功能函数
    def register():
       """注册功能"""
       username = input('username: ').strip()
       pwd = input('password: ').strip()

       with open('38a.txt', 'a', encoding='utf8') as fa:
           fa.write(f"{username}:{pwd} ")
           fa.flush()


    register()
    # 复用
    register()
    register()

     

    1.4.2 登录功能函数

    # 登录功能函数
    def login():
       """登录功能"""
       inp_username = input('username: ').strip()
       inp_pwd = input('password: ').strip()

       with open('38a.txt', 'rt', encoding='utf8') as fr:
           for user_info in fr:
               user_info = user_info.strip(' ')
               user_info_list = user_info.split(':')
               if inp_username == user_info_list[0] and inp_pwd == user_info_list[1]:
                   print('login successful')
                   break
           else:
               print('failed')


    login()

    1.4.3 函数定义阶段

    def func():
       bar()  # 不属于语法错误,不会报错
       print('*'*10)
    1. 只检测语法,不执行函数体代码

    1.4.4 函数调用阶段

    def bar():
       print('from bar')

    def foo():
       print('from foo')
       bar()

    foo()
    #运行结果:
    '''
    from foo
    from bar
    '''

     

    def foo():
       print('from foo')
       bar()
       
    def bar():
       print('from bar')

    foo()
    '''
    from foo
    from bar
    '''
    1. 执行函数代码

    二、定义函数的三种形式

    2、1 无参函数

    定义函数时参数是函数体接收外部传值的一种媒介,其实就是一个变量名

    在函数阶段括号内没有参数,称为无参函数。需要注意的是:定义时无参,意味着调用时也无需传入参数。

    如果函数体代码逻辑不需要依赖外部传入的值,必须得定义成无参函数。

    def func():
       print('hello nick')
       
    func()  # hello nick

    2、2 有参函数

    在函数定义阶段括号内有参数,称为有参函数。需要注意的是:定义时有参,意味着调用时也必须传入参数。

    如果函数体代码逻辑需要依赖外部传入的值,必须得定义成有参函数。

    def sum_self(x, y):
       """求和"""
       res = x+y
       print(res)

    sum_self(1,2)  # 3

    2、3 空函数

    当你只知道你需要实现某个功能,但不知道该如何用代码实现时,你可以暂时写个空函数,然后先实现其他的功能。

    def func():
       pass

    三、函数的返回值

    3、1 什么是返回值?

    函数内部代码经过一些列逻辑处理获得的结果。

    def func():
       name = 'nick'
       return name


    name = func()
    print(name)

    3、2 为什么要有返回值?

    如果需要在程序中拿到函数的处理结果做进一步的处理,则需要函数必须要有返回值。

    需要注意的是:

    • return是一个函数结束的标志,函数内可以有多个return,只要执行到return,函数就会执行。

    • return的返回值可以返回任意数据类型

    • return的返回值无个数限制,即可以使用逗号隔开返回多个值

      • 0个:返回None

      • 1个:返回值是该值本身

      • 多个:返回值是元组

    # 为什么要有返回值
    def max_self(salary_x, salary_y):
       if salary_x > salary_y:
           return salary_x
       else:
           return salary_y


    max_salary = max_self(20000, 30000)
    print(max_salary*12)

    # 函数返回多个值
    def func():
       name = 'nick'
       age = 19
       hobby_list = ['read', 'run']
       return name, age, hobby_list


    name, age, hobby_list = func()
    print(f"name,age,hobby_list: {name,age,hobby_list}")
    name,age,hobby_list: ('nick', 19, ['read', 'run'])

     

    四、 函数的调用

    4、1 什么是函数调用?

    第一次将函数其实就讲了函数的调用,但是你不得不再次更新你对函数调用的印象。函数名(…)即调用函数,会执行函数体代码,直到碰到return或者执行完函数体内所有代码结束。

    函数运行完毕所有代码,如果函数体不写return,则会返回None。

    def foo():
       pass

    print(foo())

    4、2 为何用调用函数?

    很愚蠢的一个问题,但是我们依然得回答:使用函数的功能。

    4、3 函数调用的三种形式

    def max_self(x,y):
       if x>y:
           return x
       else:
           return y
       
    # 1.
    max_self(1,2)
    # 2.
    res = max_self(1,2)*12
    # 3.
    max_self(max_self(20000,30000),40000)

    五、 函数的参数

    5、1 形参和实参

    5.1.1形参

    在函数定义阶段括号内定义的参数,称之为形式参数,简称形参,本质就是变量名。

    def func(x, y):
       print(x)
       print(y)

    5.1.2 实参

    在函数调用阶段括号内传入的参数,称之为实际参数,简称实参,本质就是变量的值。

    func(1, 2)

    5、2 位置参数

    5.2.1 位置形参

    在函数定义阶段,按照从左到右的顺序依次定义的形参,称之为位置形参。

     

    def func(x, y):
       print(x)
       print(y)

    特点:按照位置定义的形参,都必须被传值,多一个不行,少一个也不行。

    5.2.2 位置实参

    在函数调用阶段,按照从左到右的顺序依次定义的实参,称之为位置实参。

    func(1, 2)

    特点:按照位置为对应的形参依次传值。

    5、3 关键字实参

    在调用函数时,按照key=value的形式为指定的参数传值,称为关键字实参。

    func(y=2, x=1)

    特点:可以打破位置的限制,但仍能为指定的形参赋值。

    注意:

    1. 可以混用位置实参和关键字实参,但是位置实参必须在关键字实参的左边。

    2. 可以混用位置实参和关键字实参,但不能对一个形参重复赋值。

    func(x, y=2)
    func(y=2, x)  # SyntaxError: positional argument follows keyword argument
    func(x, x=1)  # NameError: name 'x' is not defined

    5、4 默认形参

    在定义阶段,就已经被赋值。

    def func(x, y=10):
       print(x)
       print(y)
       
    func(2)

    特点:在定义阶段就已经被赋值,意味着在调用时可以不用为其赋值。

    注意:

    1. 位置形参必须放在默认形参的左边。

    2. 默认形参的值只在定义阶段赋值一次,也就是说默认参数的值在函数定义阶段就已经固定了。

    m = 10


    def foo(x=m):
       print(x)


    m = 111
    foo()  # 10
    1. 默认参数的值通常应该是不可变类型。

    # 演示形参是可变类型
    def register(name, hobby, hobby_list=[]):
       hobby_list.append(hobby)
       print(f"{name} prefer {hobby}'")
       print(f"{name} prefer {hobby_list}")


    register('nick', 'read')
    register('tank', 'zuipao')
    register('jason', 'piao')
    ```
    运行结果:
    nick prefer read'
    nick prefer ['read']
    tank prefer zuipao'
    tank prefer ['read', 'zuipao']
    jason prefer piao'
    jason prefer ['read', 'zuipao', 'piao']
    ```
    # 修改形参是可变类型代码
    def register(name, hobby, hobby_list=None):
       if hobby_list is None:
           hobby_list = []
       hobby_list.append(hobby)
       print(f"{name} prefer {hobby}'")
       print(f"{name} prefer {hobby_list}")


    register('nick', 'read')
    register('tank', 'zuipao')
    register('jason', 'piao')
    ```
    运行结果:
    nick prefer read'
    nick prefer ['read']
    tank prefer zuipao'
    tank prefer ['zuipao']
    jason prefer piao'
    jason prefer ['piao']
    ```

    5、5 总结

    实参的应用:取决于个人习惯 形参的应用:

    1. 大多数情况的调

    2. 大多数情况的调用值一样,就应该将该参数定义成位置形参

    3. 大多数情况的调用值一样,就应该将该参数定义成默认形参

    六、可变长参数

    可变长参数:指的是在调用函数时,传入的参数个数可以不固定

    调用函数时,传值的方式无非两种,一种是位置实参,另一种是关键字实参,因此形参也必须得有两种解决方法,以此来分别接收溢出的位置实参(*)与关键字实参(**)

    6、1 可变长形参之*

    形参中的会将溢出的位置实参全部接收,然后存储元组的形式,然后把元组赋值给后的参数。需要注意的是:*后的参数名约定俗成为args。

    def sum_self(*args):
       res = 0
       for num in args:
           res += num
       return res


    res = sum_self(1, 2, 3, 4)
    print(res)
    #10

    6、2 可变长实参之**

    实参中的会将后参数的值循环取出,打散成位置实参。以后但凡碰到实参中带的,它就是位置实参,应该马上打散成位置实参去看。

    def func(x, y, z, *args):
        print(x, y, z, args)
    
    
    func(1, *(1, 2), 3, 4)
    #1 1 2 (3, 4)
    

    6、3 可变长形参之**

    形参中的*会将溢出的关键字实参全部接收,然后存储字典的形式,然后把字典赋值给*后的参数。需要注意的是:**后的参数名约定俗成为kwargs。

    def func(**kwargw):
      print(kwargw)


    func(a=5)
    {'a': 5}

    6、4 可变长实参之**

    实参中的会将**后参数的值循环取出,打散成关键字实参。以后但凡碰到实参中带**的,它就是关键字实参,应该马上打散成关键字实参去看。

    def func(x, y, z, **kwargs):
       print(x, y, z, kwargs)


    func(1, 3, 4, **{'a': 1, 'b': 2})
    # 1 3 4 {'a': 1, 'b': 2}

    6、5 可变长参数应用**

     

    def index(name, age, sex):
       print(f"name: {name}, age: {age}, sex: {sex}")


    def wrapper(*args, **kwargs):
       print(f"args: {args}")
       print(f"kwargs: {kwargs}")
       index(*args, **kwargs)



    # kwargs: {'name': 'nick', 'sex': 'male', 'age': # 19}
    # name: nick, age: 19, sex: male

    6、6 命名关键字形参**

    现在有一个需求:函数的使用者必须按照关键字实参传。

    def register(x, y, **kwargs):
       if 'name' not in kwargs or 'age' not in kwargs:
           print('用户名和年龄必须使用关键字的形式传值')
           return
       print(kwargs['name'])
       print(kwargs['age'])


    # register(1, 2, name='nick', age=19)
    # nick
    # 19

    命名关键字形参:在函数定义阶段,*后面的参数都是命名关键字参数。

    特点:在传值时,必须按照key=value的方式传值,并且key必须命名关键字参数的指定的参数名。

    def register(x, y, *, name, gender='male', age):
       print(x)
       print(age)


    register(1, 2, x='nick', age=19)  # TypeError: register() got multiple values for argument 'x'

    七、函数对象

    函数是第一类对象,即函数可以被当做数据处理。

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


    print(func)
    <function func at 0x10af72f28>

    7、1 函数对象的四大功能

    1. 引用

    x = 'hello nick'
    y = x

    f = func
    print(f)
    <function func at 0x10af72f28>

    2.当作参数传给一个函数

    len(x)


    def foo(m):
       m()


    foo(func)
    from func

    3.可以当作函数的返回值

    def foo(x):
       return x


    res = foo(func)
    print(res)
    res()
    <function func at 0x10af72f28>
    from func

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

    l = [x]

    function_list = [func]
    function_list[0]()
    from func

    7、2 练习

    def pay():
       print('支付1e成功')


    def withdraw():
       print('提现2e成功')


    dic = {
       '1': pay,
       '2': withdraw,
    }
    while True:
       msg = """
      '1': 支付,
      '2': 提现,
      '3': 退出,
      """
       print(msg)
       choice = input('>>: ').strip()
       if choice == '3':
           break
       elif choice in dic:
           dic[choice]()
    ```
    运行结果:
    '1': 支付,
       '2': 提现,
       '3': 退出,
       
    >>: 1
    支付1e成功

       '1': 支付,
       '2': 提现,
       '3': 退出,
       
    >>: 2
    提现2e成功

       '1': 支付,
       '2': 提现,
       '3': 退出,
       
    >>: 3
    ```

    八、 函数嵌套

    8、1 函数的嵌套定义

    函数内部定义的函数,无法在函数外部使用内部定义的函数。

    def f1():
       def f2():
           print('from f2')
       f2()


    f2()  # NameError: name 'f2' is not defined
    def f1():
       def f2():
           print('from f2')
       f2()

    f1()
    # from f2

     

    现在有一个需求,通过给一个函数传参即可求得某个圆的面积或者圆的周长。也就是说把一堆工具丢进工具箱内,之后想要获得某个工具,直接从工具箱中获取就行了。

    from math import pi


    def circle(radius, action='area'):
       def area():
           return pi * (radius**2)

       def perimeter():
           return 2*pi*radius
       if action == 'area':
           return area()
       else:
           return perimeter()


    print(f"circle(10): {circle(10)}")
    print(f"circle(10,action='perimeter'): {circle(10,action='perimeter')}")
    # 运行结果
    # circle(10): 314.1592653589793
    # circle(10,action='perimeter'): 62.83185307179586

    8、2 函数的嵌套调用

    4def max2(x, y):
       if x > y:
           return x
       else:
           return y


    def max4(a, b, c, d):
       res1 = max2(a, b)
       res2 = max2(res1, c)
       res3 = max2(res2, d)
       return res3


    print(max4(1, 2, 3, 4))

    4

    九、 名称空间和作用域

    函数内部的函数只能在函数内部调用,不能在函数外部调用,通过接下来的学习你将会知道为什么会出现这种情况。

    def f1():
       def f2():
           print('from f2')
       f2()

    f2()  # NameError: name 'f2' is not defined

    9、1 名称空间

    名称空间(name spaces):在内存管理那一章节时,我们曾说到变量的创建其实就是在内存中开辟了一个新的空间。但是我们一直在回避变量名的存储,其实在内存中有一块内存存储变量名与变量间的绑定关系的空间,而这个空间称为名称空间。

    9.1.1 内置名称空间

    内置名称空间:存放Pyhton解释器自带的名字,如int、float、len

    生命周期:在解释器启动时生效,在解释器关闭时失效

    9.1.2 全局名称空间

    全局名称空间:除了内置和局部的名字之外,其余都存放在全局名称空间,如下面代码中的x、func、l、z

    生命周期:在文件执行时生效,在文件执行结束后失效

    x = 1


    def func():
       pass


    l = [1, 2]

    if 3 > 2:
       if 4 > 3:
           z = 3

    9.1.3 局部名称空间

    局部名称空间:用于存放函数调用期间函数体产生的名字,如下面代码的f2

    生命周期:在文件执行时函数调用期间时生效,在函数执行结束后失效

    def f1():
       def f2():
           print('from f2')
       f2()

    f1()

    46名称空间与作用域-简单.png?x-oss-process=style/watermark

    9.1.4 加载顺序

    由于.py文件是由Python解释器打开的,因此一定是在Python解释器中的内置名称空间加载结束后,文件才开始打开,这个时候才会产生全局名称空间,但文件内有某一个函数被调用的时候,才会开始产生局部名称空间,因此名称空间的加载顺序为:内置--》全局--》局部。

    9.1.5 查找顺序

    由于名称空间是用来存放变量名与值之间的绑定关系的,所以但凡要查找名字,一定是从三者之一找到,查找顺序为: 从当前的所在位置开始查找,如果当前所在的位置为局部名称空间,则查找顺序为:局部--》全局--》内置。

    x = 1
    y = 2
    len = 100


    def func():
       y = 3
       len = 1000
       print(f"y: {y}")
       print(f"len: {len}")
       # print(a) # NameError: name 'a' is not defined


    func()
    y: 3
    len: 1000
    x = 1


    def func():
       print(x)


    x = 10
    func()
    10

    9、2 作用域

    域指的是区域,作用域即作用的区域。

    9.2.1 全局作用域

    全局作用域:全局有效,全局存活,包含内置名称空间和全局名称空间。

    # 全局作用域
    x = 1


    def bar():
       print(x)


    bar()
    # 1

    9.2.2 局部作用域

    局部作用域:局部有小,临时存储,只包含局部名称空间。

    # 局部作用域
    def f1():
       def f2():
           def f3():
               print(x)
           x = 2
           f3()
       f2()


    f1()
    2

    9.2.3 注意点

    需要注意的是:作用域关系在函数定义阶段就固定死了,与函数的调用无关。

    # 作用域注意点
    x = 1


    def f1():  # 定义阶段x=1
       print(x)


    def f2():
       x = 2
       f1()


    f2()
    #1

    9.2.4 函数对象+作用域应用

    # 作用域应用
    def f1():
       def inner():
           print('from inner')
       return inner


    f = f1()  # 把局部定义的函数放在全局之中


    def bar():
       f()


    bar()
    # from inner

    9、3 补充知识点

    9.3.1 global关键字

    修改全局作用域中的变量。

    x = 1


    def f1():
       x = 2

       def f2():
           #         global x # 修改全局
           x = 3
       f2()


    f1()
    print(x)
    # 1
    x = 1


    def f1():
       x = 2

       def f2():
           global x  # 修改全局
           x = 3
       f2()


    f1()
    print(x)
    # 3

     

    9.3.2 nonlocal关键字

    修改局部作用域中的变量。

    x = 1


    def f1():
       x = 2

       def f2():
           #         nonlocal x
           x = 3

       f2()
       print(x)


    f1()
    2
    x = 1


    def f1():
       x = 2

       def f2():
           nonlocal x
           x = 3

       f2()
       print(x)


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

     

    9.3.3 注意点

    1. 在局部想要修改全局的可变类型,不需要任何声明,可以直接修改。

    2. 在局部如果想要修改全局的不可变类型,需要借助global声明,声明为全局的变量,即可直接修改。

    lis = []


    def f1():
       lis.append(1)


    print(f"调用函数前: {lis}")
    f1()
    print(f"调用函数后: {lis}")
    #调用函数前: []
    #调用函数后: [1]

     

  • 相关阅读:
    win10 ubuntu 双系统启动顺序设置
    关于memset的使用
    POJ 2533 最小上升子序列
    Did Pong Lie? (差分系统 判负环)
    HDU 5828 Rikka with Sequence(线段树 开根号)
    SCU
    北邮校赛 I. Beautiful Array(DP)
    北邮校赛 H. Black-white Tree (猜的)
    北邮校赛 F. Gabriel's Pocket Money(树状数组)
    HDU 5862 Counting Intersections(离散化 + 树状数组)
  • 原文地址:https://www.cnblogs.com/hanyi12/p/11545294.html
Copyright © 2020-2023  润新知