Python3 学习笔记

写在前面的话

代码中的# > 表示的是输出结果

输入

  • 使用input()函数
  • 用法
str = input('请输入内容:')
print(str)
#>>    此时控制台上打印的一定是你输入的字符串
  • 注意
    • input函数输出的均是字符串,如需要接收其他类型的变量,就需要使用强制类型转换。
    • 例如:将字符串转换为数字类型的变量
num = input('请输入数字:')
print(type(num))
#    >>    <class 'str'>
#此时可以看见,就算输入数字类型的变量,num接收的值依然是一个字符串类型
num = int(num)   #强制转换为int类型
print(type(num))
#    >>     <class 'int'>
#此时控制台返回的是num变量的类型,可以看到,类型已经是int了

输出

  • 使用print()函数
  • 用法
#常用用法
print('Hello World!')
# 自定义用法
print(value,sep = ' ',end = '\n')
value表示需要打印的值
sep表示打印多个值之间的分隔符,默认是一个空格
end表示打印结束后的分隔符,默认是\n

变量、函数、类命名规则

  • 可以使用数字、字母、下划线
  • 不可以用数字开头
  • 不可以使用关键字和保留字
  • 推荐驼峰命名法
    • 类名使用大驼峰,如MyFirst
    • 变量、函数使用小驼峰,如myFirst
  • 也可使用posix写法,如my_first

常用操作

  • type()函数,查看当前变量类型
  • id()函数,查看当前变量地址
  • help()函数,查找帮助文档

变量进行赋值

  • 单独赋值
a = 5
b = 'hh'
  • 一起赋值
a , b = 5 , 'hh' 
  • 两个变量之间的互换
a , b = 5 , 10

#如果遇到JAVA程序员,会这样写
int temp = a 
a = b
b = temp

#但是在python中,只需要进行简单交换即可
a , b = b , a
#通过这种方式就完成了两个变量值的交换

变量的类型

数字型(number)

  • 整数(int)
    • 二进制
    • 八进制
    • 十进制
    • 十六进制
  • 浮点小数(float)
    • 一般采用的是科学计数法,以e表示10,e上面为次方
  • 布尔值(boolean)
    • True,可用数字表示,除了0以外的数字都为True
    • False,可用数字0表示
  • 复数(complex)
    • 一般不会涉及到

字符串型(string)

  • 用单引号,双引号或者是三引号进行表示
a = 'Jack'
b = "Jack"
c = "Bob,'haha',Jack"
print(c)

#其结果为Bob,'haha',Jack
  • 格式化的两种方式
    • 利用%

      %s : 格式化字符串

      %d : 格式化整数

n = '我是XX'
print('你好,%s'%n)
  • 利用format()函数进行格式化
n = '我是XX'
print('你好,{0}'.format(n))
#还可以这么使用
age = 18
name = 'Bob'
print('我是{0},我今年{1}岁,你也{1}岁'.format(name,age))
  • 字符串的转义
    • 用"\"表示

    • 最简单的一种方法就是字符串前面加上"r",表示的是原始字符:字符串中的所有转义字符都不进行转义

    • 使用转义字符

      \\ : 表示\

      \n : 表示换行

      \t : 表示制表符

      \r : 表示回车

   ```
   print(r'\\')
   #打印出的为\\
   
   print('\\\\')
   #这样也可以打印出\\
   ```
  • 常用函数

    capitalize() : 将字符串的第一个字母大写,其他小写

    strip() : 去掉字符串两边的空格

    replace() : 替换字符串

    lower() : 全部小写

    upper() : 全部大写

    encode() : 编码

    decode() : 解码

    s1 = ' abc '
    
    s2 = 'aBc'
    
    s1.strip() # > abc
            
    s2.capitalize() # > Abc
    
    s2.lower() # > abc
    
    s2.upper() # > ABC
    
    s2.encode('utf-8') # > b'aBc'
    
    b'aBc'.decode('utf-8') # > aBc
    
    s2.replace('B','*') # > a*c
    
    

列表(list)

  • 创建列表的方式

    list = [1,2,3,4,5]
    
  • 列表属于一个可迭代对象,可用于迭代

    • 使用for对列表中的元素进行迭代

      for item in list:
         print(item)
      #此时会输出
      # > 1
      # > 2
      # > 3
      # > 4
      # > 5
      
      #item表示的是列表中的元素
      
    • 使用下角标进行操作,但是一般不会这么使用

      for i in range(len(list)):
        print(list[i])
      
      #len()函数表示测量某个迭代序列的长度,返回int
      #range()函数用于组成从0数字到给定值的序列
      
  • 可使用成员判断符 in

    print(1 in list)
    
    # > True
    
  • 可使用 * ,表示一个列表重复N次,并组成一个新的列表

    print(list * 5)
    
    # > [1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3, 4, 5]
    
  • 可使用 + ,表示两个列表相加,组成一个新列表

    print(list + [6.7,8])
    
    # > [1, 2, 3, 4, 5, 6,7,8]
    
  • 常用函数

    del: 删除列表

    len(): 长度

    max()、min():最大、最小值

    list() : 将可迭代的序列转化为list

    append() : 在列表末尾添加元素

    index() : 返回列表中某个元素的引索值

    remove() : 移除列表中的某个元素

    pop() :移除列表中引索值为XX的元素

    insert() : 向列表中引索值为XX的位置添加一个元素

    reverse() : 反向列表元素

    copy() : 复制一份列表到新的列表中

    list = [1,2,3,4,5]
    
    del list # > 删除list
    
    len(list) # > 5
    
    max(list) # > 5
    
    min(list) # > 1
    
    list.append(6) # > [1,2,3,4,5,6]
    
    list.index(1)  # > 0
    
    list.remove(6) # > [1,2,3,4,5]
    
    list.pop(0) # > [2, 3, 4, 5]
    
    list.insert(0,7) # > [7, 1, 2, 3, 4, 5]
    
    list.reverse() # > [5, 4, 3, 2, 1]
    
    l = list.copy # > [1,2,3,4,5]
    
  • 列表生成式

    • 可操作列表中的单个元素,并迭代整个列表组成一个新列表

    • 使用方法:list2 = [i for i in list1 if ....]

      list1 = [1,2,3,4,5,6,7,8,9]
      
      #下面我们使用列表生成式挑选偶数组成一个新的列表
      
      list2 = [i for i in list1 if i % 2 == 0] 
      
      # > [2, 4, 6, 8]
      
      # i for i in list1 表示遍历list1列表中的元素
      
      # if i % 2 == 0 表示挑选出所有能整除2的元素
      
    • 列表生成式的嵌套

      list1 = [1,2]
      list2 = [3,4]
      
      list3 = [x+y for x in list1 for y in list2] # > [4, 5, 5, 6]
      
      #可以看出,新的列表的结果是list1中的每个元素加上list2中每个元素的结果
      #组成是[list1[0]+list2[0],list1[0]+list2[1],list1[1]+list2[0],list1[1]+list2[1]]
      

元组(tuple)

  • 创建元组的方式

    tuple = (1,2,3)
    
  • 元组具有list中的部分属性,但只可以进行读取操作,不能进行写入操作

  • 可以将一个list列表转化为一个元组tuple

    t = tuple(list)
    

字典(dict)

  • 字典的创建方式

    dict = {'jack':24,'bob':18}
    #或者是
    d = dict(jack = 24,bob = 18)
    
  • 字典是一组键——值对应序列,它是一个无序的对象。

  • 遍历字典

    • 使用item()方法遍历

      for key,value in dict.items():
        print(key,value)
      
      #jack 24
      #bob 18
      
    • 直接遍历

      for i in dict:
        print(i,dict[i])
      
      #jack 24
      #bob 18
      
  • 注意,一旦使用dict[key]查找value值,当key的值不存在时,就会报错,推荐使用get()方法

  • 常用方法

    get() : 查找对应键的值,当键不存在时不会报错

    keys() : 将字典所有key组成一个可迭代的序列

    values() : 将字典所有value组成一个可迭代的序列

    items() : 将字典的每组key和value组成一个元组,并将所有组组成一个序列,序列的类型是dict_items

    fromkeys() : 将一个列表作为key值传入,并指定默认的value值

    dict.get('jack') # > 24
    
    dict.keys() # > dict_keys(['jack', 'bob'])
    
    dict.values() # > dict_values([24, 18])
    
    dict.items() # > dict_items([('jack', 24), ('bob', 18)])
    
    list = ['jack', 'bob']
    
    d = dict.fromkeys(list , 18)  # > {'jack': 18, 'bob': 18}
    

集合(set)

  • 定义方式

    list = [1,2,3,4,4]
    s = set(list)
    # > {1, 2, 3, 4}
    
    #或者是
    s = {1,2,3,4}
    
  • 注意:不要与dict字典搞混淆,字典也是{}定义的,但是却是由key-value组成的一对

  • set集合中数据也是无序的,且不包含重复元素,必须是可哈希数据

  • 常用函数

    add() : 添加单个数据

    update : 添加可迭代数据

    remove() : 移除集合中某个值,一旦值不存在,便会报错

    discard() : 移除集合中某个值,就算值不存在,也不会报错

    clear() : 清空集合数据

    intersection() : 求交集

    difference() : 求差集

    union() : 求并集

    issubset() : 检查一个集合是否是另一个集合的子集

    issuperset() : 检查一个集合是否是另一集合的超集

    s = {1,2,3,4,5}
    
    s1 = {4,5,6,7,8}   
    
    list = [5,6,7,8,9]
    
    s.add(6) # > {1,2,3,4,5,6}
    
    s.update() # > {1, 2, 3, 4, 5, 6, 7, 8, 9}
    
    s.remove(5) # > {1,2,3,4}
    
    s.discard(5) # > {1,2,3,4}
    
    s.clear()
    
    s.intersection(s1) # > {4, 5}
    
    s.difference(s1) # > {1, 2, 3}
    
    s.union(s1) # > {1, 2, 3, 4, 5, 6, 7, 8}
    
    

运算符

