网站logo

Python学习小站



一、函数的两种传参方式回顶


    def myfunc1(s, vt, o):
        return "".join((o, vt, s))
    print(myfunc1("我", "爱上", "Python"))
    print(myfunc1(o="我", vt="爱上", s="Python"))
    

二、默认参数回顶

 
    #    默认参数
    def myfunc2(s, vt, o="Study"):
        return "".join((s, vt, o))
    print(myfunc2("我", "爱"))
    print(myfunc2("我", "爱", "Python"))

    def myfunc3(vt, s="苹果", o="我"):
        return "".join((o, vt, s))
    print(myfunc3("吃了"))
    print(abs(-1.5))
    print(sum([1, 2, 3], start=4))
    #!  print(abs(x = -1.5))是不被允许的,会报错
    #!  同理,sum(start=4, [1, 2, 3])也是会报错的
    #    关键字传参在位置传参后面
    

三、/与*回顶

 
    def abc1(a, /, b, c):
        print(a, b ,c)
    abc1(1, 2, 3)
    abc1(1, b = 2, c = 3)
    #    函数形参有/,说明在/之前的参数在传参时必须是位置参数而不能是关键字参数

    def abc2(a, *, b, c):
        print(a ,b ,c)
    abc2(1, b = 2, c = 3)
    abc2(a = 4, b = 5, c = 6)
    #    函数形参有*,说明在*之后的参数传参时必须是关键字参数
    

四、将参数打包为元组回顶

 
    def myfunc4(*args):
        print("有{}个参数".format(len(args)))
        print("第2个参数是:{}".format(args[1]))
        print(args)
        print(type(args))
    myfunc4(1, 2, 3, 4, 5)
    #    在参数前加*,可以将传入的位置参数打包成一个元组
    
    def myfunc8(*args, a, b):
    print(args, a, b)
    myfunc8(1, 2, 3 ,a = 4, b = 5)
    #    如果出现了打包成元组的操作,那么后面参数传参时必须用关键字参数传参,以区分元组打包传参
    

五、将返回值解包回顶

 
    def myfunc6():
        return 1, 2, 3
    print(myfunc6)
    x, y, z = myfunc6()
    print(x, y, z)
    #    多参数返回,解包操作
    

六、将参数打包为字典回顶

 
    def myfunc9(**kwargs):
        print(kwargs)
    myfunc9(a = 1, b = 2, c = 3)
    #    参数前出现**,那么就是字典打包传参,传参时要按照:键=值的方式传参,那么就会在函数内新建一个字典,键值对就是我们传参时候设置的形式。

    def myfunc10(a, *b, **cd):
        print(a, b, c, d)
    myfunc10(1, 2, 3, 4, x = 5, y = 6, d = 8)
    #    综合运用,同理,字典打包传参后面的参数同样必须用关键字传参
    

七、传参解包回顶

 
    def myfunc11(a, b, c, d):
        print(a, b, c, d)
    args = (1, 2, 3, 4)
    myfunc11(*args)
    #    传参时用*代表解包元组

    kwargs = {'a':1, 'b':2, 'c':3, 'd':4}
    myfunc11(**kwargs)
    #    使用**对传参时的字典进行解包,此时值会被保留下来,键会被舍弃
    

八、LEGB规则回顶

 
    #*  LEGB规则,python变量引用顺序:从当前作用域开始寻找变量,如果没找到就往上一层作用域寻找,
    #*  没找到就再上一层。
    #TODO   具体步骤:当前作用域局部变量->外层作用域变量->再外层作用域变量->…->当前模块全局变量->pyhton内置变量

    x = 880 #*  全局变量
    def myfunc12():
        x = 770 #*局部变量
        print(x, id(x))
    myfunc12()
    print(x, id(x))
    

九、global关键字回顶

 
    #TODO   使用global关键字,可以调用全局变量,甚至可以在函数内部创建全局变量
    x = 250
    def myfunc13():
    global x, y 
    #TODO   调用全局变量x,创建全局变量y
    x = 260
    y = 250
    print(x, y, id(x), id(y))
    myfunc13()
    print(x, y, id(x), id(y))
    

十、nonlocal关键字回顶

 
    #TODO   使用nonlocal关键字,可以在嵌套的函数内部修改外层函数的变量,
    #!  但是前提是外层函数定义了某个变量,不然使用nonlocal会因为找不到外层函数的变量而报错
    def myfunc14():
        x = 250 
        #*  myfunc14内的变量x
        def myfunc15():
            nonlocal x  
            #TODO   调用myfunc14内的变量x,如果x未定义,则报错
            #!  倘若没有这句nonlocal,那么会在内层函数内新建一个x变量,但是外层函数的x依然没有得到改变
            #!  也就是说想要修改外层函数的变量,要用这些关键字,不然根据LEGB规则,即使同名,先使用的依然是内层函数的变量x
            x = 260
            print(x, id(x))
        myfunc15()
        print(x, id(x))
    myfunc14()
    #!  当然,不使用nonlocal关键字,也可以在内层函数中调用外层函数的变量,但是不能修改,参考下面的power函数,原理是LEGB规则
    
    #*  内层函数具有记忆外层函数作用域的特性,因此可以用来记忆外层函数的变量
    #*  通过nonlocal实现记忆外层函数变量的功能
    def outer():
        x = 0
        y = 0
        def inner(x1, y1):
            nonlocal x, y
            x += x1
            y += y1
    print("现在,x = {}, y = {}".format(x, y))
    return inner
    move = outer()
    move(1, 2)
    move(-2, 2)
    

