20241402 2024-2025-2 《Python程序设计》实验四报告

课程:《Python程序设计》
班级: 2414
姓名: 刘传志
学号:20241402
实验教师:王志强
实验日期:2025年5月14日
必修/选修: 公选课

1.实验内容

考虑到现代社会对通信安全的需求,然后又想到近期即将召开或者已经召开的各种需要投票的活动或会议,我就想能否做一个内含加密算法的简单的投票系统,来帮助工作人员减少工作量,便于进行选票的收取和统计。

2. 实验过程及结果

(1)一开始只是想做一个有一些基础功能的socket连接用于投票

服务端代码(原始人版)

点击查看代码
import socket
import threading
import os
import hashlib
from datetime import datetime
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad, unpad

class VotingRound:
    def __init__(self, name, vote_mode, mode_value, vote_type, password=None):
        self.name = name
        self.vote_mode = vote_mode  # 'max' or 'fixed'
        self.mode_value = mode_value
        self.vote_type = vote_type  # '记名' or '无记名'
        self.password = password
        self.candidates = {}  # {id: [name, votes]}
        self.voters = {}      # {user_id: [candidate_ids]}
        self.invalid_votes = []
        self.status = "active"
        self.start_time = datetime.now()
    
    def add_candidate(self, name):
        cid = len(self.candidates) + 1
        self.candidates[cid] = [name, 0]
        return cid
    
    def process_vote(self, user_id, candidate_ids):
        if self.status != "active":
            return "投票已结束"
        
        # 验证投票
        if self.vote_type == '记名' and user_id in self.voters:
            return "不能重复投票"
        
        num_votes = len(candidate_ids)
        if self.vote_mode == 'max' and num_votes > self.mode_value:
            return f"最多只能选{self.mode_value}人"
        if self.vote_mode == 'fixed' and num_votes != self.mode_value:
            return f"必须选{self.mode_value}人"
        
        # 处理有效投票
        for cid in candidate_ids:
            if cid in self.candidates:
                self.candidates[cid][1] += 1
            else:
                return f"无效候选人ID: {cid}"
        
        if self.vote_type == '记名':
            self.voters[user_id] = candidate_ids
            
        return "投票成功"
    
    def get_summary(self):
        summary = [f"=== {self.name} 投票结果 ==="]
        for cid, (name, votes) in self.candidates.items():
            summary.append(f"{cid}. {name}: {votes}票")
        return "\n".join(summary)
    
    def get_candidates_info(self):
        info = [f"=== {self.name} 候选人 ==="]
        for cid, (name, _) in self.candidates.items():
            info.append(f"{cid}. {name}")
        return "\n".join(info)

class VotingSystem:
    def __init__(self):
        self.rounds = {}
        self.current_round_id = None
    
    def create_round(self, name, vote_mode, mode_value, vote_type, password=None):
        round_id = len(self.rounds) + 1
        self.rounds[round_id] = VotingRound(name, vote_mode, mode_value, vote_type, password)
        self.current_round_id = round_id
        return round_id
    
    def get_current_round(self):
        if self.current_round_id in self.rounds:
            return self.rounds[self.current_round_id]
        return None
    
    def end_round(self, round_id):
        if round_id in self.rounds:
            self.rounds[round_id].status = "closed"
            return f"轮次#{round_id}已结束"
        return "轮次不存在"
    
    def process_vote(self, user_id, candidate_ids):
        current = self.get_current_round()
        if not current:
            return "没有活动轮次"
        return current.process_vote(user_id, candidate_ids)

def encrypt(message, key, iv):
    cipher = AES.new(key, AES.MODE_CBC, iv)
    return iv + cipher.encrypt(pad(message.encode(), AES.block_size))

def decrypt(data, key):
    try:
        iv, encrypted = data[:16], data[16:]
        cipher = AES.new(key, AES.MODE_CBC, iv)
        return unpad(cipher.decrypt(encrypted), AES.block_size).decode()
    except:
        return "解密错误"

def handle_client(client_socket, system):
    key = os.urandom(16)
    iv = os.urandom(16)
    client_socket.sendall(key + iv)
    
    try:
        current_round = system.get_current_round()
        if not current_round:
            client_socket.sendall(encrypt("无活动轮次", key, iv))
            return
            
        # 发送候选人信息
        candidates_info = current_round.get_candidates_info()
        client_socket.sendall(encrypt(candidates_info, key, iv))
        
        # 发送投票类型
        vote_type_info = f"投票类型: {current_round.vote_type}"
        client_socket.sendall(encrypt(vote_type_info, key, iv))
        
        while True:
            data = client_socket.recv(1024)
            if not data: 
                break
                
            try:
                decrypted = decrypt(data, key)
                if decrypted.startswith("VOTE|"):
                    parts = decrypted.split('|')
                    if len(parts) == 3:
                        _, user_id, cids = parts
                        candidate_ids = [int(cid.strip()) for cid in cids.split(',') if cid.strip()]
                        response = system.process_vote(user_id, candidate_ids)
                        client_socket.sendall(encrypt(response, key, iv))
                    else:
                        client_socket.sendall(encrypt("命令格式错误", key, iv))
                else:
                    client_socket.sendall(encrypt("未知命令", key, iv))
            except Exception as e:
                client_socket.sendall(encrypt(f"处理错误: {str(e)}", key, iv))
                
    except Exception as e:
        print(f"客户端错误: {str(e)}")
    finally:
        client_socket.close()

def main():
    system = VotingSystem()
    server_socket = socket.socket()
    
    print("=== 投票系统管理 ===")
    while True:
        print("\n1. 创建新轮次")
        print("2. 启动投票服务")
        print("3. 查看投票结果")
        print("4. 退出系统")
        
        choice = input("\n请选择操作: ").strip()
        
        if choice == '1':
            name = input("轮次名称: ")
            vote_mode = input("投票模式(max/fixed): ")
            mode_value = int(input("投票数: "))
            vote_type = input("投票类型(记名/无记名): ")
            
            round_id = system.create_round(name, vote_mode, mode_value, vote_type)
            print(f"轮次#{round_id}创建成功!")
            
            # 添加候选人
            print("\n添加候选人(输入空结束):")
            while True:
                name = input("候选人姓名: ").strip()
                if not name: 
                    break
                cid = system.rounds[round_id].add_candidate(name)
                print(f"添加成功! ID: {cid}")
                
        elif choice == '2':
            if not system.rounds:
                print("请先创建轮次!")
                continue
                
            ip = input("监听IP(默认所有): ").strip() or "0.0.0.0"
            port = int(input("端口(默认9999): ").strip() or 9999)
            
            try:
                server_socket.bind((ip, port))
                server_socket.listen(5)
                print(f"服务已启动于 {ip}:{port}")
                print("输入'停止'结束服务")
                
                def accept_clients():
                    while True:
                        client, addr = server_socket.accept()
                        print(f"客户端连接: {addr}")
                        threading.Thread(
                            target=handle_client, 
                            args=(client, system)
                        ).start()
                        
                threading.Thread(target=accept_clients, daemon=True).start()
                
                # 等待停止命令
                while input("").strip().lower() != '停止':
                    pass
                    
            except Exception as e:
                print(f"启动失败: {str(e)}")
            finally:
                server_socket.close()
                print("服务已停止")
                
        elif choice == '3':
            if system.get_current_round():
                print(system.get_current_round().get_summary())
            else:
                print("没有活动轮次")
                
        elif choice == '4':
            print("系统已退出")
            break
            
        else:
            print("无效选择")

