• 函数


    函数

    一.函数的基本使用

    接触一个未知的知识都要先从三个方面入手

    1、什么是函数

    在程序具备某一功能的工具=》函数
    事先准备好工具=》函数的定义
    遇到应用场景、拿来就用=》函数的调用

    分为两大类:

    01.内置函数

    02.自定义函数

    2、为何要用函数

    01.代码冗余
    02.程序的组织结构不清晰,可读性差
    03.扩展性差

    3、如何用函数

       函数的使用必须遵循一个原则:
       ​    01. 先定义
       ​        定义语法:
       ​            def 函数名(参1,参2,参3,...):
       ​                """
       ​                文档注释
       ​                """
       ​                代码1
       ​                代码2
       ​                代码3
       ​                ...
       ​                return 值

       ​     02. 后调用
       ​         函数名()

    4.定义阶段: 只检测语法,不执行代码

    def func():   
    print('from func 1')
    ​        print('from func 2')
    ​        print('from func 3')
    ​        print('from func 4')

    5. 调用阶段: 开始执行函数体代码

    func()

    示范一:
    def foo():
    ​      print('from foo')
    ​      bar()

    foo()#'bar'没有被定义(define),所以会报错

    示范二:
    def bar():
    ​     print('from bar')

    def foo():
    ​     print('from foo')
    ​     bar()

    foo()#会打印出from foo
    ​                           from bar

    示范三:
    定义

    def foo():
    ​     print('from foo')
    ​     bar()

    def bar():
    ​     print('from bar')

    调用
    foo()

    6.定义函数的三种形式

    01.无参函数

    def func():
      print('from func')

    func()

    02.有参函数

    def max2(x,y):
      x=1
      y=2
      if x > y:
          print(x)
      else:
          print(y)

    max2(1,2)
    max2(3,4)

    03.空函数

    def register():
      pass
    7.调用函数的三种形式

    01.语句形式

    def func():
      print('from func')

    func()

    02.表达式形式

    def max2(x,y):
      if x > y:
          return x
      else:
          return y

    res=max2(1000,2000) * 12#此处需要的是返回值所以用return,不然不可以运算
    print(res)

    03.函数的调用可以当作另外一个函数的参数传入

    比较1,2,3的大小

    def max2(x,y):
      if x > y:
          return x
      else:
          return y

    res=max2(max2(1,2),3)#用函数依次比较大小
    print(res)

    二.函数的返回值

    1.什么是函数的返回值

    返回值是函数体代码的运行成果

    2.为何要有返回值

    需要拿到函数的处理结果做进一步的处理,则函数必须有返回值#如同上方的表达式形式需要用到返回的值的话就需要用到返回值

    3.如何用

    return 返回值的特点:

           01.返回的值没有类型限制,也没有个数限制
    ​        I: return或者函数没return:返回值None
    ​        II: return 值:返回的就是该值本身
    ​        III: return 值1,值2,值3:返回元组(值1,值2,值3)

    def func():
      return 1,1.2,'aaa',[1,2,3]
      pass#占位符,没有作用
    res=func()
    print(res)

           02.return是函数结束运行的标志,函数内可以有多个return,但只要执行一次函数就立即结束,并且将return后的值当作本次调用的结果返回

    def func():
      print('aaaa')
      return 1
      print('bbb')
      return 2
      print('ccc')
      return 3


    func()#此处就只会返回 aaa

    三.函数参数的使用

    函数的参数分为两大类

    1.形参:在定义函数是括号内制定的参数(变量名),称之为形参

    2.实参:在调用函数时括号内传入的值(变量值),称之为实参

    二者的关系:在调用函数时,实参数(变量值)会传给形参(变量名),这种绑定关系在调用函数时生效,调用结束后解除绑定。

    函数参数详解

    1.形参:

    1.1 位置形参:在定义阶段,按照从左到右的顺序依次定义的形参

    特点:必须被传值

    def func(x,y,z):
      print(x,y,z)

    func(1,2,3)#这边需要注意的是必须一一对应,对一个少一个都不可以.

    1.2 默认参数:在定义阶段,就已经为莫格形参赋值该形参称之为默认形参

    特点:在定义阶段就已经有值,意味着调用阶段可以不用为其传值

    注意点:位置形参必须放在默认形参的前面

    def func(x,y=2):#要注意这边绝不可以是   func(x=2,y)
      print(x,y)
       
    func(1)#这边输出的就是 1   2
    func(1,3333333)#这边输出的就是 1 3333333

    1.3 形参中*与**的用法

    形参中带*: *会将溢出的位置实参存在元组的形式然后赋值给其后变量名

    def func(x,y,*args):
      print(x,y,args)

    func(1,2,3,4,5,6)#输出   1 2 (3, 4, 5, 6)

    形参中带* *: **会将溢出的关键字实参存成字典的格式然后赋值给其后变量名

    def func(x,y,**kwargs):
      print(x,y,kwargs)

    func(1,y=2,z=3,m=1,n=2)#输出   1 2 {'z': 3, 'm': 1, 'n': 2}

    2:实参

    2.1 位置实参:在调用阶段,按照从左到右的顺序依次传入的值

    特点:这种传值方式会与形参一一对应  #就是上面所说的形参一一对应

    2.2 关键字实参: 在调用阶段,按照key=value的格式传值

    特点:可以完全打乱位置,但仍然能为指定的形参传值

    注意:可以混用关键字实参与位置实参,但是  1.同一个形参只能被赋值一次

                                                                             2.位置实参必须跟在关键字实参的前面

    2.3 实参中*与**的用法

    实参中带*:先将实参打散成位置参数,然后在与形参做对应

    def func(x,y,z):
      print(x,y,z)

    func(*[1,2,3]) #func(1,2,3)
    func(*'hel') #func('h','e','l')

      实参中带**: 先将实参打散成关键字实参,然后在与形参做对应

    def func(x,y,z):
      print(x,y,z)

    func(**{'x':1,'z':3,'y':2}) #func(z=3,y=2,x=1)

    四.函数对象

    就是把函数的内存地址当作一种变量值去使用

    那么具体体现在哪

    1.函数可以被引用

    x=10
    y=x   #这是我们所熟悉的变量值的使用

    def func1():
      print('from func')
    # func1=函数的内址
    func2=func1
    func2()#这边就是可以把函数的内存地址当作变量值使用

    2.函数可以作为函数的参数

    def func1():
      print('from func')

    def bar(xxx):
      print(xxx)
      xxx()
    bar(func1)#此时就是把func这函数当成xxx传入bar函数,同时这个xxx()就相当于func(),也就相当于函数的引用.此时输出的的就是 func的内存地址 与 from func

    3.函数可以作为函数的返回值

    def func1():
      print('from func')

    def bar():
      return func1

    f=bar()
    f()#这里就是func函数作为bar函数的返回值

    4可以被存储到容器类型中,也就是当作容器类型的元素

    def func1():
      print('from func')

    l=[func1,]
    print(l)
    l[0]()

    这边是一个具有一些小功能的简易购物车,函数下面就可以加上你想要写的功能

    def register():
      print('注册')

    def login():
      print('登陆')

    def shopping():
      print('购物')

    def pay():
      print('支付')

    func_dic={
      '1':register,
      '2':login,
      '3':shopping,
      '4':pay
    }

    while True:
      print("""
      0 退出
      1 注册
      2 登陆
      3 购物
      4 支付
      """)
      choice=input('>>>: ').strip()
      if choice == '0':break
      if choice in func_dic:
          func_dic[choice]()
      else:
          print('输入错误的指令')

    五.函数的嵌套

    1.函数嵌套定义:它主要就是在一个函数中定义了另外一个函数。

    要注意的是:定义在函数内的函数 只能在函数内使用 外界不能访问。

    def outter():
      def inner():
          pass#这就是在oytter函数中定义了一个inner函数

    2.函数的嵌套调用:提供了一种解决问题的思路,如果有一个大问题,可以细分成很多小问题,然后单独实现小问题,最后在大的函数中调用小函数,就比较容易解决问题

    比如比较4个值的大小就可以

    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)
      return res3
       
    a=max4(1,2,3,4)
    print(a)

    六.名称空间与作用域

    1.名称空间:就是存放名字与值内存地址绑定关系的地方

    2.名称空间的分类

                内置名称空间:存储解释器自带的一些名称与值的对应关系(python解释器启动时创建 所有代码全部执行完毕 关闭解释器时 销毁)  print len  max  min 等等这些

                全局名称空间:那些数据会存储在全局空间(执行py文件创建全局名称空间 关闭解释器时 销毁)

                        文件级别的文件   只要你的名字的定义是顶着最左边写的就在全局空间

                        除了内置的函数内的 都在全局中

                局部名称空间:只要时函数内的名称就是局部的(调用函数时创建 函数执行完毕就销毁)

    名称空间的加载顺序

    内置的-->全局的-->局部的

    名称的查找顺序

    局部的--> 全局的-->内置的

    总结:01.查找名字的顺序是从当前位置往外查找

               02.名称关系的嵌套关系是在函数定义阶段就固定死的,与调用关系无关

    def outter():
      x=11111111
      def inner():
          print(x)
      return inner


    f=outter()


    def foo():
      x=222222222222
      f()

    foo()#此处输出的就是11111111,定义阶段就固定死了所以与foo()下面的x无关

     

    3作用域(作用范围)    

    域 指的是区域 范围的的意思
    全局的名称空间和内置的名称空间 在使用上没什么区别
    局部的和全局的内置的 就区别了 局部定义的只能在局部使用
    给三个空间划分范围

    全局的和内置可以划分为同一个范围
    global 表示的全局范围 就是所谓的全局作用域

    局部的单独划分为一个范围
    local 局部作用域

    globals()
    locals()

    七.闭包函数

    闭函数:该函数一定是定义在函数内的函数

    包函数:爱函数包含对外层函数作用域名字的引用

    age = 20
    # 如下就是一个闭包函数
    def fun1():
      age = 18
      def inner():
          print("hello")
          print(age)#这是对上面age进行了引用
      # 在返回这个内部的函数时 不是单纯的返回函数 还把函数中访问到的局部名称一起打包了
      # 相当于将内部函数与 访问的数据打包在一起了 闭包这个名字就是这么得来的
      return inner
    f = fun1() # f 就是innerr
    f()

    函数的作用域在定义时就固定了 与调用的位置没有关系

    为函数传值有二种方案:

    1 直接传参

    def f(x):
      print(x)

    f(10)
    f(11)

    2 闭包函数形势下传参

    def outter(x):
      def f():
          print(x)
      return f

    f=outter(10)
    f()

    八.装饰器

    1  装饰器指的是为被装饰对象添加新功能的工具

        装饰器本身可以是任意可调用对象

        被装饰对象本身也可以是任意可以调用对象

    2 装饰器要遵循开放封闭原则(对修改封闭,对扩展开放)

    装饰器的实现原则  

          01  不能修改被装饰对象的源代码

          02  不能修改被装饰对象的调用方式

    装饰器的目的

          就是在遵循原则01和02的前提为被装饰对象添加新功能

    装饰器的模板是

    #不写这个也完全没有问题,只是更加严谨一点
    from functools import wraps#导入装饰器,为wraper添加新功能,使被装饰对象的一些内置属性赋值给wrapper

    def outter(func):
      #加在内层函数最上方
      @wraps(func)#是func下面所有的杠杠开头(_name_ ,_doc_等)的赋值给wrapper
      def wrapper(*args,**kwargs):
          res=func(*args,**kwargs)
          return res
      return wrapper
       
       

    统计运行时间装饰器


    import time

    def outter(func):
      # func=最原始那个index的内存地址
      def wrapper(*args,**kwargs):
          start=time.time()
          res=func(*args,**kwargs) #最原始那个index的内存地址()
          stop=time.time()
          print('run time is %s' %(stop -start))
          return res
      return wrapper

    #装饰器的语法糖,相当于index=outter(index)
    @outter #index=outter(index) #inex=outter(最原始那个index的内存地址) # index=wrapper的内存地址
    def index():
      time.sleep(1)
      print('welcome to index page')
      return 1234

    @outter #home=outter(home)
    def home(name):
      time.sleep(1)
      print('welcome %s to home page' %name)


    res=index() #wrapper的内存地址()
    # print('返回值',res)

    home('egon')

    认证功能装饰器

    import time

    def auth(func):
      def wrapper(*args,**kwargs):
          name=input('name>>>: ').strip()
          pwd=input('pwd>>>: ').strip()
          if name == 'egon' and pwd == '123':
              print('login successfull')
              res=func(*args,**kwargs)
              return res
          else:
              print('user or pwd error')
      return wrapper



    @auth
    def index():
      time.sleep(1)
      print('welcome to index page')
      return 1234

    res=index()
    print(res)

     

    3.叠加多个装饰器

    01.加载顺序(outter函数的调用顺序):自下而上

    02.执行顺序(wrapper函数的执行顺序):自上而下

    def outter1(func1): #func1=wrapper2的内存地址
      print('加载了outter1')
      def wrapper1(*args,**kwargs):
          print('执行了wrapper1')
          res1=func1(*args,**kwargs)
          return res1
      return wrapper1

    def outter2(func2): #func2=wrapper3的内存地址
      print('加载了outter2')
      def wrapper2(*args,**kwargs): 噩
          print('执行了wrapper2')
          res2=func2(*args,**kwargs)
          return res2
      return wrapper2

    def outter3(func3): # func3=最原始的那个index的内存地址
      print('加载了outter3')
      def wrapper3(*args,**kwargs):
          print('执行了wrapper3')
          res3=func3(*args,**kwargs)
          return res3
      return wrapper3



    @outter1 # outter1(wrapper2的内存地址)======>index=wrapper1的内存地址
    @outter2 # outter2(wrapper3的内存地址)======>wrapper2的内存地址
    @outter3 # outter3(最原始的那个index的内存地址)===>wrapper3的内存地址
    def index():
      print('from index')

    print('======================================================')
    index()

    '''
    加载了outter3
    加载了outter2
    加载了outter1
    ======================================================
    执行了wrapper1
    执行了wrapper2
    执行了wrapper3
    from index
    '''

    4.有参装饰器

    有参认证功能装饰器加使用

    import time

    current_user={'user':None}#定义一个当前的文件名

    def auth(engine='file'):
      def outter(func):
          def wrapper(*args,**kwargs):
              if current_user['user'] is not None:#判断当前文件名字是不是为空,如果不是空就没必要在进行认证了
                  res=func(*args,**kwargs)
                  return res

              user=input('username>>>: ').strip()
              pwd=input('password>>>: ').strip()

              if engine == 'file':
                  # 基于文件的认证
                  if user == 'egon' and pwd == '123':
                      print('login successfull')
                      current_user['user']=user#认证之后它会存到当前文件去
                      res=func(*args,**kwargs)
                      return res
                  else:
                      print('user or password error')
              elif engine == 'mysql':
                  # 基于mysql的认证
                  print('基于mysql的认证')
              elif engine == 'ldap':
                  # 基于ldap的认证
                  print('基于ldap的认证')
              else:
                  print('不知道engine')
          return wrapper
      return outter

    @auth('ldap') #@outter #index=outter(index) # index=wrapper
    def index():
      time.sleep(1)
      print('from index')

    @auth('mysql') #@outter # home=outter(home) #home=wrapper
    def home(name):
      print('welcome %s' %name)

    index()
    home('egon')

    一.三元表达式

    就像通常我们比较2个值大小

    def max2(x,y):
      if x > y:
          return x
      else:
          return y

    res=max2(10,20)
    print(res)

    现在直接可以用
    res=x if x > y else y
    print(res)

    二.生成式

    优点:方便,改变了编程习惯,可称之为声明式编程

    #列表生成式
    l=[]
    for i in range(10):
      if i > 4:
          l.append(i**2)#我们一开始用的方法


    l=[i**2 for i in range(10) if i > 4]#列表生成式
    print(l)

    # 字典生成式
    res={i:i**2 for i in range(10) if i > 3}
    print(res)


    三.匿名函数

    它主要是之定义了一个函数的内存地址,主要用于临时使用一次的场景

    func=lambda x,y:x+y
    print(func)#<function <lambda> at 0x0000028897231E18>
    print(func(1,2))#3

    res=(lambda x,y:x+y)(1,2)
    print(res)#3

    l=[4,2,3]
    l_new=sorted(l)#默认从小到大排序
    #l_new=sorted(l,reverse=True)#默认从大到小排序
    print(l_new)

    四.迭代器和生成器

    1.什么是迭代?

    迭代是一个重复的过程,但是每次重复都是基于上一次重复的结果而继续

    #下列循环知识单纯的重复
    while True:
      print(1)

    # 基于索引的迭代取值
    l=['a','b','c']
    i=0

    while i < len(l):
      print(l[i])
      i+=1

    迭代器就是迭代取值的工具

    2.为什么要用迭代器

      优点:01 提供一种不依赖索引的迭代取值方法

                 02 更节省内存

      缺点:01 不如按照索引的取值方式灵活

                 02 取值一次性的,只能往后取,无法预测值的个数

    3.如何用迭代器

      可迭代的对象:str\list\tuple\dict\set\文件对象

                但凡内置有iter方法的对象都称之为可迭代对象

      迭代器对象:文件处理

                即内置有iter方法又内置有next方法的对象都称之为   迭代器对象

     

    调用可迭代对象下iter方法,会有一个返回值,该返回值就是内置的迭代器对象

    字典的迭代器,不依赖索引取值
    d={'k1':111,'k2':222,'k3':333}
    iter_d=d.__iter__()

    try:#检测代码
      print(iter_d.__next__())
      print(iter_d.__next__())
      print(iter_d.__next__())
      print(iter_d.__next__())#一个一个的取值,并不知道里面又几个值,所以要try来盯这段代码
    except StopIteration:#捕捉异常,结束循环
      print('取值完毕')
       
       
       
    d={'k1':111,'k2':222,'k3':333}
    # d={1,2,3,4,5}
    # d=[1,2,3,4]

    iter_d=d.__iter__()#把可迭代对象装换为迭代器对象
    while True:
      try:
          v=iter_d.__next__()#需要去掉迭代器对象__next__
          print(v)
      except StopIteration:#需要自己检测迭代器异常
          break
    #所以这就比较麻烦
    所以这里用到for循环
    for k in d:
      print(k)
     
    for循环的底层原理:
    1. 调用in后面那个值/对象的__iter__方法,拿到一个迭代器对象iter_obj
    2. 调用迭代器对象iter_obj.__next__()将得到的返回值赋值变量名k,循环往复直到取值完毕抛出异常StopIteration
    3. 捕捉异常结束循环
           

    4.生成器

    01 生成器其实就是一种自定义的迭代器

    如何得到生成器?

    但凡函数内出现yield关键字,再去调用函数不会立即执行函数体代码,会得到一个返回值,该返回值就是生成器对象,即自定义的迭代器.

    def func():
      print('first')
      yield 1
      print('second')
      yield 2
      print('third')
      yield 3


    g=func()

    res1=next(g)
    print(res1)#第一次打印,然后暂停

    res2=next(g)
    print(res2)#第二次打印,然后暂停

    res3=next(g)
    print(res3)#第三次打印,然后暂停

    next(g)#没有值会出现StopIteration

    5.生成器表达式

    这个和列表表达式不一样的就是将中括号改为小括号

    g=(i for i in range(10) if i > 5)
    print(next(g))#这里需要注意的是一次只能打印一个数就会暂停,由于是生成器

    读取文件中字符的个数
    with open('a.txt',mode='rt',encoding='utf-8') as f:
      #print(len(f.read()))#这样一次性全读出来占用内存
      #res=sum[len(line) for line in f]#行数过多的话还是会占用内存

      res = sum(len(line) for line in f)#生成器里面是不是值,所以不论多少并不占用内存
      print(res)

     

     

    小练习:

    自己生成一个my_range,使之跟默认range有相同的效果

    def my_range(start,stop,step=1):
      while start < stop:
          yield start
          start+=step

    #range(1,5,2) # 1 3
    for i in my_range(1,5,2):
      print(i)

    五.函数的递归调用与二分法

    1.函数的递归调用:

          在调用一个函数的过程又直接或者间接地调用该函数本身,称之为递归调用

          递归调用必须满足2个条件:

                     01 每进入下一次递归调用,问题的规模都应该有所减少

                     02 递归必须又一个明确的结束条件

         递归有两个明确的阶段

                     01 回溯

                     02 递推

    就比方说想知道一个人的年龄的话,问他他说比另外一个人大2岁,以此往复,直到第五个人说出了自己的年龄18岁,那么我们可以知道
    age(5)=age(4)+2
    age(4)=age(3)+2
    age(3)=age(2)+2
    age(2)=age(1)+2
    age(1)=18#那么这个过程中重上往下推理的过程就叫回溯,然后拿到最后一个人的年龄推导出第一个的年龄就叫逆推

    age(n)=age(n-1)+2 # n > 1
    age(1)=18         # n = 1


    def age(n):
      if n == 1:
          return 18
      return age(n-1)+2

    print(age(5))


    #这边是一个列表,要求将当中的所有值都取出来
    #递归函数的运用
    l=[1,[2,[3,[4,[5,[6,[7,[8,[9,]]]]]]]]]

    def func(list1):
      for item in list1:#循环从list1里面取值
          if type(item) is not list:#判定是不是列表,不是的话打印
              print(item)
          else:
              # 如果是列表,应该继续走这个函数,直到全部取空这个列表
              func(item)

    func(l)

    2.二分法(算法)

    比如有这样nums=[3,5,7,11,13,23,24,76,103,111,201,202,250,303,341]一个列表,需要取判断一个值在不在这个列表当中

    比如我们find_num=202,最开始想到的就是取出这个了列表中所有的值,看有没有和202相等的就可以了

    for num in nums:
      if num == find_num:
          print('find it')
          break
    else:
      print('not exists')
      #find it
      #这样就可以发现202是在这个列表中的
       
    但是这样你会发现要是这个列表里面的元素非常多,这样会很占用内存
    我们可以想象是不是可以从中间开始找呢,一直切2部分找,这样是不是更合理一点嗯
    nums=[3,5,7,11,13,23,24,76,103,111,201,202,250,303,341]


    def binary_search(list1,find_num):先定义一个binary_search函数
      print(list1)
      if len(list1) == 0:判断一下列表是不是空列表,列表一直中间切片,如果到空列表还没找到的话,就不存在
          print('not exist')
          return
      mid_index=len(list1) // 2#取出中间的一个数等待和你想找的值比大小
      if find_num > list1[mid_index]:#如果找的值比中间的值大的话,那么就可以从中间这个值向后切片重新分割寻找
          binary_search(list1[mid_index + 1:],find_num)
      elif find_num < list1[mid_index]:#同上,如果小于则反之
          binary_search(list1[:mid_index],find_num)
      else:
          print('find it')#如果刚好等于的话就直接找到这个值了

    binary_search(nums,203)
     
  • 相关阅读:
    使用百度字体编辑器删除不必要字体,减少字体文件体积
    使用npm安装webpack失败时,可能被墙要为cmd命令行设置代理
    让字体图标代替雪碧图,减少请求带宽
    隐藏浏览器原生的滚动条
    台式机安装CentOS7.6 Minimal ISO系统并增加图形化桌面
    像我这样笨拙地生活(廖一梅)--节选
    随心随意亦舒经典语录
    file / from install of XXX conflicts with file from package filesystem-XXX
    nginx代理php项目的websocket
    Redis4配置文件详解
  • 原文地址:https://www.cnblogs.com/liubinliuliu/p/10374686.html
Copyright © 2020-2023  润新知