zoukankan      html  css  js  c++  java
  • 函数编程

    函数

    3.1 Bytes类型的定义

    bytes类型是指一堆字节的集合,在python中以b开头的字符串都是bytes类型

    b'xe5xb0x8fxe7x8cxbfxe5x9cx88' #b开头的都代表是bytes类型,是以16进制来显示的,2个16进制代表一个字节。 utf-8是3个字节代表一个中文,所以以上正好是9个字节
    

    3.2 Bytes类型的作用

    计算机只能存储2进制。

    • 对于文字,有 gbk,utf-8,ASCII,Unicode
    • 对于图片,必须编码成Png,JPEG
    • 对于音乐,必须编码成Mp3,wav

    在python中,数据转成2进制后不是直接以01010101的形式表示的,而是用一种叫bytes(字节)的类型来表示的。

    >>> s = "小猿圈"
    >>> s.encode("utf-8")  # 以utf-8编码 
    b'xe5xb0x8fxe7x8cxbfxe5x9cx88' #b开头的都代表是bytes类型,是以16进制来显示的,2个16进制代表一个字节。 utf-8是3个字节代表一个中文,所以以上正好是9个字节
    

    在python中,字符串必须编码成bytes后才能存到硬盘上。

    在python3中文件存储的默认编码是utf-8.

    '''
    自行改变文件的默认编码,改成gbk格式的
    '''
    f = open('book.txt', 'w','gbk')
    

    以2进制的模式操作文件

    当然,在打开文件时如果你不想让open这个对象帮你自动编码,你也可以直接往文件里存入bytes数据。

    f = open(file="encode_test",mode="wb") # wb以2进制模式打开文件
    s = "自学编程,谁不上小猿圈".encode("utf-8")  # 自行编码
    print(s )
    f.write(s)
    f.close()
    
    #以下是print(s)的输出
    b'xe8x87xaaxe5xadxa6xe7xbcx96xe7xa8x8bxefxbcx8cxe8xb0x81xe4xb8x8dxe4xb8x8axe5xb0x8fxe7x8cxbfxe5x9cx88'
    

    2进制模式打开文件有

    • wb 二进制创建
    • rb 二进制读
    • ab 二进制追加

    3.3 字符编码的转换

    编码转换是指将一种编码转成另外一种编码,比如 utf-8 to gbk。

    为何需要编码转换呢? 因为不同操作系统编码不同, utf-8在win上没办法直接看,因为windows是GBK编码的,得转成gbk。 反过来如果你的GBK字符相在LinuxMac上正常显示,就得转成utf-8编码。

    编码&解码

    img

    >>> s.encode("utf-8")   # 编码
    b'xe5xb0x8fxe7x8cxbfxe5x9cx88'
    >>> s_utf8=s.encode("utf-8")
    >>> 
    >>> s_utf8.decode("utf-8")  #解码
    '小猿圈'
    

    在py3里,内存里的字符串是以unicode编码的,unicode的其中一个特性就是跟所有语言编码都有映射关系。所以你的utf-8格式的文件,在windows电脑上若是不能看,就可以把utf-8先解码成unicode,再由unicode编码成gbk就可以了。

    img

    注意,不管在Windows or Mac or Linux上,你的pycharm IDE都可以支持各种文件编码,所以即使是utf-8的文件,在windows下的pycharm里也可以正常显示

    img

    3.4 深浅copy(杯子问题)

    • str的赋值(=)

      •   a = 1243
          b = a
          a = 12
          a和b指向同一内存地址,a改变不影响b
        
    • list,set,dict(=)和后面函数里list,set,dict(函数传递列表,字典,集合时发生的现象)

      •   a = [1,2,3,4]
          b = a
          a.append(5)# append返回的是None,所以不能赋值
          a和b 也是指向同一内存地址,但是这个[]的内存地址没有变(杯子问题),a里面的元素变了,b也会跟着变
        
    •   data = {‘name’: ‘yekai’,
               'age': 18,
               'scores': {}
               }
        d2 = data
        data['age'] = 20
        print(d2)
      

    你说d2打印的值里,age是18,还是20?

    {'name': 'alex', 'age': 20, 'scores': {}}
    

    为何是20呢? 因为d2=data相当于只是拿到了data内存地址,但data里的每个k,v都是有单独的内存的地址的。d2,data会一直共享这个dict里的数据,不会出现像之前字符串a=1,b=a, a=2, b依然等于1的情况。

    如果我确实想复制一份完成的dict数据怎么办呢?

    可以用浅copy语法(python自带,不用导入模块)

    因为浅copy会仅复制dict的第一层数据,更深层的scores下面的值依然是共享一份。

    img

    注意图中的2个dict中的name都是alex,内存地址也一样,在没改前,两个name都确实指向同一个内存地址,但只要改任何一个的值,内存地址都会变更, 如age这个key一样。

    深copy(必须导入python中的一个工具模块)(很少用,因为完全复制一份数据,占空间)

    • 深copy可以使dict,list,set完全独立,无论有多少层数据。

    3.5 函数的定义

    • 函数是什么?

      函数一词来源于数学,但编程中的「函数」概念,与数学中的函数是有很大不同的,具体区别,我们后面会讲,编程中的函数在英文中也有很多不同的叫法。在BASIC中叫做subroutine(子过程或子程序),在Pascal中叫做procedure(过程)和function,在C中只有function,在Java里面叫做method。

      定义: 函数是指将一组语句的集合通过一个名字(函数名)封装起来,要想执行这个函数,只需调用其函数名即可

      特性:

      1. 减少重复代码
      2. 使程序变的可扩展
      3. 使程序变得易维护

      语法定义

      def sayhi():#函数名
          print("Hello, I'm nobody!")
      sayhi() #调用函数
      

      可以带参数

      #下面这段代码
      a,b = 5,8
      c = a**b
      print(c)
      #改成用函数写
      def calc(x,y):
          res = x**y
          return res #返回函数执行结果
      c = calc(a,b) #结果赋值给c变量
      print(c)
      

      参数可以让你的函数更灵活,不只能做死的动作,还可以根据调用时传参的不同来决定函数内部的执行流程

      函数参数

      形参变量

      只有在被调用时才分配内存单元,在调用结束时,即刻释放所分配的内存单元。因此,形参只在函数内部有效。函数调用结束返回主调用函数后则不能再使用该形参变量

      实参

      可以是常量、变量、表达式、函数等,无论实参是何种类型的量,在进行函数调用时,它们都必须有确定的值,以便把这些值传送给形参。因此应预先给实参赋值

      注意,参数优先级顺序是 位置参数>关键参数

      函数的顺序:位置参数,默认参数,可变参数*args(结果为元组),命名关键字参数(带参数名调用),关键字参数**kwargs(结果为字典)

    函数返回值与作用域

    函数外部的代码要想获取函数的执行结果,就可以在函数里用return语句把结果返回

    注意

    • 函数在执行过程中只要遇到return语句,就会停止执行并返回结果,so 也可以理解为 return 语句代表着函数的结束
    • 如果未在函数中指定return,那这个函数的返回值为None

    3.6 全局与局部变量

    name = "Alex Li"
    def change_name():
        name = "金角大王,一个有Tesla的高级屌丝"
        print("after change", name)
    change_name()
    print("在外面看看name改了么?",name)
    

    输出

    after change 金角大王,一个有Tesla的高级屌丝
    在外面看看name改了么? Alex Li
    

    为什么在函数内部改了name的值后, 在外面print的时候却没有改呢? 因为这两个name根本不是一回事

    • 在函数中定义的变量称为局部变量,在程序的一开始定义的变量称为全局变量。
    • 全局变量作用域(即有效范围)是整个程序,局部变量作用域是定义该变量的函数。
    • 变量的查找顺序是局部变量>全局变量
    • 当全局变量与局部变量同名时,在定义局部变量的函数内,局部变量起作用;在其它地方全局变量起作用。
    • 在函数里是不能直接修改全局变量的

    就是想在函数里修改全局变量怎么办?

    name = "Alex Li"
    def change_name():
        global name #声明一个全局变量
        name = "Alex 又名金角大王,爱生活、爱自由、爱姑娘"
        print("after change", name)
    change_name()
    print("在外面看看name改了么?", name)
    

    global name的作用就是要在函数里声明全局变量name ,意味着最上面的name = “Alex Li”即使不写,程序最后面的print也可以打印name

    传递列表、字典、集合产生的现象

    d = {"name":"Alex","age":26,"hobbie":"大保健"}
    l = ["Rebeeca","Katrina","Rachel"]
    def change_data(info,girls):
        info["hobbie"] = "学习"
        girls.append("XiaoYun")
    change_data(d,l)
    print(d,l)
    

    执行结果{‘name’: ‘Alex’, ‘age’: 26, ‘hobbie’: ‘学习’ } [‘Rebeeca’, ‘Katrina’, ‘Rachel’, ‘XiaoYun’]

    不是说不能在函数里改全局变量么,怎么改了呀?

    • 和上面的一样的道理,(杯子问题),所以改了

    3.7 嵌套&匿名&高阶函数

    嵌套函数

    函数里不仅可以写代码,还可以嵌套函数

    name = "小猿圈"
    def change():
        name = "小猿圈,自学编程"
        def change2():
            # global name  如果声明了这句,下面的name改的是最外层的全局变层
            name = "小猿圈,自学编程不要钱" #这句注释掉的话,下面name打印的是哪个值?
            print("第3层打印", name) 
        change2()  # 调用内层函数
        print("第2层打印", name)
    change()
    print("最外层打印", name)
    

    输出

    第3层打印 小猿圈,自学编程不要钱
    第2层打印 小猿圈,自学编程
    最外层打印 小猿圈
    

    通过上面的例子,我们理解了,每个函数里的变量是互相独立的,变量的查找顺序也是从当前层依次往上层找。

    问个哲学问题,这东西有什么用呢?哈,现在没用,不解释,长大后学了装饰器你就知道有啥用了。

    匿名函数

    匿名函数就是不需要显式的指定函数名

    #这段代码
    def calc(x,y):
        return x**y
    print(calc(2,5))
    #换成匿名函数
    calc = lambda x,y:x**y
    print(calc(2,5))
    

    你也许会说,用上这个东西没感觉有毛方便呀, 。。。。呵呵,如果是这么用,确实没毛线改进,不过匿名函数主要是和其它函数搭配使用的呢,如下

    res = map(lambda x:x**2,[1,5,7,4,8])
    for i in res:
        print(i)
    

    输出

    1
    25
    49
    16
    64
    

    总结:lambda函数就是可以接受任意多个参数(包括可选参数)并且返回单个表达式的函数。好处有以下:

    1. lambda函数比较轻便,即用即扔,适合完成只在一处使用的简单功能
    2. 匿名函数,一般用来给filter,map这样的函数式编程服务
    3. 作为回调函数,传递给某些应用,比如消息处理

    高阶函数

    变量可以指向函数,函数的参数能接收变量,那么一个函数就可以接收另一个函数作为参数,这种函数就称之为高阶函数。

    def get_abs(n):
        if n < 0 :
            n = int(str(n).strip("-"))
        return n
    def add(x,y,f):
        return f(x) + f(y)
    res = add(3,-6,get_abs)
    print(res)
    

    只需满足以下任意一个条件,即是高阶函数

    • 接受一个或多个函数作为输入
    • return 返回另外一个函数

    3.8 函数的递归

    在函数内部,可以调用其他函数。如果一个函数在内部调用自已本身,这个函数就叫做递归函数。上面我们写的这个代码就是递归

    递归特性:

    1. 必须有一个明确的结束条件
    2. 每次进入更深一层递归时,问题规模相比上次递归都应有所减少
    3. 递归效率不高,递归层次过多会导致栈溢出(在计算机中,函数调用是通过栈(stack)这种数据结构实现的,每当进入一个函数调用,栈就会加一层栈帧,每当函数返回,栈就会减一层栈帧。由于栈的大小不是无限的,所以,递归调用的次数过多,会导致栈溢出)

    3.9 名称空间(LEGB)

    如x = 1,名称空间正是存放名字x与1 绑定关系的地方

    名称空间有4种:L-->E-->G-->B

    • locals:函数内部的名字空间,locals()方法可以查看
    • enclosing function:嵌套空间,
    • globals:类似全局变量,有globals()方法
    • __builtins__:内置模块空间

    不同变量的作用域不同就是由这个变量所在的名称空间决定的

    作用域的范围

    • 全局范围:全局存活,全局有效、
    • 局部范围:临时存活,局部有效

    查看作用域的方法:globals(),locals()

    3.10 闭包

    闭包

    闭包就是函数定义和函数表达式位于另一个函数的函数体内,内部函数可以在外部函数执行返回后被调用执行。(也就是内层函数被当成对象返回的时候夹带了外部函数的局部变量,就会形成一个闭包)

    闭包的意义:返回的对象,不仅仅是一个函数对象,在该函数外还包裹了一层作用域,这使得,该函数无论在何处调用,优先使用自己外层包裹的作用域

    3.11 函数进阶-装饰器(开放--封闭)

    • 封闭:已实现的功能代码不应该被修改
    • 开放:对现有的功能的扩展开放

    装饰器

    3.12 列表生成式

    是python内置的来创建list的生成式,可以用一行语句来代替循环来生成一个列表

    语法:[要生成的元素x * x 放在前面,后面跟for循环]

    >>>[x * x for x in range(1, 11) if x % 2 == 0]
    结果为[4, 16, 36, 64, 100]
    
    使用两层循环来生成全排列
    >>>[m + n for m in 'ABC' for n in 'XYZ']
    结果为['AX', 'AY', 'AZ', 'BX','BY', 'BZ','CX', 'CY', 'CZ']
    
    把一个list中的所有字符串变成小写
    >>>L = ['Hello', 'World', 'IBM','Apple']
    >>>[s.lower() for s in L]
    结果为['hello', 'world', 'ibm', 'apple']
    
    

    3.13 生成器

    在python中,一边循环一边计算的机制,称为生成器

     创建生成器(generator)的方法:

    1. 把一个列表生成式中的[]改成()
    2. 包含yield的关键字的函数

    generator函数的调用实际返回一个generator对象。

    把函数改成generator后,一般不会用next()来获取下一个返回值,而是常使用for循环来迭代。

    >>>g = (x * x for x in range(10))
    >>>for i in g:
       		print(i)
    
    

    斐波拉契数列:除第1个和第2个数字外.任意一个数都是其前面2个数相加得到.(用列表生成式写不出来,因为没有合适的表达式)

    def fib(max):
        n, a, b = 0, 0 , 1
        while n < max:
            yield b
            a, b = b, a + b
            n += 1
           return 'done'
    
    

    3.14 迭代器

    迭代器,可以被next()函数调用并不断返回下一个值的对象.

    1.集合数据类型(list,tuple,set ,dict ,str) 2.generator,包括生成器和带yield的generator function,以上这些可直接作用于for循环的对象统称为可迭代对象(iterable),可迭代的意思就是可遍历,可循环.

    generator 都是Iterable,但list,dict,str,tuple,set,虽然都是可迭代对象(iterable),但都不是迭代器.

    [typora中添加数学公式](https://blog.csdn.net/Ernest_YN/article/details/84064233#0_2)

    $$
    interable 对象--->>
    egin{cases}
    list
    dict
    tuple
    set
    str
    end{cases}
    $$

    都可以通过iter()方法转化为Iterator(迭代器)

    python 中,for循环本质上就是通过不断调用next()函数来实现的

    for x in [1,2,3,4,5]:
        pass
    
    
    it = iter([1,2,3,4,5])
    while True:
        try:
            x = next(it)
        except StopIteration:
            break
    
    

    for循环的本质

    总结:1.凡是可以作用于for循环的对象都是Iterable对象.2.凡是可作用于next()函数的对象都是Iterator类型,它表示一个惰性计算的序列

    3.15 内置函数

    Python的len为什么你可以直接用?肯定是解释器启动时就定义好了

    img

    内置参数详解https://docs.python.org/3/library/functions.html?highlight=built#ascii

    文件操作中的不同打开方式.

    文件模式

  • 相关阅读:
    新的一年,来看看大数据与AI的未来展望
    看过上百部片子的这个人教你视频标签算法解析
    让老板虎躯一震的前端技术,KPI杀手
    如何用RSS订阅?
    说说不知道的Golang中参数传递
    我也要谈谈大型网站架构之系列(3)——死了都要说的缓存
    我也要谈谈大型网站架构之系列(2)——纵观历史演变(下)
    我也要谈谈大型网站架构之系列(1)——纵观历史演变(上)
    抛弃NVelocity,来玩玩Razor
    挖一挖C#中那些我们不常用的东西之系列(3)——StackTrace,Trim
  • 原文地址:https://www.cnblogs.com/ylkx/p/11253052.html
Copyright © 2011-2022 走看看