Python的学习

Python 廖雪峰: https://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000

Python 的安装及.........配置(Mac 系统下)

Mac 系统下的安装

  1. 安装 Homebrew

    ~ 理解 homebrew 和 npm 区别

    homebrew: osx的软件管理工具, 软件管理助手, 可以安装Chrome浏览器等可视化工具

    npm: node.js的程序/模块管理工具, 服务于JavaScript社区

    ~ 安装 homebrew: /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"

  2. 安装 Python

    ~ python2.x 和 python3.x

    python2.x: Mac 系统自带 Python2.7(老, 被取代)

    python3.x: ① 官网下载安装 ② 通过 brew brew install python3 安装

  3. Python 解释器(交互式命令行)

    1. CPython(下载安装 python 即完成): 解释器是用 C 语言开发的,所以叫 CPython。在命令行下运行 python 就是启动 CPython 解释器

    2. 使用

    终端中运行python, 启动 python2.x

    终端中运行python3, 启动 Python3.x

    CPython 使用>>>, 作为提示符, 使用 exit()作为退出

  4. 文本编辑器 (VScode)

    1. 安装拓展 Python

    2. vscode 报错: Linter pylint is not installed(代码规范的插件)

    ~ 参考: https://blog.csdn.net/yh1061632045/article/details/81665446

    ~ 手动切换 python2.7 --> python3.7(地址是: /usr/local/bin/python3)

    ~ 配置 vscode, 设置python.pythonPath: /usr/local/bin/python3

    ~ 然后点击install pylint, 安装完成 vscode 就不弹出提示, 同时能代码提示

win 系统下的安装

  1. 官网下安装

  2. 勾选Add Python 3.7 to PATH, 然后安装即可

  3. 在命令提示符中, python会出来>>>即代表安装成功

  4. 可能报错code2305

第一个 Python 程序

  1. 步骤

    1. 添加 .py文件, 编写print('hello world!')

    2. 执行.py 文件

      1. 利用 python 命令: cd 到文件所在目录 -> 终端执行python demo.py -> 打印出结果(win, mac)

      2. 直接执行.py 文件: cd 到文件目录 -> 终端中chmod a+x demo.py 给与文件可执行的能力 -> 终端中 ./demo.py -> 打印结果(mac系统)

      3. 直接执行.py文件: 终端中 demo.py -> 即会执行(win系统)

  2. 交互模式 和 直接执行.py 文件的区别

    1. 交互模式: 启动 Python 解释器, 输入一行解释一行, 不做保存(适合单端代码的测试)

    2. .py 文件: 一次性执行完毕, 不给打断

  3. print()输出

    1. 逗号以空格输出: print('hello', 'world') -> 逗号以空格输出 -> hello world

    2. 自动计算: print(300 + 300) -> 600(可以做计算)

    3. print('300 + 100 =', 300 + 100) -> 300 + 100 = 600

  4. input 和 变量

    1. input 默认输出 str, 所以如果需要转化成整数需要用到int(input())

    2. int('abc'), 发现 abc 不能转化成整数的时候, 就会报ValueError 的错

    3. name = input()回车 -> >>>等待输入值 -> name 存储你输入的值 -> >>>name -> 打印输入值

Python 代码运行助手

  1. 作用: 让你在线输入 Python 代码,然后通过本机运行的一个Python脚本(就是learning.py)来执行代码, 直接在网页输出结果

  2. 终端执行python learning.py -> 终端输出Ready for Python code on port 39093...等代表运行成功

  3. 打开https://local.liaoxuefeng.com:39093/, 就是 Python 的在线编辑器, 点击 run 即可输出结果

  4. 网页 run 不输出结果的话, Ctrl + c 中断, 再执行步骤 2

基本语法

基础

  1. 基础

    1. # : 注释
    2. : : 当语句以冒号:结尾时,缩进的语句视为代码块(相当于js的大括号)
    3. Python 大小写敏感
  2. 数据类型和变量

    1. 整数: 十进制, 二进制, 十六进制
    2. 浮点数: 小数
    3. 字符串: ', "
    4. 转义符: 'I\'m \"OK\"!' => I'm "OK"!, \n表示换行,\t表示制表符,字符\本身也要转义,所以\\表示的字符就是\
    5. 不转义r'', ''内部字符串不转义, 直接输出
    6. 换行'''...''', 多行时, 比\n来的方便
          # 交互命令行内... 是提示符, 并不需要手动输入
          print('''line1
          ... line2
          ... lin3''')
          # .py文件中
          print('''line1
          line2
          line3''')
          # 还可以 print(r'''line1
          # line2
          # line3''')
      
    7. 布尔值(True, False) 和 布尔运算
        # 布尔值, 注意大小写
        >>> False
        False
        # 布尔运算
        >>> 3 > 2
        True
        # and、or和not运算
        >>> True and False
        False
        >>> True or False
        True
        >>> not 1 > 2
        True
      
    8. 空值: None
    9. 变量: 变量名必须是大小写英文、数字和_的组合,且不能用数字开头
    10. 常量: 通常用全部的大写
    11. 除法: /, 得到的都是浮点数, >>> 9/3 -> 3.0
    12. 地板除: //, 只取结果的整数部分
    13. 取余数: %

    总结: 万物皆对象, Python 对整数无大小限制,对浮点数超出一定范围直接显示inf(无限大)

