Top

NSD Python2 DAY02

  1. 案例1:简单的加减法数学游戏
  2. 案例2:进制转换函数
  3. 案例4:备份程序

1 案例1:简单的加减法数学游戏

1.1 问题

编写math_game.py脚本,实现以下目标:

  1. 随机生成两个100以内的数字
  2. 随机选择加法或是减法
  3. 总是使用大的数字减去小的数字
  4. 如果用户答错三次,程序给出正确答案

1.2 方案

创建4个函数,分别实现返回两数之和、返回两数之差、判断表达式正确性、是否继续计算四种方法:

1.首先调用main()函数(是否继续计算功能),main函数利用循环无限次调用exam()函数进行计算,计算结束,用户选择是否继续(此过程利用try语句捕获索引错误、ctrl+c(中断)错误、ctrl+d错误),如果选择n即结束循环,不再调用exam()函数,否则循环继续

2.调用exam()函数:

a)输出运算公式:利用列表切片将随机生成的两个数打印(这两个数利用random模块及列表生成式随机生成,并利用sort()方法进行降序排序,确保相减时一直是大的数字减小的数字),利用random模块随机生成“+”“-”号,输出在两数之间

b)用户输入值,利用for循环进行三次判断,如果运算公式结果与用户输入值相同,循环结束,系统输出“你赢了”,exam()函数执行结束,否则系统输出“你答错了”,循环继续,3次都回答错误,利用循环的else分支输出运算公式及结果

c)上诉运算公式结果:利用random模块随机生成“+”“-”值对关系调用(其中“+”“-”号作为字典键,返回和函数add()及返回差函数sub()作为值,调用时将随机生成的两个数字作为参数传递给add()函数及sub()函数)

1.3 步骤

实现此案例需要按照如下步骤进行。

步骤一:编写脚本

  1. [root@localhost day06]# vim math_game.py
  2. #!/usr/bin/env python3
  3.  
  4. import random
  5.  
  6. def add(x, y):
  7. return x + y
  8.  
  9. def sub(x, y):
  10. return x - y
  11.  
  12. def exam():
  13. cmds = {'+': add, '-': sub} # 将函数存入字典
  14. nums = [random.randint(1, 100) for i in range(2)] # 生成两个数
  15. nums.sort(reverse=True) # 降序排列
  16. op = random.choice('+-')
  17. result = cmds[op](*nums) # 调用存入字典的函数,把nums列表拆开,作为参数传入
  18. prompt = "%s %s %s = " % (nums[0], op, nums[1])
  19.  
  20. for i in range(3):
  21. try:
  22. answer = int(input(prompt))
  23. except:
  24. continue
  25.  
  26. if answer == result:
  27. print('你真棒,答对了!')
  28. break # 答对了就不要再回答了,结束循环
  29. else:
  30. print('答错了')
  31. else:
  32. print("%s%s" % (prompt, result)) # 只有循环不被break才执行
  33.  
  34.  
  35. def main():
  36. while True:
  37. exam()
  38. try:
  39. go_on = input('Continue(y/n)? ').strip()[0]
  40. except IndexError:
  41. continue
  42. except (KeyboardInterrupt, EOFError):
  43. go_on = 'n'
  44.  
  45. if go_on in 'nN':
  46. print('\nBye-bye.')
  47. break
  48.  
  49.  
  50. if __name__ == '__main__':
  51. main()

