• 初识函数——函数的定义、返回值和参数


    函数的定义

    函数在数学上的定义:y=f(x) ,y是x的函数,x是自变量。y=f(x0, x1, ..., xn) 
    python的函数是由若干语句组成的语句块、函数名称、参数列表构成,它是组织代码的最小单元, 它能完成一定的功能 。

    在Python中,用关键字def关键字来定义函数,如下:

    def greet_hello():
        print("hello!")
        
    greet_hello()

    关键字def后面是函数名, 它其实就是一个标识符。后面的括号必须有,里面可以有参数。函数定义,只是声明了一个函数,它不会被执行,需要调用,函数的调用很简单,直接函数名加括号就调用了。定义需要在调用前,也就是说调用时,已经被定义过了,否则抛NameError异常。调用时写的参数是实际参数,是实实在在传入的值,简称实参。

    函数名就是标识符,命名要求一样,同时语句块必须缩进,约定4个空格,python中的函数如果没有return语句,隐式会返回一个None值,定义中的参数列表称为形式参数,只是一种符号表达。而调用的时候写的参数是实际参数,是实实在在传入的值,简称实参。

    def add(x, y):
        result = x+y
        return result
    out = add(4,5)
    print(out)
    
    结果为:
    9

    callable(add)

    结果为:True

    上面只是一个函数的定义,有一个函数叫做add,接收2个参数,然后计算的结果,通过返回值来返回。

    调用的时候通过函数名add加2个参数,同时返回值可使用变量接收。
    义需要在调用前,也就是说调用时,已经被定义过了,否则抛NameError异常
    函数是可调用的对象,callable() 

    上面的代码可以复用,不仅仅是对数字可以相加,也可以对字符串相加。不需要事先声明。

    函数的作用

    1. 功能封装,便于后面复用。
    2. 减少冗余代码。
    3. 代码更易读、简洁美观。

    函数的返回值

    首先先来看两个例子:

    def showplus(x):
        print(x)
        return x+1
    
    showplus(5)
    
    def showplus(x):
        print(x)
        return x+1
        print(x+1)
    
    showplus(5)
    
    结果都为:
    5
    6

    上面的例子可以看到,return后,后面的语句就不会再执行了。

    函数的返回值一般有四种情况:

    1. 没有返回值,默认会返回None。
    2. 直接一个return,默认返回None。
    3. 返回一个值。
    4. 返回多个值,返回多个值其实也相当于返回一个,返回的多个值用元组接收。可以返回任意数据类型的值。可以使用解构来提取值。

    返回一个值

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

    结果为3

    返回多个值

    def ret_demo1():
        '''返回多个值''' 
        return 1,2,3,4
    
    
    def ret_demo2():
        '''返回多个任意类型的值'''
        return 1,['a','b'],3,4
    
    ret1 = ret_demo1()
    print(ret1)
    ret2 = ret_demo2()
    print(ret2)

    结果为:
    (1, 2, 3, 4)
    (1, ['a', 'b'], 3, 4)

    应当注意的是,函数执行时,一旦遇到return,就会结束整个函数

    def ret_demo():
        print(111)
        return
        print(222)
    
    ret = ret_demo()
    print(ret)
    
    结果为1111
         None

    可以看到,后面return后面的语句没有执行了。

    多条return语句

    def showplus(x):
        print(x)
        return x + 1
        return x + 2
    showplus(5)
    
    结果为:
    5
    6
    
    def guess(x):
        if x > 3:
            return "> 3"
        else:
            return "<= 3"
    print(guess(10))
    
    结果为:
    > 3
    def fn(x):
        for i in range(x):
            if i > 3:
                return i
        else:
            print("{} is not greater than 3".format(x))
    print(fn(5))
    
    print(fn(3))
    
    结果为:
    
    4
    3 is not greater than 3
    None

    所以,Python函数使用return语句返回“返回值”,所有的函数都有返回值,如果没有return语句,会隐式的调用return none。同时应该注意return语句并不一定是函数语句块的最后一句。

    一个函数可以有多个return语句,但是只有一条可以被执行。如果没有一条return语句被执行到,就会隐式调用return None。

    如果有必要,可以显示调用return none,可以简写成为return。

    如果函数执行了return语句,函数就会返回,当前被执行的return语句之后的其他语句就不会被执行了。

    所以return的作用就是结束函数调用并返回值。

    函数的参数

    参数分为形参和实参,函数定义时为形参,函数调用时为实参。参数调用时传入的参数要和定义的个数相匹配(可变参数例外)

    传递多个参数

    def mymax(x,y):
        the_max = x if x > y else y
        return the_max
    
    ma = mymax(10,20)
    print(ma)
    结果:20

    位置实参

    位置参数也就是按照参数定义顺序传入实参,传入的实参个数需要和定义的形参个数匹配。比如def f(x, y, z) 调用使用 f(1, 3, 5) ,此时是一一对应的。

    关键字实参

    def f(x, y, z) 调用使用 f(x=1, y=3, z=5) ,使用形参的名字来出入实参的方式,如果使用了形参名字,那么传参顺序就可和定义顺序不同。

    位置实参和关键字实参混合

    应当注意,位置参数必须在关键字参数的前面,位置参数是按位置对应的。同时,对于一个形参只能赋值一次。f(y=5, z=6, 2) 像这样的传参会报错。

    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 add(x=4, y=5):
        return x+y
    
    add(6, 10)
    结果为:
    16
    
    add(6, y=7) 
    结果为:
    13
    
    add(x=5)
    结果为:
    10
    
    add()
    结果为:
    9
    
    add(y=7)
    结果为:
    11
    
    add(x=5, 6)
    结果为:
     File "<ipython-input-18-a7fe3f3b9351>", line 1
        add(x=5, 6)
                ^
    SyntaxError: positional argument follows keyword argument
    
    
    add(y=8,4)
    结果为:
     File "<ipython-input-20-013636d44ec1>", line 1
        add(y=8,4)
               ^
    SyntaxError: positional argument follows keyword argument
    
    
    
    add(x=5, y=6)
    结果为:
    11
    
    add(y=5, x=6)
    结果为:
    11

    给他们传参的时候跟上面的关键字和默认值传参规则一样。

    #定义一个函数login,参数名称为host、port、username、password
    def login(host='127.0.0.1',port='8080',username='xpc',password='xpc199151'):
        print('{}:{}@{}/{}'.format(host, port, username, password))
    login()
    login('127.0.0.1', 80, 'tom', 'tom')
    login('127.0.0.1', username='root')
    login('localhost', port=80,password='com')
    login(port=80, password='magedu', host='www')
    
    结果为:
    127.0.0.1:8080@xpc/xpc199151
    127.0.0.1:80@tom/tom
    127.0.0.1:8080@root/xpc199151
    localhost:80@xpc/com
    www:80@xpc/magedu

    应该注意的是,如果默认值传参的是一个可变数据类型。后期的结果会保留前面的结果。

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

    结果为:
    ['alex']
    ['alex', 'egon']

    可变参数

    可变参数分为位置参数的可变参数和关键字参数的可变参数。

    位置参数的可变参数*

    现在有一个需求,有多个数,但又不知道具体有几个数字,需要累加求和,这个时候需要传入一个可迭代对象,迭代元素求和。

    def add(nums):
        sum = 0
        for x in nums:
            sum += x
        return sum
    
    print(add([1,3,5]))
    add((2,4,6))
    
    结果为:
    9
    12

    而可变位置参数,可以一个形参接受任意多个实参,按位置传值多余的参数都由args统一接收,保存成一个元组的形式。

    def mysum(*args):
    print(args) the_sum
    = 0 for i in args: the_sum+=i return the_sum the_sum = mysum(1,2,3,4) print(the_sum)

    (1, 2, 3, 4)
    10

    关键字参数的可变参数**

    形参前使用**符号,表示可以接收多个关键字参数,收集的实参名称和值组成一个字典 。

    def stu_info(**kwargs):
        print(kwargs)
        print(kwargs['name'],kwargs['sex'])
    
    stu_info(name = 'alex',sex = 'male')

    结果为:

    {'name': 'alex', 'sex': 'male'}
    alex male

    def showconfig(**kwargs):
      for k,v in kwargs.items():
        print('{} = {}'.format(k, v))
    showconfig(host='127.0.0.1',port='8080',username='xpc',password='12345')

    结果为:

    host = 127.0.0.1
    port = 8080
    username = xpc
    password = 12345
    位置可变参数和关键字可变参数的混合

    混合使用参数的时候,可变参数要放到参数列表的最后,普通参数需要放到参数列表前面,位置可变参数需要在关键字可变参数之前。

    def showconfig(username, *args, **kwargs):
        print(username)
        print(args)
        for k,v in kwargs.items():
            print("{}={}".format(k,v))
    
    showconfig("xpc")
    结果为:
    xpc
    ()
    
    showconfig("xpc",1,2,3,45)
    结果为:
    xpc
    (1, 2, 3, 45)
    
    showconfig("xpc",a=1,b=2,c=3)
    结果为:
    xpc
    ()
    a=1
    b=2
    c=3
    def showconfig(username,password, **kwargs,*args):
        print(username)
        print(args)
        for k,v in kwargs.items():
            print("{}={}".format(k,v))
    
    结果为:
     File "<ipython-input-48-b281bd3c7e8c>", line 1
        def showconfig(username,password, **kwargs,*args):
                                                   ^
    SyntaxError: invalid syntax

    所以,可变参数有位置可变参数和关键字可变参数,位置可变参数在形参前使用一个星号* ,关键字可变参数在形参前使用两个星号** ,位置可变参数和关键字可变参数都可以收集若干个实参,位置可变参数收集形成一个tuple,关键字可变参数收集形成一个dict,
    混合使用参数的时候,可变参数要放到参数列表的最后,普通参数需要放到参数列表前面,位置可变参数需要在关键字可变参数之前。

    def fn(x, y, *args, **kwargs):
        print(x)
        print(y)
        print(args)
        print(kwargs)
    
    fn(3,5,7,9,10,a=1,b='python')
    
    3
    5
    (7, 9, 10)
    {'a': 1, 'b': 'python'}
    
    fn(3,5)
    
    3
    5
    ()
    {}
    
    fn(3,5,7)
    
    3
    5
    (7,)
    {}
    
    fn(3,5,a=1,b='python')
    
    3
    5
    ()
    {'a': 1, 'b': 'python'}
    
    fn(7,9,y=5,x=3,a=1,b='python')
    
    TypeError: fn() got multiple values for argument 'y'

    keyword-only参数

    当位置可变参数放在了最前面,后面的普通参数变成了keyword-only参数。
    def fn(*args, x, y, **kwargs):
        print(x)
        print(y)
        print(args)
        print(kwargs)
    
    fn(3,5)
    
    TypeError: fn() missing 2 required keyword-only arguments: 'x' and 'y'
    
    fn(3,5,7)
    
    TypeError: fn() missing 2 required keyword-only arguments: 'x' and 'y'
    
    fn(3,5,a=1,b='python')
    
    TypeError: fn() missing 2 required keyword-only arguments: 'x' and 'y'
    
    fn(7,9,y=5,x=3,a=1,b='python')
    
    3
    5
    (7, 9)
    {'a': 1, 'b': 'python'}
    def fn(*args, x):#*号之后,普通形参都变成了必须给出的keyword-only 参数 
        print(x)
        print(args)
    fn(3,5)
    
    结果为:
    
    ---------------------------------------------------------------------------
    TypeError                                 Traceback (most recent call last)
    <ipython-input-53-c5c0648876f6> in <module>
          2     print(x)
          3     print(args)
    ----> 4 fn(3,5)
    
    TypeError: fn() missing 1 required keyword-only argument: 'x'
    
    
    fn(3,5,7)
    结果为:
    ---------------------------------------------------------------------------
    TypeError                                 Traceback (most recent call last)
    <ipython-input-55-da5434142bea> in <module>
    ----> 1 fn(3,5,7)
    
    TypeError: fn() missing 1 required keyword-only argument: 'x'
    
    fn(3,5,x=7)
    结果为:
    7
    (3, 5)

    上面的例子args可以看做已经截获了所有的位置参数,x不使用关键字参数就不可能拿到实参。

    keyword-only参数

    如果在一个星号参数后,或者一个位置可变参数后,出现的普通参数,实际上已经不是普通的参数了,而是keyword-only参数。就像上面的例子一样。而如果是关键字可变参数在前面,像def daf(**kwargs, x)则会直接报语法错误,可以理解为kwargs会截获所有的关键字参数,就算你写了x=5,x也永远得不到这个值,所以语法
    错误。
    def(**kwargs, x):
        print(x)
        print(kwargs)
    
    结果为:
     File "<ipython-input-57-63e64f7a052a>", line 1
        def(**kwargs, x):
           ^
    SyntaxError: invalid syntax
    def fn(*, x,y):#这个*没有意义,只是让后面的参数变成了keyword-only参数
        print(x,y)
    fn(x=5,y=6)
    
    结果为:
    5 6

    可变参数和参数默认值

    def fn(*args,x = 5):
        print(x)
        print(args)
    
    fn()#等价于fn(x=5)
    
    5
    ()
    
    fn(5)
    
    5
    (5,)
    
    fn(x=6)
    
    6
    ()
    
    fn(1,2,3,x=6)
    
    6
    (1, 2, 3)
    def fn(y,*args,x=5):
        print("x={},y={}".format(x,y))
        print(args)
    
    
    fn()
    TypeError: fn() missing 1 required positional argument: 'y'
    
    fn(5)
    
    x=5,y=5
    ()
    
    fn(x=6)
    
    TypeError: fn() missing 1 required positional argument: 'y'
    
    fn(1,2,3,x=10)
    x=10, y=1
    (2, 3)

    fn(y=17,2,3,x=10)
    结果为:
     File "<ipython-input-63-89c585e00820>", line 1
        fn(y=17,2,3,x=10)
               ^
    SyntaxError: positional argument follows keyword argument
    

    fn(1,2,y=3,x=10)

    结果为:

    ---------------------------------------------------------------------------
    TypeError                                 Traceback (most recent call last)
    <ipython-input-65-ca712b8f6fdb> in <module>
    ----> 1 fn(1,2,y=3,x=10)
    
    TypeError: fn() got multiple values for argument 'y'
     

    可变参数和参数默认值

    def fn(x=5,**kwargs):
        print("x={}".format(x))
        print(kwargs)
    fn()
    
    x=5
    {}
    
    fn(5)
    
    x=5
    {}
    
    fn(x=6)
    
    x=6
    {}
    
    fn(y=3,x=5)
    
    x=5
    {'y': 3}
    
    fn(3,y=10)
    
    x=3
    {'y': 10}

    由此可见,参数列表参数一般顺序是,普通参数、缺省参数、可变位置参数、keyword-only参数(可带缺省值)、可变关键字参数 。

    def fn(x, y, z=3, *arg, m=4, n, **kwargs):
        print(x,y,z,m,n)
        print(args)
        print(kwargs)
    def connect(host='localhost', port='3306', user='admin', password='admin', **kwargs):
        print(host, port)
        print(user, password)
        print(kwargs)
    connect(db='cmdb')
    connect(host='192.168.1.123', db='cmdb')
    connect(host='192.168.1.123', db='cmdb', password='mysql')
    
    结果为:
    
    localhost 3306
    admin admin
    {'db': 'cmdb'}
    192.168.1.123 3306
    admin admin
    {'db': 'cmdb'}
    192.168.1.123 3306
    admin mysql
    {'db': 'cmdb'}
    def add(x, y):
        return x+y
    add(*{'x': 5, 'y': 6}.keys())
    
    结果为:
    'xy'
    
    def add(x, y):
        return x+y
    add(*{'x': 5, 'y': 6}.values())
    结果为:
    11
    def fn(*args,*,x,y):
        print(x,y)
    
    结果为:
    File "<ipython-input-108-35d54ecda275>", line 1
        def fn(*args,*,x,y):
                     ^
    SyntaxError: invalid syntax
    def fn(x,y,z=3,*args,m=4,n,**kwargs):
        print(x,y,z,m,n)
        print(args)
        print(kwargs)
    fn(1,2,n=4)
    
    结果为:
    1 2 3 4 4
    ()
    {}
    
    fn(1,2,10,11,t=7,n=5)
    结果为:
    1 2 10 4 5
    (11,)
    {'t': 7}

    参数解构

    给函数提供实参的时候,可以在集合类型前使用*或者**,把集合类型的结构解开,提取出所有元素作为函数的实参 。非字典类型使用*解构成位置参数 ,字典类型使用**解构成关键字参数 。提取出来的元素数目要和参数的要求匹配,也要和参数的类型匹配。

    def add(x,y):
        return x+y
    add(4,5)
    
    9
    
    add((4,5))
    
    TypeError: add() missing 1 required positional argument: 'y'
    
    
    add(*(4,5))
    
    9
    
    t=(4,5)
    add(t[0],t[1])
    
    9
    
    add(*t)
    
    9
    
    add(*[4,5])
    
    9
    
    add(*{4,5})#集合
    
    9
    
    add(*range(1,3))
    
    3

      d = {'x': 5, 'y': 6}
      add(**d)

      11

      add(**{'a': 5, 'b': 6})

      TypeError: add() got an unexpected keyword argument 'a'

      add(*{'a': 5, 'b': 6})字典的结构可以用*d.keys()或者*d.values()

      'ab'

    def add(x, y):
      return x+y
    add(**{'x': 5, 'y': 6})

    结果为:

    11
    参数解构和可变参数
    给函数提供实参的时候,可以在集合类型前使用*或者**,把集合类型的结构解开,提取出所有元素作为函数的实参。
    def add(*iterable):
        result = 0
        for x in iterable:
            result += x
        return result
    add(1,2,3)
    
    结果为:
    
    6
    
    add(*[1,2,3])
    结果为:
    6
    
    add(*range(10))
    结果为:
    45

    练习题1:

    编写一个函数,能够接受至少2个参数,返回最小值和最大值。

    import random
    
    def double_values(*nums):
        print(nums)
        return max(nums),min(nums)
    print(*double_values(*[random.randint(10,20) for _ in range(10)]))
    
    结果为:
    (14, 11, 17, 13, 19, 15, 20, 16, 19, 10)
    20 10
    
    def nums(x,y,*args):
        print(min(x,y,*args))
        print(max(x,y,*args))
    nums(1,25,0,-3)
    
    结果为:
    -3
    25
    练习题二:编写一个函数,接受一个参数n,n为正整数,左右两种打印方式,要求数字必须对齐。

    def tan_print(n):
        for i in range(1,n+1):
            for j in range(n,0,-1):
                if i < j:
                    print(" "*len(str(j)),end=" ")
                else:
                    print(j,end=" ")
            print()
    tan_print(11)
    
    
    结果为:
    
                          1 
                        2 1 
                      3 2 1 
                    4 3 2 1 
                  5 4 3 2 1 
                6 5 4 3 2 1 
              7 6 5 4 3 2 1 
            8 7 6 5 4 3 2 1 
          9 8 7 6 5 4 3 2 1 
       10 9 8 7 6 5 4 3 2 1 
    11 10 9 8 7 6 5 4 3 2 1 
    def show(n):
        tail = " ".join([str(i) for i in range(n,0,-1)])
        width = len(tail)
        for i in range(1,n):
            print("{:>{}}".format(" ".join([str(j) for j in range(i,0,-1)]),width))
        print(tail)
    
    show(11)

    结果为:

    def showtail(n):
        tail = " ".join([str(i) for i in range(n,0,-1)])
        print(tail)
        #无需再次生成列表
        for i in range(len(tail)):
            if tail[i]==" ":
                print(" "*i,tail[i+1:])#切片,这是个copy,空间复杂度比较大。
    showtail(12)
            

    结果为:




     
     
  • 相关阅读:
    Android OpenGL(2)
    Bootloader:BareBox
    Android OpenGL(1)
    Android脚本环境
    Android用户界面开发:事件处理
    S5PV210按键控制LED
    S5PV210控制蜂鸣器
    Windows Vista/Windows 7上安装wince5.0/6.0及SDK模拟器
    Makefile
    每日英语:Apps Reorder the Job Landscape
  • 原文地址:https://www.cnblogs.com/xpc51/p/11468462.html
Copyright © 2020-2023  润新知