if __name__ == "__main__":
    main()

客户端(原始人版)

点击查看代码
import socket
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad, unpad

def encrypt(message, key, iv):
    cipher = AES.new(key, AES.MODE_CBC, iv)
    return iv + cipher.encrypt(pad(message.encode(), AES.block_size))

def decrypt(data, key):
    try:
        iv, encrypted = data[:16], data[16:]
        cipher = AES.new(key, AES.MODE_CBC, iv)
        return unpad(cipher.decrypt(encrypted), AES.block_size).decode()
    except:
        return "解密错误"

def main():
    server_ip = input("服务器IP: ").strip() or "127.0.0.1"
    server_port = int(input("端口: ").strip() or 9999)
    
    try:
        with socket.socket() as s:
            s.connect((server_ip, server_port))
            print("连接成功!")
            
            key_iv = s.recv(32)
            key, iv = key_iv[:16], key_iv[16:]
            
            # 接收候选人信息
            candidates = decrypt(s.recv(4096), key)
            print(f"\n候选人列表:\n{candidates}")
            
            # 接收投票类型
            vote_type = decrypt(s.recv(1024), key)
            print(f"\n投票类型: {vote_type}")
            
            while True:
                user_id = input("\n用户ID (q退出): ").strip()
                if user_id.lower() == 'q': 
                    break
                
                cids = input("候选人ID(逗号分隔): ").strip()
                if not cids:
                    print("候选人ID不能为空!")
                    continue
                    
                s.sendall(encrypt(f"VOTE|{user_id}|{cids}", key, iv))
                response = decrypt(s.recv(4096), key)
                print(f"\n服务器响应: {response}")
                
    except Exception as e:
        print(f"错误: {str(e)}")
    finally:
        print("客户端已关闭")

if __name__ == "__main__":
    main()

以上是相当浅薄的尝试,试运行后发现需要修改的东西有很多,比如动不动就给我来个“目标计算机积极拒绝”(我寻思这不该积极的时候瞎积极),还有端口被占用的情况,或者是指令输入错误后你不会收到报错等等等等……当然还有不少可以加的东西,比如设置密码来查看记名状态下客户端的投票情况,也可以用上文件操作的知识,把投票结果或详情信息保存到文本文档中便于后续查看。
(叽里咕噜说啥呢)反正可操作空间很大,开干!

(2)下面一部分一部分列出修改的代码:

加强密码保护:

# 密码哈希存储
class VotingRound:
    def __init__(self, name, vote_mode, mode_value, vote_type, password=None):
        self.password_hash = hashlib.sha256(password.encode()).hexdigest() if password else None

# 密码验证
def get_voter_info(self, password):
    if self.vote_type != '记名':
        return "无记名投票,无法查看"
    
    if not self.password_hash or hashlib.sha256(password.encode()).hexdigest() != self.password_hash:
        return "密码错误"

加入保存到文档的功能:

class VotingRound:
    def save_to_file(self):
        filename = f"{self.name}_结果_{datetime.now().strftime('%Y%m%d%H%M%S')}.txt"
        with open(filename, 'w') as f:
            f.write(self.get_summary())
        return f"结果已保存到: {filename}"
    
    def save_voter_info(self, password):
        info = self.get_voter_info(password)
        if "密码错误" in info or "无记名投票" in info:
            return info
        
        filename = f"{self.name}_投票详情_{datetime.now().strftime('%Y%m%d%H%M%S')}.txt"
        with open(filename, 'w') as f:
            f.write(info)
        return f"详情已保存到: {filename}"
class VotingRound:
    def get_voter_info(self, password):
        # ... 密码验证 ...
        
        info = [f"=== {self.name} 投票详情 ==="]
        info.append(f"投票模式: {'最多选' if self.vote_mode == 'max' else '必须选'} {self.mode_value} 人")
        info.append(f"总投票人数: {len(self.voters)}")
        
        for user_id, cids in self.voters.items():
            candidate_names = []
            for cid in cids:
                candidate_names.append(f"候选人{cid}({self.candidates[cid][0]})")
            info.append(f"用户 {user_id}: 投给 {', '.join(candidate_names)}")
        
        return "\n".join(info)

多轮次的投票(投票基本不会只投一轮)

class VotingRound:
    def get_voter_info(self, password):
        # ... 密码验证 ...
        
        info = [f"=== {self.name} 投票详情 ==="]
        info.append(f"投票模式: {'最多选' if self.vote_mode == 'max' else '必须选'} {self.mode_value} 人")
        info.append(f"总投票人数: {len(self.voters)}")
        
        for user_id, cids in self.voters.items():
            candidate_names = []
            for cid in cids:
                candidate_names.append(f"候选人{cid}({self.candidates[cid][0]})")
            info.append(f"用户 {user_id}: 投给 {', '.join(candidate_names)}")
        
        return "\n".join(info)

服务器日志反馈(我能够知道完成了什么步骤):

def handle_client(client_socket, system, log_callback):
    client_address = client_socket.getpeername()
    log_callback(f"客户端 {client_address} 已连接")
    
    # 处理过程中记录日志
    log_callback(f"向客户端 {client_address} 发送候选人信息")
    log_callback(f"收到来自 {client_address} 的数据: {decrypted}")
    log_callback(f"处理投票: 用户 {user_id} 投给候选人 {cids_str} - {response}")

解决服务端容易卡死的问题:

def start_server(system, ip, port, log_callback, stop_event):
    try:
        server_socket = socket.socket()
        server_socket.bind((ip, port))
        server_socket.listen(5)
        server_socket.settimeout(1)  # 设置超时以便检查停止事件
        
        while not stop_event.is_set():
            try:
                client, addr = server_socket.accept()
                # 为每个客户端创建新线程
                threading.Thread(
                    target=handle_client,
                    args=(client, system, log_callback),
                    daemon=True
                ).start()
            except socket.timeout:
                continue
    # ...

(3)看着漆黑的终端运行,总感觉差了点什么,于是搜索能够美化程序的方式,然后找到了用GUI版本来替换命令行版本的方法,不过这个确实不太会,学习了一部分然后大体搭了个框架试试后交给DeepSeek做细化处理
比如:

class VotingSystemGUI:
    def __init__(self, root):
        self.root = root
        self.root.title("投票系统服务器")
        self.root.geometry("1000x700")
        self.root.resizable(True, True)
        
        # 主框架和标签页
        self.main_frame = ttk.Frame(root, padding="10")
        self.notebook = ttk.Notebook(self.main_frame)
        
        # 创建标签页
        self.round_tab = ttk.Frame(self.notebook, padding="10")
        self.server_tab = ttk.Frame(self.notebook, padding="10")
        self.result_tab = ttk.Frame(self.notebook, padding="10")
        
        self.notebook.add(self.round_tab, text="投票轮次管理")
        self.notebook.add(self.server_tab, text="服务器管理")
        self.notebook.add(self.result_tab, text="投票结果管理")

然后我看着美化后暴涨的代码行数陷入沉思……(果然美工是重头戏)
而且转为GUI界面后无形中为我解决了一个问题:当时为了输入的密码进行保密使用了getpass语句,但它在pycharm或者IDLE无法运行,因此在这之前的调试只能在windows自带的终端调试,使用极不方便,现在舒服了。
接下来就是熟悉的pip install pyinstaller打包代码为应用程序,更好转移至其他设备进行操作使用。

完整代码附上

服务端:

点击查看代码
import tkinter as tk
from tkinter import ttk, messagebox, simpledialog, scrolledtext
import socket
import threading
import os
import hashlib
from datetime import datetime
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad, unpad
import getpass
import sys


class VotingRound:
    def __init__(self, name, vote_mode, mode_value, vote_type, password=None):
        self.name = name

        # 将中文模式转换为英文关键字
        if vote_mode == '最大':
            self.vote_mode = 'max'
        elif vote_mode == '固定':
            self.vote_mode = 'fixed'
        else:
            self.vote_mode = vote_mode

        self.mode_value = mode_value
        self.vote_type = vote_type
        self.password_hash = hashlib.sha256(password.encode()).hexdigest() if password else None
        self.candidates = {}
        self.voters = {}
        self.invalid_votes = []
        self.status = "active"
        self.start_time = datetime.now()

    def add_candidate(self, name):
        cid = len(self.candidates) + 1
        self.candidates[cid] = [name, 0]
        return cid

    def validate_vote(self, user_id, candidate_ids):
        if self.vote_type == '记名' and user_id in self.voters:
            return False, "不能重复投票"

        num_votes = len(candidate_ids)
        if self.vote_mode == 'max' and num_votes > self.mode_value:
            return False, f"最多选{self.mode_value}人"
        if self.vote_mode == 'fixed' and num_votes != self.mode_value:
            return False, f"必须选{self.mode_value}人"
        if any(cid not in self.candidates for cid in candidate_ids):
            return False, "无效候选人ID"

        return True, "投票有效"

    def process_vote(self, user_id, candidate_ids):
        is_valid, reason = self.validate_vote(user_id, candidate_ids)
        if not is_valid:
            self.invalid_votes.append((user_id, reason))
            return f"无效票: {reason}"

        for cid in candidate_ids:
            self.candidates[cid][1] += 1

        if self.vote_type == '记名':
            self.voters[user_id] = candidate_ids

        return "投票成功"

    def get_summary(self):
        summary = [f"=== {self.name} 投票结果 ==="]
        for cid, (name, votes) in self.candidates.items():
            summary.append(f"{cid}. {name}: {votes}票")
        return "\n".join(summary)

    def get_candidates_info(self):
        info = [f"=== {self.name} 候选人 ==="]
        for cid, (name, _) in self.candidates.items():
            info.append(f"{cid}. {name}")
        return "\n".join(info)

    def save_to_file(self):
        filename = f"{self.name}_结果_{datetime.now().strftime('%Y%m%d%H%M%S')}.txt"
        with open(filename, 'w') as f:
            f.write(self.get_summary())
        return f"结果已保存到: {filename}"

    def get_voter_info(self, password):
        if self.vote_type != '记名':
            return "无记名投票,无法查看"

        if not self.password_hash or hashlib.sha256(password.encode()).hexdigest() != self.password_hash:
            return "密码错误"

        info = [f"=== {self.name} 投票详情 ==="]
        info.append(f"投票模式: {'最多选' if self.vote_mode == 'max' else '必须选'} {self.mode_value} 人")
        info.append(f"总投票人数: {len(self.voters)}")
        info.append("")

        for user_id, cids in self.voters.items():
            candidate_names = []
            for cid in cids:
                candidate_names.append(f"候选人{cid}({self.candidates[cid][0]})")
            info.append(f"用户 {user_id}: 投给 {', '.join(candidate_names)}")
        return "\n".join(info)

    def save_voter_info(self, password):
        info = self.get_voter_info(password)
        if "密码错误" in info or "无记名投票" in info:
            return info

        filename = f"{self.name}_投票详情_{datetime.now().strftime('%Y%m%d%H%M%S')}.txt"
        with open(filename, 'w') as f:
            f.write(info)
        return f"详情已保存到: {filename}"


class VotingSystem:
    def __init__(self):
        self.rounds = {}
        self.current_round_id = None

    def create_round(self, name, vote_mode, mode_value, vote_type, password=None):
        round_id = len(self.rounds) + 1
        self.rounds[round_id] = VotingRound(name, vote_mode, mode_value, vote_type, password)
        self.current_round_id = round_id
        return round_id

    def get_current_round(self):
        if self.current_round_id in self.rounds:
            return self.rounds[self.current_round_id]
        return None

    def end_round(self, round_id):
        if round_id in self.rounds:
            self.rounds[round_id].status = "closed"
            return self.rounds[round_id].save_to_file()
        return "轮次不存在"