实现此案例还可利用while循环:

  1. [root@localhost day06]# vim mygui.py
  2.  
  3. #!/usr/bin/env python3
  4.  
  5. import random
  6.  
  7. def add(x, y):
  8. return x + y
  9.  
  10. def sub(x, y):
  11. return x - y
  12.  
  13. def exam():
  14. cmds = {'+': add, '-': sub} # 将函数存入字典
  15. nums = [random.randint(1, 100) for i in range(2)] # 生成两个数
  16. nums.sort(reverse=True) # 降序排列
  17. op = random.choice('+-')
  18. result = cmds[op](*nums) # 调用存入字典的函数,把nums列表拆开,作为参数传入
  19. prompt = "%s %s %s = " % (nums[0], op, nums[1])
  20. tries = 0
  21.  
  22. while tries < 3:
  23. try:
  24. answer = int(input(prompt))
  25. except:
  26. continue
  27.  
  28. if answer == result:
  29. print('你真棒,答对了!')
  30. break # 答对了就不要再回答了,结束循环
  31. else:
  32. print('答错了')
  33. tries += 1
  34. else:
  35. print("%s%s" % (prompt, result)) # 只有循环不被break才执行
  36.  
  37.  
  38. def main():
  39. while True:
  40. exam()
  41. try:
  42. go_on = input('Continue(y/n)? ').strip()[0]
  43. except IndexError:
  44. continue
  45. except (KeyboardInterrupt, EOFError):
  46. go_on = 'n'
  47.  
  48. if go_on in 'nN':
  49. print('\nBye-bye.')
  50. break
  51.  
  52.  
  53. if __name__ == '__main__':
  54. main()

步骤二:测试脚本执行

  1. [root@localhost day06]# python3 math_game.py
  2. 54 + 19 =
  3. 54 + 19 =
  4. 54 + 19 = 73
  5. 你真棒,答对了!
  6. Continue(y/n)? y
  7. 60 + 39 = 99
  8. 你真棒,答对了!
  9. Continue(y/n)? y
  10. 18 + 15 = 33
  11. 你真棒,答对了!
  12. Continue(y/n)? y
  13. 35 + 20 = 55
  14. 你真棒,答对了!
  15. Continue(y/n)? y
  16. 37 + 35 = 72
  17. 你真棒,答对了!
  18. Continue(y/n)? y
  19. 77 - 57 = 20
  20. 你真棒,答对了!
  21. Continue(y/n)? y
  22. 35 + 23 = 5
  23. 答错了
  24. 35 + 23 = 6
  25. 答错了
  26. 35 + 23 = 7
  27. 答错了
  28. 35 + 23 = 58
  29. Continue(y/n)? y
  30. 75 + 47 = 122
  31. 你真棒,答对了!
  32. Continue(y/n)? ^C
  33. Bye-bye.

2 案例2:进制转换函数

2.1 问题

创建myint.py脚本,要求如下:

  1. 基于int内建函数,创建int2函数,实现2进制字符串转换成10制数整数
  2. 基于int内建函数,创建int8函数,实现8进制字符串转换成10制数整数
  3. 基于int内建函数,创建int16函数,实现16进制字符串转换成10制数整数

2.2 方案

1. 导入functools模块

2. 修改现有的int函数,将base参数固定值

2.3 步骤

实现此案例需要按照如下步骤进行。

步骤一:编写脚本

  1. [root@localhost day06]# vim myint.py
  2. # 改造int函数,将base=2固定下来,生成的新函数叫int2
  3. int2 = partial(int, base=2)
  4. print(int2('10101100'))
  5.  
  6. int8 = partial(int, base=8)
  7. print(int8('11'))
  8.  
  9. int16 = partial(int, base=16)
  10. print(int16('11'))

步骤二:测试脚本执行,结果如图-1、图-2、图-3所示:

  1. [root@localhost day06]# python3 myint.py
  2. 172
  3. 9
  4. 17

3 案例4:备份程序

3.1 问题

编写backup.py脚本,实现以下目标:

  1. 需要支持完全和增量备份
  2. 周一执行完全备份
  3. 其他时间执行增量备份
  4. 备份文件需要打包为tar文件并使用gzip格式压缩

3.2 方案

整体框架创建3个函数,分别实现完全备份、增量备份、文件加密3种功能:

1.首先导入time模块,利用if进行判断,如果当地时间是星期一,执行完全备份函数,否则执行增量备份函数,其中,通配符%a代表时间星期几缩写,上传参数分别为要备份的原目录、目标目录、md5字典存放目录

2.调用完全备份函数:

a)首先获取新文件名,将新文件名放入目标目录下,目的是定义备份文件的绝对路径,以写压缩方式打开目标目录下新文件,将原目录写入新文件中,完成完全备份,其中os.path.join作用是将目录名和文件的基名拼接成一个完整的路径

