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

    函数

    定义函数的方式

    def 函数名():  # 定义阶段(造车轮阶段)
        """函数注释写在这里"""  # 函数相当于工具, 注释相当于工具的说明书
        <代码块>
    
    # 使用  # 调用阶段(开车阶段)
    函数名()
    
    def func():
        """func函数的注释"""
        # todo:未来写一个开车函数
        pass
    func()
    

    注意:函数在定义阶段的时候不执行函数整体代码,智能检测到语法错误

    print(func.__doc__)
    可以知道函数中的注释
    

    函数的简单实例

    我们不妨尝试用函数来实现登录注册功能

    def register():
        """注册函数"""
        username = input('请输入你的用户名:')
        pwd = input('请输入你的密码:')
    
        with open('user_info.txt', 'a', encoding='utf8') as fa:
            fa.write(f'{username}:{pwd}|')
    
    def login():
        """登录函数"""
        username = input('请输入你的用户名:')
        pwd = input('请输入你的密码:')
    
        with open('user_info.txt', 'r', encoding='utf8') as fr:
            data = fr.read()
            user_list = data.split('|')
            print(user_list)
            user_info = f'{username}:{pwd}'
            if user_info in user_list:
                print('登录成功')
            else:
                print('傻逼,密码都忘了')
    

    有没有发现,当函数没有参数的时候,里面的代码块和在外面打的没有什么区别。其实,函数更多的是一种思想,而不是一种技术

    函数的三种定义方式

    三种方式分别为无参函数,有参函数,空函数

    无参函数

    def add():
        """无参函数"""
        x = input('num1:')
        y = input('num2:')
    
        print(int(x) + int(y))
    

    无参函数可以进行单独使用,上面的登录注册也是同理

    有参函数

    有参函数在函数中增加了参数,再往函数中输入参数之后才能使用

    def add(x, y): 
        """有参函数"""
        print(int(x) + int(y))
    
    
    print(1)
    x = input('num1:')
    y = input('num2:')
    add(x, y)
    
    结果为:
    num1:1
    num2:2
    3
    

    空函数

    顾名思义,里面什么都没有

    当你不知道函数怎么写的时候,可以先放一边,等以后再来写

    def func():  # 只知道工具叫什么名字,但不知道如何造出这个工具
        pass
    

    函数的调用

    def add(x, y):
        return x+y
    
    
    res = add(10, 20)  # 函数的调用
    print(res*12)	# 360
    

    这就是函数简单的调用,无参函数一般都是直接执行func(),有参函数则要在括号中输入符合要求的参数

    函数的返回值

    函数的返回值就是return,当函数执行到return时,直接返回return后面的数值,有一点类似于break的用法,可以配合if函数的使用来返回多种情况的值

    def add(x, y):
        return (x, y, x + y)  # return可以返回任意数据类型
        return x, y, x + y  # return可以返回任意数据类型,不加括号返回多个值时,默认用元祖的形式返回
    

    到这里函数的基础部分已经结束了。函数并不难,只要多敲几遍熟悉熟悉,相信马上就能熟能生巧

    可变长参数

    形参

    位置形参

    默认形参

    实参

    位置实参

    关键字实参

    可变长参数之*

    如果当你的用户可以输入很多的值,或者输入的值不固定,也就是实参的个数很多或输入的值不固定的时候的时候,我们不可能设定很多的形参,也没法办到一一对应数量不同的实参。这个时候,我们就需要用到可变长参数。

    def func(name, pwd, *args):  
        print('name:', name, 'pwd:', pwd)
        print(args)  # args就会接收多余的位置实参
        return 1
    
    
    res = func('hyc', 123658, 18, 180, 'a', ['reading'])  # 用户使用func函数的时候会传用户的信息进入函数,但是不知道传几个
    print(res)
    
    结果为:
    name: hyc pwd: 123658
    (18, 180, 'a', ['reading'])
    1
    

    *args会接收多余的位置实参,args是自己定义的,但是Python都用args来表示。这是是约定俗成的

    *args一般用于用户使用func函数传用户的信息进入函数,但是不知道传几个的时候,他会将传出来的值合并成一个元组,其中的值可以为任意数据类型

    虽说*args看似完美的解决一些问题,但是却似乎在实际应用中并不是那么的实用。它输出的是元组,别的人看到很难理会这个元组的意思,之后讲的还有一这方法就较为完美的解决了这一个问题

    但是在这之前,不妨先看看*的其他用法,他可以将元组打散成位置实参传给形参

    def func(name, pwd, x, y, z):
        print('name:', name, 'pwd:', pwd)
        print(x, y, z)
        return 1
    
    
    tup = (4, 2, 3)
    res = func('hyc', 123456, *tup)  # *会把元祖打散成位置实参传给形参
    print(res)
    结果为:
    name: hyc pwd: 123456
    4 2 3
    1
    

    可变长参数之**

    **args的用法和*args较为类似。但是最明显的不同之处在于,**args只能传关键字实参,并且输出的是字典。这样子输出的信息就更加的清楚明了了。

    def func(name, pwd, *args, **kwargs):
        print('name:', name, 'pwd:', pwd)
        print(args)
        print(kwargs)  # kwargs会接收多余的关键字实参,以字典形式存储
        return 1
    
    
    res = func('hyc', 123456, 1, 2, 3, 4, age=21, height=180)
    print(res)
    
    结果为:
    name: hyc pwd: 123456
    (1, 2, 3, 4)
    {'age': 18, 'height': 180}
    1
    
    结果为:
    name: hyc pwd: 123456
    (1, 2, 3, 4)
    {'age': 18, 'height': 180}
    1
    
    

    当然,**也可以将字典打算成关键字实参传给形参

    def func(name, pwd, **kwargs):
        return kwargs
    
    
    dic = {'a': 5, 'b': 2}  # **dic  --> a=1 , b=2
    res = func('nick', 123658, **dic)
    # res = func('nick', 123658, a=1, b=2) 
    print(res)
    结果为
    {'a': 5, 'b': 2}
    
    

    函数对象

    函数名等同于变量名,即变量名有的方法,函数名同样也有, 被引用 ; 当做容器元素 ; 当做函数参数 ; 当做函数返回值

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

    并没有报错,可以发现它指向一块内存地址

    同时它也可以和变量名一样被赋值

    def func():
        print('from func')
    
    f = func  # func可以加()调用,那么f也可以加()调用
    print(f, func)
    f()
    

    变量名还可以放入容器类数据类型

    def func():
        print('from func')
    
    a = 1
    
    lt = [1, 2, a, func]
    lt[-1]()
    

    函数名还可以被当做参数

    def func():
        print('from func')
    
    def f2(name):  # name = func
        name()  # func()
    
    # f2(a)
    f2(func)
    

    函数的返回值

    def func():
        print('from func')
    def f3(name):  # name = func
        return name  # name = func
    
    res = f3(func)  # res = func
    print(res)
    res()
    

    函数的嵌套

    和if判断一样,函数之内也可以进行嵌套

    def f1():
        def f2():
            print('from f2')
    
        return f2
    
    
    abc = f1()  # f1()拿到函数的返回值,函数的返回值是f2, abc就相当于f2
    abc()
    

    它的用法也很简单,但是还是有一些需要让人注意的地方。这些我们会在之后讲

    现在,我们可以先用嵌套的方法,来实现一下一个函数求圆的面积和周长

    import cmath
    
    def circle(r, action):
        def area():
            return cmath.pi * r ** 2
    
        def zhouchang():
            return 2 * cmath.pi * r
    
        if action == 'area':
            area()
    
        elif action == 'zhouchang':
            zhouchang()
    
    
    area = circle(3, 'area')
    print(area)
    
    zhouchang = circle(3, 'zhouchang')
    print(zhouchang)
    

    当然也可以将它和不用函数嵌套的进行比较

    import cmath
    
    
    def circle(r, action):
        if action == 'area':
            return cmath.pi * r ** 2
    
        elif action == 'zhouchang':
            return 2 * cmath.pi * r
    
    
    area = circle(3, 'area')
    print(area)
    
    zhouchang = circle(3, 'zhouchang')
    print(zhouchang)
    

    然后你们就会发现,好像使用嵌套还让代码变得更长了。。

    这么看来嵌套除了看起来牛逼好像没有什么实际的用处。。

    其实如果代码变得很长时,适当的嵌套还是可以节省代码的长度的

    下面这个例子就很好的说明了这一点:在比较四个值中引入比较俩个值的函数:

    def self_max(x, y):
        if x > y:
            return x
        return y
    
    
    res = self_max(10, 20)
    print(res)
    
    
    # 比较四个值
    def self_4_max(x, y, z, c):
        res1 = self_max(x, y)
        res2 = self_max(z, c)
        res = self_max(res1, res2)
    
        return res
    
    
    res = self_4_max(10, 20, 1, 100)
    print(res)
    

    这时你会发现,如果比较四个值又重新写一段代码的话会复杂一些

    名称空间和作用域

    名称空间

    名称空间是用来存放名字的(变量名/函数)

    内置名称空间

    内置名称空间是Python解释器独有的

    函数调用必须得定义, 从来都没定义过. Python解释器启动的时候python自动开辟内置名称空间存放了这些python的内置方法,python解释器停止解释之后才会销毁

    全局名称空间

    除了内置空间和局部空间,其他的都是全局空间

    全局空间需要自己定义,python文件执行之后才可能有全局名称空间,文件结束之后才会销毁

    局部名称空间

    函数内定义的变量名/函数名都存放在局部名称空间

    局部名称空间也需要自己定义, 必须得在函数调用之后才会生成, 调用结束之后就会销毁

    三种名称空间的执行顺序

    内置 --> 全局 --> 局部
    
    z = 10
    def f1():
        x = 10
        def f2():
            y = 10
            print('from f2')
    

    三种名称空间的查找顺序

    首先从自己当前位置开始 --> 局部 --> 全局 --> 内置

    x = 1
    def f1():
        x = 3
    print(x)
    

    你会发现,结果为1,这就是因为x=3是函数内部的变量名,所以不会被执行

    def f1():
        return x
        # print(x)  # f1中的局部
    
    def f2():
        x = 2  # x=2只能在f2中使用
        f1()
    
    
    f2()  # f1中的局部和f2中的局部互不干涉
    

    这里也是如此,函数中的变量只能再这个函数中使用

    global & nonlocal

    如果想要把函数中的变量变为全局,就可以使用global

    x = 1
    
    
    def f1():
        global x  # 声明x为全局的x
        x = 3
    
    
    f1()
    print(x)	# 3
    

    nonlocal则可以针对嵌套函数局部之间的修改

    nonlocal
    
    def f1():
        x = 1
        def f2():
            nonlocal x  # 针对嵌套函数局部之间的修改
            x = 3
        f2()
        print(x)
    
    
    f1()	# 3
    

    作用域的关系仅适用不可变数据类型,不适用于可变数据类型

    lt = [1,2,3]  
    
    def f1():
        lt.append(4)
    
    f1()
    
    print(lt)
    
    
    dic = dict()
    
    def login():
        dic['name'] = 'nick'
    login()
    
    def shopping():
        if dic.get('name'):
            print('购物成功')
        else:
            print('请登录')
    shopping()
    
    
    s = '空'
    
    def login():
        s = 'nick'
    login()
    print(s)
    
    
    结果为:
    [1, 2, 3, 4]
    购物成功
    空
    
  • 相关阅读:
    Adobe Photoshop CC 2017-18.0安装教程
    Adobe After Effects 2017-14.0安装教程
    Qt之新手打包发布程序
    Qt之添加图标
    突然好想你
    设计模式—桥接
    设计模式—适配器
    设计模式—原型(附带介绍浅拷贝和深拷贝)
    设计模式—抽象工厂
    框架设计——MVC IOC
  • 原文地址:https://www.cnblogs.com/hyc123/p/11329480.html
Copyright © 2011-2022 走看看