def encrypt(message, key, iv):
    cipher = AES.new(key, AES.MODE_CBC, iv)
    return iv + cipher.encrypt(pad(message.encode(), AES.block_size))


def decrypt(data, key):
    try:
        iv, encrypted = data[:16], data[16:]
        cipher = AES.new(key, AES.MODE_CBC, iv)
        return unpad(cipher.decrypt(encrypted), AES.block_size).decode()
    except Exception:
        return "解密错误"


def handle_client(client_socket, system, log_callback):
    key = os.urandom(16)
    iv = os.urandom(16)
    client_socket.sendall(key + iv)

    client_address = client_socket.getpeername()
    log_callback(f"客户端 {client_address} 已连接")

    try:
        current_round = system.get_current_round()
        if not current_round:
            client_socket.sendall(encrypt("无活动轮次", key, iv))
            log_callback("无活动轮次,拒绝客户端请求")
            return

        # 发送候选人信息
        candidates_info = current_round.get_candidates_info()
        client_socket.sendall(encrypt(candidates_info, key, iv))
        log_callback(f"向客户端 {client_address} 发送候选人信息")

        # 创建投票规则信息
        if current_round.vote_mode == 'max':
            rule_info = f"投票类型: {current_round.vote_type}, 最多可选 {current_round.mode_value} 位候选人"
        elif current_round.vote_mode == 'fixed':
            rule_info = f"投票类型: {current_round.vote_type}, 必须选择 {current_round.mode_value} 位候选人"
        else:
            rule_info = f"投票类型: {current_round.vote_type}, 投票模式: {current_round.vote_mode}"

        client_socket.sendall(encrypt(rule_info, key, iv))
        log_callback(f"向客户端 {client_address} 发送投票规则")

        while True:
            data = client_socket.recv(1024)
            if not data:
                log_callback(f"客户端 {client_address} 断开连接")
                break

            try:
                decrypted = decrypt(data, key)
                log_callback(f"收到来自 {client_address} 的数据: {decrypted}")

                if decrypted.startswith("VOTE|"):
                    try:
                        # 分割命令格式:VOTE|user_id|cid1,cid2,...
                        parts = decrypted.split('|', 2)  # 最多分割2次,得到3部分

                        if len(parts) < 3:
                            raise ValueError("命令格式不完整")

                        _, user_id, cids_str = parts

                        # 验证用户ID
                        user_id = user_id.strip()
                        if not user_id:
                            raise ValueError("用户ID不能为空")

                        # 转换候选人ID
                        candidate_ids = []
                        for cid in cids_str.split(','):
                            cid = cid.strip()
                            if cid:  # 忽略空值
                                candidate_ids.append(int(cid))

                        if not candidate_ids:
                            raise ValueError("候选人ID不能为空")

                        # 处理投票
                        response = current_round.process_vote(user_id, candidate_ids)
                        client_socket.sendall(encrypt(response, key, iv))
                        log_callback(f"处理投票: 用户 {user_id} 投给候选人 {cids_str} - {response}")

                    except Exception as e:
                        error_msg = f"命令格式错误: {str(e)}"
                        client_socket.sendall(encrypt(error_msg, key, iv))
                        log_callback(f"处理投票错误: {error_msg}")

                else:
                    client_socket.sendall(encrypt("未知命令", key, iv))
                    log_callback(f"未知命令: {decrypted}")

            except Exception as e:
                error_msg = f"处理错误: {str(e)}"
                client_socket.sendall(encrypt(error_msg, key, iv))
                log_callback(f"处理错误: {error_msg}")

    except Exception as e:
        log_callback(f"客户端处理错误: {str(e)}")
    finally:
        client_socket.close()


def start_server(system, ip, port, log_callback, stop_event):
    try:
        server_socket = socket.socket()
        server_socket.bind((ip, port))
        server_socket.listen(5)

        _, actual_port = server_socket.getsockname()
        log_callback(f"服务启动于 {ip}:{actual_port}")

        # 设置超时以便检查停止事件
        server_socket.settimeout(1)

        while not stop_event.is_set():
            try:
                client, addr = server_socket.accept()
                log_callback(f"客户端连接: {addr}")

                # 为每个客户端创建新线程
                client_thread = threading.Thread(
                    target=handle_client,
                    args=(client, system, log_callback),
                    daemon=True
                )
                client_thread.start()

            except socket.timeout:
                # 超时,检查是否要停止
                continue
            except Exception as e:
                # 检查是否因关闭套接字而导致的错误
                if stop_event.is_set():
                    log_callback("服务器正常关闭")
                    break
                else:
                    log_callback(f"服务器错误: {str(e)}")
                    break

    except Exception as e:
        log_callback(f"服务器启动失败: {str(e)}")
    finally:
        server_socket.close()
        log_callback("服务器已停止")


