# 函数

  • 函数:就是对代码进行封装,以便我们调用

  • 函数式编程也叫面向过程编程,存在的问题:代码冗余、可读性差、可扩展性差(不易修改)

  • 闭包函数:内部函数包含对外部作用域而非全局作用域名字的引用,该内部函数称为闭包函数:如装饰器

    1. 判断是不是闭包函数的方法: __closure__,不是返回None
  • 装饰器

    1. 本质: 一个闭包函数

    2. 装饰器的功能:在不修改原函数及其调用方式的情况下对原函数功能进行扩展

      import time 
      def timer(func):
          def inner(*args,**kwargs):  # 固定格式接受一切参数
              start = time.time()
              re = func(*args,**kwargs)
              print(time.time() - start)
              return re
          return inner
      
      @timer   #==> func1 = timer(func1)
      def func1(a,b):
          print('in func1')
          
      print(index.__doc__)    #查看函数注释的方法
      print(dir([1,3,4,5]))   # 查看对象的内置方法
      print(index.__name__)   #查看函数名的方法
      
    3. 开放封闭原则

      1、对扩展开放:可以代码扩充、添加新功能
      
      2. 对修改封闭:因为我们并没有修改原本的函数调用方式,没有修改以前的调用方式
      
  • 多个装饰器(纵向衍生)和多参数装饰器(横向衍生)

      # 纵向衍生,多个装饰器叠加,装饰器内部通过return将本身的内层函数作为参数传给上一个,而被装饰函数名会首先往上传,最后语法糖会得到最上面一个装饰器的内层函数
      def wrapper1(func):
          def inner1():
              print('wrapper1 ,before func')
              func()
              print('wrapper1 ,after func')
          return inner1
      
      def wrapper2(func):
          def inner2():
              print('wrapper2 ,before func')
              func()
              print('wrapper2 ,after func')
          return inner2
      
       # f = wrapper2(wrapper1(f))  这里我们带入后的结果为 f = wrapper2(inner1) 此时f已存在			wrapper1中,使其为闭包暂存,
      @wrapper2   #2、 warpper2本来想接收被装饰函数名,但是却得到了包裹着被装饰函数名的inner1,只能接收作为参数,最后返回他的inner2给f
      @wrapper1   #1、接收被装饰得函数名,返回一个内层函数inner1给wrapper2
      def f():
          print('in f')
      f()
      
      #wrapper2 ,before func
      #wrapper1 ,before func
      #in f
      #wrapper1 ,after func
      #wrapper2 ,after func
      
      ------------------------------------------------------------
      # 横向衍生,装饰器函数内有多层函数,需要一层层的剥装饰器函数内部,每剥一次会得到新的装饰器
      def lo():
          print('logger is working!')
          
      def logger(flag):  # 通过添加logger函数来使在想要添加日记记录lo函数时,添加
          def show_time(f):
              def inner(*x,**y):
                  start = time.time()
                  f(*x,**y)
                  end = time.time()
                  print('spend %s'%(end-start))
                  if flag == 'True':
                      lo()
                  return 'over'
              return inner
          return show_time
      
      @logger('True')   # foo = logger(True)(f) ==> show_time(f) ==> inner  注意这就是装饰器的横向的特性,也就是一层层往里翻
      def foo(a,b):
          print(a+b)
          time.sleep(1)
      
      print(foo(2,4))
      
       #注意:重点,这是我在学到Flask的时候突然明白的一点,恍然大悟,我们之前写装饰器默认是没有括号的(默认是将装饰的函数名传递给装饰器当参数,如纵向衍生),在横向衍生的实列中我们看到传入了一个True的参数,所以先执行login函数,进入后因为闭包保存了flag = True,同时返回了show_time函数,此时再将装饰的函数名传入给f。
      # 判断是何类型
    from collections import Iterable
    l=[1,2,3,4]
    print(isinstance(l,Iterable))  # True
    