字符串编码

  1. ord(), 函数获取字符的整数表示, 英文转 ascii 码的数字

  2. chr(), 函数把编码转换为对应的字符, 相反

  3. b'ABC', 把 Python 的 str 转成以字节为单位的 bytes, ABCb'abc', 一个是 str, 一个是 bytes, 每个字符只占一个字节

  4. str通过encode()方法可以编码为指定的 bytes

      >>> 'ABC'.encode('ascii')
      b'ABC'
      >>> '中文'.encode('utf-8')
      b'\xe4\xb8\xad\xe6\x96\x87'
      # 中文可以转成utf-8, 但是不可以转成ascii, 因为中文超出范围
      # 在bytes中,无法显示为ASCII字符的字节,用\x##显示。
      >>> '中文'.encode('ascii')
      Traceback (most recent call last):
        File "", line 1, in 
      UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-1: ordinal not in range(128)
    
  5. bytes通过decode方法, 转成 str

      >>> b'ABC'.decode('ascii')
      'ABC'
      >>> b'\xe4\xb8\xad\xe6\x96\x87'.decode('utf-8')
      '中文'
      # 如果bytes中包含无法解码的字节,decode()方法会报错
      # 如果bytes中只有一小部分无效的字节,可以传入errors='ignore'忽略错误的字节
      >>> b'\xe4\xb8\xad\xff'.decode('utf-8', errors='ignore')
      '中'
    
  6. len() 函数计算的是 str 的字符数,如果换成 bytes,len()函数就计算字节数

      >>> len(b'ABC')
      3
      >>> len(b'\xe4\xb8\xad\xe6\x96\x87')
      6
      >>> len('中文'.encode('utf-8'))
      6
    
  7. str 和 bytes 的互相转换。为了避免乱码问题,应当使用 UTF-8 编码对 str 和 bytes 进行转换

      # 为了告诉Linux/OS X系统,这是一个Python可执行程序,Windows系统会忽略这个注释;
      # 告诉Python解释器,按照UTF-8编码读取源代码,否则,你在源代码中写的中文输出可能会有乱码。
      #!/usr/bin/env python3
      # -*- coding: utf-8 -*-
    
  8. 格式化(有点像 js 的模板字符串)

      # %s:字符串,  %d:整数,  %f: 浮点数,  %x:十六进制整数
      # 需要替换的地方依次填写参数
      # %% 代表转义 %
      >>> 'hello, %s, you have $%d' % ('bin.wang', 100)
      'hello, bin.wang, you have $100'
    
  9. 计算机储存使用 Unicode,只有在传输的时候才转换成 UTF-8

  10. str 不可变性

      >>> a = 'abc'
      # replace方法
      >>> a.replace('a', 'A')
      # 变的是变量a, 字符串'abc'并没有变
      >>> a
      'Abc'
    

list 和 tuple

  1. list(类比 js 数组)

      >>> list = ['one', 'two', 'three']
      # 求得list长度
      >>> len(list)
      3
      # list下标代表具体某个值
      >>> list[len(list) - 1]
      'three'
      # -1 代表最后一个, -2代表倒数第二个
      >>> list[-1]
      # 正向反向越界都报 `IndexError` 的错
    
  2. list 的方法

    1. append(): list.appen('test'), 末尾追加

    2. pop(): list.pop(), 删除末尾, list.pop(1), 删除下标为 1 的那个

    3. insert: list.insert(1, 'test'), 在索引 1 的地方插入

    4. sort(): list.sort(), 根据 ascii 大小排序

  3. tuple(元组)

    1. 跟 list 类似, 但是 tuple 一旦初始化就不能修改, 取得方式一致, 但是没有 list 的方法

        >>> tuple = (1, 2, 3)
        >>> tuple[0]
        1
        # 注意当python只有一个元素的元组时候, 必须带上逗号, 以免识别成数学的括号运算
        >>> t = (1, )
      
    2. 关于 tuple 的不可变

        # 元组指向list是一直不变的, 只是list可变
        >>> t = ('a', 'b', ['c', 'd'])
        >>> t[2][0] = 'X'
        >>> t[2][1] = 'Y'
        >>> t
        ('a', 'b', ['X', 'Y'])
      

条件判断

  1. 条件判断

      age = 20
      # 每个条件判断以 : 分割, elif 是 else if的缩写
      if age >= 18:
        print('成年人')
      elif age >= 16:
        print('青少年')
      else:
        print('少年')
    