class VotingSystemGUI:
    def __init__(self, root):
        self.root = root
        self.root.title("投票系统服务器")
        self.root.geometry("1000x700")
        self.root.resizable(True, True)

        # 设置应用图标
        try:
            self.root.iconbitmap("server_icon.ico")
        except:
            pass

        # 初始化投票系统
        self.system = VotingSystem()
        self.server_thread = None
        self.stop_event = threading.Event()

        # 创建主框架
        self.main_frame = ttk.Frame(root, padding="10")
        self.main_frame.pack(fill=tk.BOTH, expand=True)

        # 创建标签页
        self.notebook = ttk.Notebook(self.main_frame)
        self.notebook.pack(fill=tk.BOTH, expand=True, pady=5)

        # 轮次管理标签页
        self.round_tab = ttk.Frame(self.notebook, padding="10")
        self.notebook.add(self.round_tab, text="投票轮次管理")

        # 创建轮次部分
        self.create_frame = ttk.LabelFrame(self.round_tab, text="创建新轮次", padding="10")
        self.create_frame.pack(fill=tk.X, pady=5)

        # 轮次名称
        ttk.Label(self.create_frame, text="轮次名称:").grid(row=0, column=0, padx=5, pady=5, sticky=tk.W)
        self.round_name_entry = ttk.Entry(self.create_frame, width=30)
        self.round_name_entry.grid(row=0, column=1, padx=5, pady=5)

        # 投票模式
        ttk.Label(self.create_frame, text="投票模式:").grid(row=0, column=2, padx=5, pady=5, sticky=tk.W)
        self.vote_mode_var = tk.StringVar()
        self.vote_mode_combobox = ttk.Combobox(self.create_frame, textvariable=self.vote_mode_var,
                                               values=["最大", "固定"], width=8)
        self.vote_mode_combobox.grid(row=0, column=3, padx=5, pady=5)
        self.vote_mode_combobox.current(0)

        # 投票数
        ttk.Label(self.create_frame, text="投票数:").grid(row=0, column=4, padx=5, pady=5, sticky=tk.W)
        self.vote_num_entry = ttk.Entry(self.create_frame, width=8)
        self.vote_num_entry.grid(row=0, column=5, padx=5, pady=5)
        self.vote_num_entry.insert(0, "1")

        # 投票类型
        ttk.Label(self.create_frame, text="投票类型:").grid(row=1, column=0, padx=5, pady=5, sticky=tk.W)
        self.vote_type_var = tk.StringVar()
        self.vote_type_combobox = ttk.Combobox(self.create_frame, textvariable=self.vote_type_var,
                                               values=["记名", "无记名"], width=8)
        self.vote_type_combobox.grid(row=1, column=1, padx=5, pady=5)
        self.vote_type_combobox.current(0)

        # 查看密码
        ttk.Label(self.create_frame, text="查看密码:").grid(row=1, column=2, padx=5, pady=5, sticky=tk.W)
        self.password_entry = ttk.Entry(self.create_frame, width=20, show="*")
        self.password_entry.grid(row=1, column=3, padx=5, pady=5)

        # 创建轮次按钮
        self.create_btn = ttk.Button(self.create_frame, text="创建轮次", command=self.create_round)
        self.create_btn.grid(row=1, column=4, padx=10, pady=5)

        # 候选人管理部分
        self.candidate_frame = ttk.LabelFrame(self.round_tab, text="候选人管理", padding="10")
        self.candidate_frame.pack(fill=tk.X, pady=5)

        ttk.Label(self.candidate_frame, text="候选人姓名:").grid(row=0, column=0, padx=5, pady=5, sticky=tk.W)
        self.candidate_name_entry = ttk.Entry(self.candidate_frame, width=30)
        self.candidate_name_entry.grid(row=0, column=1, padx=5, pady=5)

        self.add_candidate_btn = ttk.Button(self.candidate_frame, text="添加候选人",
                                            command=self.add_candidate, state=tk.DISABLED)
        self.add_candidate_btn.grid(row=0, column=2, padx=10, pady=5)

        # 候选人列表
        self.candidate_list_frame = ttk.LabelFrame(self.round_tab, text="候选人列表", padding="10")
        self.candidate_list_frame.pack(fill=tk.BOTH, expand=True, pady=5)

        columns = ("ID", "姓名")
        self.candidate_tree = ttk.Treeview(self.candidate_list_frame, columns=columns, show="headings")
        self.candidate_tree.heading("ID", text="ID")
        self.candidate_tree.heading("姓名", text="姓名")
        self.candidate_tree.column("ID", width=100, anchor=tk.CENTER)
        self.candidate_tree.column("姓名", width=300)

        self.candidate_scroll = ttk.Scrollbar(self.candidate_list_frame, orient=tk.VERTICAL,
                                              command=self.candidate_tree.yview)
        self.candidate_tree.configure(yscroll=self.candidate_scroll.set)

        self.candidate_tree.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
        self.candidate_scroll.pack(side=tk.RIGHT, fill=tk.Y)

        # 服务器管理标签页
        self.server_tab = ttk.Frame(self.notebook, padding="10")
        self.notebook.add(self.server_tab, text="服务器管理")

        self.server_frame = ttk.LabelFrame(self.server_tab, text="服务器设置", padding="10")
        self.server_frame.pack(fill=tk.X, pady=5)

        ttk.Label(self.server_frame, text="监听IP:").grid(row=0, column=0, padx=5, pady=5, sticky=tk.W)
        self.listen_ip_entry = ttk.Entry(self.server_frame, width=20)
        self.listen_ip_entry.grid(row=0, column=1, padx=5, pady=5)
        self.listen_ip_entry.insert(0, "0.0.0.0")

        ttk.Label(self.server_frame, text="端口:").grid(row=0, column=2, padx=5, pady=5, sticky=tk.W)
        self.listen_port_entry = ttk.Entry(self.server_frame, width=10)
        self.listen_port_entry.grid(row=0, column=3, padx=5, pady=5)
        self.listen_port_entry.insert(0, "9999")

        self.start_btn = ttk.Button(self.server_frame, text="启动服务器", command=self.start_server)
        self.start_btn.grid(row=0, column=4, padx=10, pady=5)

        self.stop_btn = ttk.Button(self.server_frame, text="停止服务器", command=self.stop_server, state=tk.DISABLED)
        self.stop_btn.grid(row=0, column=5, padx=10, pady=5)

        # 服务器状态
        self.status_frame = ttk.LabelFrame(self.server_tab, text="服务器日志", padding="10")
        self.status_frame.pack(fill=tk.BOTH, expand=True, pady=5)

        self.status_text = scrolledtext.ScrolledText(self.status_frame, wrap=tk.WORD, height=15)
        self.status_text.pack(fill=tk.BOTH, expand=True)
        self.status_text.config(state=tk.DISABLED)

        # 结果管理标签页
        self.result_tab = ttk.Frame(self.notebook, padding="10")
        self.notebook.add(self.result_tab, text="投票结果管理")

        # 轮次选择
        self.result_select_frame = ttk.LabelFrame(self.result_tab, text="选择轮次", padding="10")
        self.result_select_frame.pack(fill=tk.X, pady=5)

        ttk.Label(self.result_select_frame, text="选择轮次:").grid(row=0, column=0, padx=5, pady=5, sticky=tk.W)
        self.round_var = tk.StringVar()
        self.round_combobox = ttk.Combobox(self.result_select_frame, textvariable=self.round_var, width=30)
        self.round_combobox.grid(row=0, column=1, padx=5, pady=5)

        self.view_result_btn = ttk.Button(self.result_select_frame, text="查看结果", command=self.view_result)
        self.view_result_btn.grid(row=0, column=2, padx=10, pady=5)

        self.save_result_btn = ttk.Button(self.result_select_frame, text="保存结果", command=self.save_result)
        self.save_result_btn.grid(row=0, column=3, padx=10, pady=5)

        self.view_details_btn = ttk.Button(self.result_select_frame, text="查看详情", command=self.view_details)
        self.view_details_btn.grid(row=0, column=4, padx=10, pady=5)

        # 结果显示
        self.result_frame = ttk.LabelFrame(self.result_tab, text="投票结果", padding="10")
        self.result_frame.pack(fill=tk.BOTH, expand=True, pady=5)

        self.result_text = scrolledtext.ScrolledText(self.result_frame, wrap=tk.WORD)
        self.result_text.pack(fill=tk.BOTH, expand=True)
        self.result_text.config(state=tk.DISABLED)

        # 状态栏
        self.status_bar = ttk.Label(root, text="就绪", relief=tk.SUNKEN, anchor=tk.W)
        self.status_bar.pack(side=tk.BOTTOM, fill=tk.X)

        # 设置样式
        self.style = ttk.Style()
        self.style.configure("TButton", padding=6)
        self.style.configure("TLabel", padding=5)
        self.style.configure("TLabelframe.Label", font=('Arial', 10, 'bold'))
        self.style.configure("Treeview", font=('Arial', 9))
        self.style.configure("Treeview.Heading", font=('Arial', 10, 'bold'))

        # 绑定事件
        self.notebook.bind("<<NotebookTabChanged>>", self.on_tab_changed)

        # 日志函数
        self.log_callback = self.log_message
        self.server_socket = None

    def log_message(self, message):
        """记录消息到日志区域"""
        timestamp = datetime.now().strftime("%H:%M:%S")
        full_message = f"[{timestamp}] {message}"

        self.status_text.config(state=tk.NORMAL)
        self.status_text.insert(tk.END, full_message + "\n")
        self.status_text.see(tk.END)  # 滚动到最新消息
        self.status_text.config(state=tk.DISABLED)

        # 更新状态栏
        self.status_bar.config(text=message)

    def on_tab_changed(self, event):
        """标签页切换事件处理"""
        # 当切换到结果管理标签页时,更新轮次列表
        if self.notebook.index(self.notebook.select()) == 2:  # 结果管理标签页索引
            self.update_round_combobox()

    def create_round(self):
        """创建新的投票轮次"""
        name = self.round_name_entry.get().strip()
        if not name:
            messagebox.showwarning("输入错误", "轮次名称不能为空")
            return

        vote_mode = self.vote_mode_var.get()
        if not vote_mode:
            messagebox.showwarning("输入错误", "请选择投票模式")
            return

        try:
            mode_value = int(self.vote_num_entry.get().strip())
        except ValueError:
            messagebox.showwarning("输入错误", "投票数必须是整数")
            return

        vote_type = self.vote_type_var.get()
        if not vote_type:
            messagebox.showwarning("输入错误", "请选择投票类型")
            return

        password = self.password_entry.get().strip()
        if vote_type == '记名' and not password:
            if messagebox.askyesno("密码确认", "记名投票需要密码才能查看详情,是否继续不设置密码?"):
                password = None
            else:
                return

        try:
            round_id = self.system.create_round(name, vote_mode, mode_value, vote_type, password)
            self.add_candidate_btn.config(state=tk.NORMAL)

            # 清空候选人列表
            for item in self.candidate_tree.get_children():
                self.candidate_tree.delete(item)

            self.log_message(f"投票轮次 '{name}' 创建成功! ID: {round_id}")
            messagebox.showinfo("成功", f"轮次#{round_id}创建成功!")

            # 清空输入框
            self.round_name_entry.delete(0, tk.END)
            self.vote_num_entry.delete(0, tk.END)
            self.vote_num_entry.insert(0, "1")
            self.password_entry.delete(0, tk.END)

        except Exception as e:
            messagebox.showerror("错误", f"创建轮次失败: {str(e)}")
            self.log_message(f"创建轮次错误: {str(e)}")

    def add_candidate(self):
        """向当前轮次添加候选人"""
        name = self.candidate_name_entry.get().strip()
        if not name:
            messagebox.showwarning("输入错误", "候选人姓名不能为空")
            return

        current_round = self.system.get_current_round()
        if not current_round:
            messagebox.showwarning("错误", "没有活动轮次")
            return

        try:
            cid = current_round.add_candidate(name)
            self.candidate_tree.insert("", "end", values=(cid, name))
            self.log_message(f"候选人 '{name}' 已添加,ID: {cid}")
            self.status_bar.config(text=f"候选人 '{name}' 已添加,ID: {cid}")
            self.candidate_name_entry.delete(0, tk.END)
        except Exception as e:
            messagebox.showerror("错误", f"添加候选人失败: {str(e)}")
            self.log_message(f"添加候选人错误: {str(e)}")

    def start_server(self):
        """启动投票服务器"""
        if not self.system.rounds:
            messagebox.showwarning("错误", "请先创建至少一个投票轮次")
            return

        ip = self.listen_ip_entry.get().strip() or "0.0.0.0"
        try:
            port = int(self.listen_port_entry.get().strip() or 9999)
        except ValueError:
            messagebox.showwarning("输入错误", "端口必须是整数")
            return

        # 检查端口是否在合法范围
        if port < 0 or port > 65535:
            messagebox.showwarning("输入错误", "端口号必须在0-65535之间")
            return

        try:
            # 设置停止事件
            self.stop_event.clear()

            # 创建服务器线程
            self.server_thread = threading.Thread(
                target=start_server,
                args=(self.system, ip, port, self.log_callback, self.stop_event),
                daemon=True
            )
            self.server_thread.start()

            # 更新UI状态
            self.start_btn.config(state=tk.DISABLED)
            self.stop_btn.config(state=tk.NORMAL)
            self.status_bar.config(text="服务器正在运行")

        except Exception as e:
            messagebox.showerror("服务器错误", f"无法启动服务器: {str(e)}")
            self.log_message(f"启动服务器失败: {str(e)}")

    def stop_server(self):
        """停止投票服务器"""
        try:
            self.stop_event.set()
            self.log_message("正在停止服务器...")

            # 创建临时套接字连接到服务器以解除accept()阻塞
            try:
                temp_socket = socket.socket()
                temp_socket.connect(("127.0.0.1", int(self.listen_port_entry.get().strip() or 9999)))
                temp_socket.close()
            except:
                pass

            # 等待服务器线程结束
            if self.server_thread and self.server_thread.is_alive():
                self.server_thread.join(timeout=3.0)

            # 更新UI状态
            self.start_btn.config(state=tk.NORMAL)
            self.stop_btn.config(state=tk.DISABLED)
            self.log_message("服务器已停止")
            self.status_bar.config(text="服务器已停止")

        except Exception as e:
            messagebox.showerror("错误", f"停止服务器失败: {str(e)}")
            self.log_message(f"停止服务器错误: {str(e)}")

    def update_round_combobox(self):
        """更新轮次下拉框"""
        values = []
        for rid, rnd in self.system.rounds.items():
            status = "活动" if rnd.status == "active" else "已结束"
            values.append(f"{rid}: {rnd.name} ({status})")
        self.round_combobox["values"] = values
        if values:
            self.round_combobox.current(0)

    def get_selected_round_id(self):
        """获取选择的轮次ID"""
        selected = self.round_combobox.get()
        if not selected:
            messagebox.showwarning("选择错误", "请先选择轮次")
            return None

        try:
            # 提取轮次ID(格式:ID: 名称)
            return int(selected.split(":")[0])
        except:
            messagebox.showwarning("选择错误", "无效的轮次选择")
            return None

    def view_result(self):
        """查看投票结果"""
        round_id = self.get_selected_round_id()
        if round_id is None:
            return

        rnd = self.system.rounds.get(round_id)
        if not rnd:
            messagebox.showwarning("错误", "轮次不存在")
            return

        self.result_text.config(state=tk.NORMAL)
        self.result_text.delete(1.0, tk.END)

        # 显示投票结果摘要
        self.result_text.insert(tk.END, rnd.get_summary())
        self.log_message(f"查看轮次 {round_id} 的投票结果")
        self.status_bar.config(text=f"显示轮次 {round_id} 的投票结果")

        self.result_text.config(state=tk.DISABLED)

    def view_details(self):
        """查看投票详情(仅记名投票)"""
        round_id = self.get_selected_round_id()
        if round_id is None:
            return

        rnd = self.system.rounds.get(round_id)
        if not rnd:
            messagebox.showwarning("错误", "轮次不存在")
            return

        if rnd.vote_type != '记名':
            messagebox.showinfo("提示", "无记名投票,无法查看详情")
            return

        password = simpledialog.askstring("密码验证", "请输入查看密码:", show="*")
        if not password:
            return

        self.result_text.config(state=tk.NORMAL)
        self.result_text.delete(1.0, tk.END)

        # 显示投票详情
        details = rnd.get_voter_info(password)
        self.result_text.insert(tk.END, details)
        self.log_message(f"查看轮次 {round_id} 的投票详情")
        self.status_bar.config(text=f"显示轮次 {round_id} 的投票详情")

        self.result_text.config(state=tk.DISABLED)

    def save_result(self):
        """保存投票结果"""
        round_id = self.get_selected_round_id()
        if round_id is None:
            return

        rnd = self.system.rounds.get(round_id)
        if not rnd:
            messagebox.showwarning("错误", "轮次不存在")
            return

        if rnd.vote_type == '记名':
            password = simpledialog.askstring("密码验证", "请输入查看密码:", show="*")
            if not password:
                return
            result = rnd.save_voter_info(password)
        else:
            result = rnd.save_to_file()

        messagebox.showinfo("保存结果", result)
        self.log_message(f"保存轮次 {round_id} 的结果: {result}")
        self.status_bar.config(text=f"保存轮次 {round_id} 的结果")

    def on_closing(self):
        """关闭窗口时的处理"""
        # 停止服务器
        if self.stop_btn["state"] == tk.NORMAL:
            self.stop_server()

        # 自动结束所有活动轮次
        for rid, rnd in self.system.rounds.items():
            if rnd.status == "active":
                self.log_message(f"自动结束轮次 {rid}")
                rnd.status = "closed"
                self.log_message(rnd.save_to_file())

        self.root.destroy()