算数运算符

  • 加法运算符 +

    • 在数字中表示两个数字相加

      print(2+5) # > 7
      
    • 在字符串中表示两个字符串相连

      print('hello'+'world') # > helloworld
      
    • 在列表中表示两个列表首尾相连

      l1 = [1,2,3]
      l2 = [3,4,5]
      l = l1 + l2
      # > [1,2,3,3,4,5]
      
  • 减法运算符 -

     print(5 - 3) # > 2
    
  • 乘法运算符 *

    • 当对象为数字型时,执行乘法运算

      print(5 * 2) # > 10
      
    • 当对象为非数字型时,打印N次,并组成一个新对象

      print('a'*3) # > aaa
      print([1,2,3] * 2) # > [1, 2, 3, 1, 2, 3]
      
  • 除法运算符 /

    print(6 / 2) # > 3
    
  • 取余运算符 %

    • 计算两个数字的余数
      print(3 % 2) # > 1
      
  • 整除运算符 //

    • 计算两个数整除的数字,不会保留小数点

      print( 3 // 2 ) # > 1
      
  • 幂运算符 **

    • 次方运算

      print(2 ** 3) # > 8
      

比较运算符

  • 注意:返回的都为布尔值类型

  • 等于 ==

    print(1 == 1) # > True
    
  • 不等于 !=

    print(1 != 1) # > False
    
  • 小于 <

    print(1 < 1) # > False
    
  • 小于等于 <=

    print(1 <= 1) # > True
    
  • 大于 >

    print(1 > 1) # > False
    
  • 大于等于 >=

    print(1 >= 1) # > True
    

赋值运算符

  • 等于 =

逻辑运算符

  • and 与
    print(True and False) # > False
    
  • or 或
    print(True and False) # > True
    
  • not 非
    s = 'abc'
    print('d' not in s)
    # > True
    

成员运算符

  • 注意:成员运算符需要判断的对象是一个可以迭代的类型

  • in 在里面

    print('a' in 'abc') # > True
    
  • not in 不在里面

    print('a' not in 'abc')
    # > False
    
    

身份运算符

  • 注意,身份运算符是比较两个对象内存地址是否相同
  • is 是
  • not is 不是

运算符的优先级

  • 永远记住,括号里面的最先开始运算,具有最高优先级

程序结构

顺序结构

  • 按照代码编写的方式逐行进行

分支、判断结构

  • if...else语句
    a = 10
    b = 5
    if a > b:
        print(a,'>',b)
    else:
        print(a,'<',b)
    # > 10 > 5 
    
  • if..elif..else语句
    • 注意,以上代码并不严谨,缺少相等的时候
    a = 10
    b = 10
    if a > b:
        print(a,'>',b)
    elif:
        print(a,'=',b)
    else:
        print(a,'<',b)
    # > 10 = 10
    

循环结构

  • 使用for进行循环(知道具体循环次数的时候)
    s = 'abcdefg'
    for i in s:
        print(i)
    
    # > a
    # > b
    # > c
    # > d
    # > e
    # > f
    # > g
    
  • 使用while进行循环(不知道具体循环次数)
    • while
      n = 0
      while n < 10:
        print(n)
        n = n + 1
      # > 0
      # > 1
      # > 2
      # > 3
      # > 4
      # > 5
      # > 6
      # > 7
      # > 8
      # > 9
      
  • break 跳出整个循环
    • 当需要跳出整个循环的时候可以用break
      n = 1
      while n < 10:
      print(n)
      n = n + 1 
      if n == 3:
        break
      
      # > 1
      # > 2
      
  • continue 结束本次循环
    • 当某些条件时,跳过本次循环
      n = 0
      while n < 10:
          n = n + 1
          if n == 3:
            continue
          print(n)
      # > 1
      # > 2
      # > 4
      # > 5
      # > 6
      # > 7
      # > 8
      # > 9
      # > 10
      

函数

定义函数

  • 使用def进行定义,后面跟着方法名和()

    def func():
        print('hello')
    

调用,传入函数

  • 调用函数时,用函数名加括号
    #调用函数
    
    func()
    
    # > hello
    
  • 传参时,只用函数名
    #如tk框架中绑定按钮事件
    
    button = Button(text = '按钮' ,command = func)
    
    #如使用help函数
    
    print(help(print))
    
    
    # > Help on built-in function print in module builtins:
    
    # > print(...)
    # > print(value, ..., sep=' ', end='\n', file=sys.stdout, flush=False)
    
    # > Prints the values to a stream, or to sys.stdout by default.
    # > Optional keyword arguments:
    # > file:  a file-like object (stream); defaults to the current sys.stdout.
    # > sep:   string inserted between values, default a space.
    # > end:   string appended after the last value, default a newline.
    # > flush: whether to forcibly flush the stream.
    
    # > None
    

函数的参数

  • 位置参数

    • 根据定义函数时参数的位置,自动进行参数传递,此时,必须严格按照参数的顺序进行传参
      def func(name,age):
        print('my name is {0},I am {1} years old.'.format(name,age))
      
      func('jack',24)
      # > my name is jack,I am 24 years old.
      # 此时传递参数的顺序必须是name,age,否则参数就会传递不正确,严重时,会报错
      
  • 关键字参数

    • 根据指定的关键字进行传递参数,传参的顺序是否正确,不会影响函数的参数传递
      
      def func(name,age):
        print('my name is {0},I am {1} years old.'.format(name,age))
      
      func(age = 24,name = 'jack')
      # > my name is jack,I am 24 years old.
      
  • 默认参数

    • 在定义函数时,就传递默认值,被传递默认值的参数如果不进行再次传参,就会使用默认值
      def func(name,age = 0):
        print('my name is {0},I am {1} years old.'.format(name,age))
      
      func('jack')
      # > my name is jack,I am 0 years old.
      func('jacl',24)
      # > my name is jack,I am 24 years old.
      
  • 可变参数

  • 解包:是将可迭代的对象变为单个元素

    • 明确一个概念,可迭代的对象可以进行解包操作,而且可以不使用解包的操作符号

      list = [1,2,3]
      a , b , c = list
      # > 1
      # > 2
      # > 3
      #可以看出,对于一个知道长度的可迭代对象我们可以用赋值的方式将其解包
      
    • 使用*对可迭代的对象进行解包

      #但如果对于一个我们不知道长度的列表如何进行解包呢?
      #这就是*的用法
      list = [1,2,3,4]
      print(*list)
      # > 1 2 3 4 
      #可以看出,此时列表list已经被用*解包为单个元素
      
    • 使用**对字典进行解包

      #首先我们使用*对dict解包,看能得到什么?
      dict = {'jack':24 , 'bob':18}
      print(*dict) 
      # > jack bob
      # 结论:使用*进行解包时,我们只是得到了字典的key值
      # 那我们试试**吧
      print(**dict)
      # > TypeError: 'jack' is an invalid keyword argument for this function
      # 百度后得知print()函数不支持打印**对象
      # 那就试试打印出key对应的值
      print('{jack},{bob}'.format(**dict))
      # > 24,18
      #参考网上资料,**dict解包之后应该是这样的
      **dict = Jack = 24 , bob = 18 
      #现在你想到了什么?
      
  • 压包,将几个不同的可迭代对象的单个元素按顺序用元组的形式结合成一个列表

    • 使用zip()函数进行压包
      list1 = [1,2,3]
      list2 = ['a','b','c']
      list3 = ['one','two','three']
      for i in zip(list1,list2,list3):
          print(i)
      # > (1, 'a', 'one')
      # > (2, 'b', 'two')
      # > (3, 'c', 'three')
      # 可以看出这里对每个列表的第一元素、第二个元素、第三个元素,都进行了组合,使之成为了了一个新的元组
      a = zip(list1 ,list2 ,list3 )
      print(list(a)) 
      # > [(1, 'a', 'one'), (2, 'b', 'two'), (3, 'c', 'three')]
      #可以看出,将三个序列压包后,每个序列中下标相同的元素,重新组成了一个元组,而这些元组都构成了一个列表
      
  • 可变参数函数

    • 了解了*解包之后我们就可以明白函数的可变参数是怎么回事了
    • 定义一个可变参数的函数
       #注意:这里args可以是任何变量,但为了程序的可读性,我们需要遵循传统,写上args,让其他人一看就知道这里应该是可变参数
      
      def func(*args):              
        print(args)
      func('jack','hello',28,41) # >('jack', 'hello', 28, 41)
      
      #可以看出我们不论写多少参数,函数就会接收多少个参数,看到这里你也许会说,这并没有什么用啊?
      #是的,但是你想想如果我们需要将一个列表的元素都传入函数中去的时候要怎么办?
      #总不能用list[0],list[1]...一个一个进行传参吧?
      
      list = ['name','age','sex','birth']
      func(*list) # > ('name', 'age', 'sex', 'birth')
      
      #可以看出,*list确实是将单个元素传入了函数中
      
    • 可变参数的关键字函数
       #刚才对字典解包后得出**dict = Jack = 24 , bob = 18
       #仔细一想,如果把**dict当成参数,这不就是一个关键字参数嘛 
      
    • 定义一个可变参数的关键字函数
      def func(**kwargs):
         print('我的名字是{0},我今年{1},我是{2},我的生日是{3}'.format(kwargs.get('name'),kwargs.get('age'),kwargs.get('sex'),kwargs.get('birth')))
      
      func(name = 'jack',age = 24 ,sex = 'male',birth = '1980-10-10')
      
      # > 我的名字是jack,我今年24,我是male,我的生日是1980-10-10
      
      #定义一个字典传入函数中
      
      dict = {'name':'jack','age':24}
      func(**dict)
      
      # > 我的名字是jack,我今年24,我是None,我的生日是None        
      # 可以看出,即使有些参数是不填的函数也可以正常运行,这是不可变参数函数所不能达到的效果
      
  • 参数混用

    • 需要遵照函数(位置函数,关键字函数,可变参数函数,可变参关键字函数)的顺序进行
      def func(name , age = 24 , *args , **kwargs):
        pass
      

函数的返回值

  • 所有函数都有返回值,表示函数调用已经结束
  • 定义函数返回值,使用return
    def func(a):
        return a+10
    
    b = func(1) # > 11
    
  • 即使没有定义return,解释器也会自动加上 return None

函数文档

  • 当使用help()函数时,显示的帮助文档

  • 也可以使用doc调用文档

  • 当你在不知道怎么使用函数或者是类时,都可以调用帮助文档,里面会给你详细说明各种用法

  • 自定义函数帮助文档,在开头加上''' '''即可,如果使用的ide,会自动给你加上其他参数

    def func(x,y):
    '''
    这个函数什么也不做
    :param x: 传参x
    :param y: 传参y
    :return: None
    '''
        pass
    
    print(help(func))
    
    # > Help on function func in module __main__:
    
    # > func(x, y)
    # > 这个函数什么也不做
    # > :param x: 传参x
    # > :param y: 传参y
    # > :return: None
    
    # > None
    
    print(func.__doc__)
    
    # > 这个函数什么也不做
    # > :param x: 传参x
    # > :param y: 传参y
    # > :return: None
    

高阶函数

  • 指该函数的参数中传入另一个函数

  • map()函数,映射操作,将传入的函数作用于可迭代对象的每个元素上,返回一个可以迭代的map对象

    list = [1,2,3,4]
    
    def func(x):
        return x+1
    
    a = map(func,list)
    
    for i in a:
        print(i)
    
    # > 2
    # > 3
    # > 4
    # > 5
    
  • reduece()函数,归并操作,将列表的两个值经过函数计算后与列表中的第三个值再次进行相同计算,直到列表元素完成循环。

    • 简单的用公式表达就是这样,f(f(f(x1,x2),x3),x4)....

      #计算列表中所有元素的乘积
      
      from functools import reduce
      
      def func(x,y):
          return x*y
      
      list = [2,3,4]
      
      print(reduce(func,list))        
          
      # > 24
      
  • filter()函数,过滤操作,用于筛选列表中的元素

    #筛选出列表中的偶数
    
    def func(x):
       if x % 2 == 0:
           return True
       else:
           return False
    
    list = [2,3,4]
    
    for i in filter(func,list):
       print(i)
    
    # > 2
    # > 4
    
  • sorted()函数,排序操作,可以使用自定义的函数进行排序

    • 默认排序

      list = [5,4,3,9,8]
      
      l = sorted(list)
      
      print(l)
      
      # > [3, 4, 5, 8, 9]
      
    • 关键字排序

      • 关键字, key = 函数名

      • 反向排序,传入reverse = True

      #将列表的元素按绝对值大小进行排序并倒序      
      
      list = [5,-11,6,9,1]
      
      l = sorted(list, key=abs,reverse = True)
      
      print(l)
      
      # > [-11, 9, 6, 5, 1]
      
  • zip()函数,压缩操作,将多个传入的列表每个下标相同的元素组成一个新的元组,再将这些元组组成一个可迭代的zip对象,返回zip对象

    • 注意 : 如果传入列表的长度不一致,那么zip操作只会取最短的那个列表的长度,组成zip对象
    l1 = [1,2,3,4]
    
    l2 = ['a','b','c','d']
    
    l3 = ['one','two','three','four']
    
    l = zip(l1,l2,l3)
    
    for i in l:
       print(i)
    
    # > (1, 'a', 'one')
    # > (2, 'b', 'two')
    # > (3, 'c', 'three')
    # > (4, 'd', 'four')
    
    # 如果用列表生成式会怎么样?
    list = [i for i in l]
    print(list)
    
    # > [(1, 'a', 'one'), (2, 'b', 'two'), (3, 'c', 'three'), (4, 'd', 'four')]
    
    #长度不一致时
    
    l4 = ['jack','bob']
    
    l5 = zip(l1,l4)
    
    list = [i for i in l5]
    print(list)
    
    # > [(1, 'jack'), (2, 'bob')]
    
  • enumerate()函数,将列表中每个引索值和其对应的元素组成一个新的元组,再将这些元组组成一个enumerate对象,返回enumerate对象

    l = ['a','b','c','d']
    em = enumerate(l)
    list = [i for i in em]
    print(list)
    
    # > [(0, 'a'), (1, 'b'), (2, 'c'), (3, 'd')]
    

返回函数

  • 将函数作为返回结果

    def hello():
       
       def say():
           print('hello')
       return say
    
    f = hello()
    print(f)
    # > <function hello.<locals>.say at 0x00000233A6EF8A60>
    
    f()
    # > hello
    
  • 闭包,当一个函数定义在另一个函数的内部时,使用外部函数的变量或者参数,当这个内部函数被当做返回值时,外部函数的所有参数和变量都保存在返回的函数中时,就叫做闭包

    def hello(n):
       def say():
           print(n)
       return say 
    
    f = hello('hello')
    f()
    # > hello
    
  • 注意:在返回函数中不能引用循环变量,如果确实需要引用循环变量时,需要在内部函数外面再包裹一层函数,该函数以参数的形式接受循环变量,内部函数就可以安全使用循环变量

    def func():
       list = []
       for i in range(1,4):
           def funb():
               return   i * i
           list.append(funb)
       return list
    
    list = func()
    print(list)
    
    # > [<function func.<locals>.funb at 0x000001F1CE5A8A60>, <function func.<locals>.funb at 0x000001F1CE5A8AE8>, <function func.<locals>.funb at 0x000001F1CE5A8B70>]
    
    for i in list:
       print(i())
    
    # > 9
    # > 9
    # > 9
    
    #此时可以看出,循环变量并没有正确的引入返回函数中去.
    
    #正确的写法应该是使用funb将funa包裹,然后通过funb 返回fana 的返回值
    
    def func():
       def funb(i):
           def funa():
               return i * i
            return funa
       list = []
       for i in range(1,4):
           list.append(funb(i))
        return list
     
    list = func()
    print(list)
    
    # > [<function func.<locals>.funb.<locals>.funa at 0x000001E2944C8AE8>, <function func.<locals>.funb.<locals>.funa at 0x000001E2944C8B70>, <function func.<locals>.funb.<locals>.funa at 0x000001E2944C8BF8>]
    
    for i in list:
       print(i())
    
    # > 1
    # > 4
    # > 9
    

匿名函数

  • lambda,用于自定义一个函数,此函数没有名称,比如可以向高级函数中传递这样一个匿名函数,而不必去定义它
#将每个元素的前面加上hello
list = ['jack','bob','luce','alice']
l = map(lambda x:'hello'+x,list)
for i in l:
  print(i)

# > hellojack
# > hellobob
# > helloluce
# > helloalice

偏函数

  • 偏函数,将一个函数中的参数固定住,然后创建一个新的函数,这就是偏函数

  • 定义偏函数:偏函数名 = functools.partial(原函数,固定值)

  #假设,我们有'jack','bob','luce','alice',这4个人,他们的年龄都是24岁
  # 此时有一个函数say,是为了介绍自己,需要传入name和age两个参数
  # 希望在不改变say函数的情况下,简化操作,此时我们就可以使用偏函数了
  
  import functools
  
  list = ['jack','bob','luce','alice']
  
  def say(name,age):
      print('My name is {0},age is {1}'.format(name,age))
  
  say24 = functools.partial(say,age = 24)
  
  for person in list:
      say24(person)
  
  # > My name is jack,age is 24
  # > My name is bob,age is 24
  # > My name is luce,age is 24
  # > My name is alice,age is 24
  

递归函数

  • 函数内部自己调用自己的情况叫做递归函数
  #遍历一个盘符下的所有文件(包括子文件夹、文件)
  
  import os

  def getFile(path):
      try:
          filelist = os.listdir(path) #得到该文件夹下的所有文件
          for file in filelist:
              file = os.path.join(path,file) #将文件名和路径结合起来
              if os.path.isdir(file):
                  getFile(file)  #在这里如果判断一个文件是文件夹,那么就会再次调用自己
              else:
                  print(file)
      except:
          print('出错,跳过')
  
  getFile(r'd:/')

装饰器

  • 对原函数进行扩展功能但是不改变原函数

  • 使用@+装饰器名进行装饰,具有固定格式

  • 使用@装饰时,执行函数时,相当于执行行了装饰器函数(原函数)

    #首先我们思考一下,接收一个无参数的函数,并且扩展功能后返回这个函数
    import time
    
    
    def printTime(f):
        print('Time: ', time.strftime('%Y-%m-%d %H:%M:%S', time.localtime()))
        return f
    
    
    def hello():
        print('hello')
    
    
    print(printTime(hello)()) 
    
    # > Time:  2018-08-09 08:45:25
    # > hello
    # > None  
    
    # 这时,我们可以看到定义了一个printTime函数,并且执行打印时间后返回原函数,这可以说是一个最简单的装饰器了。
    # 为什么会返回一个None?我认为是hello函数后系统自己添加了一个return None后导致的,我们将hello函数手动添加return 5后试试,结果返回的是5
    
    
    
    # 如果我们需要接收一个带参数的函数呢?
    import time
    
    def printTime(f):
       
       def wrapper(*args,**kwargs):
           print('Time: ',time.strftime('%Y-%m-%d %H:%M:%S',time.localtime()))
           return f(*args,**kwargs)
        return wrapper
    
    @printTime 
    def hello():
       print('hello')
    
    hello()
    
    # > Time:  2018-08-08 20:21:26
    # > hello
    
    # printTime函数接收一个函数,返回一个wrapper函数
    # wrapper函数接收可变参数,然后将这些参数赋值给printTime接收到的函数,最后再返回这个函数。 
    

高级特性

切片 slice

  • 切片操作是对一个可迭代的对象进行切割,并组成一个新的迭代对象

  • 方式:切片对象[开始引索:结束引索:步长]

  • 注意:切片操作中包含开始引索,不包含结束引索,如果开始引索是0,可以省略,如果步长是1,也可以省略

  • 开始引索也可以是负数,当为负数时,则从最后开始计算

  list = [1,2,3,4,5,6,7,8,9,10]
  
  #挑选出所有奇数
  print(list[:9:2])
  # > [1, 3, 5, 7, 9]
  
  #取前4个数
  print(list[:4])
  # > [1, 2, 3, 4]
  
  #取后2个数
  print(list[-2:])
  # > [9, 10]
  
  #取3~7
  print(list[2:7])
  # > [3, 4, 5, 6, 7]

迭代

  • 使用for进行迭代

列表生成式

  • 见list列表中

generator生成器

  • 存疑

思想

  • OO面向对象
  • OOA面向对象的分析
  • OOD面向对象的设计
  • OOI面向对象的实现
  • OOP面向对象的编程

类和对象

    • 类是一个抽象的名词,是共性事物

    • 类有成员属性,是表明事物的特征,可以被读取,写入,删除

    • 类有成员函数,表明事物的功能或者动作

  • 对象

    • 对象是一个具体的名词,是一个具体事物

    • 对象是类的实例化

  • 说明:我们都是人,这就是类;但我们有男人,也有女人,每个人高矮胖瘦都不一样,我们每个人就是对象,是类的实例化

  • 类和对象的对成员访问顺序

    • 首先访问对象中的成员

    • 对象没有成员的会访问类中的成员

    • 类中没有成员会访问父类中的成员

    • 依次向上递推

定义类

  • class 类名(继承类):定义

  • 类中只允许出现成员属性和成员方法

  • 定义类之后,会在内存中专门划分一块地方储存类和类的成员;当实例化类之后,继承的成员会指向内存中的这块地方

  class Person(object):
      
      #定义属性
      name = 'NoName'
      age = 0
     
      #定义函数
      def say(self):
          print('say')
      def eat(self):
          print('eat')
      def sleep():
          print('sleep')
  • self

    • self不是一个关键字,可以是任何一个变量代替,表示的是当前对象本身,当调用方法时,对象会将自己传入到方法中去

    • 有self的函数称为非绑定方法,可以通过对象进行访问

      • 使用实例化对象访问

        p = Person()
        p.say()
        # > say
        
      • 使用类().方法访问

        Person().say()
        # > say
        
      • 使用类.方法(类())访问

        Person.say(Person())
        # > say
        
      • 实质是都是需要实例化一个对象,并传入非绑定方法中去

    • 无self的函数称为绑定方法,只能通过类进行访问

      p = Person()
      p.sleep
      # > TypeError: sleep() takes 0 positional arguments but 1 was given
      #可以看出,绑定方法不可以用对象进行访问,因为方法中没有定义传入对象本身的参数,所以出现了sleep函数不需要参数,但传入了一个参数,传入的参数就是对象本身
      Person.sleep()
      # > sleep
      
      • 如果一定要使用对象访问,可用对象.__class__.方法访问
        p.__class__.sleep()
        # > sleep
        
  • super

    • super也不是一个关键字,但他是一个类

    • 作用是获取MRO列表中的第一个类

    • 可以使用类.__mro__获取类的MRO列表

    • 在单继承中,表示父类

    • 在多继承中,代表了了MRO列表中的第一个元素代表的类

面向对象

  • 封装,对类的成员访问进行限制

    • public 公开

      • 公开成员,都可以进行访问
    • protected 受保护

      • 受保护成员,只能在类、子类、对象中进行访问

      • 定义:使用_成员进行定义

      class Person():
      
         _name = 'NoName'
         _age = 24
      
      class Student(Person):
          pass
      
      s = Student()
      print(s._name,s._age)
      
      # > NoName 24
      #可以看出,子类和子类的对象访问都是没有问题的
      
    • private 私有

      • 私有成员,只能在当前类中进行访问

      • 注意:子类不继承私有成员

      • 定义:使用__成员进行定义

      class Person():
      
        __name = 'NoName'
        __age = 24
      
      class Student(Person):
        pass
      
      p = Person()
      print(p.__name, p.__age)
      # > AttributeError: 'Person' object has no attribute '__name'
      s = Student()
      print(s.__name, s.__age)
      # > AttributeError: 'Student' object has no attribute '__name'
      #可以看出,子类并没有继承父类中的私有属性,而且对象也不允许使用私有成员
      
  • 继承

    • 类成员扩充,当子类继承了父类时,就拥有了父类除私有成员以外的所有其他成员,这相当于将子类成员指向父类成员的地址,同时,还可以继续添加相应的属性和函数

      class Person():
        
        def talk(self):
            print('talk')
      
      class Student(Person):
        
        def study(self):
            print('study')
      s = Student()
      s.talk()
      
      # > talk
      
      s.study()
        
      # > study
      
    • 子类扩充父类函数方法

      • 使用self,其中self指的是子类本身

        class Person():
          
          def talk(self):
              print('talk')
        
        class Student(Person):
          def talk(self):
             Person.talk(self) 
             print('扩充函数')
        
        s = Student()
        s.talk()
        
        # > talk
        # > 扩充函数
        
      • 使用super,指的是父类

        class Person():
          
          def talk(self):
              print('talk')
        
        class Student(Person):
          def talk(self):
             super().talk() 
             print('扩充函数')
        
        s = Student()
        s.talk()
        
        # > talk
        # > 扩充函数
        
    • MRO钻石继承\菱形继承(继承顺序)

      • 子类永远在父类前面

      • 根据括号内继承的顺序存放

      • 继承的多个类中,有共同的父类,子类只会继承括号中第一个父类的父类

    • 单继承

      • 单继承隐患少,但不利于扩展功能

      • 子类如果没有构造函数,则会查找父类的构造函数,如果都没有,那么就一直向上查找。按照MRO顺序

      class Person():
      
      def talk(self):
          print('talk')
      
      
      class Student(Person):
      
          def talk(self):
              super().talk()
              print('asdasd')
      
      print(Student.mro())
      
      # > [<class '__main__.Student'>, <class '__main__.Person'>, <class 'object'>]
      
      #可以看出,Student类的MRO顺序为Student、Person、object
      
    • 多继承

      • 扩展类的功能方便但隐患多

      • 继承方式按照MRO顺序

      class Animal():
        def sleep(self):
            print('sleep')
      
      class Person():
          def talk(self):
              print('talk')
      
      class Student(Person,Animal):
          pass
      
      s = Student()
      s.talk()
      s.sleep()
      
      # > talk
      # > sleep 
      
      print(Student.mro())
      
      #[<class '__main__.Student'>, <class '__main__.Person'>, <class '__main__.Animal'>, <class 'object'>]
      
      #可以看出继承顺序遵照MRO顺序
      
    • Mixin设计模式

      • 我认为主要是为了避免继承关系的混乱,为了让代码可以进行更改的维护,已经实现类中方便的功能增减

      • Mixin类中必须定义单一功能

      • 子类不继承Mixin类,只是缺少某项功能,但不影响子类的正常使用

      • Mxin功能不依赖子类进行实现

       class TalkMixin():
          def talk(self):
              print('talk')
       
       class EatMixin():
           def eat(self):
               print('eat')
       
       class SleepMixin():
           def sleep(self):
               print('sleep')
       
       class Person(TalkMixin,EatMixin,SleepMixin):
           pass
       
       p  = Person()
       p.talk()
       p.eat()
       p.sleep()
      
       # >talk
       # >eat
       # >sleep
      
  • 多态

    • 同一对象在不同状态的不同功能

    • 注意:在Python中没有对应的语法

类的实例化

  • 方法:变量 = 类名()

访问类

  • 检查类中的所有成员,使用类名.__dict__

  • 访问类的成员属性,使用类名.属性

  • 访问类的成员函数,使用类名.函数名()

类的函数

  • 魔法函数(不需要自己进行调用,但满足某些特定条件时,会自动调用)

    • __init__ 构造方法

      • 在实例化类的时候可以向其中传递参数

      • 在将类实例化对象时,会自动调用

      • 此函数也可以进行扩充

      class Person():
      
        def __init__(self,name,age):
            self.name = name
            self.age = age
        
        def say(self):
            print('My name is {0},I am {1} years old.'.format(self.name,self.age))
      
      p = Person('jack',24)
      p.say()
      # > My name is jack,I am 24 years old.
      p.name = 'bob'
      p.age = 18
      p.say()
      # > My name is bob,I am 18 years old.
      
    • __call__ 方法

      • 将对象当做函数执行时调用
      class Person():
      
        def __init__(self,name,age):
            self.name = name
            self.age = age
      
        def __call__(self, *args, **kwargs):
            print('为什么要执行我?')
      
      p = Person('jack',24)
      p()
      
      # > 为什么要执行我?
      
    • __str__ 方法

      • 将对象打印时,输出的方法
      class Person():
        def __str__(self):
            return '打印了一个Person类的实例'
      
      p = Person()
      print(p)
      # > 打印了一个Person类的实例
      
  • 实例方法

    • 也就是非绑定方法,只有当类实例化后,才可以使用的方法

    • 详情参照self-非绑定方法

  • 静态方法

    • 采用@staticmethod装饰

    • 里面没有self参数

    • 注意,这里和绑定方法有区别

      • 绑定方法,只能通过类名.方法名()调用,如果采用对象调用就会报错

      • 静态方法,既可以通过类来调用,也可以通过对象调用

    class Person():
    
    @staticmethod
    def say():
        print('say')
    
    p = Person()
    
    Person.say()
    # > say
    p.say()
    # > say
    
  • 类方法

    • 采用@classmethod装饰

    • 不需要实例化就可以使用

    • 方法中传入的是cls,也就是类对象

    class Person():
    
        @classmethod
        def say(cls):
            print('say')
    
    p = Person()
    
    Person.say()
    p.say() 
    
  • 总结:无论是实例方法、静态方法、类方法都可以使用对象进行调用,但是实例方法必须实例化后才可以使用;静态方法、类方法可以直接用类名进行调用

  • 注意:静态方法不是绑定方法

  • property

    • 主要是将对函数的访问装饰起来变为对属性的访问
    #例如,我们需要定义一个Person类,但是需要对Person类中传入的几个属性进行检查,那么这时候property就派上用场了
    
    class Person():
       _name = 'Noname'
       _age = 0
        
    #此时,我们想对age的值进行检查,不允许小于0
    #我们就可以定义一个property的装饰器,将函数伪装为变量进行访问
    
    • 调用Property装饰器

      @property
      def age(self):
         print('调用get')
         return self._name
      
      @age.setter
      def age(self,value):
          print('调用set')
          if value > 0:
              self._age = value
          else:
              print('不允许负值')
      
      @age.deleter
      def age(self):
          print('调用del')
          del (self._age) 
      
      p = Person()
      p.age = -15
      
      # > 调用get
      # > 不允许负值
      
    • 调用property方法属性 = property( fget, fset, fdel, doc)

       def get_age(self):
          print('调用get_age')
          return self._age
      
      def set_age(self,value):
          print('调用set_age')
          if value > 0:
              self._age = value
          else:
              print('不允许是负值')
      
      def del_age(self):
          print('调用del_age')
          del self._age
      
      age = property(get_age,set_age,del_age)
      
      p = Person()
      p.age = -15
      
      # > 调用get
      # > 不允许是负值
      
  • 类的描述符

    • 只要包含了__get____set____del__三个方法中的一个就是描述符

    • 描述符的作用,对类的属性进行限制操作

    • __get__(self,instance,owner)方法和__set__(self, instance, value)方法

      class Dscrip():
      
        def __init__(self,property_name):
            self.property_name = property_name
      
        def __get__(self, instance, owner):
            print('调用了get方法')
            print('self:%s'%self)
            print('*'*20)
            print('instance:%s'%instance)
            print('*' * 20)
            print('owner:%s'%owner)
            print('-'*20)
      
        def __set__(self, instance, value):
            print('调用了set方法')
            print('self:%s' % self)
            print('*' * 20)
            print('instance:%s' % instance)
            print('*' * 20)
            print('value:%s'%value)
            print('-' * 20)
      
      class Person():
      
          name = 'Noname'
          age = Dscrip('age')
      
          def __init__(self,name,age,sorce):
              self.name = name
              self.age = age
              self.sorce =sorce
      
      p = Person('jack',24,60)
      p.age
      print('p:%s'%p) 
      
      # > 调用了set方法
      # > self:<__main__.Dscrip object at 0x000001F71AD78EF0>
      # > ********************
      # > instance:<__main__.Person object at 0x000001F71AD78F60>
      # > ********************
      # > value:24
      # > --------------------
      # > 调用了get方法
      # > self:<__main__.Dscrip object at 0x000001F71AD78EF0>
      # > ********************
      # > instance:<__main__.Person object at 0x000001F71AD78F60>
      # > ********************
      # > owner:<class '__main__.Person'>
      # > --------------------
      # > p:<__main__.Person object at 0x000001F71AD78F60>
      
      #在这里我们我们打印了get、set方法中的self、instance、owner的值
      
      #可以看出self 指的是一个Dscrip类,在Person类中,指的就是age变量
      #instance 和我们打印对象p的地址一致,instance就是指的对象p
      #owner 是一个名为Person的类    
      #value 就是我们传入对象p中age的值
      
    • 总结:

      1. 在get和set方法中,self指的就是我们定义的类描述符的实例对象,也就是类中的属性

      2. 在get和set方法中,instance指的是包含了该实例对象的实例化类对象

      3. 在set方法中,value就是传入该类对象的值

    • 使用类的描述符进行多个类属性检查

      #上面学习了如何使用property对属性进行检查
      #现在如果我想要将Person类中新定义一个sorce,并且也进行检查,此时如果使用property就会显得异常繁杂
      #这时候就可以使用类的描述符了
      
      class Desc():
      
      def __init__(self, propertyname):
          self.propertyname = propertyname
      
      def __get__(self, instance, owner):
          if instance is None:
              return self
          return instance.__dict__[self.propertyname]
      
      def __set__(self, instance, value):
          if value < 0:
              print('不允许是负值')
          else:
              instance.__dict__[self.propertyname] = value
      
      def __del__(self):
         del self.propertyname
      
      
      class Person():
      
         age = Desc('age')
         sorce = Desc('sorce')
      
         def __init__(self, name, age, sorce):
             self.name = name
             self.age = age
             self.sorce = sorce
      
         p = Person('jack',18,60)
         p.sorce = -5
      
      # > 不允许是负值
      
      #这样就检查了两个属性age、sorce,从而避免了写多个property
      
  • 类的常用函数

    • __dict__ : 获取类和对象中的所有成员属性

    • hasattr : 检测某类中是否有某属性

    • issubclass : 检测某类是否是另一个类的子类

    • isinstance : 检测某个对象是否是一个类的实例

    • getattr : 获取某个对象的某个属性值

    • __getattribute__ :获取某个对象的某个属性值

    • setattr : 设置某个对象的某个属性值

    • delattr : 删除某个对象的某个属性

    • __slots__ : 在类的开头定义一个slots的元组,实例化对象后,对象只能添加元组中定义的属性

      • 如果子类中没有定义slots,那么子类不存在slots限制

      • 如果子类中定义了slots,那么子类中slots的限制是子类和父类元组相加的限制

属性和函数的动态绑定

  • 对类的实例对象进行动态绑定

    • 动态绑定属性,使用对象.属性 = 值绑定

      class Person():
        pass
      
      p = Person()
      p.name = 'jack'
      
    • 动态绑定函数,使用对象.方法名 = types.MethodType(方法名,对象)绑定

      import types
      
      class Person():
          pass
      
      def say():
          print('say')
      
      p = Person()
      p.say = types.MethodType(say, p)
      
    • 注意:此时绑定的方法只对绑定的该实例对象有效,如果重新定义一个对象,此方法就失效了

  • 对类进行动态绑定

    • 类.方法名 = 方法名绑定

      class Person():
          pass
      
      def say():
          print('say')
      
      Person.say = say
      
      Person.say()
      
    • 类名.方法名 = types.MethodType(方法,类名)绑定

      • 用法参照对象绑定

抽象类

  • 定义抽象类是为了让其子类必须实现抽象类中的属性或者方法,对子类进行规范化,设定子类的标准

  • 抽象类只能被继承使用,不可以进行实例化

  • 定义抽象使用class 抽象类名(metaclass = abc.ABCMeta)

  • 定义抽象方法使用@abc.abstractclassmethod

import abc

class Man(metaclass= abc.ABCMeta):
   
    @abc.abstractmethod
    def say(self):
        pass

    @abc.abstractmethod
    def eat(self):
        pass


class Person(Man):
'''
    def say(self):
        pass
    def eat(self):
        pass
'''
  pass
p = Person()


# > TypeError: Can't instantiate abstract class Person with abstract methods eat, say
#如果子类中不定义抽象方法就会报错

  • 包的结构

    • __init__.py 包的标志文件

    • 模块

    • 子包

  • 导入包

    • import 包名

    • import 包名.模块名

    • 导入后可以使用__init__.py中定义的文件

      • __init__.py里如果定义了__all__,就只能使用__all__里面定义的内容

      • __init__.py里如果没有定义__all__,就可以使用全部文件

  • 模块

    • 定义模块的规范

      • 函数,单一功能

      • 测试代码

      • if __name__ = 'main': 代表此模块作为主线程执行时就执行以下内容

    • 导入模块

      • import 模块名

      • from 模块名 import 类名

      • import 模块名 as 别名

    • 模块、包的查找路径

      • 默认路径,可使用sys.path查看

      • 添加路径,sys.path.append(),也就是向列表中添加一个新元素

    • 模块的加载顺序

      • 内存中已经加载好的模块

      • Python中内置的模块

      • 按顺序查找sys.path中的内容

异常

  • 异常是一个类,可以进行处理和使用

  • 常见异常错误类

    • AssertErro 断言语句失败

    • AttributeErro 尝试方问未知的对象属性

    • FloatingPointErro 浮点计算错误

    • ImportErro 导入模块失败

    • IndexErro 下标引索值超出序列的范围

    • KeyErro 字典中查找一个不存在的关键字

    • NameErro 尝试访问一个不存在的变量

    • RuntimeErro 运行错误

    • OSErro 操作系统产生异常

    • SynataxErro 语法错误

    • IndentationErro 缩进错误

    • TypeErro 不同类型间的无效操作

  • 异常处理

     try:
        问题语句
     except 异常类 as e:  #将异常类实例化为e
        print(e)
     else:
        不出错执行语句
     finally:
        无论是否出错都执行的语句
    
  • 手动引发异常

    raise 异常类
    

    注意:推荐使用自定义异常,并且标注清楚。方便别人或者自己进行查看和快速定位

  • 自定义异常类

    • 自定义异常必须是系统异常的子类

    • 抛出异常时的文字提示

    • 异常发生的行数

常用模块

calendar 日历模块

  • calendar() 以字符串形式返回某年的日历

  • isleap() 判断某年是否是闰年

  • leapdays() 获取指定年份之间的闰年个数

  • month() 以字符串形式返回某年某月的日历

  • monthrange() 获取某年某月第一天是周几和某月的天数,返回的是一个元组

  • monthcalendar() 返回某年某月以二维列表组成的矩阵,其中每周组成一个列表

  • weekday() 获取某年某月某日是周几

import calendar

c = calendar.calendar(2018)
print(c)

                                  2018

   January                   February                   March
Mo Tu We Th Fr Sa Su      Mo Tu We Th Fr Sa Su      Mo Tu We Th Fr Sa Su
1  2  3  4  5  6  7                1  2  3  4                1  2  3  4
8  9 10 11 12 13 14       5  6  7  8  9 10 11       5  6  7  8  9 10 11
15 16 17 18 19 20 21      12 13 14 15 16 17 18      12 13 14 15 16 17 18
22 23 24 25 26 27 28      19 20 21 22 23 24 25      19 20 21 22 23 24 25
29 30 31                  26 27 28                  26 27 28 29 30 31

    April                      May                       June
Mo Tu We Th Fr Sa Su      Mo Tu We Th Fr Sa Su      Mo Tu We Th Fr Sa Su
                1          1  2  3  4  5  6                   1  2  3
2  3  4  5  6  7  8       7  8  9 10 11 12 13       4  5  6  7  8  9 10
9 10 11 12 13 14 15      14 15 16 17 18 19 20      11 12 13 14 15 16 17
16 17 18 19 20 21 22      21 22 23 24 25 26 27      18 19 20 21 22 23 24
23 24 25 26 27 28 29      28 29 30 31               25 26 27 28 29 30
30

     July                     August                  September
Mo Tu We Th Fr Sa Su      Mo Tu We Th Fr Sa Su      Mo Tu We Th Fr Sa Su
                1             1  2  3  4  5                      1  2
2  3  4  5  6  7  8       6  7  8  9 10 11 12       3  4  5  6  7  8  9
9 10 11 12 13 14 15      13 14 15 16 17 18 19      10 11 12 13 14 15 16
16 17 18 19 20 21 22      20 21 22 23 24 25 26      17 18 19 20 21 22 23
23 24 25 26 27 28 29      27 28 29 30 31            24 25 26 27 28 29 30
30 31

   October                   November                  December
Mo Tu We Th Fr Sa Su      Mo Tu We Th Fr Sa Su      Mo Tu We Th Fr Sa Su
1  2  3  4  5  6  7                1  2  3  4                      1  2
8  9 10 11 12 13 14       5  6  7  8  9 10 11       3  4  5  6  7  8  9
15 16 17 18 19 20 21      12 13 14 15 16 17 18      10 11 12 13 14 15 16
22 23 24 25 26 27 28      19 20 21 22 23 24 25      17 18 19 20 21 22 23
29 30 31                  26 27 28 29 30            24 25 26 27 28 29 30
                                                 31


 print(calendar.isleap(2018))   
    
 # > False
 
 print(calendar.leapdays(1990,2018))
 
 # > 7
 
 m = calendar.month(2018,8)
 print(m)
 
 # >     August 2018
     Mo Tu We Th Fr Sa Su
            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 26
     27 28 29 30 31
 
 week,day = calendar.monthrange(2018,8)
 print(week)
 print(day)
 
 # > 2
 # > 31
 
 
 list = calendar.monthcalendar(2018,8)
 for i in list:
     print(i)
     
 # > [0, 0, 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, 26]
 # > [27, 28, 29, 30, 31, 0, 0]
 
 print(calendar.weekday(2018,8,7))
 
 # > 1
 

time 时间模块

  • time() 指一个时间戳,是从1970年1月1日0时到至今所经历的秒数

      import time 
    
      print(time.time())
      
      # > 1533604231.9603899
    
  • 时间元组,是一个包含时间内容的元组,组成由(年,月,日,时,分,秒,周几,年的第几天,夏令时)

  • localtime() 获取当前时间的元组

      import time
      
      print(time.localtime())
      
      # > time.struct_time(tm_year=2018, tm_mon=8, tm_mday=7, tm_hour=9, tm_min=14, tm_sec=51, tm_wday=1, tm_yday=219, tm_isdst=0)
      
    
  • timezone 标准时区与当前时区相差的秒数

    import time
    
    print(time.timezone)
    
    # > -28800
    #中国是东8区,相比于国际时间,要快8小时,28800/60/60 = 8
    
  • asctime() 返回系统格式化后的时间

    import time
    
    print(time.asctime())
    
    # > Tue Aug  7 09:20:40 2018
    
  • ctime() 返回系统格式化后的时间

    import time
    
    print(time.ctime)
    
    # > Tue Aug  7 09:21:37 2018
    
  • maketime() 将时间元组变成时间戳

    import time
    
    t = time.localtime()
    s = time.mktime(t)
    print(s)
    
    # > 1533605096.0
    
  • sleep() 使程序休眠指定秒数后继续执行

    import time
    
    for i in range(1,11):
        time.sleep(1)
        print(i)
    
    # > 1
    # > 2
    # > ....
    
  • strftime() 自定义格式化时间,作用于时间元组

    • %Y 完整年份

    • %m 月份

    • %d 每月中的第几天

    • %H 24小时制

    • %M 分钟 60

    • %S 秒 60

    import time
    
    t = time.localtime()
    
    st = time.strftime('%Y-%m-%d %H:%M:%S',t)
    
    print(st)
    
    # > 2018-08-07 09:41:23
    

    注意:

    • print(time.strftime('%Y年%m月%d日 %H时%M分%S秒',time.localtime()))

    • 同样方法,在windows下报错UnicodeEncodeError: 'locale' codec can't encode character '\u5e74' in position 2: Illegal byte sequence

    • 在ubuntu下正常执行,返回2018年08月06日 20时28分31秒

  • strptime() 将时间字符串格式化为时间元组

    import time
    
    st = '2018-8-7 12:00:00'
    
    t = time.strptime(st,'%Y-%m-%d %H:%M:%S')
    
    print(t)    
    
    
    # > time.struct_time(tm_year=2018, tm_mon=8, tm_mday=7, tm_hour=12, tm_min=0, tm_sec=0, tm_wday=1, tm_yday=219, tm_isdst=-1)        
    

datetime 理想的日期和时间模块

  • date类 提供一个理想的日期

    • year 属性

    • month 属性

    • day 属性

    • today() 返回当前日期

    • fromtimestamp() 从一个时间戳中获得日期

    • strftime() 自定义格式化日期

    import datetime
    
    dt = datetime.datetime(2018,8,7)   #将2018.8.7实例化为datetime类
    
    print(dt.year)
    # > 2018
    
    print(dt.month)
    # > 8
    
    print(dt.day)
    # > 7
    
    dt = datetime.datetime.today()
    print(dt)
    # > 2018-08-07 13:49:04.943500
      
    import time
    
    t = time.time()   #获取当前时间的时间戳
    dt = datetime.datetime.fromtimestamp(t)
    print(dt)
    # > 2018-08-07 13:51:44.969500
    
    print(dt.strftime('%Y.%m.%d'))
    # > 2018.08.07
    
  • datetime类 提供理想的日期和时间

    • year 属性

    • month 属性

    • day 属性

    • hour 属性

    • minute 属性

    • second 属性

    • mircosecond 属性

    • today() 获取当前日期和时间

    • fromtimestamp() 从时间戳中获取日期和时间

  • timedelta类 提供日期和时间的计算

    • 可以通过days = 天数 , hours = 小时数 , minutes = 分钟数进行实例化

    • timedelta类 + date类或者datetime类 = date类或者datetime类

    • date类或者datetime类 - date类或者datetime类 = timedelta类

    import  datetime
    
    dt1 = datetime.date(2018,6,15)
    dt2 = datetime.date(2018,8,7)
    td = dt2 - dt1
    print(td)
    # > 53 days, 0:00:00
    print(type(td))
    # > <class 'datetime.timedelta'>
    
    
    dt3 = datetime.datetime(2018,6,15,8,30,15)
    dt4 = datetime.datetime(2018,8,7,14,4)
    td = dt4 - dt3 
    print(td)
    # > 53 days, 5:33:45
    
    
    td = datetime.timedelta(days=10,hours=15,minutes=30)
    dt = datetime.datetime.now()
    temp = dt + td
    print(type(temp))
    # > <class 'datetime.datetime'>
    print(temp)
    # > 2018-08-18 05:37:51.464770
    

timeit 代码时间测试模块

  • timeit(smlt = 测试代码,setup = 运行环境 ,number = 重复次数) 测试代码时间

     #现在我们测试一下列表生成式和循环加入列表中的两个代码谁执行的快
     import timeit
     
     st1 = '[i for i in range(1000)]'
     
     st2 = '''
     list = []
     for i in range(1000):
         list.append(i)
     '''
     
     t1 = timeit.timeit(stmt = st1,number = 100000)
     
     t2 = timeit.timeit(stmt = st2,number = 100000)
     
     print(t1)
     # > 3.6470414876165864
     
     print(t2)
     # > 9.188953135455993
     
     
     
     #当测试带参数的函数的写法
    import timeit
    
    c = '''
    def func(num):
        for i in range(num):
            print('repeat for {}'.format(i))
    '''
    
    t = timeit.timeit(stmt='func(num)', setup = c + 'num=2', number=100000)
    print(t)
    
    # > repeat for 1
    # > repeat for 0
    # > ....
    # > 1.1565270608769294
    

os 系统操作模块

  • 绝对路径 总是从根目录上开始

    D:\360安全浏览器下载\chromeinstall-8u151.exe

  • 相对路径 以当前所在工作目录开始

    \360安全浏览器下载\chromeinstall-8u151.exe

    • os.curdir 当前目录,用‘ . ’表示

    • os.pardir 父亲路径,用‘ .. ’表示

    • os.sep 系统分隔符

      • windows下用‘ \ ’

      • linux下用‘ / ’

    • os.linesep 系统的换行符号

      • windows下用‘ \r\n ’

      • linux下用‘ \n ’

    • os.name 系统名称

      • windows下用‘ nt ’

      • linux下用‘ posix ’

  • 函数

    • getcwd() 返回当前工作目录

    • chdir() 改变工作目录

    • listdir() 显示路径下所有文件和文件夹,返回一个列表

    • makedirs() 创建一个空文件夹

    • rename() 重命名文件

    • removedirs() 删除空文件夹

    • system() 运行系统shell命令

    • getenv() 获取当前系统环境变量值

    • putenv() 添加环境变量

    • exit() 退出当前程序

  • path类中的函数

    • abspath() 将路径转化为绝对路径

    • basename() 获取路径中的文件名部分

    • dirname() 返回路径中的文件夹部分

    • split() 将路径切割为文件夹和文件名组成的元组

    • splittext() 将路径切割为文件和后缀名组成的元组

    • join() 将两个路径拼接成一个路径

    • exists() 判断文件或文件夹是否存在

    • isfile() 判断是否为文件

    • isdir() 判断是否为文件夹

    • getsize() 返回文件大小

    • getatime() 返回文件或文件夹最后读取时间

    • ghetmtime() 返回文件或文件夹最后修改时间

shutil 文件操作模块

  • copy() 复制文件,还可以将文件重命名

  • copy2() 尽可能保留元数据的情况下复制文件

  • copyfile() 讲一个文件里的内容复制到另一个文件中

  • copytree() 复制文件夹里所有内容

  • move() 移动文件或文件夹

  • rmtree() 删除文件或文件夹下的所有文件

  • make_archive(压缩后的名称,压缩类型,需要压缩的文件) 压缩文件,返回压缩文件的路径

  • unpack_archive(压缩包路径,解包之后的路径) 解压文件

collections 模块

  • namedtuple类

    • 是一个可以命名的元组类型

    • 定义方式类名 = collections.namedtuple(类类型,属性组成的列表)

     import collections
     
     Point = collections.namedtuple('Point',['x','y'])
     
     p = Point(15,22)
     
     print(p)     
     
     # > Point(x=15, y=22)   
    
  • deque类

    • 解决频繁插入、删除带来的效率问题

    • 定义方式:对象 = collections.deque(列表)

    import collections
    
    d = collections.deque([1,2,3,4,5])
    
    d.appendleft(10)
    # > deque([10, 1, 2, 3, 4, 5])
    
  • defaultdict类

    • 当读取一个不存在的key时,返回一个默认值

    • 定义方式:对象 = collections.defaultdict(带有返回值的函数)

      import collections
      
      def pr():
          return '默认值'
      
      d = collections.defaultdict(pr)
      
      d['a'] = 1
      d['b'] = 2
      
      print(d)
      
      # > defaultdict(<function pr at 0x028AB6F0>, {'a': 1, 'b': 2})
      
      print(d['dd'])
      
      # > 默认值
      
  • Counter类

    • 主要是统计元素在迭代对象中出现的次数,以元素:次数的字典形式返回
    import collections
    
    s = 'afjkasdhfklashgkjash'
    
    dict = collections.Counter(s)
    
    print(dict) 
    
    # > Counter({'a': 4, 'k': 3, 's': 3, 'h': 3, 'f': 2, 'j': 2, 'd': 1, 'l': 1, 'g': 1})
    
    import collections
    
    s = ['ss','jack','alice','bob','jack','jack','jack']
    
    dict = collections.Counter(s)
    
    print(dict)  
    
    # > Counter({'jack': 4, 'ss': 1, 'alice': 1, 'bob': 1})  
    

pickle 持久化模块

  • dump(数据,句柄),将数据持久化到文件

    import pickle
    
    file = r'C:\Users\wudi.HAPMMAGNA\Desktop\b.txt'
    
    l = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
    
    with open(file, 'wb') as f:
        pickle.dump(l,f)
        
    #将数据以二进制方式写入到b.txt文件中
    
  • load(句柄),将持久化的数据提取到程序中

    import pickle
    
    file = r'C:\Users\wudi.HAPMMAGNA\Desktop\b.txt'
    
    with open(file, 'rb') as f:
        l = pickle.load(f)
    print(l)
    
    # > [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
    
    #将b.txt文件中储存的数据读取到程序中
    

shelve 持久化模块

  • 将数据以字典的形式写入文件中

  • shelve只能允许一个句柄进行写入,但可以允许多个句柄进行读取

  • writeback = Ture在关闭文件时,检查更改,并将数据写入文件

import shelve

file = r'C:\Users\wudi.HAPMMAGNA\Desktop\a'

sv = shelve.open(file,writeback= True)

sv['one'] = 1
sv['two'] = 2
sv['three'] = 3

sv.close()

# 此时桌面上出现了三个文件,为a.dat、a.bak、a.dir
  • 读取文件
import shelve

file = r'C:\Users\wudi.HAPMMAGNA\Desktop\a'

sv = shelve.open(file)

print(sv['one'])

sv.close()

# > 1

IO文件模块

  • 文件是一种长久保存信息数据的集合,保存在磁盘上

  • 文件一旦被打开,就需要进行关闭操作,否则可能造成文件信息的丢失

  • 常用操作

    • 读写模式

      • r 只读,指针在文件开头

      • w 只写,指针在文件开头

      • x 创建文件并只进行写入,如果文件已经存在则会报错,指针在文件开头

      • a 追加写入,指针在文件末尾

      • b 以二进制方式

      • t 以文本方式

      • + 读写

    • open(文件地址,读写模式) 打开文件、close() 关闭文件

      file = r'C:\Users\wudi.HAPMMAGNA\Desktop\b.txt'
      f = open(file)
      f.close()
      
    • with 语句

      • 使用上下文管理协议技术

      • 自动关闭已经不再使用的文件

      • 推荐一定文件读写一定要使用with语句

      file = r'C:\Users\wudi.HAPMMAGNA\Desktop\b.txt'
      with open(file,'wb') as f:
        pass
      
    • 读取操作

      • read(字符数) 如果不使用字符数,那么read将会将所有字符读取出来,如果指定字符,就按照指定字符数进行读取

      • readline() 按行读取文件内容

      • list(句柄) 将文件所有内容读取出来,以列表形式进行保存

      • 使用while 数据进行读取,当数据不为空时,就会一直循环下去,如果数据为空时,当前循环就会停止

      # b.txt中有以下内容
      # 关雎
      # 关关雎鸠,在河之洲。窈窕淑女,君子好逑。
      # 参差荇菜,左右流之。窈窕淑女,寤寐求之。
      # 求之不得,寤寐思服。悠哉悠哉,辗转反侧。
      # 参差荇菜,左右采之。窈窕淑女,琴瑟友之。
      # 参差荇菜,左右芼之。窈窕淑女,钟鼓乐之。
      
      
      
      file = r'C:\Users\wudi.HAPMMAGNA\Desktop\b.txt'
      with open(file, 'r') as f:
          data = f.read(1024)
          while data:
              print(data)
              data = f.read(1024)
        
      # > 关雎
      # > 关关雎鸠,在河之洲。窈窕淑女,君子好逑。
      # > 参差荇菜,左右流之。窈窕淑女,寤寐求之。
      # > 求之不得,寤寐思服。悠哉悠哉,辗转反侧。
      # > 参差荇菜,左右采之。窈窕淑女,琴瑟友之。
      # > 参差荇菜,左右芼之。窈窕淑女,钟鼓乐之。
      
       with open(file, 'r') as f:
          data = f.readline()
          while data:
              print(data)
              data = f.readline()  
            
      # > 关雎
      # > 
      # > 关关雎鸠,在河之洲。窈窕淑女,君子好逑。
      # > 
      # > 参差荇菜,左右流之。窈窕淑女,寤寐求之。
      # > 
      # > 求之不得,寤寐思服。悠哉悠哉,辗转反侧。
      # > 
      # > 参差荇菜,左右采之。窈窕淑女,琴瑟友之。
      # > 
      # > 参差荇菜,左右芼之。窈窕淑女,钟鼓乐之。
      
      with open(file,'r') as f:
        list3 = list(f)
      for i in list3:
        print(i) 
      
      # > 结果同readline() ,为什么会出现这种情况? 使用read时,中间没有隔行,而使用其他时就会有隔行呢?
      #我们来做个试验,将所有读取到的内容添加到列表,看他们在列表中是如何组成的
      file = r'C:\Users\wudi.HAPMMAGNA\Desktop\b.txt'
      list1 = []
      list2 = []
      
      with open(file, 'r') as f:
          data = f.read(1024)
          while data:
              list1.append(data)
              data = f.read(1024)
      
      
      with open(file, 'r') as f:
          data = f.readline()
          while data:
              list2.append(data)
              data = f.readline()
      
      with open(file,'r') as f:
          list3 = list(f)
      
      print(list1)
      print('@'*30)
      print(list2)
      print('@'*30)
      print(list3)
      
      # > ['关雎\n关关雎鸠,在河之洲。窈窕淑女,君子好逑。\n参差荇菜,左右流之。窈窕淑女,寤寐求之。\n求之不得,寤寐思服。悠哉悠哉,辗转反侧。\n参差荇菜,左右采之。窈窕淑女,琴瑟友之。\n参差荇菜,左右芼之。窈窕淑女,钟鼓乐之。']
      # > @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
      # > ['关雎\n', '关关雎鸠,在河之洲。窈窕淑女,君子好逑。\n', '参差荇菜,左右流之。窈窕淑女,寤寐求之。\n', '求之不得,寤寐思服。悠哉悠哉,辗转反侧。\n', '参差荇菜,左右采之。窈窕淑女,琴瑟友之。\n', '参差荇菜,左右芼之。窈窕淑女,钟鼓乐之。']
      # > @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
      # > ['关雎\n', '关关雎鸠,在河之洲。窈窕淑女,君子好逑。\n', '参差荇菜,左右流之。窈窕淑女,寤寐求之。\n', '求之不得,寤寐思服。悠哉悠哉,辗转反侧。\n', '参差荇菜,左右采之。窈窕淑女,琴瑟友之。\n', '参差荇菜,左右芼之。窈窕淑女,钟鼓乐之。']
      
      # 可以看出,read中整个列表只有一个元素,也就是遇到'\n'时会自动进行换行,而在其他函数中,列表中有多个元素,遇到n时会换一次行,而单次打印内容时也会换一次行。
      
    • 写入操作

      • write(数据) 将单次数据写入文件中

      • writelines(数据序列) 将一个可迭代的数据序列按顺序写入文件

      data = '我是数据'
      with open(file,'w') as f:
        f.write(data)
      
      # > 文件中的内容是我是数据
      
      data = ['关关雎鸠,在河之洲。窈窕淑女,君子好逑。\n', '参差荇菜,左右流之。窈窕淑女,寤寐求之。\n']
      with open(file,'w') as f:
        f.writelines(data)
      
      # > 关关雎鸠,在河之洲。窈窕淑女,君子好逑。
      # > 参差荇菜,左右流之。窈窕淑女,寤寐求之。
      
    • 指针操作 seek(指针偏移量,参照物)

      • 指针偏移量

        • 以字节byte为单位,和read函数有巨大区别,read是字符为单位

        • 不同编码中汉字所占的字节数有所不同

      • 参照物

        • 0 表示在文件开头

        • 1 表示指针当前位置

        • 2 表示在文件末尾

    # b.txt中有以下内容:阿富汗喀什的法律可攻可受的复合弓  
    
    #注意:需要保存为utf-8编码
        
    # 如果我们需要读取文件末尾两个字符,就需要
    
    file = r'C:\Users\wudi.HAPMMAGNA\Desktop\b.txt'
    with open(file, 'rb') as f:
        f.seek(-6,2) 
        data = f.read(1024)
        while data:
            print(data.decode())
            data = f.read(1024)
    
    # > 合弓
    
    # seek中的含义为以文件末尾为指针位置,向左移动6个字节的位置,然后向后读取
    

logging 日志模块

  • 主要是将文件运行的信息保存在磁盘文件上

  • logging不要频繁进行IO读写,运行过程中,只需要保存重要信息和错误信息

  • 作用

    • 程序调试

    • 了解软件运行状况

    • 分析定位问题

  • 日志应包含信息

    • 时间

    • 地点

    • 级别

    • 内容

  • 日志级别

    • DEBUG

    • INFO

    • WARNING

    • ERROR

    • CRITICAL

  • 直接使用系统定制的logging

    • logging.debug(msg) 创建一条debug日志

    • logging. info(msg) 创建一条info日志

    • logging.warning(msg) 创建一条warning日志

    • logging.error(msg) 创建一条error日志

    • logging.critical(msg) 创建一条critical日志

    • logging.basicConfig(file=文件名,format=格式化字符串,level=输出的最低级别) 配置log

      • level对照以上日志级别作为参数,默认为warning

      • format的相应参数

        • %(asctime)s 时间和日期

        • %(levelname)s 日志级别

        • %(message)s 日志内容

        • %(filename)s 程序名

        • %(pathname)s 程序路径

        • %(funcName)s 当前函数

        • %(lineno)d 当前行数

        • %(thread)s 当前线程ID

        • %(ThreadName)s 当前线程名

        • %(process)s 当前进程ID

        import logging
        
        logging.basicConfig(format='%(asctime)s - %(levelname)s - %(message)s',level=logging.DEBUG)
        
        logging.debug('我是一条DEBUG日志')
      
        # > 2018-08-10 15:10:09,089 - DEBUG - 我是一条DEBUG日志
      
  • 使用四大组件进行定制

    • Logger类 日志器 产生日志

      • getLogger(日志器名称) 创建一个日志器对象

      • setlevel(日志级别) 设置处理日志的最低级别

      • addHandler(处理器对象),removeHandler() 添加、删除一个处理器对象

      • addFilter(过滤器对象),removeFilter() 添加、删除一个过滤器对象

      • debug()、info()、.....创建一条日志

    • Filter类 过滤器 存疑

      • 可被Logger、Handler使用
    • Formatter类 格式器 格式化输出日志

      • 可以直接实例化使用

      • Formatter(fmt,datefmt)

        • fmt 格式化字符串

        • datefmt 格式化时间和日期字符串

    • Handler类 处理器 将日志输出到相应位置

      • Handler是基类,不能直接使用,需要使用其子类

        • StreamHandler() 向控制台输出

        • FileHandler(file=文件) 向文件写入日志

        • handlers.RotatingHandler(file=文件,maxBytes=大小,backupCount=日志保留个数) 向文件写入日志,如果超过指定大小,则新建日志文件

        • handlers.TimeRotatingFileHandler(file=文件, when=时间, interval=间隔, backupCount=保留日志个数) 向文件写入日志,按时间进行分割

          • when的参数

            • 'S'

            • 'M'

            • 'H'
              - 'D'
              - 'midnight' 每日午夜12:00
              - 'W0-W6' 周一 ~ 周日

        • handlers.HTTPHandler 发送给一个HTTP服务器

        • handlers.SMTPHandler 发送给一个指定的邮件地址

    • setlevel(日志级别) 设置最低需要写入日志的级别

    • setFormatter(格式器对象) 添加一个格式器对象,用来控制输出的日志格式

    • addFilter(过滤器对象)、removeFilter()

    #定义记录两个日志,每天记录一次
    #一个日志all.log,所有级别的日志都被记录,格式为“日期和时间 - 级别 - 内容”
    #一个日志error.log,error以上的的日志都被记录,格式为“日期和时间 - 级别 - 文件名【:行号】 - 内容”
    
    
    import logging
    import logging.handlers
    import datetime
    
    #定义Logger
    logger = logging.getLogger('mylogger')
    logger.setLevel(logging.DEBUG)
    
    #定义debughandler格式器
    formatter1 = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
    
    #定义记录所有日志的debughandler
    file1 = r'C:\Users\wudi.HAPMMAGNA\Desktop\all.log'
    debughandler = logging.handlers.TimedRotatingFileHandler(file1, when='midnight', interval=1, backupCount=3000)
    debughandler.setFormatter(formatter1)
    
    #定义errorhandler格式器
    formatter2 = logging.Formatter('%(asctime)s - %(levelname)s - %(filename)s[:%(lineno)d] - %(message)s')
    
    #定义记录error以上级别的日志errorhandler
    file2 = r'C:\Users\wudi.HAPMMAGNA\Desktop\error.log'
    errorhandler = logging.handlers.TimedRotatingFileHandler(file2, when='midnight', interval=1, backupCount=3000)
    errorhandler.setFormatter(formatter2)
    errorhandler.setLevel(logging.ERROR)
    
    #为日志器添加处理器
    logger.addHandler(debughandler)
    logger.addHandler(errorhandler)
    
    
    logger.debug('我是debug日志')
    logger.info('我是info日志')
    logger.warning('我是warning日志')
    logger.error('我是error日志')
    logger.critical('我是critical日志')
    
    • 总结思路

      • 实例化一个Logger对象

      • 依照不同的日志文件实例化不同处理方式的Handler

      • 给每个Hadnler对象添加实例化的Formatter格式器对象

      • 输出

queue 同步队列模块

  • Queue类 先进先出队列

  • LifoQueue 后进先出队列

  • PriorityQueue 优先级队列

  • 常用方法

    • qsize() 返回队列长度

    • put() 将数据放入队列中

    • get() 从队列中取出数据,并删除

    • empty() 检测队列是否为空

    • full() 检测队列是否已满

import queue

que = queue.Queue()
for i in range(1,11):
    que.put(i)

print(que.get())

# > 1

print(que.empty())

# > False

threading 多线程模块

  • 首先明确几个概念

    • 阻塞: 程序在等待某个操作完成期间,自身无法干其他事情,处于挂起状态

    • 非阻塞: 程序在等待某个操作完成期间,自身不被阻塞,还可以继续干其他事情,是为了提高程序的整体执行效率,让程序在IO过程中执行其他计算任务

    • 并行: 利用富余的资源(多核)加速完成某个任务 多进程

    • 并发: 利用有限的资源使多个任务被实时或者近似实时的执行,让多个任务都有机会被尽快执行,不一定能加快进度 多线程

    • 异步: 不同程序单元执行过程中不需要通信也能完成某个任务,高效的组织非阻塞任务的方式,异步意味着无序

    • 同步: 不同程序单元为了完成某个任务,在执行过程中需要靠通信进行步调一致,同步就意味着有序

  • 定义线程的两种方式

    • 通过传入函数来启动线程

      import threading    
      
      def func():
         while True:
            pass
      
      t = threading.Thread(target=func,args=())
      t.setDaemon(True)
      t.start()
      print(threading.enumerate())
      # > [<_MainThread(MainThread, started 7176)>, <Thread(Thread-1, started daemon 9260)>]
      # > 可以看出开启了两个线程,一个是主线程,另一个是子线程
      
    • 通过继承threading.Thread类,重写其init和run方法定义

      import threading
      import time
      
      
      class myThread(threading.Thread):
      
            def __init__(self):
                super().__init__()
          
            def run(self):
                print('{0} say hello'.format(self.name))
                time.sleep(5)
      
      for i in range(5):
            t = myThread()
            t.start()
            t.join()
      
      print('over')
      
      # > Thread-1 say hello
      # > Thread-2 say hello
      # > Thread-3 say hello
      # > Thread-4 say hello
      # > Thread-5 say hello
      # > over
      
  • 注意: 主线程执行完毕后,子线程并不会关闭,而是会等到子线程中的函数执行完毕后再关闭

  • 线程常用的方法

    • start() 启动线程
    • join() 阻塞线程,等待该线程执行完毕
    • active_Count() 返回当前活动的线程数量
    • current_thread() 返回当前正在活动的线程
    • enumerate() 返回当前活动的线程对象列表
  • 守护线程

    • 当A线程是B线程的守护线程时,一旦B关闭,A线程不论是否已经完成,都会随着B线程而关闭

    • setDaemon(True) 设置一个线程为守护线程

  • GIL 全局解释器锁

    • 在多线程中,每个线程都会在执行之前申请一把锁,这把锁就是GIL,但是这个锁只有一把,也就是只有等一个线程释放锁之后,其他线程才能申请这个锁,同一时间,线程只能并发,却不能并行.
  • 多线程安全

    • 在多线程中,对变量的读操作,并不会影响线程安全

    • 在多线程中,对变量的写操作,很有可能会引起线程安全的问题

    • 不安全线程的例子

      import threading
      import time
      
      num = 0
      loop = 1000000
      
      def funa(loop):
          global num
          for i in range(loop):
              num += 1
          print('funa done')
      
      def funb(loop):
          global num
          for i in range(loop):
              num -= 1
          print('funb done')
      
      t1 = threading.Thread(target=funa,args=(loop,))
      t2 = threading.Thread(target=funb,args=(loop,))
      
      t1.start()
      t2.start()
      
      time.sleep(2)
      
      print(num)
      
      # > 281811
      # > 结果并不是固定的,每次执行都会产生不同的结果
      
    • 这是因为有可能在线程中,多个线程可能在同时更改这个变量,造成结果错误

    • 为了解决共享变量冲突的问题

    • Lock 类

      • 只能执行一次锁定,锁定完成后必须释放锁,才能进行下一次锁定

      • 可以使用上下文管理协议

      • acqurie() 上锁

      • release() 解锁

      • 还是上一个例子,如果将全局变量加锁,同一时间,只能允许一个线程进行读写操作,这时候结果就一定为0

        import threading
        import time
        
        lock = threading.Lock()
        num = 0
        loop = 1000000
        
        def funa(loop):
            global num
            for i in range(loop):
                with lock:
                    num += 1
            print('funa done')
        
        def funb(loop):
            global num
            for i in range(loop):
                lock.acquire()
                num -= 1
                lock.release()
            print('funb done')
        
        t1 = threading.Thread(target=funa,args=(loop,))
        t2 = threading.Thread(target=funb,args=(loop,))
        
        t1.start()
        t2.start()
        
        time.sleep(2)
        
        print(num)
        
        # > funa done
        # > funb done
        # > 0
        
    • Rlock 类 可重入锁

      • 与Lock相同用法,但是可以实现锁内再次上锁
      # 使用Lock 实现两次上锁
      
      import threading
      
      lock = threading.Lock()
      
      def funa():
          lock.acquire()
          print('第一道锁上锁')
          lock.acquire()
          print('第二道锁上锁')
          lock.release()
          print('释放第二道锁')
          lock.release()
          print('释放第一道锁')
      
      t = threading.Thread(target=funa,args=())
      
      t.start()
      t.join()
      print('done')
      
      # > 第一道锁上锁
      # 可以看出,此时,一直是在等待解锁过程中,出现阻塞状态,后面的代码永远不会执行
      
      # 使用Rlock 实现两次上锁
      
      import threading
      
      lock = threading.RLock()
      
      def funa():
          lock.acquire()
          print('第一道锁上锁')
          lock.acquire()
          print('第二道锁上锁')
          lock.release()
          print('释放第二道锁')
          lock.release()
          print('释放第一道锁')
      
      t = threading.Thread(target=funa,args=())
      
      t.start()
      t.join()
      print('done')
      
      # > 第一道锁上锁
      # > 第二道锁上锁
      # > 释放第二道锁
      # > 释放第一道锁
      # > done
      
      #可以看出,程序按照预想的执行了
      
    • Condition 类 条件锁

      • 当某一事件触发后线程进行等待或者执行操作

      • 相比于Lock,Condition有一个等待池和一个锁定池,运行时处于锁定池中,阻塞时,处于等待池中

      • 可使用上下文管理协议

      • 常用方法

        • acqurie() 上锁

        • release() 解锁

        • notify(线程数量) 通知指定数量的线程运行

        • notify_all() 通知所有线程开始执行

        • wait() 使本线程处于阻塞状态,等待条件

      • 案例

      import threading
      import time
      
      con = threading.Condition()
      
      num = 0
      
      class A(threading.Thread):
      
          def run(self):
              global num
              with con:
                  while True:
                      if num >20:
                          print('产品大于20个了,我不生产了')
                          con.notify()
                          con.wait()
                      num += 1
                      time.sleep(0.5)
                      print('产品总数为:{0}'.format(num))
      
      
      class B(threading.Thread):
      
          def run(self):
              global num
              with con:
                  while True:
                      if num < 5:
                          print('产品小于5了,我不消费了')
                          con.notify()
                          con.wait()
                      num -= 1
                      time.sleep(0.5)
                      print('产品总数为:{0}'.format(num))
      
      
      
      
      a = A()
      b = B()
      
      a.start()
      b.start()
      
      # > 结果太长不写了
      
    • Semaphore 类 信号量

      • 该类主要控制线程对资源的最大使用数量,当线程超过指定数量时,就变为等待状态,一旦空出线程,其他线程就会接着执行

      • 当一个线程申请时,信号量就会-1 ,一旦变为负值,其他线程就不允许申请了.当一个线程被放出时,信号量就会+1,一旦不是负值,其他线程就可以申请.

      import threading
      import time
      
      st = 'st'
      
      se = threading.Semaphore(2)
      
      class A(threading.Thread):
      
          def run(self):
              with se:
                  for i in range(3):
                      print('我是{0},我被{1}访问了'.format(st, self.name))
                      time.sleep(1)
                      print(threading.current_thread())
      
      for i in range(20):
          t = A()
          t.start()
      
    • Event 类 事件信号灯

      • 相当于设置一个红绿灯,当红绿灯为True时,线程才会向下执行,否则就会一直等待信号

      • 常用方法

        • set() 设置信号灯为True

        • clear() 设置信号灯为False

        • wait() 本线程等待信号灯

      import threading
      import time
      
      event = threading.Event()
      
      class A(threading.Thread):
      
          def run(self):
              print('等待信号灯')
              event.wait()
              print('信号灯通过')
              print('hello')
              
      
      a = A()
      a.start()
      
      time.sleep(2)
      
      event.set()
      
      a.join()
      
      print('done')
      
      # > 等待信号灯
      # > #2秒后
      # > 信号灯通过
      # > hello
      # > done
      
    • Timer 类 定时器

      • Timer(间隔秒数,执行的函数,函数的参数)

      • 指定多少秒后以一个线程执行某函数

      import threading
      
      def func():
          print('hello')
      
      timer = threading.Timer(2,func)
      
      timer .start() 
      
      # > #2秒后
      # > hello     
      
  • 生产者消费者模型

    • 生产者只负责向队列中写入任务,而消费者只向队列中拿出任务
    import threading, queue, time
    
    class Producer(threading.Thread):
        def __init__(self,queue):
            super().__init__()
            self.queue = queue
    
        def run(self):
            num = 0
            while True:
                num += 1
                self.queue.put(num)
                print('向仓库中放入%s'%num)
                time.sleep(0.5)
    
    class Consumer(threading.Thread):
        def __init__(self,queue):
            super().__init__()
            self.queue = queue
    
        def run(self):
            while True:
                data = self.queue.get()
                print('{0}消费了{1}'.format(self.name,data))
                time.sleep(1.5)
    
    
    que = queue.Queue()
    
    p = Producer(que)
    p.start()
    
    for i in range(2):
        c = Consumer(que)
        c.start()
      
    # > 向仓库中放入1
    # > Thread-2消费了1
    # > 向仓库中放入2
    # > Thread-3消费了2
    # > 向仓库中放入3
    # > Thread-2消费了3
    # > 向仓库中放入4
    # > Thread-3消费了4
    # > 向仓库中放入5
    # > 向仓库中放入6
    

多进程

  • 推荐使用多进程,因为多进程中没有GIL锁,可以并行执行

  • 在使用多进程时,主函数必须在 if __name__ == '__main__:'下执行

  • 实现多进程的方式

    • Process 类

      • 通过实例化Process类,将函数传入参数中

        import multiprocessing
        
        def func():
          print('hello')
        t = multiprocessing.Process(target=func,args=())
        t.start()
        
      • 通过继承Process类,并重写init和run方法

        import multiprocessing
        
        class A(multiprocessing.Process):
        
              def run(self):
                  print('hello')
        
        a = A()
        a.start()
        
    • 使用os.fork() 仅在Unix系统下有效

    • 使用Pool 进程池

      • pool池可以指定并行最大进程执行的数量,我认为和线程中的Semaphore信号量相似,同样有一个等待池和一个工作池,当工作池中的进程空出来时,等待池的其它线程就会顶替上去执行

      • 可使用上下文管理协议

      • 常用函数

        • apply(函数名,参数) 向工作池提交任务,阻塞的等待任务完成,并返回结果.

        • apply_async(函数名,参数) 向工作池提交任务,返回消息对象,等待进程池所有任务执行完毕后,再拿回任务执行的结果

          • 因为apply_async是并行的,所以需要申明一个list用来保存消息对象,等待所有的任务完成后再列表中的单个消息对象返回结果

          • get() 返回结果

          • ready() 如果调用完成,则返回True

          • successful() 如果调用没有引发异常,返回True

          • wait() 等待结果执行完毕

          • terminate() 立即终止该工作进程

        • close() 关闭进程池

        • join() 阻塞,直到进程池所有任务完成

        • terminate() 立即终止进程池进程

      • 阻塞式进程池

        import multiprocessing
        import random
        import time
        
        
        def getresult(x):
            return x * random.randint(1, 10)
        
        
        if __name__ == '__main__':
        
            pool = multiprocessing.Pool(2)
        
            for i in range(1, 5):
                res = pool.apply(getresult, (i,))
                time.sleep(1)
                print(res)
        
        # > 4
        # > 10
        # > 9
        # > 4
        
      • 异步线程池

        import multiprocessing
        import random
        
        
        def getresult(x):
            return x * random.randint(1, 10)
        
        
        if __name__ == '__main__':
        
            pool = multiprocessing.Pool(2)
        
            list = []
            for i in range(1, 5):
                res = pool.apply_async(getresult, (i,))
                list.append(res)
            
            p.close()
            p.join()
                
            print(list)
        
            for i in list:
                print(i.get())
                
        
        
        # > [<multiprocessing.pool.ApplyResult object at 0x02C78630>, <multiprocessing.pool.ApplyResult object at 0x02C78690>, <multiprocessing.pool.ApplyResult object at 0x02C786F0>, <multiprocessing.pool.ApplyResult object at 0x02C78750>]
        # > 7
        # > 12
        # > 3
        # > 20
        
  • 进程常用方法

    • start() 启动进程

    • join() 阻塞,知道本进程执行完毕

    • daemon = True 设置守护进程

    • terminate() 强制关闭进程

    • close() 关闭进程

    • is_alive() 返回进程是否存活

    • name 返回进程名称

    • pid 返回进程ID

    • active_chrildren() 返回活动的进程列表

    • cpu_count() 返回电脑CPU核心数

  • 进程之间的同步

    • 多进程中,同步并不是向多线程那样重要,因为多使用IPC通信

    • 其用法与多线程中相同类的用法一样

    • Condition类 条件变量

    • Event类 事件信号灯

    • Semaphore类 信号量

    • Lock 锁

    • Rlock 可重入锁

  • IPC通信 多进程之间的通信

    • 源自于同一父进程

      • Queue类 先入先出队列

        • put() 放入元素

        • get() 取出元素

        • qsize() 返回队列长度

        • empty() 检测队列是否为空

        • full() 检测队列是否已满

      • JoinableQueue队列

        • 每当一个任务完成后,可手动调用task_done,告诉系统一个任务已经执行完成,当所有任务执行完成后,将取消join的阻塞,向下执行代码

        • 常用函数

          • Queue队列的其它方法

          • task_done() 任务已完成

          • join() 阻塞,直到本队列中的所有元素被task_done

        import multiprocessing,time
        
        def func(queue):
            while True:
                if queue.empty():
                    break
                data = queue.get()
                print('取出%s'%data)
                queue.task_done()
                time.sleep(0.5)
        
        if __name__ == '__main__':
        
            que = multiprocessing.JoinableQueue()
        
            for i in range(20):
                que.put(i)
        
            p = multiprocessing.Process(target=func, args=(que,))
        
            p.start()
        
            que.join()
        
            print('当队列中的元素都被取完后才会看见我') 
        
        # > 太长不打了          
        
      • Pipe 类 管道 两个进程之间的通信

        • 相当于管道,实例化后返回两个端口,默认是双工的

        • send() 发送消息

        • recv() 接收消息

        import multiprocessing,time
        
        class A(multiprocessing.Process):
        
            def __init__(self,con):
                super().__init__()
                self.con = con
        
            def run(self):
                while True:
                    self.con.send('hello')
                    time.sleep(1)
        
        
        class B(multiprocessing.Process):
        
            def __init__(self, con):
                super().__init__()
                self.con = con
        
            def run(self):
                while True:
                    data = self.con.recv()
                    print(data)
                    time.sleep(1)
        
        if __name__ == '__main__':
        
            con1, con2 = multiprocessing.Pipe()
        
            a = A(con1)
        
            b = B(con2)
        
            a.start()
        
            b.start()   
        
        # > hello
        # > ...
        
    • 不是源自于同一父进程

      • Value类 将值放在系统的共享内存中,多个进程可以共同读写这块内存,

        • 定义 Value(值类型,值)

        • 值类型

          • u unicodechar

          • i signedint

          • f float

          • d double

        • value 获取值

        import multiprocessing
        
        class A(multiprocessing.Process):
        
            def run(self):
        
                s = v.value
        
                print('{0} get {1}'.format(self.name,s))
        
        
        if __name__ == '__main__':
            
            v = multiprocessing.Value('u','哈')
        
            for i in range(5):
                p = A()
                p.start()
        
        # > A-1 get 哈
        # > A-2 get 哈
        # > A-4 get 哈
        # > A-5 get 哈
        # > A-3 get 哈
        
      • Array类 Vaule的数组

        • 数组中存放的变量类型需要一致

        • 定义 Array(值类型,列表)

        • 返回的是一个可迭代的对象

        import multiprocessing
        
        class A(multiprocessing.Process):
        
            def run(self):
        
                for i in arr:
                    print(i,end='')
        
        
        if __name__ == '__main__':
        
            list = ['哈','哈','-','我','是','A','r','r','a','y']
        
            arr = multiprocessing.Array('u',list)
        
            p = A()
            p.start()
        
        # > 哈哈-我是Array
        
      • Manager类 管理器

        • list 列表

        • dict 字典

        • Condition() 创建一个线程共享条件信号灯

        • Event() 创建一个线程共享事件信号灯

        • Lock() 创建一个线程共享锁

        • Rlock() 创建一个线程共享可重入视频

        • Queue() 创建一个共享先进先出队列

        • Semaphore() 创建一个线程共享信号量

      • managers

        • BaseManager类

          • 原理: 通过Manager启动一个server进程监听socket,其他进程通过使用socket与主进程进行联系,进行信息交换

          • 定义 BaseManager(address=(ip,端口),authkey=秘钥) 创建一个服务器Manager对象,其他客户端Manager通过与此对象连接,进行数据交换

          • 常用方法

            • start() 启动服务进程

            • get_server() 返回一个服务对象

            • server_forever() 永远执行下去

            • connect() 将本地管理器连接到主服务管理器上

            • shuntdown() 关闭本管理器

            • register(对外暴露的方法名,callable=返回对象的函数)

        • 分布式进程 Master-Worker模式

          • Master 负责分发任务,并接受返回的最终结果
          from multiprocessing.managers import BaseManager
          import multiprocessing
          import time
          
          class Master(BaseManager):
              pass
          
          task_que = multiprocessing.Queue()
          result_que = multiprocessing.Queue()
          
          def returntaskque():
              return task_que
          
          def returnresultque():
              return result_que
          
          
          if __name__ == '__main__':
          
              ip = 'XXXXXXXXXXXXXX'
              port = 8888
              key = b'123'
              server = Master((ip,port),key)
          
          
          
              server.register('get_task_que', callable=returntaskque)
              server.register('get_result_que', callable=returnresultque)
          
              server.start()
          
              task = server.get_task_que()
              result = server.get_result_que()
          
              num = 0
              while True:
                  num += 1
                  task.put('工作%s'%num)
                  time.sleep(1)
                  if not result.empty():
                      print('结果是:%s'%result.get())
          
          • Worker 负责处理任务,并返回结果
          from multiprocessing.managers import BaseManager
          import time
          
          
          class Worker(BaseManager):
              pass
          
          if __name__ == '__main__':
          
              ip = 'XXXXXXXXXXXXXX'
              port = 8888
              key = b'123'
          
              worker = Worker((ip,port),key)
          
              worker.register('get_task_que')
              worker.register('get_result_que')
          
              worker.connect()
          
              task = worker.get_task_que()
              result = worker.get_result_que()
          
              while True:
                      if not task.empty():
                          data = task.get()
                          print('收到{0}'.format(data))
                          time.sleep(2)
                          result.put('完成{0}'.format(data))
                          time.sleep(1)
          

迭代器 Iterator

  • 一次产生一个对象,可以装下无限大的对象

  • 可迭代 Iterable 可用于for循环的对象

  • 迭代器一定是一个可迭代的,但是可迭代的不一定是迭代器

  • 将一个可迭代对象转换为迭代器

    • 通过iter()函数

      from collections import Iterator
      
      list = [i for i in range(5)]
      print(isinstance(list, Iterator))
      
      list = iter(list)
      print(isinstance(list, Iterator))
      
      # > False
      # > True
      
    • 通过__iter__()函数

      from collections import Iterator
      
      list = [i for i in range(5)]
      print(isinstance(list, Iterator))
      
      list = list.__iter__()
      print(isinstance(list, Iterator))
      
      # > False
      # > True
      
  • 使用方式

    • 使用for循环

      
      
      for i in list:
         print(i)
      
    • 使用next()函数,直到没有值抛出StopIteration异常

      list = iter([i for i in range(5)])
      next(list)
      next(list)
      ....
      
    • 使用__next__()函数,直到没有值抛出StopIteration异常

      list = iter([i for i in range(5)])
      list.__next__()
      list.__next__()
      ....
      

生成器

  • 一边循环,一边计算下一个对象,直到遇见yield语句后停止,每次调用只返回一个值

  • 满足三个条件

    • 每次调用都产生一个用于for循环遍历的对象

    • 元素到达最后一个后抛出StopIteration异常

    • 可以被next调用

  • 定义生成器

    • 直接使用生成器

      • 列表生成表达式 list = [i for i in range(5)]

      • 生成器 gen = (i for i in range(5))

    • 函数中包含yield语句

      def func(n):
          st = '产品'
          num = 0
          while num < n:
              yield st+str(num)
              num += 1
              
      g = func(5)
      print(list(g))  # 将生成器转化为列表,方便查看,也可以使用next(g)挨个输出值
      
      # > ['产品0', '产品1', '产品2', '产品3', '产品4']
      
    • 可使用send()函数向生成器发送值

      • 注意: 生成器每执行一次都会在yield处停止,yield前的变量可以接收send发送的值,yield后是生成器返回的值
      def func():
          num = yield
          print(num)
      
      g = func()
      next(g)
      g.send(5)
      
      # > 5
      
    • 特别注意: 无论是next()还是send(),调用后都会在下一个yield语句处停止

  • yield from

    • 相当于中间层,每当生成器生成一个元素,都由中间层将元素传递给主函数

    • 包含yield from的生成器叫做委派生成器

    def func():
        yield from 'asds'
    
    g = func()
    print(next(g))
    print(next(g))
    print(next(g))
    print(next(g))
    
    # > a
    # > s
    # > d
    # > s
    

协程

  • 非抢占式多任务,协程允许不同入口点不同位置执行程序

  • 多线程之间的切换会消耗一定的资源,一些高并发的程序在执行时,如果使用多线程,多线程之间的切换会消耗大量的资源和时间,但协程之间的切换却消耗的资源少

  • 协程的实现

    • 使用next()预激协程

    • 使用yield获取返回值

    • 使用send()发送值

  • 协程的四个状态

    • GEN-CREATED 等待开始执行

    • GEN-RUNNING 解释器正在执行

    • GEN-SUSPENED 在yield语句处暂停

    • GEN-CLOSED 执行结束

  • 协程终止

    • 可以使用哨兵值结束
  • 获取协程的返回值

    import time
    
    def average():
        sum = 0
        count = 0
        avrge = 0
        while True:
            data = yield (avrge,sum,count)
            if data == None:
                break
            sum = sum + data
            count += 1
            avrge = sum / count
        print('协程结束')
    
    g = average()
    next(g)
    
    for i in range(1,20,3):
        avr,sum,count = g.send(i)
        print('总数为:{0} , 个数为:{1} , 平均数为{2}'.format(sum,count,avr))
        time.sleep(1)
    
    
  • 协程的消费者-生产者模型

import time

def Producer():
    num = 0
    while True:
        num += 1
        print('生产者:总产品为%s'%num)
        num = yield num
        time.sleep(0.5)


def Consumer():
    num = 0
    while True:
        num = yield num
        num -= 1
        print('消费者:总产品为%s'%num)
        time.sleep(0.5)

p = Producer()
c = Consumer()
num = next(p)
next(c)
while True:
    num = c.send(num)
    num = p.send(num)

结构化文件储存

  • 为了解决不同设备之间的数据交换

  • xml库

    • 构成: 处理指令,xml版本和编码

      • 根元素: 树形结构的根元素,有且只能有一个

      • 子元素: 可以有多个

      • 属性: 定义在括号中

      • 内容: 标签中定义的信息

      • 注释: 使用``

    • 保留字符

      • 转义 保留字符需要用转义进行代替

      • CDATA定义的字符不进行转义,相当于python中的r'', <!{CDATA[不需要进行转义的字符]}>

    • 命名空间

      • xmlns,是xml name space的缩写

      • 直接在标签中添加相应空间

    • SAX (Simple API for xml)

      • 基于事件驱动

      • 包含解析器和事件处理两个部分

      • 流式读取

      • 速度快

    • DOM (Document Object Model)

      • minidom类

        • parse(file) 打开xml文件,返回节点树

        • documentElement 返回xml的根节点

        • creatElement(tag) 创建新节点

        • createAttribute(attr) 创建此节点的新属性

        • getElementsByTagName(tag) 获取此节点下的名为tag标签的集合

        • getAttribute(attr) 返回此节点的attr属性的值

        • parentNode 返回当前节点的父节点

        • previousSibling 返回此节点的前一个兄弟节点

        • nextSibling 返回此节点的下一个兄弟节点

        • childNodes 返回当前节点的子节点列表

        • firstChild 返回第一个子节点

        • lastChild 返回最后一个子节点

        • nodeName 返回此节点的标签名

        • 注意:节点中的汉字也是有节点的,是text节点

      • etree.ElementTree类

        • Element(标签名) 创建一个节点

        • SubElement(parent,tag) 生成一个子节点,并添加到调用函数的节点

        • ElementTree(tag) 生成一个节点树,将tag标签作为根

        • 节点树.write(file) 将节点树写入文件

        • set(key,vlue) 修改属性

        • append(子节点) 此节点最后添加子节点

        • remove(子节点) 删除此节点子节点

        • parse(file) 打开xml文件,返回节点树

        • getroot() 返回节点树的根节点

        • iter(tag) 返回此节点下的所有节点,如果指定标签名,则遍历所有指定标签名

        • find() 查找第一个匹配的节点

        • findall() 查找此节点下所有匹配的子节点,返回集合

        • tag 标签名

        • text 标签的文本值

        • attrib 返回标签所有属性的字典

# 利用etree创建一个xml文件
import xml.etree.ElementTree

root = xml.etree.ElementTree.Element('Root')

name = xml.etree.ElementTree.SubElement(root,'Name')
name.text = 'Jack'

age = xml.etree.ElementTree.SubElement(root,'Age')
age.text = '22'

tree = xml.etree.ElementTree.ElementTree(root)

tree.write('a.xml')

  • json库

    • 轻量级的数据交换工具 (JavaScripObjectNotation)

    • 类似于字典,基于key-value形式

    • 常用函数

      • 以文件形式存在

        • dump(file) 写入文件,转化为json对象

        • load(file) 读取文件,转化为python对象

      • 以数据形式存在

        • dumps(data) 写入内容,转化为json对象

        • loads(data) 读取内容,转化为python对象

import json

data = {
    'name':'jack',
    'age':18,
    'sex':'male'
}

# 写入json文件
with open('a.json','w')as f:
    json.dump(data,f)

#读取json文件
with open('a.json','r')as f:
    data = json.load(f)
print(type(data))
print(data)

# > <class 'dict'>
# > {'name': 'jack', 'age': 18, 'sex': 'male'}

re 正则表达式模块

  • 转义字符

    • 重复限定符

      . 匹配除换行符\n之外的所有字符

      ^ 匹配字符串开头

      $ 匹配字符串结尾

      * 匹配前一个字符0次以上,贪婪模式

      + 匹配前一个字符1次以上,贪婪模式

      ? 匹配前一个字符0次或1次,贪婪模式

      {m,n} 匹配前一个字符至少m次,最多n次,当没有n时则为无限次,贪婪模式

      [] 字符集,匹配字符集中的任意字符,字符客单个给出,也可范围给出

      () 分组,分组从0开始

      | 或者

      *? +? ?? {m,n}? 非贪婪模式,尽可能少的匹配字符

      import re
      
      text = '<div>name</div><div>age</div>'
      
      regex = re.search(r'<div>.*?</div>',text)
      print(regex.group())
      # > <div>name</div>
      
    • 特殊字符集

      r 原始字符串,字符串中的特殊字符不需要再进行转义,否则则需要使用\进行转义

      [\u4E00-\u9FA5] 汉字

      \b 单词边界

      \d 数字 [0-9]

      \s 任何空白字符 [\n\t\r\f\v<空格>]

      \w 匹配包括下划线的任何字字符 [A-Za-z0-9_]

      除汉字外,上述特殊集大写为与之相反的意思

  • flags 编译标志位

    re.I 不区分大小写

    re.M 多行匹配

    re.U 根据Unicode字符集解析字符

    re.S 使.匹配包括换行符在内的所有字符

  • compile(pattern) 编译正则表达式,返回正则表达式对象

    • 正则表达式对象可以直接调用以下函数,也可使用re进行调用.
    import re
    
    text = 'https://docs.python.org/3/library/re.html#regular-expression-syntax'
    
    #使用re进行调用
    regex = re.search(r'\d',text)
    print(regex)
    
    # > <_sre.SRE_Match object; span=(24, 25), match='3'>
    
    #使用compile调用
    par = re.compile(r'\d')
    regex = par.search(text)
    print(regex)
    
    # > <_sre.SRE_Match object; span=(24, 25), match='3'>
    
    • search(pattern,string[,flags]) 查找,查找字符串的所有位置,返回一个匹配对象

    • match(pattern,string[,flags]) 匹配,匹配字符串的开头位置,与search不同,返回一个匹配对象

    import re
    
    text = 'https://docs.python.org/3/library/re.html#regular-expression-syntax'
    regex = re.match('library',text)
    print(regex)
    # > None
    
    regex = re.match('https',text)
    print(regex)
    # > <_sre.SRE_Match object; span=(0, 5), match='https'> 
    
    • findall(pattern,string[,flags]) 查找字符串中所有匹配正则表达式的内容,返回一个匹配的字符串列表
    import re
    
    text = 'https://docs.python.org/3/library/re.html#regular-expression-syntax'
    list = re.findall('o',text)
    print(list)
    # > ['o', 'o', 'o', 'o']
    
    • split(pattern,string[,flags]) 按照正则表达式分割字符串对象,将正则表达式中的匹配选项替换为'',并返回一个分割的字符串列表
    import re
    
    text = 'https://docs.python.org/3/library/re.html#regular-expression-syntax'
    list = re.split(r'/',text)
    print(list)
    # > ['https:', '', 'docs.python.org', '3', 'library', 're.html#regular-expression-syntax']
    
    • sub(pattern,repl,string,count) 替换查找的字符串
    import re
    
    text = 'https://docs.python.org/3/library/re.html#regular-expression-syntax'
    # 将所有字符替换为@
    regex = re.sub(r'\W','@',text)
    print(regex)
    # > https@@@docs@python@org@3@library@re@html@regular@expression@syntax
    
  • Match 匹配对象

    • 下面的regex即为一个匹配对象
    import re
        
    text = 'https://docs.python.org/3/library/re.html#regular-expression-syntax'
        
    regex = re.search(r'\d',text)
    # > <_sre.SRE_Match object; span=(24, 25), match='3'>
    
    • 匹配对象的布尔值始终为True,一旦没有匹配到的对象,则返回None

    • group(index) 当index没有或者为0时,返回匹配对象的整个字符串,当index不为0时,返回正则表达式中用()括起来的字符

    regex.group()
    # > 3
    
    • groups() 返回被匹配字符串的元组
    regex = re.search(r'(org).+(syntax$)',text)
    # > ('org', 'syntax')
    
    • start() 返回匹配开始位置
    regex.start()
    # > 24
    
    • end() 返回匹配结束位置
    regex.end()
    # > 25
    
    • span() 返回包含start和end的元组
    regex.span()
    # > (24, 25)
    

推荐阅读更多精彩内容

  • 一、快捷键 ctr+b 执行ctr+/ 单行注释ctr+c ...
    o_8319阅读 1,729评论 0 9
  • Scala与Java的关系 Scala与Java的关系是非常紧密的!! 因为Scala是基于Java虚拟机,也就是...
    灯火gg阅读 1,346评论 0 23
  • 一直以来,我都需要有人去告诉我怎样去生活,给我现成的答案,但是现在,在天津的年末照旧雾霾沉沉,夜空中最亮的启明星难...
    D八两阅读 77评论 2 2
  • 受不了太多世俗和压迫,我最终还是逃了出来,手里捏的是去成都的火车票,下一站去哪,我还是没想好,去云南还是西藏?窗外...
    苍耳loveyou阅读 194评论 0 0
  • 虽然撒盐樱花练习又失败了、但是我还没放弃,没尝试完美之前先更其他的练习,今天又练习用纸巾擦出的效果、勉强能看到天空...
    鹿小乖hi阅读 104评论 5 3