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

    什么是函数

    具备某一功能的工具

    对于工具的使用,我们应该先准备好工具,然后再使用。即我们对函数应该先定义后引用。和变量一样。

    为什么用函数

    1、程序的组织结构不清晰,可读性差
    2、如果要用到重复的功能,只能拷贝功能的实现代码=》代码冗余
    3、可扩展性差

    如何使用函数

    函数使用原则:先定义后引用

    # 定义函数:
    def 函数名(参数1,参数2,参数3,...):
                """文档注释"""
                代码1
                代码2
                代码3
                ...
                return 返回值
    
    # 调用函数:
    函数名(值1,值2,值3,...)
    

    函数的基本使用

    # 1、定义函数:申请内存空间把函数体代码保存下来,然后把内存地址绑定给函数名-》函数名=函数的内存地址
    def sayhi():
        print('*'*10)
        print('hello')
        print('*'*10)
    
    print(sayhi)  # 函数名本质是一串内存地址,注意下方开头是function,表明是一个函数。
    ---------------------
    <function hello at 0x7fdc6a5ba1f0>
    
    
    # 2、调用函数: 函数名()=> 函数的内存地址(),会触发函数体代码的运行
    sayhi()
    

    定义函数的三种格式

    # 2.1: 无参函数,即函数本身不需要参数传入
    def login():
        inp_name=input("your name: ").strip()
        inp_pwd=input("your pwd: ").strip()
        if inp_name == "yang" and inp_pwd == "123":
            print('login successful')
        else:
            print('login error')
    
    login()
    
    # 2.2: 有参函数,函数内需要外部参数传入
    def max2(x,y):
        if x > y:
            print(x)
        else:
            print(y)
    max2(10,20)
    max2(11,22)
    
    
    '''2.3: 空函数,空函数在项目初期,有些功能还没做完时,可以先用空函数顶替,防止程序无法运行'''
    def func():
        pass
    

    函数的返回值

    利用return 可以将函数内处理之后的结果返回

    # 函数内可以有多个return,但只要执行一次函数就立刻结束,并会把return后的值当作本次调用的结果返回
    '''
    函数可以有三种形式的返回值
    1、return 值:返回的就是该值本身
    2、return 值1,值2,值3:返回一个元组
    3、没有return:默认返回None
    '''
    def max2(x, y):
        if x > y:
            return x
        else:
            return y
    
    
    res = max2(1, 2)
    print(res)
    --------------------
    2
    

    函数调用的三种格式

    # 语句形式:单纯地调用一下函数就完了
    def hello(s,n):
        print(s*n)
        print('hello')
        print(s*n)
    
    hello('*',30)
    
    # 表达式形式:
    def max2(x,y):
        if x > y:
            return x
        else:
            return y
    
    res=max2(11,22) * 12
    print(res)
    
    #可以把函数的调用当作值传给另外一个函数
    print(max(33,max2(11,22)))
    

    总结

    '''
    函数的使用一定要分两个阶段去看:
    1、定义阶段:只检测语法,不执行代码
    2、调用阶段:执行函数体代码
    '''
    # 如果发生的语法错误,定义阶段就会立马检测出来
    def func():
        print("hello"  # 直接报错
    
    # 如果发生的不是语法错误,而是逻辑错误,只能在调用阶段检测到
    def func():
        xxx
              
    func()  # 报错
    

    三、函数参数

    函数的参数分为两大类:形参与实参

    # 1、形参:在定义函数时,括号内定义的变量名,称之为形式参数,简称形参=》变量名
    def func(x,y):  # x,y就是形参
        x=1
        y=2
    print(x)
    print(y)
    
    
    # 2、实参:在调用函数时,括号内传入的值,称之为实际参数,简称实参=》变量值
    func(1,2)
    

    在python中参数的种类

    '''
    1、位置参数:
    (1)位置形参:在函数定义阶段按照从左到右的顺序依次定义形参(变量名),称之为位置形参
     特点:必须被传值
    '''
    def func(x,y):
        print(x,y)
    
    func(1,2)
    func(1) # 少一个不行
    func(1,2,3) # 多一个也不行
    
    '''
    (2)位置实参:在函数调用阶段按照从左到右的顺序依次定义实参(传入的变量值),称之为位置实参
    特点:按照位置传值,一一对应
    '''
    def func(x1,x2,x3,x4,x5,x6):
        print(x1,x2,x3,x4,x5,x6)
    
    func(1,2,3,4,5,6)
    
    '''
    2、关键字实参:在函数调用阶段按照key=value的形式为指定的形参名传值,该形式称之为关键字实参
    特点:在传值时可以完全打乱顺序,但是仍然能够指名道姓地为指定的形参传值
    '''
    def func(name, age):
        print(name, age)
    
    func("yang",18)
    func(18,"yang")
    func(age=18,name="yang")
    -----------------------
    yang 18
    18 yang
    yang 18
    
    
    '''
    注意:可以混用位置实参与关键字实参,但是
    1 位置实参必须放在关键字实参的前面
    2 不能为同一个形参重复赋值
    '''
    def func(name, age, salary):
        print(name,age,salary)
    
    
    func('yang',salary=3.1,age=18)
    # func('yang',salary=3.1,18) # 错误
    func('yang', 18, salary=3.1)
    # func('yang',18,age=19,salary=3.3) # 错误
    -------------------------------
    yang 18 3.1
    yang 18 3.1
    
    
    '''
    3、默认形参:在函数定义阶段就已经为某个形参赋值,该形参称之为有默认值的形参,简称默认形参
    特点: 定义阶段就已经被赋值意味着在函数调用阶段可以不用为其赋值
    '''
    def func(x,y=2):
        print(x,y)
    
    func(1)  # 不会报错,因为有默然形参,如果没有传值,那么y默认为2
    func(1,33333)
    '''
    注意:
    1 默认形参应该放在位置形参的后面
    '''
    def func(y=2,x): # 错误
        pass
    '''
    
    
    2 默认形参的值通常应该是不可变类型,因为若值是可变类型,我们获取到的是一个id地址,传入值的时候也是通过id,就会导致传的每个值都通过这个id存值,使得所有值都放在了一起
    '''
    def func(name,hobby,hobbies=[]):
        hobbies.append(hobby)
        print("%s 的爱好是:%s" %(name,hobbies))
    
    func("yang",'play')
    func('yang','music')
    func("egon",'read')
    ---------------------------------------
    yang 的爱好是:['play']
    yang 的爱好是:['play', 'music']
    egon 的爱好是:['play', 'music', 'read']
    '''
    要实现以上单独存的功能,应该把hobbies放到函数里面定义
    '''
    def func(name,hobby,hobbies=None):
        if hobbies is None:
            hobbies=[]
        hobbies.append(hobby)
        print("%s 的爱好是:%s" %(name,hobbies))
    
    func("yang",'play')
    func('yang','music')
    func("egon",'read')
    ------------------------------------
    yang 的爱好是:['play']
    yang 的爱好是:['music']
    egon 的爱好是:['read']
    '''
    3 默认形参的值只在函数定义阶段被赋值一次,函数定义之后的改变对默认形参没有影响
    '''
    m=333
    def func(x,y=m): # y=333
        print(x,y)
    
    m=444
    func(1)
    -------------------
    1 333
    
    
    def register(name,age,gender='male'):
        print(name)
        print(age)
        print(gender)
    
    register('yang',18,)
    register('jack',20,)
    register('mary',18,'female')
    
    

    四:可变长参数

    可变长指的是参数的个数不固定
    站在实参的角度,实参是用来为形参赋值的,如果实参的个数不固定,那么必须要有对应的形参能够接收溢出实参
    *与**在形参与实参中的应用
    在形参中用*与**

    在形参名前加 *

    * 会把溢出的位置实参存成元组,然后赋值其后的形参名

    def func(x,*y):  # y=(2,3,4)
        print(x)
        print(y)
    
    
    func(1,2,3,4)
    func(1)
    ----------------
    1
    (2, 3, 4)
    1
    ()
    # func() # 位置形参x必须被传值
    

    在形参名前加**

    **会把溢出的关键字实参存成字典,然后赋值其后的形参名

    def func(x, **y):  # y=(2,3,4)
        print(x)
        print(y)
    
    
    func(1,a=111,b=222,c=333)
    func(a=111, b=222, x=1, c=333)
    ----------------------------------
    1
    {'a': 111, 'b': 222, 'c': 333}
    1
    {'a': 111, 'b': 222, 'c': 333}
    

    在实参前加*

    *会把其后的值打散成位置实参

    def func(x,y,z):
        print(x,y,z)
    
    nums = [1, 2, 3]
    func(*nums)  # func(1,2,3)
    # 若只有一个形参,则多余出来的值没处放,会出错
    -------------------
    1 2 3
    

    在实参前加 **

    **会把其后的值打散关键字实参

    def func(x,y,z):
        print(x, y, z)
    
    dic = {'y': 111, 'z': 222, 'x': 333}
    func(**dic)  # func(y=111,z=222,x=333)
    ------------------------
    333 111 222
    

    *args与**kwargs一起使用

    '''
    同时使用*与**的作用是可以把外部传的值原封不动的传给另一个函数,这在闭包函数,装饰器中有很大作用
    '''
    def index(x,y,z,a,b,c):
        print("index===>",x,y,z,a,b,c)
    
    def wrapper(*args, **kwargs):  
      # args=(1, 2, 3,)   kwargs={"a":111,"b":222,"c":333}
        index(*args, **kwargs)  
        # index(*(1, 2, 3,),**{"a":111,"b":222,"c":333})
        # index(1,2,3,c=333,b=222,a=111)
    
    wrapper(1, 2, 3, a=111, b=222, c=333)
    ---------------------------------
    index===> 1 2 3 111 222 333
    

    五、函数对象

    函数对象指的是函数可以被当成变量去使用

    def foo():  # foo = 函数的内存地址
        print('from foo')
    

    可以被赋值

    f = foo
    print(f is foo)
    f()
    

    可以当作参数传给一个函数

    def bar(func):
        print(func)
        func()
    
    bar(foo)
    

    可以当成一个函数的返回值

    def bar(func):
        return func
    
    res=bar(foo)
    print(res)
    

    可以当成容器类型的元素

    在之前学习if判断时候,我们举过这个例子,当有大量if判断的分支且结果是执行函数时,可以用字典。

    l = [foo]
    
    print(l)
    l[0]()
    

    示例:当我们的主函数是由多个其他函数组成,即我们需要使用许多的if来判断用户输入且调用对应的函数时候,可以使用这种容器的方法,使得代码量减少,同时,在面试时,若被问到如何减少if操作时,可使用字典替换

    def login():
        print('登录功能......')
    
    def withdraw():
        print('提现功能......')
    
    def transfer():
        print('转账功能......')
    
    def recharge():
        print('充值功能')
    
    func_dic={
        "1": [login,"登录"],
        "2": [withdraw,"提现"],
        "3": [transfer,"转账"],
        "4": [recharge,"充值"]
    }
    
    while True:
        print("0    退出")
        for k in func_dic:
            print("%s    %s" %(k,func_dic[k][1]))
    
        choice = input("请输入你的指令编号: ").strip()
        if choice == "0":
            break
        if choice in func_dic:
            func_dic[choice][0]()
        else:
            print('输入的指令不存在')
    

    六、函数嵌套

    函数的嵌套调用

    def bar():
        print('from bar')
    
    def foo():
        print('from foo')
        bar()
    
    foo()
    ---------------------
    from foo
    from bar
    
    def 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)
        print(res3)
    
    max4(1,2,3,4)
    -----------------
    4
    

    函数的嵌套定义

    def f1():
        print('from f1')
        def f2():
            print("from f2")
    f1()  # 此时不会触发f2运行
    -----------------
    from f1
    

    定义在函数内的函数特点是: 正常情况只能在函数体内调用

    from math import pi
    
    def circle(radius,mode=0):
        def perimiter(radius):
            return 2 * pi * radius
    
        def area(radius):
            return pi * (radius ** 2)
    
        if mode == 0:
            return perimiter(radius)
        elif mode == 1:
            return area(radius)
    
    res1=circle(3,0)
    res2=circle(3,1)
    print(res1)
    print(res2)
    -------------------------
    18.84955592153876
    28.274333882308138
    
    
    def func():
        x = 10
        print(x)
        def f2():
            print('from f2')
        f2()
    func()
    print(x)
    ---------------------------------
    10
    from f2
    NameError: name 'x' is not defined
    

    七、名称空间与作用域

    名称空间

    名称空间在python中有非常重要的地位,在python之禅中专门提到了名称空间,在python解释器中输入 impoet this 可以看到python之禅.

    Namespaces are one honking great idea -- let's do more of those!
    

    名称空间就是存放名字的地方
    名(函数名、变量名等):存放在内存里
    值(变量值等):存放在内存里
    通过划分出各个名字空间,允许在不同的空间内,可以有相同的名字

    内置名称空间

    存放内置的名字(python解释器启动时便定义好的)如 print、input、len
    生命周期:解释器启动则产生,解释器关闭则销毁 (python解释器在pyhton程序执行时运行,执行结束时结束)

    全局名称空间

    存放的是顶级的名字

    生命周期:python程序运行时则产生,python程序结束则销毁

    局部名称空间

    即函数内的名字
    只有调用函数时,局部名字才产生,定义函数时并不会产生,因为代码并未执行,python解释器只会扫描是否有语法错误。

    生命周期:调用函数时产生,函数调用结束则销毁

    名字的查找优先级

    从当前位置往外查找,如果当前是在局部:局部名称空间->全局名称空间->内置名称空间
    从当前位置往外查找,如果当前是在全局:全局名称空间->内置名称空间

    内置名称空间

    存放的是内置的名字,如print,input,len等等
    生命周期: 解释器启动则产生,解释器关闭则销毁

    全局名称空间

    存放的是顶级的名字
    生命周期: python程序运行时则产生,python程序结束则销毁

    一般来说,没有缩进的名字都是顶级名字,但是如果这个名字在if分支里,他也属于顶级名字

    x = 10
    
    def func():
        x = 111
        print(x)
    
    
    if 1:
        y = 6666  # y也是顶级名字
    

    局部名称空间

    函数内的名字

    生命周期: 调用函数时则产生,函数调用结束则销毁

    名字的查找优先级:
    从当前位置往外查找,如果当前是在局部:局部名称空间->全局名称空间->内置名称空间
    从当前位置往外查找,如果当前是在全局:全局名称空间->内置名称空间

    def func():
        len = 222
        # print(len)
    
    # len = 111
    
    func()
    print(len)
    
    # 名称空间可以理解为一层套一层的关系,问题是嵌套关系是在函数定义阶段生成的,还是在函数调用阶段生成的?
    
    x = 111
    
    def foo():
        print(x)
    
    def bar(f):
        x=222
        f()
    
    bar(foo)
    ------------
    111
    

    一个非常重要的结论

    名称空间的嵌套关系是函数定义阶段(即扫描语法时)就固定死的,与函数的调用位置无关

    x = 111
    def func():
        print(x)
        x=2222
    
    func()
    '''
    以上这个例子会报错,因为python解释器在扫描语法时发现需要打印x,但是x定义在print后面,
    本来如果func中不写x=2222,那么func会到外面去找111,但是在函数中写了,那么会优先用
    自己的,但是又因为还未定义,所以会报错
    '''
    # 练习
    x=0
    def f1():
        x=1
        def f2():
            x=2
            print(x)
    
        f2()
    f1()
    ---------
    2
    

    全局范围/全局作用域对应内置名称空间+全局名称空间
    特点:全局存活,全局有效

    局部范围/局部作用域对应局部名称空间
    特点:临时存活,局部有效

    案例1

    x = 10
    
    def func(x):
        x = 20
    
    func(x)
    print(x)
    --------------
    10
    

    案例2

    x = [11,22,33]
    
    def func(x): 
        x[0] = 66
    
    func(x)
    print(x)
    --------------
    [66,22,33]
    

    案例3

    x = [11,22,33]
    def func():
        x[0] = 66
    
    func()
    print(x)
    -------------
    [66,22,33]
    

    案例4

    x = 10
    def func():
        global x
        x=22
    
    func()
    print(x)
    ----------
    22
    

    案例5:nonlocal生命名字是来自于外层函数的(了解)

    x = 10
    
    
    def f1():
        x = 111
    
        def f2():
            nonlocal x
            x = 222
    
        f2()
        print(x)
    
    
    f1()
    print(x)
    -------------
    222
    10
    
  • 相关阅读:
    Python爬取暴走漫画动态图
    ADB server didn't ACK 的解决方法
    安装APK时报Local path doesn't exist错误
    当Web Services遇到Android(初步接触时可能遇到的错误)
    eclipse启动时出现Incompatible JVM Version [###] of the JVM is not suitable for this product ...
    两个Activity之间的切换和响应
    关于不同Android手机适配的几个问题(转)
    ERROR: Unknown command 'crunch' 解决方法
    eclipse无法启动的常见原因
    获取手机屏幕的宽和高
  • 原文地址:https://www.cnblogs.com/chiyun/p/14066255.html
Copyright © 2011-2022 走看看