循环

  1. for...in循环(list, tuple, dict都可使用)

      name = ['Bob', 'Jack']
      # Python 以 : 代替JS的 {}
      for key in name:
        print(key)
    
  2. range()函数,可以生成一个整数序列

  3. list()函数, 可以转换为 list

      # range 生成从0开始小于5的整数, list把他们合成数组
      >>> list(range(5))
      [0, 1, 2, 3, 4]
    
  4. while 循环, 只要条件满足,就不断循环

      num = 5
      sum = 0
      while num > 0:
        sum = sum + num
        num = num - 1
      print(sum)
    
  5. break, 可提前结束循环

  6. continue语句跳过某些循环

dict 和 set

  1. dict, 官网说像 map, 个人觉得像对象

      >>> d = {'Nacy': 83, 'Bob': 93}
      >>> d['Bob']
      93
      # 多次赋值, 会覆盖
      >>> d['Bob'] = 98
      >>> d['Bob']
      98
    
  2. dict 的 key 值

    1. key 值不存在, 会报错KeyError

    2. 判断 key 值存在

        b = {'Bob': 123}
        # 第一种in
        >>> 'test' in b
        False
        # 第二种 get()方法,返回None 在交互模式下None不显示
        >>> b.get('test')
        # 不显示
        >>> b.get('Bob')
        123
        >>> b.get('test', -1)
        -1
        >>> b.get('Bob', -1)
        123
      
    3. dict 和 list 的区别

      1. dict: key 找 value, 根据 key 值算出(哈希算法)'页码', 再根据页码查找 value, 所以占用内存较多
      2. list: 下标找 value, 查找是从头到尾, 直到找到, list 越长, 耗时慢
    4. 删除一个 key, pop()方法, 对应的 value 也会被删除

      相反, 新增的话直接是d['Bob'] = '123', 即可

  3. set

    1. 传入 list 作为输入集合, 重复元素自动被过滤, 只存储 key, 不存 value, 且无序

        >>> s = set([1, 1, 2, 2, 3, 3])
        >>> s
        {1, 2, 3}
        # 通过add(key)添加新key, 重复添加没效果
        >>> s.add(4)
        >>> s
        {1, 2, 3, 4}
        # 通过remove(key)方法可以删除元素
        >>> s.remove(1)
        >>> s
        {2, 3, 4}
      
    2. set 可以看成无序和无重复元素的集合, 因此,两个 set 可以做数学意义上的交集、并集等操作

        >>> s1 = set([1, 2, 3])
        >>> s2 = set([2, 3, 4])
        >>> s1 & s2
        {2, 3}
        >>> s1 | s2
        {1, 2, 3, 4}
      

函数

内置函数

  1. 参考地址: https://docs.python.org/3/library/functions.html

  2. 求绝对值函数: abs(num) abs(-100) -> 100

  3. 求最大值: max(num1, num2) max(1, 2, -3) -> 2

  4. 类型转换, 交互模式下, help(hex)查看 hex 函数作用

      >>> int('123')
      123
      >>> int(12.84)
      12
      >>> int('abc')
      # int第二个参数base是代表进制, 第一个参数是16进制, 转成十进制, 16^1 + 6 = 22
     >>> int('16', base=16)
     22
     >>> int('123', 8)
     83
      # 报错 ValueError
      >>> float('12.34')
      12.34
      >>> str(1.23)
      '1.23'
      >>> str(100)
      '100'
      >>> bool(1)
      True
      >>> bool('')
      False
    

自定义函数

  1. def定义函数

      # 在交互模式下直接敲出函数
      # 没有定义return的, 最后还是会return None
      def my_abs(x):
        if x >= 0:
          return x
        else:
          return -x
      # 在交互模式下 import 引入函数
      >>> from demo(文件名不带.py) import my_abs(函数名)
      >>> my_abs(-10)
      10
    
  2. 定义空函数

      def nop():
        # pass相当于占位符,代表不干啥事, 不填写会报错
        pass
    
  3. 检查参数

      def my_abs(x):
      # isinstance判断参数类型是不是整数或者浮点数, 不是就raise 一个错误
      if not isinstance(x, (int, float)):
          raise TypeError('参数错误')
      if x >= 0:
          return x
      else:
          return -x
    
  4. 返回多个值

      >>> def Fn(x, y):
            return x, y
      >>> r = Fn('abc', 'cba')
      >>> print(r)
      # 返回多个值其实返回的是一个tuple, 返回一个tuple可以省略括号
      ('abc', 'cba')
    

