Py学习  »  Python

【Python开个脑洞】如何做一个运营小姐姐爱不释手的解压缩小工具

蚂蚁学Python • 4 年前 • 606 次点击  

此前接单做了一个批量解压缩小工具后脑洞大开,打算给运营小姐姐们做一个小工具,解放一下她们时间,同时享受一下小姐姐们的崇拜的眼神......好吧,话不多说,咱们直接开始:

使用场景与需求分析

解释一下使用场景:公司为了数据安全,给所有下载的数据压缩包设置了一个6位数字密码,密码使用短信发送到下载人指定手机上。如果一次下载单个文件,那解压缩很简单,但是如果下载的文件数量较多,一个一个压缩包与密码短信进行配对那就有点让人烦躁了。

因此,小工具要实现的需求就是:能够批量的将压缩包与密码短信文本进行自动匹配,将匹配到的压缩包进行解压缩;同时,要实现一个交互界面,并打包成可执行文件。


分步实现

有了需求,那么就是一步一步实现它:

  1. 要打包成exe可执行文件,可以使用pyinstaller进行打包,若要打包文件最小,需要创建一个虚拟环境,安装最少的第三方包,因此,使用pycharm进行虚拟环境创建,并安装需要使用到的包。
pycharm创建虚拟环境

安装需要使用到的包:pyinstaller、pyzipper(zipfile不支持我司的压缩包格式解压)

安装需要的包
  1. 实现交互界面,使用了tkinter
# -*- coding: utf-8 -*-
# @author: Lin Wei
# @contact: 580813@qq.com
# @time: 2021/11/23 10:35
# @file: unzip_file.py
# @desc:

import tkinter as tk
from tkinter import filedialog
from extract_files import ExtractFiles  # 需要自行实现的方法

# 定义一个tk窗口
wd = tk.Tk()
wd.title('解压小程序V1.0')
wd.geometry('800x500')

# 设置选择解压缩文件包所在路径的按钮及功能
# 定义选择压缩包的路径为tk的字符串变量
unzip_files_path = tk.StringVar()


def get_files_path():
    """
    运行函数将设置路径
    """

    unzip_files_path.set(filedialog.askdirectory())


# 定义压缩包路径的标签,并设置所在位置
files_path_label = tk.Label(wd, text='需要批量解压的文件路径:')
files_path_label.grid(row=0, column=0)
# 定义压缩包路径的输入框,并设置所在位置,其中输入框所显示内容为tk变量unzip_files_path
files_path_entry = tk.Entry(wd, textvariable=unzip_files_path, width=80)
files_path_entry.grid(row=0, column=1)
# 定义压缩包路径的选择按钮,并设置所在位置,其中按钮点击的命令为get_unzip_files_path
files_path_button = tk.Button(wd, text='选择路径', command=get_files_path)
files_path_button.grid(row=0, column=2)


# 设置解压缩密码文本的text框,同时实现可输入或选择txt文件进行读取的功能
# 定义选择解压缩密码文本文件的函数
def choose_password_text():
    password_text_path = filedialog.askopenfilename(
        title='请选择解压缩的密码文本',
        initialdir='/',
        filetypes=[('文本文档''*.txt')]
    )
    if password_text_path:
        text = 'TXT文件错误或编码非utf-8和ansi,请在文本框中直接输入密码文本'
        for code in ['utf-8''ansi']:
            try:
                with open(password_text_path, 'r', encoding=code) as fin:
                    text = fin.read()
                break
            except Exception:
                pass
        password_text_text.delete(1.0'end')
        password_text_text.insert(1.0, text)


info_mes = """这里写上短信文本的示例,作为提示"""

# 定义密码文本的标签,并设置所在位置
password_text_label = tk.Label(wd, text='解压缩文件的密码短信文本:')
password_text_label.grid(row=1 , column=0)
# 定义密码文本的输入框,并设置所在位置,其中输入框所显示内容为tk变量password_text
password_text_text = tk.Text(wd, height=15)
password_text_text.insert(1.0f'直接在此粘贴短信文本,示例如下:\n{info_mes}\n(可以多条)\n也可以点击左侧按钮读取存放在TXT文件中的短信文本')
password_text_text.grid(row=1, column=1, sticky=tk.E+tk.W+tk.N)
# 定义压缩包路径的选择按钮,并设置所在位置,其中按钮点击的命令为choose_path1
password_text_button = tk.Button(wd, text='选择密码文件', command=choose_password_text)
password_text_button.grid(row=1, column=2)