十一、闭包回顶

 
    #*  什么是闭包?
    #*  如果在一个内部函数里,对在外部作用域(但不是在全局作用域)的变量进行引用,那么内部函数就被认为是闭包
    #*  闭包就像是一个流水线,而闭包的外层函数是一个工厂,通过给外层函数传递参数,来确定工厂的流水线
    #TODO   考虑下面的例子,power是次方函数,接收参数exp,exp=2就是求2次方,exp=3就是求三次方,
    #TODO   外层函数作为工厂,决定加工性质,而内层函数闭包作为流水线,将产品生产出来
    #*  闭包,使得我们能够使用内层函数
    def power(exp):
        def exp_of(base):
            return base ** exp
        return exp_of
    square = power(2)
    cube = power(3)
    print(square(2), square(5), cube(2), cube(5))
    #*  通过将工厂power赋一参数,得到流水线————内层函数,然后赋值给变量,
    #*  这时,变量就是内层函数,也就是流水线,然后再进而给内层函数赋值,得到产品————结果
    

十二、函数作为参数传递回顶

 
    #*  可以将函数作为参数传递给函数
    def myfunc():
        print("正在调用myfunc...")
    def report(func):
        print("我要开始调用函数了...")
        func()
        print("调用函数完毕")
    report(myfunc)
    

十三、装饰器回顶

 
    #?  什么是装饰器?有一个目标函数,这个函数是我们想要调用的,
    #?  装饰器就是创建一个闭包,然后对目标函数进行加工修饰,
    #?  达到不改动目标函数的同时,增加函数的功能
    #TODO   所以装饰器是通过闭包+函数作为参数来实现的
    import time
    def time_master(func):
        #*  闭包第一层函数参数用来接收外界函数
        def call_func():
            print("开始运行程序...")
            start = time.time()
            func()
            stop = time.time()
            print("结束程序运行...")
            print("一共耗费了{:.12f}秒".format(stop - start))
        return call_func    #*  返回内层函数

    @time_master    #*通过@,我们将@下面的目标函数进行@内容的装饰,增加函数功能
    #!  @time_master本质是:myfunc15 = time_master(myfunc15)
    #!  所以,装饰器的本质就是将以定义好的函数myfunc15传入闭包内加工装饰成新的函数,
    #!  然后返回给myfunc15自己,那么myfunc15本身就相当于给自己加了装饰。
    def myfunc15():
        time.sleep(2)
        print("I love Python")
    myfunc15()
    

十四、多重装饰回顶

 
    #*  多重装饰,装饰顺序为从下至上
    def add(func):
        def inner():
            x = func()
            return x + 1
        return inner
    def cube(func):
        def inner():
            x = func()
            return x * x * x
    return inner
    def square(func):
        def inner():
            x = func()
            return x * x
        return inner
    @add
    @cube
    @square
    def test():
        return 2
    print(test())
    

十五、给装饰器传参回顶

 
    #*  如果想给装饰器传入参数,那么我们可以给装饰器外层,即闭包外层再嵌套一个函数
    #*  这个函数专门接收参数
    def logger(msg):
        def time_counter(func):
            def call_func():
                start = time.time()
                func()
                stop = time.time()
                print("小{0}睡了{second:.2f}秒".format(msg, second = stop - start))
            return call_func
        return time_counter

    @logger("A")
    def funA():
        time.sleep(1)
        print("正在调用funA...")

    @logger(msg = "B")
    def funB():
        time.sleep(1)
        print("正在调用funB...")
    funA()
    funB()
    

十六、lambda函数回顶

 
    squareX = lambda x : x * x
    print(squareX(3))
    print(type(squareX))
    y = [lambda y : y * y, 2, 3]
    print(y[0](y[1]), y[0](y[2]))
    mapped = map(lambda x : ord(x) + 10, "Python")
    print(list(mapped))
    

十七、生成器与yield关键字回顶

 
    def counter():
        i = 0
        while i <= 5:
            yield i
            #*  yield将函数变成生成器函数
            #*  使用yield,将会暂停函数,但是函数的进程将会被记住,然后返回一个值,一次调用返回一次值
            i += 1
    print("生成器:")
    print(counter())
    #*  当调用生成器函数,会返回生成器对象
    for i in counter():
        #*  从此我们知道,生成器实际上是一个迭代器,通过for的i每次调用生成器,
        #*  相对于启动-关闭-启动-关闭...,由于for循环会自动调用next,因此在print i的时候
        #*  会打印生成器的元素
        print(i)
    c = counter()
    print(next(c))
    #TODO   通过next可以将生成器的元素打印出来
    print("生成器不支持c[2]")
    def fib():
        back1, back2 = 0, 1
        while 1:
            yield back1
            back1, back2 = back2, (back1 + back2)
    f = fib()
    print(next(f))
    print(next(f))
    print(next(f))
    print(next(f))

    def add(x, y):
        """两数做和
            x (int): 其中一个加数
            :param y:另外一个加数
            :param return:返回和
        """
        result = x + y
        print(f"两数相加的结果是:{result}")
        return result
    add(1, 9)