迭代器(iter)

  1. 可迭代对象: Iterable, 里面有__iter__()可以获取迭代器, 没有 __next__()

    迭代器: Iterator, 里面有 __iter__() 可以获取迭代器, 还有迭代器的 __next__() 方法用来获取下一个元素

​ 2.可迭代协议:如果对象中有 __iter__() 函数方法,我们就认为这个函数遵守可迭代协议

  1. 迭代器协议: 必须拥有__iter__()__next__() 函数方法
  2. 4.for循环内部机制
lst = [1,2,3]  #list是可迭代对象
lst_iter = lst.__iter__()    #获取迭代器
while True:
    try:    #试试
        i = lst_iter.__next__()   #调用next方法,获取下一个元素
        print(i)
    except StopIteration:    #除了这事我兜着
        break 
  • 生成器(Generator):

    1. 生成器函数: 常规函数定义 ,但是使用yield语句替代return语句,每次yield语句返回一个结果,且使得函数处于挂起状态以便下次传值进入函数或是执行下面的代码

    2. 生成器表达式:类似于列表推导式,但是生成器返回的是一个生成器对象

    3. 生成器的生成方式:1、生成器推导式;2、还有yield的函数

    4. 生成器本质: 就是一个迭代器(所以自带了__iter__()__next__() 方法,不需要我们去实现)

    5. 特点: 惰性运算,开发者自定义,节省空间,从上自下,只执行一次

      def produce():
          """生产衣服"""
          for i in range(2000000):
              yield "生产了第%s件衣服"%i
      
      product_g = produce()  # 生成一个生成器对象
      print(product_g.__next__()) #要一件衣服
      print(product_g.__next__()) #再要一件衣服
      print(product_g.__next__()) #再要一件衣服
      
    6. 生成器的各种方法:

    ```python
      # send() 获取下一个值得时候和next基本一样,但是在第一次触发的时候,要么send(None)要么 __next__(),同时send可以给上一个yield传递数据    
     def averager():
           total = 0.0
           count = 0
           average = None
           while True:   # 生成器无底洞
               term = yield average  # 最先返回一个None,紧接着接受一个传来的新值
               total += term
               count += 1
               average = total/count
       
       g_avg = averager()
       next(g_avg)  #触发第一次的yield,返回None
       print(g_avg.send(10))  #10
       print(g_avg.send(30))  #20
       print(g_avg.send(5))   #15
       
         #yield from 从某对象中迭代的拿出来
       def gen1():
           for c in 'AB':
               yield c
           for i in range(3):
               yield i
       print(list(gen1()))   #['A', 'B', 0, 1, 2]
       
       def gen2():
           yield from 'AB'
           yield from range(3)
       print(list(gen2()))   #['A', 'B', 0, 1, 2]
       
         # next(生成器对象)方法和.__next__()方法一样,取下一个值
            
     #总结经验:yield一般和 while 使用,在外部用for触发
    ```
    
    1. 列表和生成器推导式:

         #列表推导式
         lst = ['python%s' % i for i in range(1,15)]
         print(lst)  #[python1,python2……]
         #列表推导式加条件
         lst = [i for i in range(1, 100) if i % 2 == 0]
         print(lst)  #[2,4,6,8……98]
         #生成器推导式
         s = (x for x in range(10))  # s是一个生成器地址
         #生成器推导式加条件
         gen = (i for i in range(1,100) if i % 3 == 0)
         for num in gen:
             print(num)  # 3,6,9……99
          
      li =[[1,2,4],[5,6,9],'abc']
      l=[ x for v in li for x in v] #注意顺序,先打开,再for,从一个里拿 
      print(l)  #[1, 2, 4, 5, 6, 9, 'a', 'b', 'c'] 
      
      #以下代码执行的结果是?解释其原因: {x:y for x in ['Male','Female'] for y in ['Red','Black']}?
      
      {'Male': 'Black', 'Female': 'Black'} #正确答案,原因很简单,键唯一,每次否被最后一个覆盖
      print([{x: y} for x in ['Male', 'Female'] for y in ['Red', 'Black']])  #从两个里拿
      #结果:[{'Male': 'Red'}, {'Male': 'Black'}, {'Female': 'Red'}, {'Female': 'Black'}]
          # 从左至右的去可迭代对象
      
      lis = [['L', 'E', 'E', 'T'],  # 解包zip
              [0, 0, 'C', 0],
              [0, 'O', 0, 0],
              ['D', 'E', 'I', 'S'],
              [0, 0, 0, 'H'],
              [0, 0, 'I', 0],
              ['R', 'I', 'N', 'G']]
      args= [ x for i in zip(*lis) for x in i if x != 0]
      print(args)
      
       #三元运算
      a ,b= 1,4
      c=a if a<b else b
      print(c)  #1
      
        #列表推导式和生成器推导式的区别:
            #1.列表推导式比较耗内存,一次性加载,生成器几乎不占内存,使用的时候才分配
            #2.生成的结果不一样,一个是列表一个是生成器地址
      
      1. 递归函数:在函数里面调用这个函数本身
        #递归函数的最大递归次数:1000,但是一般电脑最多到998就不行了
          def age(n):
            if n == 1:  # 递归函数需要设计一个出口,不然会一直递归下去
                return 40
            else:
                return age(n-1)+2   #  age(3)+2=>(age(2)+2)+2=>((age(1)+2)+2)+2
        print(age(4)) # 46
        
        #压栈实现三级菜单
        l=[menu]  #menu 是一个三级字典
        while l:
            print(l)
            for key in l[-1]:print(key)  # 打印字典得键
            k = input('input>>').strip()   # 北京
            if k in l[-1].keys() and l[-1][k]:l.append(l[-1][k]) #妙 满足条件则增加新环境
            elif k == 'b':l.pop()
            elif k == 'q':break   
      

