跟学廖雪峰python教程新学新知

LPF05 / 2024-02-07 / 原文

廖雪峰python教程新学新知

python基础

  1. r'' 表示''内部的字符串默认不转义。

  2. ord()函数获取字符的整数表示,chr()函数把编码转换为对应的字符。

  3. bytes类型的数据用带b前缀的单引号或双引号表示:
    x = b'ABC'

  4. encode('编码类型')方法可以编码为指定的bytes,decode('编码类型')方法将bytes转换为str

  5. decode('编码类型', errors='ignore')中,传入errors='ignore'忽略错误的字节。

  6. 保存文件时指定编码类型以及增加运行头文件。

    #!/usr/bin/env python3
    # -*- coding: utf-8 -*-
    
  7. 输出格式化有三种方法:

    • % 运算符 :同c语言
    >>> 'Hello, %s' % 'world'
    'Hello, world'
    >>> 'Hi, %s, you have $%d.' % ('Michael', 1000000)
    'Hi, Michael, you have $1000000.'
    
    • format函数 :传入的参数依次替换字符串内的占位符{0}、{1}……
    >>> 'Hello, {0}, 成绩提升了 {1:.1f}%'.format('小明', 17.125)
    'Hello, 小明, 成绩提升了 17.1%'
    
    • f-string

    小明的成绩从去年的72分提升到了今年的85分,请计算小明成绩提升的百分点,并用字符串格式化显示出'xx.x%',只保留小数点后1位:

    s1 = 72
    s2 = 85
    r = (s2-s1)/s1
    print('%004.1f%%' % r)
    
  8. tuple元素不可变,但tuple中的list可变。

  9. 模式匹配可匹配复杂语句和列表。

    age = 15
    
    match age:
        case x if x < 10:
            print(f'< 10 years old: {x}')
        case 10:
            print('10 years old.')
        case 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18:
            print('11~18 years old.')
        case 19:
            print('19 years old.')
        case _:
            print('not sure.')
    
    args = ['gcc', 'hello.c', 'world.c']
    # args = ['clean']
    # args = ['gcc']
    
    match args:
        # 如果仅出现gcc,报错:
        case ['gcc']:
            print('gcc: missing source file(s).')
        # 出现gcc,且至少指定了一个文件:
        case ['gcc', file1, *files]:
            print('gcc compile: ' + file1 + ', ' + ', '.join(files))
        # 仅出现clean:
        case ['clean']:
            print('clean')
        case _:
            print('invalid command.')
    
    

函数

  1. hex()函数转换16进制。

  2. 函数名是一个指向函数对象的引用,可以把函数赋给一个变量。

  3. 函数返回值是一个单一值,当有多个值返回时,实际上是返回一个tuple。

  4. 默认参数必须指向不变对象。

    def add_end(L=[]):
    L.append('END')
    return L
    >>> add_end([1, 2, 3])
    [1, 2, 3, 'END']
    >>> add_end()
    ['END']
    >>> add_end()
    ['END', 'END']
    

    修改后

    def add_end(L=None):
    if L is None:
        L = []
    L.append('END')
    return L
    >>> add_end()
    ['END']
    >>> add_end()
    ['END']
    
  5. 可变参数*argv,可传入数量是任意的,在list或tuple前面加一个*号,把list或tuple的元素变成可变参数传进去。

  6. 关键字参数**kw,传入的是dict内容,**dict可以将dict中内容传入函数,同时**kw获得的参数则是**dict的一份拷贝。

  7. 命名关键词参数特殊分隔符**后面的参数被视为命名关键字参数。其作用是限制关键字参数。

    def person(name, age, *, city, job):
    print(name, age, city, job)
    

    如果函数定义中已经有了一个可变参数,后面的命名关键字参数就不再需要一个特殊分隔符*了。

    def person(name, age, *args, city, job):
    print(name, age, args, city, job)
    
  8. 定义函数可以组合使用这5种参数:必选参数、默认参数、可变参数、关键字参数和命名关键字参数。
    参数定义的顺序必须是:必选参数、默认参数、可变参数、命名关键字参数和关键字参数。

  9. 对于任意函数,都可以通过类似func(*args, **kw)的形式调用它,无论它的参数是如何定义的。

  10. 对于递归来说,不需要展开思考的它的中间过程,这违背了递归的初心。
    按照递归的思路,只需要考虑n和n-1的关系就好,至于n-2,n-3...完全没必要考虑。

