python函数

函数啊函数

多解决问题,踩的坑多了,就有经验了

  1. 函数作用: 以功能(完成一件事)为导向的代码块,一个函数就是一个功能.
    • 随调随用,不用不调
    • 减少代码重复性,增强了代码可读性
  2. 函数的结构与调用:
    • def func(args):..expr..return a,b
    • 函数名 加上 括号 才执行
    • return的功能:
      • 1-结束函数,函数里面的while循环内部遇到return也会跳出.
      • 2-给函数外部返回一个或多个值(元组). 返回就是传给一个变量一个值
    • 调用一次函数,就执行一次
  3. 函数的返回值
    • return 单个值 type是值本身
    • return 多个值 是以元组的形式返回给函数的执行者
    • 没有return 实际返回是None
    • 一个函数可以有多个return –根据条件
  4. 函数的参数\传参
    • 形参角度 : 位置参数, 默认参数, 仅限关键字参数, 万能参数
    • 实参角度 : 位置参数, 关键字参数, 混合参数
    • 形参的顺序 : 位置参数, *args, 默认参数, 仅限关键字参数, **kwargs
    • 位置参数: 按顺序一一对应
    • 关键字参数:一一对应
    • 混合参数: 位置参数一定要在关键字前面
    • 默认参数的设置是经常用的
    • 万能参数args/**kwargs : 函数定义时,代表聚合元组和字典,执行时代表打散,拆成元组和字典
    • 形参角度的参数顺序: (位置参数 *args, 默认参数, 关键字参数, **kwargs)
    • 定义函数时括号参数加* ** 代表聚合
    • 执行函数时括号加* ** 代表打散 表体里的参数加* ** 也是打散
    def func(*args, **kwargs):
        print(args)
        print(kwargs) 
    func(*'abc', *[1, 2, 3], *[66, 77], **{'a': 1, 'b': 2}, **{'tt': 100, 'yy': 101})
     ## ('a', 'b', 'c', 1, 2, 3, 66, 77)
     ## {'a': 1, 'b': 2, 'tt': 100, 'yy': 101}
    *的另类用法
    list1 = [1,2,3,4,5,6]
    a, b, *c = list1
    print(a, b, c)   # 1, 2, [3,4,5,6]
    
    *x, y = list1
    print(x, y) = [1,2,3,4,5], 6
    
  5. 三元运算符–列表的和字典的三元表达式
    • aa = list[:2] if expr else 1
    • return a if a>b else b
    • list1 = [a for a in range(10)] # 列表三元表达式
    • dict1 = {key: value for key, value in enumerate(list1)} # 字典三元表达式
  6. 名称空间 和 作用域
    • 内置名称空间: python内置函数的预置空间print input ord chr id isinstance….
    • 全局名称空间: 记录变量名与值/函数名与函数体的对应关系 py文件root就是全局空间
    • 局部名称空间:执行时开辟,函数运行结束关闭….也叫临时空间 +
    • 加载顺序: 内置–全局–局部
    • 取值顺序: 局部–全局–内置 就近原则
    • 作用域: 2个 全局域和局部域
      • 全局作用域–内置名称空间和全局名称空间
      • 局部作用域–局部名称空间
      • 局部作用域可以”引用”全局作用域的变量,但不能修改这个变量
      • local–eclose–global–builtin ==>LEGB就近原则
  7. 函数的嵌套–高阶函数–函数里面套函数
  8. 循环一个列表,实际是按索引去循环的,不要在循环时改变列表的大小.出错.
  9. 内置函数–
    • globals() 返回的是字典: 字典里面的键值对:全局作用域的所有内容
    • locals() 返回的是字典: 字典里面的键值对:当前作用域的所有内容
    a=1
    b=2
    def func():
        c=3
        print(locals())
    print(globals())
    func()
    ###========----------内容如下 
    {'__name__': '__main__', '__doc__': None, '__package__': None, 
    '__loader__': <_frozen_importlib_external.SourceFileLoader object 
    at 0x000001393FED4820>, '__spec__': None, '__annotations__': {}, 
    '__builtins__': <module 'builtins' (built-in)>, 
    '__file__': 'E:\\software\\python\\pythonProject\\fuxitigao\\1106.py', 
    '__cached__': None, 'a': 1, 'b': 2, 'func': <function func at 
    0x000001393FE13EB0>}
    ---
    {'c': 3}   
    
  10. 默认参数的一个陷阱–坑.—-如果默认参数为可变列表?? (列表竟然是异次空间一直存在…)
    +如果你的默认参数指向的是可变数据类型,那么无论你调用多少次这个默认参数,都是同一个.

     def func(name, alist=[]):
         alist.append(name)
         return alist
     ret1 = func('张三')
     print(id(ret1), ret1)  # 2185581476224 ['张三']
     import time
     time.sleep(2)  # 停了2秒
     ret2 = func("李四")
     print(id(ret2), ret2)  # 2185581476224 ['张三', '李四'] 结果还是列表2个值
    
  11. 关键字–nonlocal global
    • global
      • 在局部作用域声明一个全局变量 e.g.把函数内部定义的变量声明为全局变量
      • 在局部作用域可以修改全局作用域的变量 e.g. 把函数外部定义的变量 声明为函数内部可修改的.(限于字符串,数字)。
      • 以上两个要在心中记出具体场景 (限于字符串,数字)。
      • 只有当函数执行后,函数内部声明的全局变量,才可以被全局调用.“`
    count = 1
    def func():
       global count
       count += 1
    print(count)  # 1  #此时函数体没有执行,不起作用
    func()
    print(count)  # 2  #此时函数体已经执行,起作用了 值被修改
    
    • nonlocal 在局部作用域中,对父级作用域(或者更外层作用域非全局作用域)的变量进行引用和修改,并且引用的哪层,从那层及以下此变量全部发生改变。
      • nonlocal 不能操作全局变量,只能在局部域的各层之间,用于内层引用外层函数定义的变量
  12. 函数名的运用—-这个函数名的运用–非常重要
    • 函数名 + 括号 ,就能执行这个函数
    • 函数名实际是个变量,指向一个内存地址 ,实际是这个内存地址加括号去执行函数体
    • 函数名可以做列表和字典的元素 e.g. 传入一个列表3个函数,循环list后i()执行
    • 函数名可以做为函数的参数 e.g. def abc() func(abc)
    • 函数名可以作为函数的返回值
    def func1():
        print("in func1")
    
    def func2(x):
        x()
        print("in func2")
        return x
    
    ret = func2(func1)
    ret()
    #执行结果--------------
    in func1  # 执行func2(func1)的运行结果
    in func2  # 执行func2(func1)的运行结果
    in func1  ###--返回值   
    
  13. 格式化输出
    • f”我叫{name.upper().center(30,”=”)}, 今年{age:02f} ” 太他妈好用了
    • f”我叫{dict[‘name11’]}, 今年{dict[‘age’} ” 可以取值
    • f’今年多少岁{age ** 2}’ 可以放表达式
    • f’最终计算结果是{sum(list_A)}‘ 可以放函数
  14. 迭代器:
    • 可迭代对象
      • 内部含有__iter__方法的,就是可迭代对象 e.g. str1 = 'abcde / str1.__iter__
      • 目前的可迭代对象:str list tuple dict set range 文件句柄
    • 获取对象的方法 str1 = 'abc' / print(dir(str1)) 会显示这个对象的所有方法,是个列表
    • 判断一个对象是否是可迭代对象 if __iter__ in dir(object) 返回True就是可迭代对象
    • 小结: 能直接显示,拥有方法比较多,但非常占内存,不能for循环(目前能循环是for内部我的淘宝转化)
    • 迭代器定义
      • 内部含有__iter__和__next__方法的,就是迭代器
    • 判断一个对象是否是迭代器 __iter__和__next__ in dir(object 目前只有文件句柄是迭代器
    • 迭代器的取值 迭代器可以直接取值,可迭代对象通过for循环并经转化后也可取值。
    • 可迭代对象如何转化成迭代器—- iter方法(ob)–next(ob)
    s = 'abcde'
    s1 = iter(s)  # 发音器函数iter   生成一个迭代器  (也可以s1.__iter__转成迭代器)
    s2 = next(s1)
    print(next(s1))         # a  next生成的是第1个值
    print(next(s1))         # b  next生成的是第2个值
    print(next(s1))         # c  next生成的是第3个值
    ---另一个方法生成迭代器 用自身的__iter__   两种方法是一样的
    s3 = s.__iter__()
    s4 = s3.__next__()
    print(s4)            # a
    s4 = s3.__next__()
    print(s4)            # b
    s4 = s3.__next__()
    print(s4)            # c
    
    • while循环模拟for循环机制 面试经常考while模拟for
      l1 = [11,22,333,44,515] obj = iter(l1) while 1: try: print(next(obj)) except StopIteration: break 这块代码要背过….硬背
    • 小结:
      • 特征:内部含有__iter__和__next__方法
      • iter(iteration)可将可迭代对象转成迭代器,next(object)可以取值
      • 迭代器的优点:
        • 节省内存。 迭代器在内存中相当于只占一个数据的空间:因为每次取值都上一条数据会在内存释放,加载当前的此条数据。
        • 惰性机制。 next一次,取一个值,绝不过多取值。
        • 有一个迭代器模式可以很好的解释上面这两条:迭代是数据处理的基石。扫描内存中放不下的数据集时,我们要找到一种惰性获取数据项的方式,即按需一次获取一个数据项。这就是迭代器模式。
      • 迭代器的缺点:
        • 速度慢
        • 不能直观的查看里面的数据。
        • 取值时不走回头路,只能一直向下取值。
    • 可迭代对象与迭代器的对比
      • 可迭代对象:是一个私有的方法比较多,操作灵活(比如列表,字典的增删改查,字符串的常用操作方法等),比较直观,但是占用内存,而且不能直接通过循环迭代取值的这么一个数据集。
      • 应用:当你侧重于对于数据可以灵活处理,并且内存空间足够,将数据集设置为可迭代对象是明确的选择。
      • 迭代器: 是一个非常节省内存,可以记录取值位置,可以直接通过循环+next方法取值,但是不直观,操作方法比较单一的数据集。
      • 应用:当你的数据量过大,大到足以撑爆你的内存或者你以节省内存为首选因素时,将数据集设置为迭代器是一个不错的选择。(可参考为什么python把文件句柄设置成迭代器)。
  15. 占位符: pass,,,,先占位,先把总体理清,有思路或者时间后再所内容填充上.
  16. 生成器:
    • 什么是生成器? 在python,生成器与迭代器是一个东西.本质一样.唯一区别是:生成器是我们自己用python代码生成的数据结构,迭代器都是提供的或者转化得来的.

      • 获取生成器的三种方式
        • 生成器函数 s = (i**2 for i in range(100))
        • 生成器表达式-自己写的 function + yield
        • python内部提供了一些.
    • yield

      • 一个yield,对应一个next
      • return 和yield的区别:
    • yield 有什么作用?———-转迭代器的时候有可能会先把内存爆,但是在定义生成器的时候不会.

      • 大文件转成迭代器,用iter生成,需要先调到内存把内存撑爆再转,比较危险.
      • 真正的节省内存
    • yield return的区别

      • return 结束函数,给执行者返回值
      • 只要函数中有yield,它就是生成器.
      • 生成器函数中可以存在多个yield,一个yield对应一个next
      • yield不会结束函数
      • 生成器实际上就是一只会下蛋的母鸡, 只有list\next\for循环才会执行下蛋
    • yield from: 优化了内层循环,提高了效率. 一行yield from 相当于多行yield

    def func4():
        l1 = [1,2,3,4,5]
        yield from l1    #yield from相当于把各项聚合了,等价于分开各行写
    ret = func4()
    print(next(ret))  # 1
    print(next(ret))  # 2
    print(next(ret))  # 3   将这个列表变成了一个迭代器返回
    
  17. 生成器表达式 列表推导式
    • 列表推导式 就是用一行代码构建一个有规律比较复杂的列表
    • 循环模式: list1 = [ i**2 for i in range(1, 100)/dict / set / str] –这个例子是错的,应是())
    • 筛选模式: list2 = [i for i in range(1,100) if i % 2 ==0]
    list3 = [i**2 for i in range(1,11)]
    list4 = [i for i in range(1,101) if i % 2 ==0]
    list4 = [i for i in range(2,100,2)]
    list5 = [f'python{i}期' for i in range(1,101)]
    # 列表套列表,,,有难度 
    names = [['tom', 'billy', 'jefferson', 'andrew', 'Wesley', 'Steven', 'Joe'], ['Alice', 'Jill', 'Ana', 'Wendy', 'Jennifer', 'Sherry', 'Eva']]
    
    # 找到嵌套列表中名字含有两个e的所有名字 (有难度)
    names2 = []
    for i in names:
        for ii in i:
            if ii.count('e') >= 2:
                names2.append(ii)
    print(names2)  # ['ABCDE', 'ZHANGSAN', 'LISI', 'MONIKA']
    #   神奇的推导式,
    names3 = [name.upper() for i in names for name in i if name.count('e') >= 2]
    print(names3)  # ['JEFFERSON', 'WESLEY', 'STEVEN', 'JENNIFER']
    
    • 生成器表达式
      • 把列表生成式的方括号 换成 小括号 ,就成了生成器
      • 也有筛选模式,也可以循环套循环
      • 列表生成式占内存,生成器表达式节省内存.
      • 好好弄清一筐鸡蛋和一只母鸡的区别. 表现形式是什么?
    ## 列表的方括号换成小括号 ,就成了生成器表达式
        ```js
        list5 = (i for i in range(10))
        print(list5, type(list5))
        for i in list5:
            print(i)
            
    
    list5 = (i for i in range(10))
    print(list5, type(list5))  # <generator object <genexpr> at 0x000001DFE485A810> <class 'generator'>
    for i in list5:
        print(i)     # 直接把迭代器循环打印出来,节省内存   
    # -----------------看代码写结果
    v = [i % 2 for i in range(10)]  # 这是一个列表推导式
    print(v)
    
    v = (i % 2 for i in range(10))  #  这是一个生成器表达式
    print(v)
    for i in v:  # 可以直接使用for循环直接取数
        print(i)
    
    
    • 列表推导式\迭代器\生成器 —小结
      • 1.有毒: 列表推导式只能构建比较复杂且有规律的列表,不要太着迷
      • 2.超过三层循环才能构建的,不建议用列表推导式
      • 3.排错不行,不能debug
      • 11.简单,一行搞定
      • 12.装逼
      • 21.怎么让生成器产生值? < 重要–next, for循环, list直接转化 这3个方法才能让迭代器下蛋 重要 >
    • 列表推导式 与 生成器表达式 的区别
      • 写法上: [] () 其他完全一样
      • 生成器是自己写的,迭代器是iter转化的
    • 字典的推导式
    # 字典生成式
    list7 = ['name', 'age', 'class']
    list8 = ['张三', '18', '三']
    dic1 = {list7[i]: list8[i] for i in range(len(list7))}
    print(dic1)  # {'name': '张三', 'age': '18', 'class': '三'}
    
    dic2 = {key:value for value, key in enumerate(list7)}  # {'name': 0, 'age': 1, 'class': 2}
    print(dic2)
    
    ##1\  迭代器取值的面试题 默写
        ```
        def demo():
            for i in range(4):
                yield i
        
        haha = demo()
        
        t1 = (i for i in haha if i <2)
        t2 = (i for i in t1)
        print(list(t1)) #这里已经取完值了,迭代器已经走到了最后
        print(list(t2))  # 迭代器已经到了最后,取不到值,所以为空
        ```
    
    ##2\  生成器最难的题  明白原理与运行的时间
        ```js
        def add(n,i):
            return n+i
        def test():
            for i in range(4):
                yield i
        g=test()
        for n in [1,10]:
            g = (add(n,i) for i in g)
            print(g)
        print(list(g))  # [20, 21, 22, 23]
        # 我的理解
        # 1\开始运行后, for 循环之前的都没运行
        # 2\for循环执行了2次,每次生成两个g的地址 //  因为生成器只有next list for循环才会执行
        # 3\第1次n=1,第2次n=10,但是第2次的n把第1次的n覆盖了,n=10   --关键
        # 4\开始list后,运行的g是for循环第2次的g, in g为第一次循环的g, in g取的是生成器[0,1,2,3]
        # 5\画图理解的快    
        
        ```
       ![img.png](img.png)
    
    • 集合的推导式
  18. 匿名函数: 一句话函数,比较简单的函数
    • 示例 lambda a,b(形参) : a+b 返回 # 冒号前是形参,冒号后是返回值
    mm = lambda a, b: a+b    #mm相当于函数名, lambda相当于def, a,b相当于形参
    print(mm(1,3))  # 4
    
    # 两个题
    # 写匿名函数:接收一个可切片的数据,返回索引为0与2的对应的元素(元组形式)。
    la1 = lambda x:(x[0], x[2])
    ss = la1('abcde')
    print(ss)  # ('a', 'c')
    # 写匿名函数:接收两个int参数,将较大的数据返回。
    la2 = lambda a, b: a if a>b else b
    print(la2(55, 66))  # 66
    
    • lambda经常与内置函数结合,经常用
  19. 内置函数
    • 71个内置函数 要经常练习一下
    ** eval()
    #  ** help()
    #  *** callable()
    # int()
    # float()
    # bin()
    # oct()
    # hex()
    # **divmod()  # 分页时使用
    # **round()
    # pow() x的y次幂  / 对单取余
    # ***bytes()   # s = s.encode('utf-8') # s1 = bytes(s1,'utf-8')
    # **ord()
    # **chr()  可应用于随机验证码,超出ascii找unicode,ascii只有128个字符
    # print(ord("中"))
    # print(chr(20013))
    # ***repr()  原形毕露函数,debug调试时很有用/格式化输出时也用到
    # all()
    # any()
    # abs()
    # enumerate()
    # filter()
    # map()
    # open()
    # range()
    # print()
    # len()
    # list()
    # dict()  # 创建字典的七种方式  要熟悉 //直接创建 元组解构 关键字定义 fromkeys updates 字典推导式
    # str()
    # float()
    # reversed()
    # set()
    # sorted()
    # sum()  可以设置初始值
    # tuple()
    # type()
    # ***zip()
    # dir()
    #  ###################最重要的,最牛的,加key的内置函数
    # max()
    # min()
    # reduce()  累加效果from functools import reduce
    

    求绝对值最小的值对应的键 min函数 key和lambda组合使用

    ``` key的用法很玄妙啊
    dic = {'a':3,'b':-2,'c':1}
    # 求最小的键
    # 求最小的键对应的值
    # 求最小的值
    # 求绝对值最小的值
    # 求最小的值对应的键
    # 求绝对值最小的值对应的键
    print("--")
    num1 = min(dic)
    print(num1)  # a求最小的键
    num2 = dic[min(dic)]
    print(num2) # 3  这是求最小的键对应的值
    
    num3 = min(dic.values())
    print(num3)  # -2 求最小的值
    
    num4 = min(dic.values(), key=abs)
    print(num4)  # 1 求绝对值最小的值
    
    num5 = min(dic, key=lambda x:dic[x])
    print(num5)  # b 值最小的key
    
    num6 = min(dic, key = lambda x: abs(dic[x]))
    print(num6)  # c 绝对值最小的值对应的key
    ```
    
  20. 闭包
    • 闭包只能存在于嵌套函数中
    • 内层函数对外层函数非全局变量的引用(使用), 就会形成闭包
    • 被引用的非全局变量称为自由变量,与内层函数有一个绑定关系
    • 自由变量不会在内存中消失
    • 闭包的作用: 保证数据安全,自由变量在内存中不会消失,而且全局还引用不到。
    • 闭包的定义:
      • 闭包是嵌套在函数中的函数。
      • 闭包必须是内层函数对外层函数的变量(非全局变量)的引用。
    • 闭包应用在哪里?
      • 可以保存一些非全局变量但是不易被销毁、改变的数据。
      • 装饰器。
    这是一个典型的闭包...
    def func():
        l1 = []
        def inner(num):
            l1.append(num)
            total_amount = sum(l1)
            return total_amount/len(l1)
        return inner
    haha = func()
    l1 = haha(100)
    print(l1)  # 100.0
    l2 = haha(150)
    print(l2)  # 125.0
    #### 装饰器肯定是闭包?  如果装饰器内层函数没有调用外层函数变量呢
    #### 利用函数自带方法打印查看有无自由变量,有自由变量,即是闭包
    print(haha.__code__.co_freevars)  # ('l1',)  # 发现自由变量
    #### 打印出一个自由变量,那这肯定就是一个闭包.
    print(func().__code__.co_freevars)
    
  21. 字符串 列表 元组 按索引取值时的小坑
    ##  字符串 列表 元组 按索引取值时的小坑
        ```js
        s1 = '1,2,3'
        # print(s1[10])  # 会报错,string index out of range
        print(repr(s1[10:])) # ''  结果是空,不报错
        print(s1[10:])  # ''  结果是空,不报错
        ```
    
  22. 装饰器 — 完美体现了开放封装原则
    • 装饰器就是一个函数, 实际也就5行,有的人半年都没理解…
    • 装饰器用在哪? 有什么作用?
    • 装饰器本质:闭包 –>自由变量
    def deco(func):
        def inner(*args, **kwargs):
            res = func(*args, **kwargs)
            return res
        return inner
    
    ----------------------
    def deco(func):  # 标准装饰器模版
        ...为所欲为 你想做啥
        def inner(*args, **kwargs):
            ...为所欲为 你想做啥
            res = func(*args, **kwargs)  # func函数在此处被执行,等号右边
            ...为所欲为 你想做啥
            return res    # 返回func的return结果
        return inner      # inner = deco(func)
    
    ----
    @deco    #  f = deco(f)  这一步非常重要,,加了语法糖,就是转义赋值
    def f(a):
        print(111,a)
    
    f(123)    #实际执行的是wrapper(f)(123)
    
  23. 怎么得到函数的名字? (func._ name _)
  24. 怎么得到现在的时间? import time/ now_time = time.strftime(“%Y-%m-%d %H:%M:%S”,time.localtime())
版权声明:本文为leeyong49原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/leeyong49/p/16860949.html