# 内置函数

  • 内置函数中含有iterable参数的方法:

    1. 大部分内置函数,也是使用迭代器协议访问对象的。例如 sum、sorted函数等:

      # 在内置函数中,只要参数需要iterable的就含有next方法,它们会自动调用其中的每个值。
      all(iterable) #如果集合元素都是true或集合为空,就返回True, 
      any(iterable) #只要一项True就True
      enumerate(iterablem,start=0) #枚举,返回每个元素和索引且开始值为0,可设置
      filter(function,iterable) #过滤函数,可以对每个可迭代对象中的值进行筛选,返回一个迭代器
      map(function,iterable) #遍历函数,将function作用于每个iterable项,返回一个迭代器
      sorted(iterable,key=function,reverse=False) # 少用占内存,可以使用自带list.sort()
      sum(iterable,start) # 求和函数,求完后会再加start
      zip(*iterable)  #会先将可迭代对象打散,返回一个zip可迭代对象,可以list或则tuple
      open(file,mode='r'……)  # 返回一个可迭代对象,的文件句柄
      next(iterable,default)  # 如果给定default则会在下一项不存在的时候返回defaut的值
      max(iterable,key) #key 可以接受一个返回数字的函数名,返回这个函数名最值的那一个,min也如此
      
      frozenset([iterable]) # 返回一个冻结的集合
      set([iterable]) #返回一个可变、无序不重复的集合
      list([iterable]) #返回一个列表
      tuple([iterable]) #返回一个元组
      dict([iterable]) #返回一个字典
      
    2. 其他内置函数

      __init__(self,...)	 #初始化对象,在创建新对象时调用
      __del__(self)	   #释放对象,在对象被删除之前调用
      __new__(cls,*args,**kwd)	#实例的生成操作
      __str__(self)	   #在使用print语句时被调用
      __getitem__(self,key)	 #获取序列的索引key对应的值,等价于seq[key]
      __len__(self)	   #在调用内联函数len()时被调用
      __cmp__(stc,dst)	 #比较两个对象src和dst
      __getattr__(s,name)	 #获取属性的值
      __setattr__(s,name,value)	 #设置属性的值
      __delattr__(s,name)	 # 删除name属性
      __getattribute__()	 #__getattribute__()功能与__getattr__()类似
      __gt__(self,other)	 #判断self对象是否大于other对象
      __lt__(slef,other)	 #判断self对象是否小于other对象
      __ge__(slef,other)	 #判断self对象是否大于或者等于other对象
      __le__(slef,other)	 #判断self对象是否小于或者等于other对象
      __eq__(slef,other)	 #判断self对象是否等于other对象
      __call__(self,*args)	 #把实例对象作为函数调用
      
      abs() # 返回数字的绝对值
      ascii() #返回一个表示对象的字符串
      bin()  #返回0b的二进制字符串
      bool()  #返回布尔值
      callable(对象) #判断对象是否可调用
      chr() # 返回Unicode代码点为整数的字符串,如chr(97) ='a'
      ord(c) #将字母转化为数字
      compile() # 函数将一个字符串编译为字节代码,常和exec和eval连用
      
      getattr(对象,名称,没找到的返回值)  #获取对象的属性
      hasattr(对象,名称)  #返回True或False
      setattr(对象,名称,值)  #设置对象的属性
      delattr(对象,名称)  #删除对象属性
      
      dir([对象]) # 返回对象属性
      divmod(a,b)  #a除以b的商和余数
      eval(表达式,globals=Nonelocals=None) #执行表达式内容,可取全局和局部变量
      exec(object,globals=Nonelocals=None)  #执行储存在字符串或文件中的Python语句
      format(value,操作方式) #可设置字符串的显示方式,居中,小数点位置,转换二进制,十进制等
      hash(对象) # 返回一个hash值
      help(对象) # 用于获取帮助
      hex(x)  #将整数转化为一个‘0x'的十六进制
      
      id(对象)  # 返回代表对象身份的整数,唯一的,可以理解为地址
      instance(object,classinfo) # 判断对象是不是后者的实例化对象
      issubclass(class,classinfo) # 如果class是classinfo的子类返回一个True
      iter(object) # 返回一个迭代器对象
      len(s)  # 返回对象的长度
      oct(x) # 将整数转换为'0o'的八进制
      pow(x,y)  # 返回x的y次方
      print*objects, sep=' ', end='\n', file=sys.stdout, flush=False#可以打印到文件中
      range(start,stop,step)  #返回一个不可变的有序类型,顾头不顾尾
      repr(object) #查看原生代码
      
      reversed() # 反转序列对象
      round(number,保留位数) #四舍五入,可设置位数
      super() #函数是用于调用父类(超类)的一个方法。解决多继承,遵循MRO顺序,和钻石继承
      vars(对象)  # 返回对象的属性和属性值
      __import__(name, globals=None, locals=None, fromlist=(), level=0) #动态导入模块
      
    3. 内置函数使用补充:

      #max的key用法
      import re
      reg = re.compile('a*b*c*d*e*f*g*h*i*j*k*l*m*n*o*p*q*r*s*t*u*v*w*x*y*z*')
      #返回一个字符串里按字母表排序的最长子字符串
      def longest(s):
          print(reg.findall(s))  #['abcde', 'ap', 'bcdefgh', '']
          return max(reg.findall(s), key=len)  # 此函数名必是返回一个整数的,可自定义函数
      #加或不加效果相同
      print(longest('abcdeapbcdefgh'))  # bcdefgh
        
          # compile用法
      str = "for i in range(0,10): print(i)"
      c = compile(str,'','exec')
      print(c)  #<code object <module> at 0x10141e0b0, file "", line 1>
      exec(c)  #打印0,1,3.....
      
      	#eval的用法
      a=1
      g={'a':20}
      print(eval("a+1",g))  #对全局变量g进行操作  返回21
      
      
    4. 匿名函数:为了解决那些功能比较简单的需求而设计的一句话

      	#用匿名函数实现取出字典中最大的值
      dic={'k1':10,'k2':100,'k3':30}
      print(max(dic))
      print(dic[max(dic,key=lambda k:dic[k])])  #100
      
      #现有两个元组(('a'),('b')),(('c'),('d')),请使用python中匿名函数生成列表[{'a':'c'},{'b':'d'}]
      #答案一
      test = lambda t1,t2 :[{i:j} for i,j in zip(t1,t2)]
      print(test(t1,t2))
      #答案二
      print(list(map(lambda t:{t[0]:t[1]},zip(t1,t2))))
      #还可以这样写
      print([{i:j} for i,j in zip(t1,t2)])
      
      #以下代码的输出是什么?请给出答案并解释。
      def multipliers():
          return [lambda x:i*x for i in range(4)]
      print([m(2) for m in multipliers()])
      #请修改multipliers的定义来产生期望的结果
      #第一问 :[6,6,6,6]
      #第二问 :
      def multipliers():
           return (lambda x:i*x for i in range(4)) # 不需要yield因为,for必须在推导式内也就是说必须有括号
           	# for i in range(4):
          	 #     yield lambda x:i*x
      print([m(2) for m in multipliers()])  #[2,4,6]
      
      #内置函数的使用
      1# 列出奇数索引的元素
      lis = [12,13,13,13,151,5,16,17,117,133,144,177]  
      print(list(filter(lambda x:x,(lis[i] for i in range(1,len(lis),2)))))
      
      2#reduce 的用法 打印出1累加到100的和,传入开头两个参数,然后运算结果给第一个,第二个参数继续拿,以此类推(累加或类乘器)
      from functools import reduce  #需要导入模块
      print(reduce(lambda x,y:x+y,list(range(1,101))))
      
      #生成器题目:
      def add(a, b):
          return a + b
      def test():
          for r_i in range(4):
              yield r_i
      
      g = test()  # 实现一个生成器 
      for n in [2, 10]:   # 这里只执行了两次for循环,所以将10带入两次。最开始g=(0,1,2,3),执行两次add后,就是两次10于g累加
          g = (add(n, i) for i in g)
      print(list(g))     #[20, 21, 22, 23]
      
      
      #重点:filter注重的是lambda :条件是否正确;map是对每个元素操作
      #有列表 a = ["7net","www.7net","www.septnet","7net","www"]现需要从中将包 含字符7net的元素给删掉,请以最少代码量实现
      a = ["7net","www.7net","www.septnet","7net","www"]
      # print(list(filter(lambda x:'7net' not in x,a)))  #这个过滤含有'7net'
      print(list(map(lambda x:x.replace('7net',''),a)))
      
      
      ----------------------------------------
      #实例:递归查看文件下的所有文件的总共文件大小
      
      import os
      def get_size(path):
          size = 0   # 定义一个变量用来存储文件的大小
          l = [path]   # 将庐江添加到列表中
          while l:
              path = l.pop() # 删除列表中的最后一个元素,并接受返回值
              lst = os.listdir(path)    # 查看接受逇返回值的目录下的所有文件
              for name in lst:        # 遍历这个目录下的素有文件
                  son_path = os.path.join(path,name)  # 路径拼接,将路径和文件名进行拼接
                  if os.path.isfile(son_path):        # 如果这个文件不是目录,
                      size += os.path.getsize(son_path) # 计算这个文件的大小
                  else:
                      l.append(son_path)            # 如果是目录,就将这个路径添加到列表中,用来进入这个目录中
          return size                # 返回这个目录中的文件的大小
          
      size = get_size(r'D:\s14\算法')
      print(size)
      

补充:

# 注意fliter,map,返回的是一个迭代器,返回一个地址集的迭代器只能取一次,因为可以使用__next__()方法,
lst = filter(lambda n:n%3==1,range(10))
# print(lst.__next__())
print(len(list(lst)))  #3
print(len(list(lst)))  #0