zoukankan      html  css  js  c++  java
  • 函数基础

    一、 函数的定义

    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]

     

  • 相关阅读:
    ADB命令大全
    Backup your Android without root or custom recovery -- adb backup
    Content portal for Pocketables Tasker articles
    Is there a way to detect if call is in progress? Phone Event
    Tasker to proximity screen off
    Tasker to detect application running in background
    Tasker to create toggle widget for ES ftp service -- Send Intent
    Tasker to proximity screen on
    Tasker to answer incoming call by pressing power button
    Tasker to stop Poweramp control for the headset while there is an incoming SMS
  • 原文地址:https://www.cnblogs.com/hanyi12/p/11545294.html
Copyright © 2011-2022 走看看