高级特性

  1. 代码越少,开发效率越高。

  2. enumerate函数可以把一个list变成 索引-元素 对。

  3. 在一个列表生成式中,for前面的if ... else是表达式(所以必须有else),而for后面的if是过滤条件,不能带else

    >>> [x for x in range(1, 11) if x % 2 == 0]
    [2, 4, 6, 8, 10]
    >>> [x if x % 2 == 0 else -x for x in range(1, 11)]
    [-1, 2, -3, 4, -5, 6, -7, 8, -9, 10]
    
  4. 创建一个generator有两种办法:

    • 只要把一个列表生成式的[]改成(),就创建了一个generator。

      >>> 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>
      
    • 如果一个函数定义中包含yield关键字,那么这个函数就不再是一个普通函数,而是一个generator函数,调用一个generator函数将返回一个generator。

      def fib(max):
          n, a, b = 0, 0, 1
          while n < max:
              yield b
              a, b = b, a + b
              n = n + 1
          return 'done'
      >>> f = fib(6)
      >>> f
      <generator object fib at 0x104feaaa0>
      
  5. generator函数和普通函数的执行流程不一样。
    普通函数是顺序执行,遇到return语句或者最后一行函数语句就返回。
    而变成generator的函数,在每次调用next()的时候执行,遇到yield语句返回,再次执行时从上次返回的yield语句处继续执行。

    def odd():
        print('step 1')
        yield 1
        print('step 2')
        yield(3)
        print('step 3')
        yield(5)
    
    >>> 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
    
  6. 调用generator函数会创建一个generator对象,多次调用generator函数会创建多个相互独立的generator。

    接上
    >>> next(odd())
    step 1
    1
    >>> next(odd())
    step 1
    1
    >>> next(odd())
    step 1
    1
    
  7. 解杨辉三角,有三种解法(至少会一种,且理解第一种解法):

    def triangles():
        L = [1]
        while True:
            yield L
            L = [sum(i) for i in zip([0]+L, L+[0])]
    
    def triangles():
        ret = [1]
        while True:
            yield ret
            for i in range(1, len(ret)):
                ret[i] = pre[i] + pre[i - 1]
            ret.append(1)
            pre = ret[:]
    
    def YangHui (num = 10):
        LL = [[1]]
        for i in range(1,num):
            LL.append([(0 if j== 0 else LL[i-1][j-1])+ (0 if j ==len(LL[i-1]) else LL[i-1][j]) for j in range(i+1)])
        return LL
    
  8. 通函数调用直接返回结果:

    >>> r = abs(6)
    >>> r
    6
    

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

    >>> g = fib(6)
    >>> g
    <generator object fib at 0x1022ef948>
    

    一般用for循环来调用generator:

    >>> g = (x * x for x in range(10))
    >>> for n in g:
    ...     print(n)
    
    >>> for n in fib(6):
    ...     print(n)
    
  9. 可以直接作用于for循环的对象统称为可迭代对象:Iterable, 包括集合数据类型、generator
    可以被next()函数调用并不断返回下一个值的对象称为迭代器:Iterator

  10. 可以用isinstance()函数判断是否是IterableIterator

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

  12. Python的Iterator对象表示的是一个数据流,Iterator对象可以被next()函数调用并不断返回下一个数据,直到没有数据时抛出StopIteration错误。可以把这个数据流看做是一个有序序列,但我们却不能提前知道序列的长度,只能不断通过next()函数实现按需计算下一个数据,所以Iterator的计算是惰性的,只有在需要返回下一个数据时它才会计算。
    Iterator甚至可以表示一个无限大的数据流,如全体自然数。
    总结:

    • 凡是可作用于for循环的对象都是Iterable类型;

    • 凡是可作用于next()函数的对象都是Iterator类型,它们表示一个惰性计算的序列;

    • 集合数据类型是Iterable但不是Iterator,可以通过iter()函数获得一个Iterator对象。

函数式编程

  1. 一个函数可以接收另一个函数作为参数,这种函数称之为高阶函数。

    def add(x, y, f):
        return f(x) + f(y)
    
    推导过程:
    x = -5
    y = 6
    f = abs
    f(x) + f(y) ==> abs(-5) + abs(6) ==> 11
    return 11
    
  2. map(f,Iterable)函数接受两个参数,一个是函数f,一个是Iterable
    作用是将函数f作用到Iterable中的每个元素上,返回类型是Iterator

  3. reduce(g,Iterable)函数接受两个参数,一个是函数g,一个是Iterable
    作用是将函数g作用到Iterable中的每个元素上并把结果继续和序列的下一个元素做累积计算,返回类型是Iterator

    reduce(f, [x1, x2, x3, x4]) = f(f(f(x1, x2), x3), x4)
    
  4. filter(h,Iterable)函数接受两个参数,一个是函数h,一个是Iterable
    作用是将函数f作用到Iterable中的每个元素上,然后根据返回值是True还是False决定保留还是丢弃该元素。返回类型是Iterator

  5. filter()函数应用于求素数的埃氏筛法。

    def _odd_iter():
        n = 1
        while True:
            n = n + 2
            yield n
    
    def _not_divisible(n):
        return lambda x: x % n > 0
    
    def primes():
        yield 2
        it = _odd_iter() # 初始序列
        while True:
            n = next(it) # 返回序列的第一个数
            yield n
            it = filter(_not_divisible(n), it) # 构造新序列    
    
  6. sorted()函数也是一个高阶函数,它还可以接收一个key函数来实现自定义的排序。

    >>> sorted([36, 5, -12, 9, -21], key=abs)
    [5, 9, -12, -21, 36]
    
  7. 返回函数,把函数作为一个函数的返回值。

    def lazy_sum(*args):
        def sum():
            ax = 0
            for n in args:
                ax = ax + n
            return ax
        return sum
    
    >>> f = lazy_sum(1, 3, 5, 7, 9)
    >>> f
    <function lazy_sum.<locals>.sum at 0x101c6ed90>
    
    >>> f()
    25
    
  8. 调用lazy_sum()时,每次调用都会返回一个新的函数,即使传入相同的参数。