# 设置解压运行按钮以及解压缩的方法
def run_unzip():
    # 从路径选择框里获取路径文本
    root_path = files_path_entry.get()
    # 从密码输入框里获取密码文本
    password_text = password_text_text.get(1.0'end')
    # 清除执行情况框中的全部内容
    run_message_text.delete(1.0, tk.END)
    # 运行解压缩程序
    obj = ExtractFiles(root_path, password_text)
    obj.get_all_zip_files()
    obj.parse_password()
    obj.matching()
    obj.do_extract()
    # 往执行情况框里添加内容
    # 添加成功解压缩的记录
    run_message_text.insert(tk.END, '\n'.join([f'成功解压{x}' for x in obj.success_files_list]) + '\n')
    # 添加解压失败的记录(注:有匹配到密码,但解压缩失败)
    run_message_text.insert(tk.END, '\n'.join([f'解压失败{x}' for x in obj.fail_files_list])+'\n')
    # 添加未匹配到压缩包的文件记录
    run_message_text.insert(tk.END, '未匹配到密码的压缩包有:\n' + '\n'.join([f'{x}' for x in obj.not_found_pwd_files])+'\n')
    # 添加整体运行情况统计结果
    run_message_text.insert(tk.END, f"""
共找到{obj.zip_file_count}个压缩包,其中{len(obj.success_files_list)}个解压成功,{len(obj.fail_files_list)}个解压失败,
{len(obj.not_found_pwd_files)}个压缩包未匹配到密码,有{len(obj.not_found_file_password)}个密码未匹配到压缩包文件。"""
)


submit_button = tk.Button(wd, text='提交运行', command=run_unzip, bg='yellow')
submit_button.grid(row=2, column=2)

# 解压缩文件的执行情况
run_message_label = tk.Label(wd, text='解压情况:')
run_message_label.grid(row=5, column=0)
# 定义密码文本的输入框,并设置所在位置,其中输入框所显示内容为tk变量password_text
run_message_text = tk.Text(wd, height=15)
run_message_text.grid(row=5, column=1, sticky=tk.E+tk.W+tk.N)

# 添加一个说明标签
info_label = tk.Label(wd, text='本小程序为学习Python程序进行开发,仅用于方便工作使用,不得用于任何违法事宜。Power By Wei Lin')
info_label.grid(row=6, column=1)

# 使窗口保持刷新
wd.mainloop()

思路解析:很基础的页面,不过记得哪个大佬说过,先实现核心的功能,其他的(美化)以后再说。此外,代码中已经把后续写的一些代码贴上了,在实际编写过程中实际上是先预留空位,后续代码实现后补充的。

  1. 有了界面,要实现功能
    3.1先实现单个文件解压缩的功能
import pathlib
import pyzipper as zp