函数的参数(挺绕的, 多练习)

  1. 位置参数(必选参数)

      # x必须且依次, 即位置参数
      def sum (x):
        return x * x
    
  2. 默认参数

    概念: 默认参数必须要用不可变对象

      #必选参数在前, 默认参数在后
      def sum (x, n = 2, age = 18)
        pass
      # n还是使用默认值
      sum(2, age = 16)
      # 默认参数的坑, 默认参数必须指向不变对象!
      def add_end(L = []):
        L.append('END')
        return L
      # 调用三次, 会一直加, 原因在: Python函数在定义的时候,默认参数L的值就被计算出来了,即[],因为默认参数L也是一个变量,它指向对象[],每次调用该函数,如果改变了L的内容,则下次调用时,默认参数的内容就变了,不再是函数定义时的[]了。
      add_end()
      ['END', 'END', 'END']
      # 修改
      def add_end(L = None):
        if L is None:
          L = []
        L.append('END')
        return L
    
  3. 可变参数

    概念: 允许传入0个或若干个参数, 定义时参数带*

      # 定义一个a^2 + b^2...的函数
      def getSum(*number):
        sum = 0
        for key in number:
          sum = sum + key * key
        return sum
      tuple = (3, 4)
      # 直接传参, 调用时自动组装成tuple
      getsum(1, 2)
      # 直接拿tuple使用. 记得带星号
      getSum(*tuple)
    
  4. 关键字参数

    概念: 允许传入0个或若干个含参数名的参数, 调用时自动组成dict, 定义和调用都需要使用**

      def person(name, age, **kw):
        print('name: %s, age: %d, other: %s' % (name, age, kw))
      dict = {'city': 'sz'}
      # kw得到的是dict的拷贝
      person('bin.wang', 18, **dict)
    
  5. 命名关键字参数

    概念: 为限制关键字参数名字, key = value, 且不是dict

      # 以*为分割, 后面的为命名关键字参数
      def person(name, *, city = 'SZ', age)
        print(name, city, age)
      person('test', age = 18)
      # 传参时, 必须传入参数名和参数值, 否则相当于多传了位置参数, 报错
      person('test', 18)
      # 命名关键字参数可以由默认值, city
    
  6. 参数组合

    概念: 参数定义的顺序必须是:必选参数、默认参数、可变参数、命名关键字参数和关键字参数

      def f1(a, b, c=0, *args, **kw):
      >>> f1(1, 2, 3, 'a', 'b')
      a = 1 b = 2 c = 3 args = ('a', 'b') kw = {}
      # 通过tuple 和 dict的话, 可简单到
      >>> args = (1, 2, 3, 4)
      >>> kw = {'d': 99, 'x': '#'}
      >>> f1(*args, **kw)
      a = 1 b = 2 c = 3 args = (4,) kw = {'d': 99, 'x': '#'}
      # 必选, 默认, 命名关键字参数, 关键字参数
      def f2(a, b, c=0, *, d, **kw):
      >>> f2(1, 2, d=99, ext=None)
      a = 1 b = 2 c = 0 d = 99 kw = {'ext': None}
    

递归函数

  1. 函数内部, 调用本身, 过深的调用可能导致栈溢出

  2. 栈溢出科普: 计算机中函数的调用通过栈数据结构实现的, 每进一个函数, 栈就加一层栈帧, return一次就减少一层栈帧, 当函数调用过多, 即会栈溢出

  3. python栈溢出报错: RecursionError: maximum recursion depth exceeded in comparison

      def getFn(n):
        if n == 1:
          return 1
        return n * getFn(n - 1)
    
  4. 例子: 移动汉诺塔

高级特性

切片(slice)

  1. 对list, tuple, str的数据处理

      # 对list或者tuple
      L = []
      n = 0
      while n <= 99:
        L.append(n)
        n = n + 1
      # 生成L是[0-99]的list
      # slice切片, 取索引0到索引3(不包括3), 即0, 1, 2
      >>> L[ : 3]
      >>> [0, 1, 2]
      # L[-1]代表最后一位, 那么包括倒数第三位, 不包括倒数第一位
      >>> L[-3: -1]
      >>> [97, 98]
      # 前十位, 每隔5位取一个
      >>> L[ : 10 : 5]
      >>> [0, 5]
      # 所有list, 隔20取一位
      >>> L[ : : 20]
      >>> [0, 20, 40, 60, 80]
      # 完全复制一个list
      >>> n = L[ : ]
    
      >>> str = 'mytest'
      # 每隔2位取一位
      >>> str[ : : 2]
      >>> 'mts'
    