if __name__ == "__main__":
    root = tk.Tk()
    app = VotingSystemGUI(root)
    root.protocol("WM_DELETE_WINDOW", app.on_closing)
    root.mainloop()

客户端:

点击查看代码
import tkinter as tk
from tkinter import ttk, messagebox, scrolledtext
import socket
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad, unpad
import time


class VotingClientGUI:
    def __init__(self, root):
        self.root = root
        self.root.title("投票系统客户端")
        self.root.geometry("800x600")
        self.root.resizable(True, True)
        self.key = None
        self.iv = None
        self.socket = None

        # 创建主框架
        self.main_frame = ttk.Frame(root, padding="20")
        self.main_frame.pack(fill=tk.BOTH, expand=True)

        # 连接服务器部分
        self.connection_frame = ttk.LabelFrame(self.main_frame, text="服务器连接", padding="10")
        self.connection_frame.pack(fill=tk.X, pady=10)

        ttk.Label(self.connection_frame, text="服务器IP:").grid(row=0, column=0, padx=5, pady=5, sticky=tk.W)
        self.ip_entry = ttk.Entry(self.connection_frame, width=20)
        self.ip_entry.grid(row=0, column=1, padx=5, pady=5)
        self.ip_entry.insert(0, "127.0.0.1")

        ttk.Label(self.connection_frame, text="端口:").grid(row=0, column=2, padx=5, pady=5, sticky=tk.W)
        self.port_entry = ttk.Entry(self.connection_frame, width=10)
        self.port_entry.grid(row=0, column=3, padx=5, pady=5)
        self.port_entry.insert(0, "9999")

        self.connect_btn = ttk.Button(self.connection_frame, text="连接服务器", command=self.connect_server)
        self.connect_btn.grid(row=0, column=4, padx=10, pady=5)

        # 候选人信息显示
        self.candidates_frame = ttk.LabelFrame(self.main_frame, text="候选人信息", padding="10")
        self.candidates_frame.pack(fill=tk.BOTH, expand=True, pady=10)

        self.candidates_text = scrolledtext.ScrolledText(self.candidates_frame, wrap=tk.WORD, height=10)
        self.candidates_text.pack(fill=tk.BOTH, expand=True)
        self.candidates_text.config(state=tk.DISABLED)

        # 投票规则显示
        self.rules_frame = ttk.LabelFrame(self.main_frame, text="投票规则", padding="10")
        self.rules_frame.pack(fill=tk.X, pady=10)

        self.rules_label = ttk.Label(self.rules_frame, text="请先连接服务器获取投票规则")
        self.rules_label.pack(fill=tk.X, padx=5, pady=5)

        # 投票部分
        self.voting_frame = ttk.LabelFrame(self.main_frame, text="投票", padding="10")
        self.voting_frame.pack(fill=tk.X, pady=10)

        ttk.Label(self.voting_frame, text="用户ID:").grid(row=0, column=0, padx=5, pady=5, sticky=tk.W)
        self.user_id_entry = ttk.Entry(self.voting_frame, width=20)
        self.user_id_entry.grid(row=0, column=1, padx=5, pady=5)

        ttk.Label(self.voting_frame, text="候选人ID(多个用逗号分隔):").grid(row=0, column=2, padx=5, pady=5,
                                                                            sticky=tk.W)
        self.candidates_entry = ttk.Entry(self.voting_frame, width=30)
        self.candidates_entry.grid(row=0, column=3, padx=5, pady=5)

        self.vote_btn = ttk.Button(self.voting_frame, text="提交投票", command=self.submit_vote, state=tk.DISABLED)
        self.vote_btn.grid(row=0, column=4, padx=10, pady=5)

        # 状态栏
        self.status_var = tk.StringVar(value="就绪")
        self.status_bar = ttk.Label(root, textvariable=self.status_var, relief=tk.SUNKEN, anchor=tk.W)
        self.status_bar.pack(side=tk.BOTTOM, fill=tk.X)

        # 设置样式
        self.style = ttk.Style()
        self.style.configure("TButton", padding=6)
        self.style.configure("TLabel", padding=5)
        self.style.configure("TLabelframe.Label", font=('Arial', 10, 'bold'))

    def connect_server(self):
        try:
            server_ip = self.ip_entry.get().strip() or "127.0.0.1"
            server_port = int(self.port_entry.get().strip() or 9999)

            self.status_var.set(f"正在连接服务器 {server_ip}:{server_port}...")
            self.root.update()

            self.socket = socket.socket()
            self.socket.settimeout(5)
            self.socket.connect((server_ip, server_port))

            # 接收密钥和初始化向量
            key_iv = self.socket.recv(32)
            self.key, self.iv = key_iv[:16], key_iv[16:]

            # 接收候选人信息
            candidates_data = self.socket.recv(4096)
            candidates = self.decrypt(candidates_data, self.key)

            # 接收投票规则
            vote_info_data = self.socket.recv(1024)
            vote_info = self.decrypt(vote_info_data, self.key)

            # 更新UI
            self.candidates_text.config(state=tk.NORMAL)
            self.candidates_text.delete(1.0, tk.END)
            self.candidates_text.insert(tk.END, candidates)
            self.candidates_text.config(state=tk.DISABLED)

            self.rules_label.config(text=vote_info)
            self.vote_btn.config(state=tk.NORMAL)

            self.status_var.set(f"成功连接到服务器 {server_ip}:{server_port}")

        except Exception as e:
            messagebox.showerror("连接错误", f"无法连接到服务器: {str(e)}")
            self.status_var.set("连接失败")
            if self.socket:
                self.socket.close()
                self.socket = None

    def submit_vote(self):
        if not self.socket:
            messagebox.showerror("错误", "未连接到服务器")
            return

        user_id = self.user_id_entry.get().strip()
        if not user_id:
            messagebox.showwarning("输入错误", "用户ID不能为空")
            return

        cids = self.candidates_entry.get().strip()
        if not cids:
            messagebox.showwarning("输入错误", "候选人ID不能为空")
            return

        try:
            self.status_var.set("正在提交投票...")
            self.root.update()

            # 发送投票
            vote_data = f"VOTE|{user_id}|{cids}"
            encrypted_data = self.encrypt(vote_data, self.key, self.iv)
            self.socket.sendall(encrypted_data)

            # 接收响应
            response_data = self.socket.recv(4096)
            response = self.decrypt(response_data, self.key)

            messagebox.showinfo("投票结果", response)
            self.status_var.set("投票完成")

            # 清空输入框
            self.user_id_entry.delete(0, tk.END)
            self.candidates_entry.delete(0, tk.END)

        except Exception as e:
            messagebox.showerror("投票错误", f"投票失败: {str(e)}")
            self.status_var.set("投票失败")

    def encrypt(self, message, key, iv):
        cipher = AES.new(key, AES.MODE_CBC, iv)
        return iv + cipher.encrypt(pad(message.encode(), AES.block_size))

    def decrypt(self, data, key):
        try:
            iv, encrypted = data[:16], data[16:]
            cipher = AES.new(key, AES.MODE_CBC, iv)
            return unpad(cipher.decrypt(encrypted), AES.block_size).decode()
        except Exception:
            return "解密错误"

    def on_closing(self):
        if self.socket:
            self.socket.close()
        self.root.destroy()