b)了解os.walk()目录遍历器输出文件结构,利用for循环将要备份原目录中文件遍历出来作为字典键值对键, md5加密结果作为字典键值对的值(此时将原目录中文件作为上传参数调用文件加密函数),存入空字典中,字典中每个文件对应一个md5值,最后将字典写入到md5字典存放目录中

3.调用文件加密函数:将原目录文件循环读取逐一加密,返回加密结果

4.调用增量备份函数:

a)增量备份函数代码与完全备份函数基本一致

b)区别在于,备份前要先以二进制读方式打开md5字典存放目录,读取旧数据,判断旧数据中键对应的加密值与新加密值是否相同,如果不相同,则将新增内容写入到目标文件中(即只备份新数据)

5.注意:md5主要用于原文件与新文件判断

3.3 步骤

实现此案例需要按照如下步骤进行。

步骤一:编写脚本

  1. [root@localhost day06]# vim backup.py
  2. #!/usr/bin/env python3
  3.  
  4. import time
  5. import os
  6. import tarfile
  7. import hashlib
  8. import pickle
  9. #用于判断两个文件是否相同,提取每个文件中的前4字节的内容然后输出md5码进行比较
  10. def check_md5(fname):
  11. m = hashlib.md5()
  12. with open(fname, 'rb') as fobj:
  13. while True:
  14. data = fobj.read(4096)
  15. if not data:
  16. break
  17. m.update(data)
  18. return m.hexdigest()
  19.  
  20. def full_backup(src_dir, dst_dir, md5file):
  21. fname = os.path.basename(src_dir.rstrip('/'))
  22. fname = '%s_full_%s.tar.gz' % (fname, time.strftime('%Y%m%d'))
  23. fname = os.path.join(dst_dir, fname)
  24. md5dict = {}
  25.  
  26. tar = tarfile.open(fname, 'w:gz')
  27. tar.add(src_dir)
  28. tar.close()
  29.  
  30. for path, folders, files in os.walk(src_dir):
  31. for each_file in files:
  32. key = os.path.join(path, each_file)
  33. md5dict[key] = check_md5(key)
  34.  
  35. with open(md5file, 'wb') as fobj:
  36. pickle.dump(md5dict, fobj)
  37.  
  38.  
  39. def incr_backup(src_dir, dst_dir, md5file):
  40. fname = os.path.basename(src_dir.rstrip('/'))
  41. fname = '%s_incr_%s.tar.gz' % (fname, time.strftime('%Y%m%d'))
  42. fname = os.path.join(dst_dir, fname)
  43. md5dict = {}
  44.  
  45. with open(md5file, 'rb') as fobj:
  46. oldmd5 = pickle.load(fobj)
  47.  
  48. for path, folders, files in os.walk(src_dir):
  49. for each_file in files:
  50. key = os.path.join(path, each_file)
  51. md5dict[key] = check_md5(key)
  52.  
  53. with open(md5file, 'wb') as fobj:
  54. pickle.dump(md5dict, fobj)
  55.  
  56. tar = tarfile.open(fname, 'w:gz')
  57. for key in md5dict:
  58. if oldmd5.get(key) != md5dict[key]:
  59. tar.add(key)
  60. tar.close()
  61.  
  62. if __name__ == '__main__':
  63. # mkdir /tmp/demo; cp -r /etc/security /tmp/demo
  64. src_dir = '/tmp/demo/security'
  65. dst_dir = '/var/tmp/backup' # mkdir /var/tmp/backup
  66. md5file = '/var/tmp/backup/md5.data'
  67. if time.strftime('%a') == 'Mon':
  68. full_backup(src_dir, dst_dir, md5file)
  69. else:
  70. incr_backup(src_dir, dst_dir, md5file)

步骤二:测试脚本执行

  1. [root@localhost day07]# python3 backup.py
  2. [root@localhost day07]# cd /var/tmp/backup/
  3. [root@localhost backup]# ls
  4. md5.data security_full_20180502.tar.gz security_incr_20180502.tar.gz