迭代

  1. for...in...遍历也叫迭代, list, tuple, dict, str, generator都可迭代

  2. 迭代的用法

    1. list 和 tuple的迭代

        # 如果迭代的时候想把下标输出
        # 引用python内置的enumerate函数
        >>> list = [4, 5, 6]
        >>> for index, value in enmurate(list):
        >>> 0 4  1 5  2 6
        # 常见多变量
        >>>  for x, y in [(1, 1), (2, 4)]:
        ...     print(x, y)
        >>> 1 1   2 4
      
    2. dict的迭代

        >>> dict = {'Bob': 18, 'Nancy': 17}
        # 获取key值
        >>> for key in dict:
        >>> 'Bob' 'Nancy'
        # 获取value
        >>> for value in dict.values():
        # 获取key和value值
        >>> for key, value in dict.items():
        >>> Bob 18 Nancy 17
      
    3. str的迭代

        >>> str = 'test'
        >>> for key in str:
        >>> t e s t
      
  3. 检测可迭代对象(区别于Iterator, 迭代器)

    1. 概念: 只要是可迭代对象, for循环就不会报错, 通过collections模块的Iterable类型判断可迭代对象
      >>> from collections import Iterable
      >>> isinstance([1,2,3], Iterable)
      >>> True
      >>> isinstance('str', Iterable)
      >>> True
      >>> isinstance(123, Iterable)
      >>> False
    