if __name__ == "__main__":
    root = tk.Tk()
    app = VotingClientGUI(root)
    root.protocol("WM_DELETE_WINDOW", app.on_closing)
    root.mainloop()

代码已上传到gitee
https://gitee.com/liuchuanzhi123/PyCharmMiscProject

下面是运行情况(设备和录制手法有限,只能以图片代替)
服务端可在端口输入0,系统为你自动分配可用端口

视频链接
【自制Python投票系统】https://www.bilibili.com/video/BV1DiT6zvE96?vd_source=597cd3a9f8a6910111809d1747435c8b

3. 实验过程中遇到的问题和解决过程

问题1:忘记保存就关闭服务器关闭系统
解决:加入自动保存的代码
问题2:getpass可以让我在输入密码时隐形,但是pycharm上卡死
解决:查询资料才知道必须在系统的原生终端上运行
问题3:总是在关闭指令输入后迟迟不关闭怎么办
解决:参考CSDN帖子后加入了关于临时套接字和等待线程结束的代码
(问题过多…)

其他(感悟、思考等)

Python是我在C语言后接触的第二门编程语言,比C语言简洁也更好操作实现更多的功能,本实验为什么要做socket相关而不是大家都在做的爬虫,应该是我对通信的情有独钟吧,一直觉得相隔万里却能通过百行代码,一架天线,几段光纤进行信息交流,乃至心灵的互动是件很酷很酷的事情,像是一种我们用汗水与思考就能够获得的超能力,平凡但又无比令我向往。
当然,很感谢王志强老师在Python的路上带我入门,直至找到用Python来实现我梦想中超能力的现实依存,王sir真的是一位于我而言相当好的老师,不仅自己是位编程大师,更有上课诙谐幽默的风格,相信我多少年后也无法忘记的签到手势,还有每次上课为我们强化上节课知识的做法,让我确信当时选课秒锁Python是个多么明智的决定,感谢王志强老师!
“人生苦短,我用Python”
(附一张结课时强哥美照)

参考文献

零基础学Python (全彩版)——https://www.processon.com/view/link/5b8ddd6de4b0534c9bc94b4d
https://www.csdn.net/

posted @ 2025-06-10 13:05  lcz莱茵河畔  阅读(24)  评论(0)    收藏  举报