class ExtractFile:
    def __init__(self, file_path, pwd=None, out_path=None):
        """
        初始化解压文件类的信息
        :param file_path: 需解压文件的完整路径
        :param pwd: 解压的密码,默认为None
        :param out_path: 解压出来的文件存放的路径,默认为None,即解压缩到压缩文件的相同目录下
        """

        self.file_path = file_path
        self.pwd = pwd
        # 判断一下文件是否是zip压缩包,给self.is_zip_file赋值
        if zp.is_zipfile(self.file_path):
            self.is_zip_file = True
        else:
            self.is_zip_file = False
            print(f'{self.file_path}文件并不是压缩文件')
        # 判断输出地址是否为正确路径,如正确则定义为解压缩后的输出路径,如果错误,则以压缩包所在路径为准
        if out_path is not None:
            out_path = pathlib.Path(out_path)  # 将out_path转成path对象
            if out_path.is_dir():  # 根据out_path对象的is_dir方法判断是否文件夹
                self.out_path = out_path
            else:  # 如果不是文件夹则将文件所在的路径作为输出文件夹
                self.out_path = pathlib.Path(self.file_path).parent
        else:  # 如果不是文件夹则将文件所在的路径作为输出文件夹
            self.out_path = pathlib.Path(self.file_path).parent

    def extract_all(self, pwd, out_path):
        """
        解压缩所有文件
        :return: bool
        """

        if self.is_zip_file:
            try:
                with zp.AESZipFile(self.file_path, mode='r'as zip_file:
                    zip_file.extractall(path=out_path, pwd=pwd.encode('utf-8'))
                return True
            except Exception as e:
                print(f'解压缩文件错误,错误信息为: {e}')
                return False
        else:
            print(f'{self.file_path}文件并不是压缩文件')
            return False
            
      
if __name__ == '__main__':
    file_path = 'd:/测试文件.zip'
    obj = ExtractFile(file_path)
    obj.run_extract()

思路解析:上面的代码都有注释就不一一解释了,需要提一下的是,单独建了一个py文件存放这个代码,以便调试以及后续更新功能。

3.2再实现批量解压缩文件的功能

import pathlib
import re
from extract_single_file import ExtractFile


class ExtractFiles:
 def __init__(self, root_path, password_text):
     # 需要解压缩文件所在的路径
     self.root_path = root_path
     # 初始化zip文件的个数
     self.zip_file_count = 0
     # 初始化未找到匹配密码的文件list
     self.not_found_pwd_files = []
     # 根目录中所有的zip文件字典,其中key为去除扩展名后的文件名,value为list格式,为zip文件的完整路径
     self.zip_file_dict = {}
     # 密码文本
     self.password_text = password_text
     # 初始化密码字典,其中key为短信中任务名称(与压缩包名称对应),value为set集合格式,为六位数字密码
     self.password_dict = {}
     # 初始化密码信息的个数
     self.password_count = 0
     # 初始化解压缩的dict,key为需要解压缩文件的完整路径名称,value为匹配到的密码(list格式)
     self.unzip_dict = {}
     # 初始化未找到匹配文件的密码list
     self.not_found_file_password = []
     # 初始化成功解压缩的文件list
     self.success_files_list = []
     # 初始化解压缩失败的文件list
     self.fail_files_list = []

 def get_all_zip_files(self):
     """
     为防止模块报毒,使用pathlib模块遍历根目录下的所有zip文件,生成zip_file_dict
     """

     for file in pathlib.Path(self.root_path).rglob('*.zip'):
         if not file.name.startswith('~'):
             self.zip_file_count += 1
             full_path = str(file)  # 完整路径是file对象的str格式
             filename = file.stem  # file对象的stem是去除扩展名后的文件名
             if filename in self.zip_file_dict.keys():
                 self.zip_file_dict[filename].append(full_path)
             else:
                 self.zip_file_dict[filename] = [full_path]

 def parse_password(self):
     """
     使用正则解析密码字符串,生成字典
     """

     if self.password_text is not None:
         res = re.findall(r'任务名为(.*?) 的压缩包,解压密码为(\d{6}),', self.password_text)
         if len(res) > 0:
             for key, value in res:
                 self.password_count += 1
                 if key in self.password_dict.keys():
                     self.password_dict[key].add(value)
                 else:
                     self.password_dict[key] = {value}

 def matching(self):
     """
     给压缩包与密码配对
     """

     # 创建文件字典、密码字典的副本以便后续进行修改操作
     file_dict = self.zip_file_dict.copy()
     password_dict = self.password_dict.copy()
     # 遍历匹配
     for i in range(len(file_dict)):  # 根据file_dict的长度,确定循环次数
         # 每一次从file_dict中取出一对键值对,分别为不含扩展名的文件名与文件绝对路径list
         file, full_path_list = file_dict.popitem()
         if  file in password_dict.keys():  # 如果压缩文件名在密码字典的key中
             # 取出密码的list
             pwd_list = password_dict.pop(file)
             # 将绝对路径与密码进行配对放入待解压的文件字典中
             for full_path in full_path_list:
                 self.unzip_dict[full_path] = pwd_list
         else:  # 否则将文件绝对路径放入未找到密码的文件list中
             for full_path in full_path_list:
                 self.not_found_pwd_files.append(full_path)
     if len(password_dict) > 0:  # 如果匹配结束后,密码字典中还有剩余的键值对,把它们放入未找到文件的密码list中
         for i in range(len(password_dict)):
             key, pwd_list = password_dict.popitem()
             for pwd in pwd_list:
                 self.not_found_file_password.append((key, pwd))

 def do_extract(self):
     """
     运行批量解压 
     """

     for file, pwd_list in self.unzip_dict.items():  # 遍历待解压的文件字典
         add_to_fail = True  # 将加入失败list初始化为True
         for pwd in pwd_list:  # 遍历密码list进行解压
             extract_obj = ExtractFile(file, pwd)
             res = extract_obj.extract_all(pwd=extract_obj.pwd, out_path=extract_obj.out_path)
             if res:  # 如果成功
                 self.success_files_list.append(file)
                 add_to_fail = False
                 break
         if add_to_fail:  # 如果值为True,说明所有密码均没有成功解压缩文件,因此加入有密码但解压失败的文件list中
             self.fail_files_list.append(file)

思路解析:这部分代码主要实现读取出所有zip文件,解析短信密码文本,并且进行匹配解压的过程,考虑到方便使用者,因此将文件与密码的匹配设计为多对多的方式,即目录下可能存在相同文件名的压缩包(同名文件可能在子文件夹中),密码文本可能存在重复或多条短信密码均为相同文件名的情形。
此外,解析密码文本是,需要用到正则表达式,找出文本中的文件名与密码的键值对,我使用的是“r'任务名为(.*?) 的压缩包,解压密码为(\d{6}),'”,具体情形需要具体修改。

  1. 打包成exe
    5.1 打开cmd,进入到虚拟环境的路径中:我的是"D:/work/pythonproject/venv/scripts/activate.bat"
    5.2 在cmd中,进入到存放py文件的路径,运行pyinstaller命令,我的是“pyinstaller -Fw unzip_file.py”(命令就不具体解释了,详见pyinstaller的教程)

打包结束后,目录下会出现一个dist目录,里面就是打包好的exe文件了,测试运行了一下,一切正常。

只有11M
特别说明:

如果在代码中使用os模块,则在后续打包成exe文件时,会被杀毒软件识别为病毒,所以,一开始就要避免使用os模块。(此前版本代码中使用os.walk进行文件遍历,就踩坑了,还好使用的地方不多,都改成pathlib了)


加个彩蛋

前面那些只是正常的解压缩步骤,适用的场景很小,不能体现咱的水平,咱得憋个大招:搞个暴力破解压缩包密码的功能,要是没收到密码短信或者误删短信了,还是能够解压缩文件,这才牛^_^(后来想了想,似乎这个功能也没什么软用,但就是要折腾,脑洞继续开)。

暴力破解功能实现

暴力破解只针对单个文件,那么直接在前面单个解压文件的程序中增加这个功能即可,由于密码为6位数字,一共有100万种组合,因此单线程肯定是不行的,要上多线程。此外,试验密码的顺序对于破解密码的消耗时间是有巨大影响的,因此,提供一些密码的排序方式供选择“正序、倒序、1优先、2优先等等”,以及一个“随机”方式,如果选择的方式恰好命中了正确密码的范围,那么将大大节约破解时间(有点买彩票的感觉^_^),上代码:

from extract_single_file import ExtractFile
import pyzipper as zp
import queue
import threading
import time
from itertools import product
from random import shuffle

comm = False  # 定义一个全局变量信号
pwd_queue = queue.Queue()  # 定义一个存放待测试密码的queue,用于多线程通信
fail_queue = queue.Queue()  # 定义一个存放失败密码的queue
method_dict = {
    '1优先'1,
    '2优先'2,
    '3优先'3,
    '4优先'4,
    '5优先'5,
    '6优先'6,
    '7优先'7,
    '8优先'8,
}  # 定义优先级数字字典,0和9分别是正序和倒序,无需特别定义


class CrackExtract(ExtractFile):
    def __init__(self, file_path, pwd_length=6, method=None, pwd=None, out_path=None):
        # 继承父类属性
        super(CrackExtract, self).__init__(file_path, pwd, out_path)
        # 初始化密码长度
        self.pwd_length = int(pwd_length)
        # 初始化暴力破解的顺序
        self.method = method
        # 初始化打开zip文件,后续需要关闭
        self.zip_file = zp.AESZipFile(self.file_path, mode='r')
        # 初始化尝试次数
        self.try_count = 0

    def product_pwd_queue(self):
        """
        创建存放待破解密码的queue
        """

        global comm, pwd_queue  # 声明全局变量
        num_list = list(range(10))
        if self.method == '逆序':  # 如果是逆序方式,则倒转num_list
            num_list = num_list[::-1]
        elif self.method in method_dict.keys():  # 如果数字优先,则重新组合num_list,将优先数字向两测重新排序
            k = method_dict[self.method] + 1
            part1 = num_list[:k]
            part2 = num_list[k:]
            num_list = []
            while part1 or part2:
                if part1:
                    num_list.append(part1.pop())
                if part2:
                    num_list.append(part2.pop(0))
        # 使用itertools的product方法创建6位密码的生成器,拼接后放入pwd_queue中
        if self.method == '随机':  # 使用random模块的shuffle随机打散num_list
            pwd_list = [x for x in product(num_list, repeat=self.pwd_length)]
            shuffle(pwd_list)
            for s in pwd_list:
                if comm:
                    return
                pwd_queue.put(''.join([str(x) for x in s]))
        else:
            for s in product(num_list, repeat=self.pwd_length):
                if comm:  # 如果信号已经转为True,无需继续生产密码
                    return
                pwd_queue.put(''.join([str(x) for x in s]))

    def crack_extract(self):
        """
        尝试解压缩程序
        """

        # 声明全局变量
        global comm, pwd_queue, fail_queue
        while True:  # 开启循环
            if not pwd_queue.empty():  # pwd_queue不为空时
                pwd = pwd_queue.get()  # 取出一个密码尝试解压缩是否成功
                try:
                    self.zip_file.extractall(path=self.out_path, pwd=pwd.encode('utf-8'))
                    pwd_queue.queue.clear()  # 若成功清除pwd_queue剩余密码
                    comm = True  # 若成功,修改信号为True
                    self.pwd = pwd.encode('utf-8')
                    print(f'解压缩成功,密码是{pwd}')
                except (TypeError, RuntimeError):
                    self.try_count += 1
                    fail_queue.put(pwd)  # 若失败,将失败密码存入失败队列
                    print(f'正在解压,尝试密码串{pwd}失败,还剩余{pwd_queue.qsize()}个密码串待试验')
                pwd_queue.task_done()  # 处理完成
            elif comm:  # 若信号为True,终止循环
                break
            else:  # 若pwd_queue为空,等待0.2秒
                time.sleep(0.2)

    def run_extract(self):
        """
        运行解压程序
        """

        # 声明全局变量
        global comm, pwd_queue, fail_queue
        comm = False  # 将信号初始化为False
        # 初始化多线程list,并添加一个声明密码queue的线程
        threadlist = [threading.Thread(target=self.product_pwd_queue, name='create')]
        # 添加10个破解线程
        for x in range(10):
            th = threading.Thread(target=self.crack_extract, name=f'extract{x}')
            threadlist.append(th)
        start = time.time()  # 开始时间
        for t in threadlist:  # 开启线程
            t.start()
        for t in threadlist:  # 等待线程结束
            t.join()
        end = time.time()  # 结束时间
        cost_time = round(end - start, 4 )  # 所用时长(秒)
        self.zip_file.close()  # 关闭zip文件
        print(f'解压成功,总用时{cost_time}秒')
        return self.pwd, self.try_count, cost_time


if __name__ == '__main__':
    file = r'D:\test\upzip\测试文件.zip'
    obj = CrackExtract(file, method='3优先')
    obj.run_extract()
暴力破解测试截图
破解功能的界面

由于不想直接体现在程序界面中,因此,破解功能的界面写了一个新的交互界面,代码如下:

import tkinter as tk
from tkinter import filedialog, messagebox
from crack_extract import CrackExtract

RADIO_DICT = {
    0'正序',
    9'倒序',
    1'1优先',
    2'2优先',
    3'3优先',
    4'4优先',
    5'5优先',
    6'6优先',
    7'7优先',
    8'8优先',
    10'随机'
}


class PowerExtractWindow:
    def __init__(self):
        self.wd = tk.Toplevel()
        self.wd.title('破解密码小程序V1.0')
        self.wd.geometry('600x200')
        self.file_path = tk.StringVar()
        self.path_button = None
        self.path_entry = None
        self.submit_button = None
        self.mes_window = None
        self.radio = tk.IntVar()

    def choose_file(self):
        """
        设置选择解压缩文件包所在路径的按钮及功能方法
        """

        self.file_path.set(filedialog.askopenfilename(
            title='请选择文件',
            initialdir='/',
            filetypes=[('压缩文件''*.zip')]
        ))

    def power_extract(self):
        """
        开启破解的方法
        """

        # 由于破解耗时较长,弹出一个窗口让用户再次确认是否开始
        answer = messagebox.askokcancel('确认是否开始''破解密码需要等待较长时间,你确定更要开始吗?')
        if answer:  # 确认开始
            self.mes_window.delete(1.0, tk.END)  # 清理信息窗口的内容
            # 初始化破解的对象
            crack_obj = CrackExtract(self.path_entry.get(), method=RADIO_DICT.get(self.radio.get()))
            # 执行破解程序,并获得正确破解后的密码、尝试次数、用时等信息,并输出到窗口上
            pwd, try_count, cost_time = crack_obj.run_extract()
            mes_text = f'解压文件{self.path_entry.get()}成功,正确密码是{pwd.decode("utf-8")},' \
                       f'共尝试了{try_count}次,总耗时{int(cost_time // 60)}{cost_time % 60}秒'
            self.mes_window.insert(1.0, mes_text)

    def layout(self):
        """
        对窗口进行布局的方法
        """

        # 定义压缩包路径的选择按钮,并设置所在位置,其中按钮点击的命令为get_unzip_files_path
        self.path_button = tk.Button(self.wd, text='请选择破解文件', command=self.choose_file)
        self.path_button.grid(row=0, column=0)
        # 定义压缩包路径的输入框,并设置所在位置,其中输入框所显示内容为tk变量unzip_files_path
        self.path_entry = tk.Entry(self.wd, textvariable=self.file_path, width=60)
        self.path_entry.grid(row=0, column=1, columnspan=10)
        # 定义压缩包路径的输入框,并设置所在位置,其中输入框所显示内容为tk变量unzip_files_path
        self.submit_button = tk.Button(self.wd, text='开始破解', command=self.power_extract, bg='red')
        self.submit_button.grid(row=5, column=0)
        
        # 定义选择破解密码的排序方式,单选项,并布局
        r_label = tk.Label(self.wd, text='请选择破解的密码排序方式:')
        r_label.grid(row=1, column=0, rowspan=3)
        col = 0
        for k, v in RADIO_DICT.items():
            exec(f'tk.Radiobutton(self.wd, text="{v}", variable=self.radio, value={k}).grid(row={col//5+1}, column={col%5+1})')
            col += 1

        # 定义运行消息窗口
        self.mes_window = tk.Text(self.wd, height=5, width=60)
        self.mes_window.grid(row=5, column=1, columnspan=10)

        self.wd.mainloop()


def open_power_window():
    """
    打开破解的窗口函数
    """

    new_wd = PowerExtractWindow()
    new_wd.layout()


if __name__ == '__main__':
    wd = tk.Tk()
    wd.title('暴力破解testV1.0')
    wd.geometry('400x200')
    submit_button = tk.Button(wd, text='打开二级窗口', command=open_power_window, bg='red')
    submit_button.pack()
    wd.mainloop()
破解窗口的测试截图

将破解功能加入原界面中(隐藏界面)

给压缩包加密码本身是为了数据保密,而暴力破解密码,某种角度看,似乎有点不合适,因此,直接放在界面中不太合适,咱搞个隐藏界面,必须输入密码口令才能打开,至于口令就用“上上下下左右左右BABA”(致敬一下经典,嘿嘿)。

以下代码在原交互界面上增加
# 引入新的破解界面
from power_unzip import open_power_window  

def judge(func):
    """
    装饰器函数,用于判断是否为特殊口令打开隐藏界面,
    :param func: 需要增加功能的函数
    """

    def open_power_wd():
        # 获取密码文本的输入框的输入内容,如何符合条件,则运行破解程序
        password_text = password_text_text.get(1.0'end')
        if password_text.strip() == '上上下下左右左右BABA'
            open_power_window()
        return func()

    return open_power_wd
将装饰器放置在需要装饰的函数前

完成上述步骤后,再次使用pyinstaller进行打包即可。

成品展示
破解测试
破解程序的测试结果

最终总结

之所以开这么一个脑洞,其实主要是设计了一个场景,为了锻炼一下综合的应用能力,把学到的分项知识进行一次融合。

整个过程中用到的模块有:

  1. pyinstaller,打包exe
  2. tkinter,交互界面
  3. pathlib,路径与文件
  4. theater,多线程
  5. queue,多线程通信
  6. itertools.product,求多个可迭代对象的笛卡尔积
  7. random.shuffle,随机打散

在实现过程中,按自己理解进行了分项的拆解实现,以便后续进行功能的迭代;同时,应用了装饰器,在不改变原代码的情况下,增加功能等等。通过这样的过程,加深对各个功能的理解。

好吧,这次的脑洞先开到这里了,等有时间了咱继续开脑洞。


最后,推荐蚂蚁老师的《Python零基础入门到数据分析到办公自动化实战》课程,购买后加老师微信答疑:ant_learn_python




Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/123714