列表生成式

  1. 概念: Python内置的用来创建list的生成式
      # list部分
      >>> list(range(1, 6))
      >>> [1, 2, 3, 4, 5]
      # 生成 [1 * 1, 2 * 2, 3 * 3]
      >>> [x * x for x in range(1, 4)]
      >>> [1, 4, 9]
      # 还可以进行筛选
      >>> [x * x for x in range(1, 4) if x % 2 == 0]
      >>> [4]
      # 使用两层循环,可以生成全排列, 即排列组合
      >>> [m + n for m in 'ABC' for n in 'XYZ']
      ['AX', 'AY', 'AZ', 'BX', 'BY', 'BZ', 'CX', 'CY', 'CZ']
      # 把list中所有的字符串变成小写
      >>> list = ['Hello', 'World']
      >>> [s.lower() for s in list]
      >>> ['hello', 'world]
    
      # dict部分
      >>> dict = {'x': 'A', 'y': 'B'}
      >>> [k + '=' + v for k, v in dict.items()]
      >>> ['x=A', 'y=B']
    

生成器(generator)

  1. 概念通过列表生成式, 直接生成的list, 当量大时耗内存, 使用生成器按照逻辑边循环边计算, 节省空间

  2. 简单方式

      # 直接把列表生成式改成 简单的generator 区别: [] => ()
      >>> g = (x * x for x in [1, 2])
      >>>  at 0x1022ef630>
    
  3. 遍历每一项

      # 每次调用next(g),就计算出g的下一个元素的值,直到计算到最后一个元素,没有更多的元素时,抛出StopIteration的错误
      >>> next(g)
      >>> 1
      >>> next(g)
      >>> 4
      # 没有了就结束
      >>> next(g)
      >>> StopIteration
      # 通过 for...in...(常用)
      >>> for n in g
      >>> 1 4
    
  4. 斐波那契

      # 相当于 t = (0, 0, 1) => a = t[0]
      >>> n, a, b = 0, 0, 1
      # 关键字yield, 生成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'
      # generator和函数的执行流程不一样, 函数是顺序执行,遇到return语句或者最后一行函数语句就返回, 变成generator的函数,在每次调用next()的时候执行,遇到yield语句返回,再次执行时从上次返回的yield语句处继续执行。
    

迭代器

  1. 概念: 可以被next()函数调用并不断返回下一个值的对象称为迭代器:Iterator, 生成器都是Iterator对象, 但list、dict、str虽然是Iterable(可迭代),却不是Iterator(可迭代对象)

  2. dict, list, tuple, str数据类型为何不是Iterator ?

    Iterator对象表示的是一个数据流, 被next()函数调用时不断返回下一个数据, 直到没有数据时抛出StopIteration错误, 我们不能提前知道序列长度, 所以Iterator的计算是惰性的,只有在需要返回下一个数据时它才会计算. Iterator甚至可以表示一个无限大的数据流,例如全体自然数。而使用list是永远不可能存储全体自然数的.

  3. 判断是否Iterator对象

      >>> from collections import Iterator
      >>> isinstance([], Iterator)
      # 不是迭代器
      >>> False
    
  4. 使用iter()函数变成迭代器Iterator

      # 使用内置iter()函数, list、dict、str等Iterable变成Iterator
      >>> isinstance(iter([]), Iterator)
      >>> True
    
  5. 小结:

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

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

    ~ 集合数据类型如list、dict、str等是Iterable但不是Iterator,不过可以通过iter()函数获得一个Iterator对象。

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

      it = iter([1, 2, 3])
      while True:
        try:
          x = next(it)
          print(x)
        except StopIteration:
          print('done')
          break
    

函数式编程

  1. 由于python允许使用变量, 故而python不是纯函数式(固定输入固定输出)编程语言

高阶函数

  1. 函数名也是变量, 只是函数名已经指定到函数

  2. 概念: 一个函数接受另外一个函数作为参数, 高阶函数

      >>> def add(x, y, f):
      ...  return f(x) + f(y)
      >>> add(-5, 1, abs)
      >>> 6
    

map/reducer

  1. map: 接受两个参数,函数Iterable, map将传入的函数依次作用到序列的每个元素,结果返回新的Iterator

      >>> def Fn(x):
      ...   return x * x
      # 第一个参数是函数, 第二个参数是Iterable
      >>> map(Fn, [1, 2, 3])
      # 直接输出的是map对象
      
      # Iterator是惰性序列,通过list()函数让它把整个序列都计算出来并返回一个list
      >>> print(list(map(Fn, [1, 2, 3])))
      [1, 4, 9]
    
  2. reduce: 接受函数Iterable, 这个函数必须接收两个参数, reduce把结果继续和序列的下一个元素做累积计算, 返回结果不是Iterator

    1. 效果就是: reduce(f, [x1, x2, x3, x4]) = f(f(f(x1, x2), x3), x4)
      >>> from functools import reduce
      >>> def add(x, y):
      ...     return x * 10 + y
      ...
      >>> reduce(add, [1, 2, 3])
      123
    
  3. 自己实现原生的int函数

      from functools import reduce
      num = {'1': 1, '2': 2, '3'}
      def str2int(s):
        def fn(x, y):
          return x * 10 + y
        def char2num(x):
          return num[x]
        return reduce(fn, map(char2num, s))
      # 借用map和reduce, 此时输入'123', 调用str2int函数, 会转化成数字123
    

filter

  1. 概念: 内置高阶函数, 用于过滤序列, 返回Iterator

      def no_empty(s):
        return s and s.strip()
      l = filter(no_empty, ['a', '', None, 'B'])
      # 返回的是惰性序列, 使用list()获得结果, 并返回list
      list(l)
    
  2. 埃氏筛法: 求解素数, 2素数, 2倍数删除, 3素数, 3的倍数删除, 一次下去, 求得所有素数

sorted

  1. 概念: 内置高阶函数, 对list进行排序, 可以接收一个key函数来实现自定义的排序, 返回排序之后的list

      # 基本用法
      >>> sorted([10, -10, 2, 5])
      [-10, 2, 5, 10]
      # 作为高阶函数用法, 按key作用后的结果排序
      sorted([5, -4, 3], key=abs)
      [3, -4, 5]
    
  2. 字符串排序, 按ASCII码进行排序

      # ascii码里面, b > A
      >>> sorted(['bob', 'Amazing', 'Test'])
      ['Amazing', 'Test', 'bob']
      # 全部转化为小写再比较
      >>> sorted(['bob', 'Amazing', 'Test'], key=str.lower)
      ['Amazing', 'bob', 'Test']
    
  3. 反向排序, 可以传入第三个参数reverse=True

返回函数

  1. 例子

      # 类似js闭包, 内部函数可以得到外部函数的局部变量
      def lazy_sum(*num):
        def get_sum():
          sum = 0
          for key in num:
            sum = sum + key
          return sum
        return get_sum
      # 每次调用都是返回新的函数
      f = lazy_sum(1, 2, 3)
      # 再次调用才得出结果
      f()
    
  2. 闭包

返回闭包时牢记一点:返回函数不要引用任何循环变量,或者后续会发生变化的变量。

```py
  # 所引用的变量i全部变成了3
  def count():
    l = []
    for i in range(1, 4):
      def fn():
        return i * i
      l.append(fn)
    return l
  f1, f2, f3 = count()
  >>> f1()
  9
  >>> f2()
  9
  >>> f3()
  9
  # 改进
  def count():
    def f(j):
        def g():
            return j*j
        return g
    fs = []
    for i in range(1, 4):
        fs.append(f(i)) # f(i)立刻被执行,因此i的当前值被传入f()
    return fs
```

匿名函数

  1. 概念: 关键字lambda表示匿名函数,冒号前面的x表示函数参数, 只能有一个表达式, 表达式结果就是return的返回值

      >>> f = lambda x: x * x
      # 等价
      >>> def f(x):
      ...  return x * x
    

装饰器(decorator)

  1. 概念: 在代码运行期间动态增加功能, 本质上是返回函数的高阶函数

  2. __name__属性,可以拿到函数的名字

      def fn(x):
        return x
      f = fn
      # 通过__name__拿到函数名字
      >>> fn.__name__
      'fn'
      >>> f.__name__
      'fn'
    
  3. 例子

      # 不带参数的decorator
      import functools
    
      def log(func):
        # 为了最终返回的decorator的__name__指向的还是func而不会变成wrapper
        @functools.wraps(func)
        def wrapper(*args, **kw):
          print('call %s():' % func.__name__)
          return func(*args, **kw)
        return wrapper
    
      # 带参数的decorator
      import functools
      # 带参数的decorator, 在原来函数中可以做其他的操作
      def log(text):
        def decorator(func):
          @functools.wraps(func)
          def wrapper(*args, **kw):
            print('%s %s():' % (text, func.__name__))
            return func(*args, **kw)
          return wrapper
        return decorator
      # 调用
      def fn(x):
        print('调用', x)
      fn = log('log日志')(fn)
      fn(88)
    

偏函数

  1. 概念: 偏函数是由functools模块提供, functools.partial把一个函数的某些参数固定住(设置默认值), 返回新的函数

      >>> import functools
      >>> int2 = functools.partial(int, base = 16)
      # 预设有10, 如果都不比是10大就选择10
      >>> max2 = functools.partial(max, 10)
      >>> max2(1, 2, 3)
      10
    

模块

  1. 概念: 一个.py文件一个模块, 跨模块不会出现变量名冲突

  2. 包: 为避免模块名冲突, 每个包下面都有__init__.py文件

      # 引入模块, myCompany是顶层包, a是a.py模块, test是a.py的方法
      import myCompany.a
      myCompany.a.test()
    
  3. 自己创建模块时要注意命名,不能和Python自带的模块名称冲突

[图片上传失败...(image-f76f68-1547169230449)]

使用模块

  1. 例子

      # 任何模块代码的第一个字符串都被视为模块的文档注释;
      'my first module'
      # 代码作者
      __author__ = 'bin.wang'
      import sys
      def test():
        args = sys.argv
        print(args)
        if len(args) == 1:
          print('hello world')
        elif len(args) == 2:
          print('hello, %s' % args[1])
        else:
          print('too many arguments')
      # 当运行到这个模块时, __name__会更改为__main__, 当导入时判断语句为False
      # 这是常见的运行测试
      if __name__ == '__main__':
          test()
    
  2. 作用域

    1. 变量的命名规则
      模块内部使用: _XXX__XXX
      公开的函数: XXX
      特殊变量: __XXX__, 直接引用但一般特殊用途, __name__

第三方模块(类比js的npm)

  1. 安装第三方模块, 包管理工具pip

  2. 安装pip

    1. win下安装: 安装python时, 勾选pip和Add python.exe to Path(但我是勾选了Add Python 3.7 to PATH)

    2. mac系统下安装: 记得使用pip3

    3. pip安装第三方模块: 一个一个安装费时, 且需要考虑兼容性

  3. 安装Anaconda(需要研究)

    1. 概念: 基于Python的数据处理和科学计算平台, 内置许多第三方库, MySQL驱动程序,Web框架Flask,科学计算Numpy等. Anaconda会把系统Path中的python指向自己自带的Python,并且,Anaconda安装的第三方模块会安装在Anaconda自己的路径下,不影响系统已安装的Python目录

    2. 地址: https://www.anaconda.com/download/

面向对象编程

  1. 概念: OOP, 设计思想, 封装继承和多态

类和实例

  1. 类和实例: 类实例化出对象, 对象之间方法一致, 数据不一样

      # 关键字class, 类名大写, object类, 代表该类是从object类继承下来
      class Student(object):
        # 在创建实例时就把, name和score绑定上去, 类比jsconstruct
        # self代表实例本身(类比js的this), 创建实例就需要传入name和score
        def __init__(self, name, score):
            # 加上__, 变成私有变量, 外部就不能访问
            self.__name = name
            self.__score = score
        def print_score(self):
            print('%s: %s' % (self.__name, self.score))
      # 调用, 创建实例, 调用方法
      ben = Student('ben', 99)
      ben.print_score()
    

访问限制

  1. 概念: 在属性名称前加上__, 变成私有变量, 这样外部就不能访问了(_XX还能访问, 但也约定是私有变量), 注意区分__XXX__, 特殊变量, 能直接访问

继承和多态

```py
  class Animal(object):
    def run(self):
      print('animal is running')
  # 继承, 子类方法覆盖父类同类方法
  class Cat(Animal):
    def run(self):
      print('cat is running')
  # 实例化的对象跟str, list等一样都属于某个数据类型, 所以也能做类型判断
  cat = Cat()
  isinstance(cat, Cat) # True
  isinstance([], list) # True
```

获取对象信息(判断对象类型)

  1. type()函数: type(123) ->

  2. isinstance()

  3. dir(): 获得一个对象的所有属性和方法, 返回一个包含字符串的list

      >>> dir(list)
      ['__add__', '__len__',..., 'copy']
      # 调用python内置的len(list), 其实就是调用了__len__方法, 跟list.__len__() 是一样的
    
  4. getattr(), hasattr(), setattr()

      class MyObject(object):
        def __init__(self):
            self.x = 9
        def power(self):
            return self.x * self.x
      obj = MyObject()
      hasattr(obj, 'x') # True
      hasattr(obj, 'power') # True
      setattr(obj, 'y', 2)
      hasattr(obj, 'y') # True
    

实例属性和类属性

  1. 给实例绑定属性, 方法, 给类绑定属性, 方法
      from types import MethodType
      # 定义类
      class Student(object):
        # 给类加属性
        gender = '男'
        def __init__(self, name):
          self.name = name
      # 类绑定方法, 实例化的对象都可以使用
      def setScore(self, score):
        self.score = score
      Student.setScore = setScore
      # 实例化, 绑定的属性可方法, 只适用于本实例
      s = Student('Ben')
      # 实例绑定属性, 优先级大于类
      s.gender = '女'
      def getAge(self, age):
        self.age = age
      s.getAge = MethodType(getAge, s)
      s.getAge(18)
      s.age       # 18, 且此实例不会影响别的实例
    

面向对象高级编程(进阶版本)

使用slots

  1. 概念: 限制实例的属性, 继承它的子类不起作用, 但是子类设置了__slots__, 则会把父类的slots一起继承

      class Student(object):
        # 用tuple的方式限定实例可增加的属性
        __slots__ = ('name', 'age')
      s = Student()
      # AttributeError: 'Student' object has no attribute 'gender'
      s.gender = '男'
    

使用@property(不太理解)

  1. 概念: @property装饰器, 把一个方法变成属性调用的

      # score是可读写属性, 那么是只读属性
      class Student(object):
        # property装饰器, 方法变成属性一样使用
        @property
        def score(self):
          return self._score
        # score的设置, 像设置属性一样使用里面的方法
        @score.setter
        def score(self, val):
          self._score = val
        # 只设置了读, 没设置写入, 就是只读
        @property
        def name(self):
          return self._name
      # 实例
      s = Student()
      s.score = 60
      print(s.score)  # 60
    

多重继承

  1. 概念: 多重继承,一个子类就可以同时获得多个父类的所有功能

      class Animal(object):
        pass
      class Runable(object):
        pass
      # 狗继承了两个父类
      class Dog(Animal, Runable):
        pass
    
  2. MixIn: 上述的继承多个的设计, 目的就是给一个类增加多个功能

定制类

  1. __str__: 使得输出更易理解

      class Student(object):
        def __init__(self, name):
          self.name = name
        def __str__(self):
          return 'Student object (name: %s)' % (self.name)
        __repr__ = __str__
      # 原本: <__main__.Student object at 0x00000244C4994CF8>
      # 现在: Student object (name: test)
      print(Student('test'))
    
  2. __iter__: 使得类返回迭代对象, 使得类可迭代(不太理解作用)

      class Fib(object):
        def __init__(self):
          self.a, self.b = 0, 1
        def __iter__(self):
          return self
        def __next__(self):
          self.a, self.b = self.b , self.a + self.b
          # 退出循环条件
          if self.a > 10000:
            raise StopIteration
          return self.a
        for key in Fib():
          print(key)
    
  3. __getitem__: 使得类除了forin迭代之外, 还可以根据下标取值

      class Fib(object):
        def __getitem__(self, n):
          a, b = 1, 1
          for x in range(n):
            a, b = b, a + b
          return a
      # 3, 能根据下标取值
      print(Fib()[3])
    
  4. __getattr__

  5. __call__

枚举类

  1. 概念: Enum可以把一组相关常量定义在一个class中,且class不可变,成员可以直接比较

      from enum import Enum, unique
      # value属性则是自动赋给成员的int常量,默认从1开始计数
      Month = Enum('Month', ('Jan', 'Feb'))
      # 如果value的属性想自定义, 则需要用到@unique装饰器
      @unique
      class Weekday(Enum):
        Sun = 0
        Mon = 1
      #访问
      for name, member in Weekday.__members__.items():
        print(name, '=>', member, ',', member.value)
        # Sun => Weekday.Sun , 0
      # 根据value获取值
      print(Weekday.Sun.value)  # 0
      # 根据value也可以反推member
      print(weekday(0))  # Sun
    

使用元类

  1. 概念

错误, 调试和测试

错误处理

try...except...finally...

  1. try某步报错立马跳转到except, 最后finally都会执行

      try:
      print('try...')
      r = 10 / int('2')
      print('result:', r)
      except ValueError as e:
        print('值错误:', e)
      except ZeroDivisionError as e:
        print('0做分母:', e)
      # try执行不报错的话, 才会执行else
      else:
        print('no error!')
      finally:
        print('finally...')
      print('END')
    
  2. python 的错误也是class, 错误类型都继承自BaseException, 且捕获该类型错误, 还会顺便捕获其子类错误

  3. 常见的错误类型和继承关系: https://docs.python.org/3/library/exceptions.html#exception-hierarchy

调用栈

  1. 概念: 分析错误的调用栈信息,才能定位错误的位置。

记录错误

  1. 概念: 内置的logging模块可以记录错误信息

      import logging
      def foo(s):
        return 10 / int(s)
      def bar(s):
        return foo(s) * 2
      def main():
        # 直接通过logging捕获错误, 但是不会退出程序的执行, END还会执行
        try:
          bar('0')
        except Exception as e:
          logging.exception(e)
      main()
      print('END')
    

抛出错误

  1. 概念

未完待续...

你可能感兴趣的:(Python的学习)