zoukankan      html  css  js  c++  java
  • 函数基础-(引子、定义函数、调用函数、函数返回值、函数参数)

    函数基础

    • 引子

    • 定义函数

    • 调用函数

    • 函数返回值

    • 函数参数

    一、引子

    1.不用函数会引发什么问题?

      代码的组织结构不清晰,可读性差;

      遇到重复的功能只能重复编写实现代码,代码冗余;

      功能需要扩展的时候,需要找出所有实现该功能的地方修改,无法统一管理且维护难度极大;

      举个例子:

      比如说,len方法突然不能直接用了,然后现在有一个需求,让你计算'hello world'的长度,你怎么计算?

      这个需求对于现在的你其实不难,我们一起来写一下。

    s1 = "hello world"
    length = 0
    for i in s1:
        length = length+1
    
    print(length)

      好了,功能实现了,非常完美。然后现在又有了一个需求,要计算另外一个字符串的长度,"hello eva".

      于是,这个时候你的代码就变成了这样:

    s1 = "hello world"
    length = 0
    for i in s1:
        length = length+1
    
    print(length)
    
    s2 = "hello eva"
    length = 0
    for i in s2:
        length = length+1
    
    print(length)

      这样确实可以实现len方法的效果,但是总感觉不是那么完美?为什么呢?

      首先,之前只要我们执行len方法就可以直接拿到一个字符串的长度了,现在为了实现相同的功能我们把相同的代码写了好多遍 —— 代码冗余

      我们就想啊,要是我们能像使用len一样使用我们这一大段“计算长度”的代码就好了。这种感觉有点像给这段代码起了一个名字,等我们用到的时候直接喊名字就能执行这段代码似的。要是能这样,是不是很完美啊?这时候就需要我们的函数出场啦!

      

    2.函数是什么?

      函数的定义:函数就是对功能的封装;

      想象生活中的例子,修理工需要事先准备好工具箱里面放好锤子、扳手、钳子等工具,然后遇到锤钉子的时候,拿上锤子用就可以,而无需临时再造一把;

      修理工 -------> 程序员

      具备某功能的工具 --------> 函数

      要想使用工具,需要事先准备好,然后拿来就用且可以重复使用

      想要用函数,需要先定义,再使用;.

      那么我们如何用函数方式解决上面问题呢?

      现在就教大家一个既能,让你们把代码装起来。

    def mylen():
        s1 = "hello world"
        length = 0
        for i in s1:
            length = length+1
        print(length)

      这样我们就定义了一个函数,怎么使用呢,只需要mylen()就可以实现函数调用。

    3.函数分类

      a.内置函数

      为了方便我们的开发,针对一些简单的功能,python解释器已经为我们定义好了的函数即内置函数,对于内置函数,我们可以拿来就用而无需事先定义,另一篇文章里专门有内置函数的讲解;

      b.自定义函数

      毕竟是内置函数,所能提供的功能是有限的,这样就需要我们自己根据需求,事先定义好我们自己的函数来实现某功能,以后在遇到应用场景时,调用自定义的函数即可。

    二、定义函数

    1.如何定义函数,函数的结构体。

    ## 语法
    def  函数名(形参1,形参2,形参3,.):
           '''注释'''
           函数体
           return  返回的值
    
    ## 函数名要有意义
    
    ##注意:
    #1.def关键字开头,空格之后接函数名称和圆括号(),最后还有一个“:”;
    #2.def是固定的,不能变,必须是连续的def三个字母,不能分开;
    #3.空格,为了将def关键字和函数名分开,必须空开,也可以使用2个或多个空格,正常人都是一个空格;
    #4.函数名,函数名只能包含字符串,下划线和数字且不能以数字开头,要想变量一样的去定义;
    #5.括号,是必须加的,不要问为什么,加上就对了;
    #6.每一个函数都应该对功能和参数进行相应的说明,应该写在函数下面的第一行,以增强代码的可读性;
    #7.调用,就是函数名(),一定要记得加上括号,不加括号获取的就是函数的内存地址了。

      例如:

    def auth(user,password):
        '''
        auth function
        user:用户名
        password:密码
        return:认证结果      
        '''
        if user == "zjk" and password == "123":
             return 1
    
    user = input(user:).strip()
    password = input(pws:).strip()
    res = auth(user,password)
    print(res)
    函数定义实例

    2.函数的使用原则:先定义,再调用;

      函数即“变量”,变量必须先定义后饮用,未定义而直接引用函数就相当于在引用一个不存在的变量;用例子来说明:

    #测试1.
    >>> def foo():
    ...     print("from foo")
    ...     bar()
    ... 
    >>> foo()
    from foo
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File "<stdin>", line 3, in foo
    NameError: name 'bar' is not defined
    
    #报错,提示bar这个函数没有定义;
    
    #测试2.
    >>> def bar():
    ...     print("from bar")
    ... 
    >>> def foo():
    ...     print("from foo")
    ...     bar()
    ... 
    >>> foo()
    from foo
    from bar
    
    ##正常执行;
    
    #测试3
    >>> def foo():
    ...     print("from foo")
    ...     bar()
    ... 
    >>> def bar():
    ...     print("from bar")
    ... 
    >>> foo()
    from foo
    from bar
    
    #说明在代用函数时,函数是不分前后的,但需要注意的时,函数的使用,必须遵循原则,先定义,后使用;不然的话代码调用函数的时候,函数却还没有加载到内存;所以,我们在使用函数时,一定要明确地区分定义阶段和调用阶段。
    
    #定义阶段
    >>> def foo():
    ...     print("from foo")
    ...     bar()
    ... 
    >>> def bar():
    ...     print("from bar")
    ... 
    >>> foo()
    #调用阶段
    >>> foo()
    from foo
    from bar

    3.函数在定义阶段都干了啥子事情?

      只检测语法,不执行代码;

      也就是说,语法错误在函数定义阶段就 会检测出来,而代码的逻辑错误只有在执行的时候才知道;

    4.定义函数的三种形式;

      无参:应用场景仅仅只是执行一些操作,比如与用户交互,打印;

      有参:需要根据外部传来的参数,才能执行相应的逻辑,比如统计长度,求最大值最小值;

      空函数:设计代码结构;

      例如:

    #定义阶段
    def tell_tag(tag,n): #有参数
        print(tag*n)
    
    def tell_msg(): #无参数
        print('hello world')
    
    #调用阶段
    tell_tag('*',12)
    tell_msg()
    tell_tag('*',12)
    
    '''
    ************
    hello world
    ************
    '''
    
    #结论:
    #1、定义时无参,意味着调用时也无需传入参数
    #2、定义时有参,意味着调用时则必须传入参数
    无参、有参
    def auth(user,password):                             
        '''                                                           
        auth function                                                 
        :param user: 用户名                                              
        :param password: 密码                                           
        :return: 认证结果                                                 
        '''                                                           
        pass                                                          
                                                                      
    def get(filename):                                                
        '''                                                           
        :param filename:                                              
        :return:                                                      
        '''                                                           
        pass                                                          
                                                                      
    def put(filename):                                                
        '''                                                           
        :param filename:                                              
        :return:                                                      
        '''                                                           
    def ls(dirname):                                                  
        '''                                                           
        :param dirname:                                               
        :return:                                                      
        '''                                                           
        pass                                                          
    
    #程序的体系结构立见           
    空函数

    三、调用函数

    1.调用函数

      函数的调用:函数名加括号,func()

      a.先找到名字

      b.根据名字调用代码

    2.函数返回值

      return 是一个关键字,这个词翻译过来就是“返回”,所以我们管写在return后面的值叫“返回值",一旦遇到return,结束整个函数;

      三种类型,无返回值,返回1个值,返回多个值;

      无返回值:函数体中没有return,或者return后没有跟任何值,这时此函数会返回一个None;

      返回1个值:return 后跟一个值;

      返回多个值:return后跟多个值,多个值之间用逗号分隔,此时return返回的是一个元组类型;

      那么,什么时候该有返回值呢?

        调用函数,经过一系列的操作,最后要拿到一个明确的结果,则必须要有返回值;

        通常有参函数需要有返回值,输入参数,经过计算,得到一个最终的结果;

      什么时候不需要有返回值?

        调用函数,仅仅只是执行一系列的操作,最后不需要得到什么结果,则无需有返回值;

        通常无参函数不需要有返回值;

    3.函数调用的三种形式

      a.语句形式:foo()

      b.表达式形式:3 * len("hello")

      c.当中另外一个函数的参数:range(len("hello"))

    4.函数结束的方式:

      a.遇到return结束整个函数;

      b.函数体正常执行完毕,整个函数也会结束;

    四、函数的参数

      我们已经把函数返回值相关的事情研究清楚了,我们自己已经完成了一个可以返回字符串长度的函数,但是现在这个函数还是不完美,之前我们使用len函数的时候得是length = len("hello world"),这样我可以想计算谁就计算谁的长度。但是现在我们写的这个函数,只能计算一个“hello world”的长度,换一个字符串好像就是不行了。这可怎么办?

    #函数定义
    def mylen(s1):
        """计算s1的长度"""
        length = 0
        for i in s1:
            length = length+1
        return length
    
    #函数调用
    str_len = mylen("hello world")
    print('str_len : %s'%str_len)
    
    带参数的函数
    带参数的函数

      我们告诉mylen函数要计算的字符串是谁,这个过程就叫做 传递参数,简称传参,我们调用函数时传递的这个“hello world”和定义函数时的s1就是参数

    1.实参和形参:

      我们调用函数时传递的这个“hello world”被称为实际参数,因为这个是实际的要交给函数的内容,简称实参。

      定义函数时的s1,只是一个变量的名字,被称为形式参数,因为在定义函数的时候它只是一个形式,表示这里有一个参数,简称形参

    2.传递多个参数:

      参数可以传递多个,多个参数之间用逗号分割。

    def mymax(x,y):
        the_max = x if x > y else y
        return the_max
    
    ma = mymax(10,20)
    print(ma)
    传递多个参数

      也正是因为需要传递多个参数、可以传递多个参数,才会有了后面这一系列参数相关的故事。。。

    3.根据实参和形参角度出来的不同参数类型:

    1)位置参数:

      站在实参角度:

      a.按照位置传值:  位置形参:必选参数;位置实参:按照位置给形参传值

    def mymax(x,y):
        #此时x=10,y=20
        the_max = x if x > y else y
        return the_max
    
    ma = mymax(10,20)
    print(ma)
    
    按照位置传参
    按照位置传值

      b.按照关键字传值: 无需按照位置为形参传值;

      注意的问题:关键字实参必须在位置实参右面;对同一个形参不能重复传值;并且关键字传值就相当于变量赋值,所以实参中的关键字名称一定要符合变量命名的定义;

    def mymax(x,y):
        #此时x = 20,y = 10
        print(x,y)
        the_max = x if x > y else y
        return the_max
    
    ma = mymax(y = 10,x = 20)
    print(ma)
    
    按照关键字传参
    按照关键字传参

      c.位置、关键字形式混合使用

    def mymax(x,y):
        #此时x = 10,y = 20
        print(x,y)
        the_max = x if x > y else y
        return the_max
    
    ma = mymax(10,y = 20)
    print(ma)
    
    位置、关键字混用传参
    位置、关键字混用传参

      正确用法:

      问题一:位置参数必须在关键字参数的前面;

      问题二:对于一个形参只能赋值一次;

      站在形参角度:

      位置参数必须传值;

    def mymax(x,y):
        #此时x = 10,y = 20
        print(x,y)
        the_max = x if x > y else y
        return the_max
    
    #调用mymax不传递参数
    ma = mymax()
    print(ma)
    
    #结果
    TypeError: mymax() missing 2 required positional arguments: 'x' and 'y'
    
    位置参数必须传参
    位置参数必须传参

    2)默认参数:

      形参在定义时就已经为其赋值;可以传值也可以不传值,经常需要变得参数定义成位置形参,变化较小的参数定义成默认参数(形参);

      注意的问题:

      只在定义时赋值一次;默认参数的定义应该在位置形参右面;默认参数通常应该定义成不可变类型;

    def stu_info(name,sex = "male"):
        """打印学生信息函数,由于班中大部分学生都是男生,
            所以设置默认参数sex的默认值为'male'
        """
        print(name,sex)
    
    
    stu_info('alex')
    stu_info('eva','female')
    
    默认参数
    默认参数的定义

      参数陷阱:默认参数是一个可变数据类型;

    def defult_param(a,l = []):
        l.append(a)
        print(l)
    
    defult_param('alex')
    defult_param('egon')

    3)动态参数(可变长参数):

      可变长指的是实参值的个数不固定,而实参有按位置和按关键字两种形式定义,针对这两种形式的可变长,形参对应有两种解决方案来完整地存放它们,分别是*args,**kwargs;

      按位置传值多余的参数都由args统一接收,保存成一个元组的形式;

      *args   : 接收的是按照位置传参的值,组织成一个元组;

      **kwargs: 接受的是按照关键字传参的值,组织成一个字典;

      args必须在kwargs之前;

      动态传参的另一种方式:

      站在形参的角度上,给变量加上*,就是组合所有传来的值形成一个元组;

      站在实参的角度上,给一个序列加上*,就是将这个序列按照顺序打散,形成一个个参数传递进去;

    -------------- 给形参加* ------------------------------------------
    >>> def func(*args):
    ...     print(args)
    ... 
    >>> func(1,2,3,4,5)
    (1, 2, 3, 4, 5)
    
    
    >>> def func1(**kwargs):
    ...     print(kwargs)
    ... 
    >>> func1(a="A",b="b",c="C")
    {'a': 'A', 'b': 'b', 'c': 'C'}
    
    
    -----------------------给实参加*------------------------------------
    >>> def func(a,b,c,d):
    ...     print(a,b,c,d)
    ... 
    >>> func(*[1,2,3,4])
    1 2 3 4
    >>> func(**{"a":"A","b":"B","c":"C","d":"D"})
    A B C D

    4.关于函数的默认参数隐藏的巨坑

      我们先来看一个例子:

    #定义一个函数并调用这个函数
    def qqxing(l = []):
        l.append("zjk")
        print(l)
    
    qqxing()
    qqxing()
    qqxing()
    qqxing()
    -------------打印结果------------------------------
    ['zjk']
    ['zjk', 'zjk']
    ['zjk', 'zjk', 'zjk']
    ['zjk', 'zjk', 'zjk', 'zjk']
    
    #我们让第二次调用时添加一个参数[];
    def qqxing(l = []):
        l.append("zjk")
        print(l)
    
    qqxing()
    qqxing([])
    qqxing()
    qqxing()
    -------------打印结果-------------------------------
    ['zjk']
    ['zjk']
    ['zjk', 'zjk']
    ['zjk', 'zjk', 'zjk']
    
    ##那么请问,不加参数调用时和加参数调用时,每次的列表的id是否一致?
    
    ##我们再进行一次加参数和不加参数的打印,这次我们带上id
    def qqxing(l = []):
        l.append("zjk")
        print(id(l))
    
    qqxing()
    qqxing()
    qqxing()
    qqxing()
    ----------------打印结果--------------------------------
    38680776
    38680776
    38680776
    38680776
    
    ##------------加参数[]-----------------------------------------
    38811848
    36969224
    38811848
    38811848
    
    ##这次大家看出结果了吧!
    ##说明:如果默认参数的值是一个可变数据类型,那么每一次调用函数的时候,如果不传值就公用这个数据类型的资源。
    默认参数隐藏的坑

    5.关于形参中各参数的顺序:

      对于形参:

      位置参数,*args,默认参数,**kwargs

      对于实参:

      位置参数,关键字参数

      

    参数总结图:

      

      

      

  • 相关阅读:
    js拷贝
    git
    移动端网页电话号码的拨打
    es6 promise对象
    h5调用qq客户端
    ActiveMQ中的Destination高级特性(一)
    ActiveMQ集群下的消息回流功能
    ActiveMQ的静态网络链接
    深入理解IoC/DI
    ActiveMQ启动多个broker
  • 原文地址:https://www.cnblogs.com/zhangjunkang/p/9440866.html
Copyright © 2011-2022 走看看