python学习笔记

python学习笔记

该笔记记录了了自己学习python基础语法的一些记录,为了方便以后的复习

该笔记为学习阮一峰python教程过程中的记录

  • 第一个python程序

    1. python代码的运行

      • 通过文本编译器,代码可以保存,但在运行过程中不能加入其他指令
      • 使用python的交互命令,没输入一行指令,均会进行相应的运算

        在写程序的过程中可以结合使用,将文本编译器的代码片段子在python交互命令中查看正确性

    2. 输入和输出

      • 输入 input()

        1
        2
        3
        4
        5
        6
        # input返回值类型为字符串,优势需要类型转换
        name = input()
        # 输入该命令后,程序会等待用户输入,输入完成后回车,程序继续运行

        # 为了有更好的交互,可以输出提示
        name = input('请输入您的姓名:')
      • 输出 print()

        1
        2
        3
        # 可以输出字符串,也可以输出变量,可以一次输出多个,以逗号隔开
        # python会在,(逗号)分隔的变量间自动加空格 即运行结果为 (hello xiaoming)
        print('hello', name)
  • python基础

  1. python采用缩进的方式区分代码块

    • 好处:
      • 可以强迫我们泄出格式化的代码(一般坚持4歌缩进的原则, 不要使用tab)
      • 强迫我们写出缩进较少的代码,使代码更模块化
    • 坏处
      • 不能写的太长,否则代码结构不清晰
      • 重构代码时,必须检查缩进是否正确
    • python严格区分大小写
  2. python的数据类型

    • 整数

    • 浮点数 (可用科学计数法表示 1.23e6 = 1230000)

    • 复数

    • Number数据类型的转换 参考

    • 字符串

      • 单引号和双引号不能混用
      • python允许使用三引号’’’内容’’’来实现格式不变的输出
      • 在输出字符床前加r,表示忽略转移字符
        • 如:print(r’aaa\nbbb’) 输出结果为: aaa\nbbb
    • 布尔值 (首字母大写 True, False)

      • 逻辑运算符 and与 or或 not非
    • 空值 None

    • 变量

      • 变量在程序中就是用一个变量名表示了,变量名必须是大小写英文、数字和_的组合,且不能用数字开头
      • python属于动态语言(即变量本身类型不固定)
        • 与之对应的是静态语言(如C语言)静态语言在定义变量时必须指定变量类型,如果赋值的时候类型不匹配,就会报错。
        • 理解变量在内存中的表示
    • 常量

      • 不能变的量,python中用大写字母来表示
    • 字符串

      • 字符编码(扩展)
        • 由于计算子只处理数字,所以要想处理文本,就需要把文本转化为数字,这就需要用到编码表
        • 最早计算机设计时,是8bit一个字节,所以一个字节可以表示的最大整数为255(二进制11111111)
        • 由于计算机是由美国人发明的,所以最早只有127歌编码被储存到计算机中,这个编码表被称为ASCII编码。但随着计算机发展,各国都要用到计算机,如要处理中文显然一个字节是不够的,至少需要两个字节,而且还不能和ASCII编码冲突,所以,中国制定了GB2312编码,用来把中文编进去;各国有各国的标准,就会不可避免地出现冲突,结果就是,在多语言混合的文本中,显示出来会有乱码。
        • 因此,Unicode应运而生。Unicode把所有语言都统一到一套编码里,这样就不会再有乱码问题了。
        • ASBCII编码和Unicode编码区别
        • 三种编码之间的关系
      • python中的字符串
        • 采用Unicode,即支持多国语言
        • 编码encode(‘相应的编码方式’),解码decode() 详细解释
        • 字符串的格式化表示
          • %s等
          • format()
    • 列表 list

      • 定义: list是一种有序的集合,可以随时添加和删除其中的元素
      • 通过索引来随机list每个位置的元素 (也可以使用a[-1], 表示最后一个元素,-2,-3… 均可)
      • list中数据元素类型可以不同 p = [‘qwe’, 12, [34, ‘0’]]
      • 常用函数 len()长度 append()追加 insert(i, ‘fa’)插入指定位置 pop(i)删除指定位置元素
    • 元组 tuple

      • 另一种有序列表叫元组:tuple。tuple和list非常类似,但是tuple一旦 初始化就不能修改
      • 使用和list一样
      • 注意: 只有1个元素的tuple定义时必须加一个逗号,否则()解释为运算中的小括号
      • ‘可变的tuple’
    • 字典 dict (无序,其数据的先后与存入的顺序无关)

      • 定义: 在其他语言中也称为map,使用键-值(key-value)存储,具有极快的查找速度

      • 和list的区别与比较

        1
        2
        3
        >>> d = {'Michael': 95, 'Bob': 75, 'Tracy': 85}
        >>> d['Michael']
        95
      • 注意:

        1. 一个key只能对应一个value,多次对一个key放入value,后面的值会把前面的值冲掉
        2. 如果key不存在,则会报错(下面为两种避免的方法)
          • 通过in判断key是否存在
          • 通过dict提供的get()方法,如果key不存在,可以返回None,或者自己指定的value
          • 注意:返回None的时候Python的交互环境不显示结果
    • set

      • set和dict一样,也是一个key的集合,不过set不能添加value
      • set的key也不能重复
      • set的创建: s = set([1,2,3]) 需要传入一个list
      • set的添加: s.add(key) 添加重复的key,原set不变
      • set的删除: s.remove(key) 若删除的key不存在,会报错
    • 类型转换了,

      • 如: int(‘123’) float,tuple,list…
  3. 条件判断

    1
    2
    3
    4
    5
    6
    if a == 0:
    代码块
    elif a == 1:
    代码块
    else:
    代码块
  4. 循环

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    #第一种
    for item in items:
    print(item)

    #第二种
    while a>0:
    代码块
    else: # 党条件不满组时执行else语句
    代码块

    #break , continue
  • 函数

    • 定义: 和大多数语言中的函数概念一样,为了避免代码的重复

    • 注:

      • 1、 函数名其实就是指向一个函数对象的引用,完全可以把函数名赋给一个变量,相当于给这个函数起了一个“别名”

        1
        2
        3
        >>>a = abs
        >>>a(-1)
        1
      • 2、 pass语句什么都不做,那有什么用?实际上pass可以用来作为占位符,比如现在还没想好怎么写函数的代码,就可以先放一个pass,让代码能运行起来。pass还可以用在其他语句里,比如:

        1
        2
        if a>14:
        pass
      • 3、 数据类型检查可以用内置函数isinstance()实现

  • 函数的定义

    1
    2
    3
    4
    5
    def my_abs(x):
    if x>0:
    return x
    elif:
    return -x
  • 默认参数: 可以减小调用函数的复杂性

    • 注: 定义默认参数要牢记一点:默认参数必须指向不变对象!
      反例:
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      def add_end(L=[]):
      L.append('END')
      return L

      #正常调用时:
      >>> add_end([1, 2, 3])
      [1, 2, 3, 'END']
      >>> add_end(['x', 'y', 'z'])
      ['x', 'y', 'z', 'END']

      #非正常调用时:
      #第一次:结果正常
      >>>add_end()
      ['END']
      #第二次往后,结果不是预期效果
      >>>add_end()
      ['END', 'END']
    • 原因: Python函数在定义的时候,默认参数L的值就被计算出来了,即[],因为默认参数L也是一个变量,它指向对象[],每次调用该函数,如果改变了L的内容,则下次调用时,默认参数的内容就变了,不再是函数定义时的[]了。
    • 上述例子的修改
      1
      2
      3
      4
      5
      6
      #默认参数设为一个不变的量
      def add_end(L=None):
      if L == None:
      return L = []
      else
      return L.append('END')
    • 定义可变参数的函数
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      def a(*numbers):
      sum = 0
      for n in numbsers:
      sum = sum + n
      return sum

      #函数调用 在函数内部,参数numbers接收到的是一个tuple
      >>>a(1,2,3)
      6

      #作用和传入一个元组和列表类似
      def a_d(L):
      sum = 0
      for n in L:
      sum = n + sum
      return sum


      >>>a_d([1,2,3])
      6

      #在函数内部,参数numbers接收到的是一个tuple
      >>> nums = [1, 2, 3]
      >>> calc(*nums)
      14
  • 关键字参数

    • 两个**,相当于传入一个dict
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      #定义
      def person(name, age, **kw):
      print('name:', name, 'age:', age, 'other:', kw)

      #传参的书写
      #1. 不错的书写格式, 3种
      >>> extra = {'city': 'Beijing', 'job': 'Engineer', sex='boy'}
      >>> person('Jack', 24, city=extra['city'], job='Engnieer')
      name: Jack age: 24 other: {'city': 'Beijing', 'job': 'Engineer','sex: 'boy'}

      #2. 简便写法
      >>> extra = {'city': 'Beijing', 'job': 'Engineer'}
      >>> person('Jack', 24, **extra)
      name: Jack age: 24 other: {'city': 'Beijing', 'job': 'Engineer'}
  • 命名关键字参数

    • 和关键字参数不同,这里是 命名,即传入关键字的名字必须满足参数要求(限制关键字参数的名字)
    • 和关键字参数 ** kw不同,命名关键字参数需要一个特殊分隔符后面的参数被视为命名关键字参数
    • 命名关键字参数必须传入参数名,这和位置参数不同。如果没有传入参数名,调用将报错
    • 命名关键字函数的参数初值可以不从最后一个开始赋(如例2)
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      def person(name, age, *, city, job):
      print(name, age, city, job)

      #调用方式
      >>> person('Jack', 24, city='Beijing', job='Engineer')
      Jack 24 Beijing Engineer

      #特例
      def person(name, age, *args, city, job):
      print(name, age, args, city, job)
      #此函数定义中已经有了一个可变参数,后面跟着的命名关键字参数就 **不再需要** 一个特殊分隔符*了


      #例2
      def person(name, age, *, city='Beijing', job):
      print(name, age, city, job)

      #调用
      person('hu', 10, job='stu')
  • 参数组合

    • 在Python中定义函数,可以用必选参数、默认参数、可变参数、关键字参数和命名关键字参数,这5种参数都可以组合使用。但是请注意,参数定义的顺序必须是:必选参数、默认参数、可变参数、命名关键字参数和关键字参数。
  • 函数递归中的问题

    • 在计算机中,函数调用是通过栈(stack)这种数据结构实现的,每当进入一个函数调用,栈就会加一层栈帧,每当函数返回,栈就会减一层栈帧。由于栈的大小不是无限的,所以,递归调用的次数过多,会导致栈溢出。
    • 了解部分
      • 尾递归调用时,如果做了优化,栈不会增长,因此,无论多少次调用也不会导致栈溢出。
      • 遗憾的是,大多数编程语言没有针对尾递归做优化,Python解释器也没有做优化,所以,即使把上面的fact(n)函数改成尾递归方式,也会导致栈溢出。
      • 尾递归是指,在函数返回的时候,调用自身本身,并且,return语句 不能包含表达式 。这样,编译器或者解释器就可以把尾递归做优化,使递归本身无论调用多少次,都只占用一个栈帧,不会出现栈溢出的情况。
        1
        2
        3
        4
        5
        6
        7
        8
        #例题:阶乘
        def fact(n):
        return fact_iter(n, 1)

        def fact_iter(num, product):
        if num == 1:
        return product
        return fact_iter(num - 1, num * product)
  • 高级特性

    • 切片

      • 使用

        对于list L[1,2,3,4],如果想去前三个元素,就可以使用切片L[0:3]
        支持负数,前后可省略

        传三个参数 L[::5] 表示所有的数,每5个取一个
        L[:10:2] 表示前10个数中,每2个取一个

      • 使用对象

        list, tuple,str

    • 迭代(循环)

      1
      2
      3
      nums = [1,2,3,4,5]
      for n in nums:
      print(n)
      • 使用对象

        str,tuple,list,dict

      • dict使用解析

        默认情况下,dict迭代的是key。如果要迭代value,可以用for value in d.values(),如果要同时迭代key和value,可以用for k, v in d.items()

      • 判断迭代对象是否为可迭代对象

        方法是通过collections模块的Iterable类型判断

        1
        2
        3
        4
        5
        6
        7
        >>> from collections import Iterable
        >>> isinstance('abc', Iterable) # str是否可迭代
        True
        >>> isinstance([1,2,3], Iterable) # list是否可迭代
        True
        >>> isinstance(123, Iterable) # 整数是否可迭代
        False
      • 对list实现类似Java那样的下标循环

        Python内置的enumerate函数可以把一个list变成索引-元素对,这样就可以在for循环中同时迭代索引和元素本身。

        1
        2
        3
        4
        5
        6
        >>> for i, value in enumerate(['A', 'B', 'C']):
        ... print(i, value)
        ...
        0 A
        1 B
        2 C
    • 列表生成器

      • 入门例子: range(1, 11) [1,2,3,4,5,6,7,8,9,10] 前开后闭
      • 生成一些特殊数组list
        • 如果要生成一个[1,4,9,16,25 … 100],则可以通过 [x*x for x in range(1, 11)]
        • 生成各种可以通过循环得到的数组(如: 全排列,
    • 生成器

      (总结已近似于ctrl+c/ctrl+v 😑, 廖雪峰详细教程

      • 介绍
        通过列表生成式,我们可以直接创建一个列表。但是,受到内存限制,列表容量肯定是有限的。而且,创建一个包含100万个元素的列表,不仅占用很大的存储空间,如果我们仅仅需要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了。

        所以,如果列表元素可以按照某种算法推算出来,那我们是否可以在循环的过程中不断推算出后续的元素呢?这样就不必创建完整的list,从而节省大量的空间。在Python中,这种一边循环一边计算的机制,称为生成器:generator。generator保存的是算法,每次调用next(g),就计算出g的下一个元素的值,直到计算到最后一个元素,没有更多的元素时,抛出StopIteration的错误。

        要创建一个generator,有很多种方法。第一种方法很简单,只要把一个列表生成式的[]改成(),就创建了一个generator:

        1
        2
        3
        4
        5
        6
        >>> L = [x * x for x in range(10)]
        >>> L
        [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
        >>> g = (x * x for x in range(10))
        >>> g
        <generator object <genexpr> at 0x1022ef630>

创建L和g的区别仅在于最外层的[]和(),L是一个list,而g是一个generator。

- **打印出生成器内容**
    - 如果要一个一个打印出来,可以通过next()函数获得generator的下一个返回值:
    - 一个一个太过复杂,也不现实,正确的方法是使用for循环,因为generator也是可迭代对象;
    
1
2
3
4
5
6
7
8
9
10
11
12
13
14
>>> g = (x * x for x in range(10))
>>> for n in g:
... print(n)
...
0
1
4
9
16
25
36
49
64
81

所以,我们创建了一个generator后,基本上永远不会调用next(),而是通过for循环来迭代它,并且不需要关心StopIteration的错误。

- 定义generator的另一种方法。如果一个函数定义中包含yield关键字,那么这个函数就 **不再是**一个普通函数,而是一个 **generator**,即可迭代的对象
    - **generator和函数的执行流程不一样。函数是顺序执行,遇到return语句或者最后一行函数语句就返回。而变成generator的函数,在每次调用next()的时候执行,遇到yield语句返回,再次执行时从上次返回的yield语句处继续执行。**
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#简单的例子
def odd():
print('step 1')
yield 1
print('step 2')
yield(3)
print('step 3')
yield(5)

#结果 (当然,我们通常使用for,而不是一次一次的调用next()
>>> o = odd()
>>> next(o)
step 1
1
>>> next(o)
step 2
3
>>> next(o)
step 3
5
>>> next(o)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration

生成器只能遍历一次,之后访问抛出StopIteration。

  • 迭代器

    而生成器不但可以作用于for循环,还可以被next()函数不断调用并返回下一个值,直到最后抛出StopIteration错误表示无法继续返回下一个值了。

    可以被next()函数调用并不断返回下一个值的对象称为迭代器:Iterator。

    可以使用isinstance()判断一个对象是否是Iterator对象:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    >>> from collections import Iterator
    >>> isinstance((x for x in range(10)), Iterator)
    True
    >>> isinstance([], Iterator)
    False
    >>> isinstance({}, Iterator)
    False
    >>> isinstance('abc', Iterator)
    False

    生成器都是Iterator对象,但list、dict、str虽然是Iterable,却不是Iterator。

    把list、dict、str等Iterable变成Iterator可以使用iter()函数:

    1
    2
    3
    4
    >>> isinstance(iter([]), Iterator)
    True
    >>> isinstance(iter('abc'), Iterator)
    True

    更多了解

  • 函数式编程

    • 高阶函数

      • map(func, iterable) 接收两个参数,第一个为函数,第二个为可迭代对象,map作用是将可迭代对象都作用于func,返回一个interator

        1
        2
        3
        4
        5
        6
        >>> def f(x):
        ... return x * x
        ...
        >>> r = map(f, [1, 2, 3, 4, 5, 6, 7, 8, 9])
        >>> list(r)
        [1, 4, 9, 16, 25, 36, 49, 64, 81]
      • reduce(func, list), reduce作用是把func作用到一个序列上, func必须接收两个参数,reduce把结果继续和序列的下一个元素做累计计算。

        1
        2
        3
        4
        5
        6
        >>> from functools import reduce
        >>> def add(x, y):
        ... return x + y
        ...
        >>> reduce(add, [1, 3, 5, 7, 9])
        25
      • filter(过滤器),和map相似,也是接收一个函数和序列,并对每个元素进行函数作用,但不同点是,filter会根据返回值为True或False决定保留还是丢弃元素

        1
        2
        3
        4
        5
        6
        #例如,在一个list中,删掉偶数,只保留奇数,可以这么写:
        def is_odd(n):
        return n % 2 == 1

        list(filter(is_odd, [1, 2, 4, 5, 6, 9, 10, 15]))
        # 结果: [1, 5, 9, 15]
      • sorted(),排序函数,默认按递增排序

        1
        2
        >>> sorted([36, 5, -12, 9, -21])
        [-21, -12, 5, 9, 36]
        • sorted()函数也是一个高阶函数,它还可以接收一个key函数来实现自定义的排序,例如按绝对值大小排序

          1
          2
          >>> sorted([36, 5, -12, 9, -21], key=abs)
          [5, 9, -12, -21, 36]

          key指定的函数作用在list的每个元素上,并根据key函数返回的结果进行增序排列

        • 要进行反向排序,不必改动key函数,可以传入第三个参数reverse=True

          1
          2
          >>> sorted(['bob', 'about', 'Zoo', 'Credit'], key=str.lower, reverse=True)
          ['Zoo', 'Credit', 'bob', 'about']
    • 返回函数(函数作为返回值)

      高阶函数除了可以接受函数作为参数外,还可以把函数作为结果值返回。
      如:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      def lazy_sum(*args):
      def sum():
      ax = 0
      for n in args:
      ax = ax + n
      return ax
      return sum
      # 当我们调用lazy_sum函数时,它返回的并不是求和结果,而是求和函数sum
      >>>f = lazy_sum([2,4,1,6])
      >>>f
      <function lazy_sum.<locals>.sum at 0x101c6ed90>

      # 只有在我们调用f函数时,才会得到求和结果
      >>>f()
      13

      需要注意的是,当我们调用lazy_sum()时,每次调用都会返回一个新的函数,即使传入相同的参数:

      1
      2
      3
      4
      >>> f1 = lazy_sum(1, 3, 5, 7, 9)
      >>> f2 = lazy_sum(1, 3, 5, 7, 9)
      >>> f1==f2
      False

      由于闭包,返回一个函数时,牢记该函数并未执行,返回函数中不要引用任何可能会变化的变量。

    • 匿名函数

      在Python中,对匿名函数提供了有限支持。还是以map()函数为例,计算f(x)=x2时,除了定义一个f(x)的函数外,还可以直接传入匿名函数:

      1
      2
      >>> list(map(lambda x: x * x, [1, 2, 3, 4, 5, 6, 7, 8, 9]))
      [1, 4, 9, 16, 25, 36, 49, 64, 81]

      关键字lambda表示匿名函数,冒号前面的x表示函数参数。
      匿名函数有个限制,就是只能有一个表达式,不用写return,返回值就是该表达式的结果

  • I/O编程

    • 文件读写文件操作详解)

      • 读文件
        在进行文件读写时,要先建立一个文件对象,open()函数。

        该函数有两个参数,第一个时文件路径,第二个参数是文件的操作方式

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        # ##### 文本文件
        >>> f = open('/Users/michael/test.txt', 'r')
        # 标识符r表示以读的方式打开 **文本** 文件
        # 当文件不存在时,会报错;否则打开成功

        # 文件打开成功,可以进行接下来的操作,调用read()方法可以一次读取文件的全部内容,Python把内容读到内存,用一个str对象表示
        >>>f.read()

        # 最后一步是调用close()方法关闭文件。文件使用完毕后必须关闭,因为文件对象会占用操作系统的资源,并且操作系统同一时间能打开的文件数量也是有限的
        >>>f,close()

        # 为在文件读写时出错也可以close文件,python提供了渐变方法
        with open('/path/to/file', 'r') as f:
        print(f.read())


        # ##### 二进制文件
        >>> f = open('/Users/michael/test.jpg', 'rb')
        >>> f.read()
        b'\xff\xd8\xff\xe1\x00\x18Exif\x00\x00...' # 十六进制表示的字节
        • 注意
          • 调用read()会一次性读取文件的全部内容,如果文件有10G,内存就爆了,所以,要保险起见,可以反复调用read(size)方法,每次最多读取size个字节的内容。另外,调用readline()可以每次读取一行内容,调用readlines()一次读取所有内容并按行返回list。因此,要根据需要决定怎么调用。
        • 写文件
          和读文件一样,只是mode改为w或rw
          注意:在写文件时,如果忘记close,可能会导致数据的丢失
    • StringIO和BitesIO (具体作用还不清楚)
      转到

    • 操作文件/目录

      1
      2
      3
      4
      5
      listdir()找出该路径下文件
      isfile()判断是否为文件
      splitext()获取文件后缀名
      >>> [x for x in os.listdir('.') if os.path.isfile(x) and os.path.splitext(x)[1]=='.py']
      ['apis.py', 'config.py', 'models.py', 'pymonitor.py', 'test_db.py', 'urls.py', 'wsgiapp.py']

      通过python内置的os模块和os.path模块,我们可以实现对文件和目录的处理, 详细教程

  • 常用内建模块

    • contentlib

      在Python中,读写文件这样的资源要特别注意,必须在使用完毕后正确关闭它们。正确关闭文件资源的一个方法是使用try…finally

      写try…finally非常繁琐。Python的with语句允许我们非常方便地使用资源,而不必担心资源没有关闭。

      • @contextmanager
        只要是可以实现上下文管理的,均可以用于with语句,阮一峰教程,而python的contextlib库的@contextmanager可以简化这个(上下文管理)实现过程

      • @closing
        如果一个对象没有上下文,我们可以通过该方法使其变为具有上下文的对象,并支持with语句。