{"id":457,"date":"2025-03-12T14:32:38","date_gmt":"2025-03-12T06:32:38","guid":{"rendered":"https:\/\/zechs.taipei\/?p=457"},"modified":"2025-03-20T13:37:17","modified_gmt":"2025-03-20T05:37:17","slug":"python%e9%87%8d%e8%a4%87%e6%aa%94%e6%a1%88%e6%b8%85%e7%90%86%e5%b7%a5%e5%85%b7%ef%bc%9a%e8%87%aa%e5%8b%95%e4%bf%9d%e7%95%99%e6%9c%80%e8%88%8a%e6%aa%94%e6%a1%88%e7%9a%84%e5%ae%8c%e6%95%b4%e6%95%99","status":"publish","type":"post","link":"https:\/\/zechs.taipei\/?p=457","title":{"rendered":"[Python]\u91cd\u8907\u6a94\u6848\u6e05\u7406\u5de5\u5177\uff1a\u81ea\u52d5\u4fdd\u7559\u6700\u820a\u6a94\u6848\u7684\u5b8c\u6574\u6559\u5b78"},"content":{"rendered":"<p>\u5728\u9019\u500b\u6578\u4f4d\u6642\u4ee3\uff0c\u6211\u5011\u7684\u786c\u789f\u7e3d\u662f\u88ab\u5404\u7a2e\u6a94\u6848\u585e\u6eff\uff0c\u5176\u4e2d\u6709\u8a31\u591a\u662f\u91cd\u8907\u7684\u6a94\u6848\u5360\u7528\u4e86\u5bf6\u8cb4\u7684\u7a7a\u9593\u3002\u672c\u6559\u5b78\u5c07\u5e36\u60a8\u88fd\u4f5c\u4e00\u500b\u5f37\u5927\u7684Python\u5de5\u5177\uff0c\u4e0d\u50c5\u80fd\u627e\u51fa\u6240\u6709\u91cd\u8907\u7684\u6a94\u6848\uff0c\u9084\u80fd\u81ea\u52d5\u8b58\u5225\u4e26\u4fdd\u7559\u6700\u820a\u7684\u90a3\u4e00\u4efd\uff0c\u8b93\u60a8\u7684\u6a94\u6848\u6574\u7406\u5de5\u4f5c\u8b8a\u5f97\u8f15\u9b06\u9ad8\u6548\u3002<\/p>\n\n\n\n<p><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">\u5de5\u5177\u4ecb\u7d39\u8207\u529f\u80fd\u6982\u8ff0<\/h2>\n\n\n\n<p>\u9019\u500b\u5de5\u5177\u4f7f\u7528Python\u7684tkinter\u5eab\u5275\u5efa\u4e86\u4e00\u500b\u76f4\u89c0\u7684\u5716\u5f62\u754c\u9762\uff0c\u80fd\u5920\uff1a<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u6383\u63cf\u6307\u5b9a\u8cc7\u6599\u593e\u53ca\u5176\u5b50\u8cc7\u6599\u593e\u4e2d\u7684\u6240\u6709\u6a94\u6848<\/li>\n\n\n\n<li>\u4f7f\u7528MD5\u96dc\u6e4a\u503c\u7cbe\u78ba\u6bd4\u5c0d\u6a94\u6848\u5167\u5bb9\u4ee5\u767c\u73fe\u91cd\u8907\u6a94\u6848<\/li>\n\n\n\n<li>\u81ea\u52d5\u6309\u5275\u5efa\u65e5\u671f\u6392\u5e8f\uff0c\u6a19\u8a18\u9664\u4e86\u6700\u820a\u6a94\u6848\u5916\u7684\u6240\u6709\u91cd\u8907\u6a94\u6848<\/li>\n\n\n\n<li>\u63d0\u4f9b\u9748\u6d3b\u7684\u624b\u52d5\u8abf\u6574\u9078\u9805<\/li>\n\n\n\n<li>\u5b89\u5168\u5730\u522a\u9664\u9078\u5b9a\u7684\u91cd\u8907\u6a94\u6848<\/li>\n<\/ul>\n\n\n\n<p>\u8207\u5e02\u9762\u4e0a\u5176\u4ed6\u985e\u4f3c\u5de5\u5177\u76f8\u6bd4\uff0c\u9019\u500b\u7a0b\u5f0f\u7684\u7279\u8272\u5728\u65bc\u5b83\u80fd\u81ea\u52d5\u8b58\u5225\u4e26\u4fdd\u7559\u6700\u820a\u7684\u6a94\u6848\uff0c\u9019\u5c0d\u65bc\u90a3\u4e9b\u60f3\u4fdd\u7559\u539f\u59cb\u6a94\u6848\u7684\u4f7f\u7528\u8005\u4f86\u8aaa\u975e\u5e38\u5be6\u7528\u3002<\/p>\n\n\n\n<p><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">\u74b0\u5883\u6e96\u5099<\/h2>\n\n\n\n<p>\u672c\u5de5\u5177\u4f7f\u7528Python\u6a19\u6e96\u5eab\u4e2d\u7684\u6a21\u7d44\uff0c\u56e0\u6b64\u4e0d\u9700\u8981\u5b89\u88dd\u984d\u5916\u7684\u5957\u4ef6\u3002\u60a8\u53ea\u9700\u8981\uff1a<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>\u5b89\u88ddPython 3.6\u6216\u66f4\u9ad8\u7248\u672c\uff08\u5efa\u8b70\u4f7f\u7528\u6700\u65b0\u7684Python 3.11\uff09<\/li>\n\n\n\n<li>\u78ba\u4fdd\u60a8\u7684Python\u5b89\u88dd\u5305\u542btkinter\u5eab\uff08\u5927\u591a\u6578Python\u5b89\u88dd\u90fd\u9810\u8a2d\u5305\u542b\uff09<\/li>\n<\/ol>\n\n\n\n<p>\u6aa2\u67e5tkinter\u662f\u5426\u5df2\u5b89\u88dd\u7684\u65b9\u6cd5\uff1a<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">python -c \"import tkinter; print(tkinter.TkVersion)\"\n<\/pre>\n\n\n\n<p>\u5982\u679c\u986f\u793a\u7248\u672c\u865f\uff0c\u5247\u8868\u793atkinter\u5df2\u5b89\u88dd\u3002<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">\u5b8c\u6574\u7a0b\u5f0f\u78bc<\/h2>\n\n\n\n<p>\u5c07\u4ee5\u4e0b\u4ee3\u78bc\u8907\u88fd\u5230\u4e00\u500b\u540d\u70ba<code>duplicate_file_cleaner.py<\/code>\u7684\u6a94\u6848\u4e2d\uff1a<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">import os\nimport hashlib\nimport tkinter as tk\nfrom tkinter import filedialog, messagebox, ttk\nfrom pathlib import Path\nimport threading\nimport time\n\nclass DuplicateFileFinder:\n    def __init__(self, root):\n        self.root = root\n        self.root.title(\"\u91cd\u8907\u6a94\u6848\u6bd4\u5c0d\u8207\u522a\u9664\u5de5\u5177\")\n        self.root.geometry(\"900x600\")\n        self.root.resizable(True, True)\n        \n        # \u5efa\u7acb\u4e3b\u6846\u67b6\n        self.main_frame = ttk.Frame(root, padding=\"10\")\n        self.main_frame.pack(fill=tk.BOTH, expand=True)\n        \n        # \u5efa\u7acb\u63a7\u5236\u5340\u57df\n        self.control_frame = ttk.LabelFrame(self.main_frame, text=\"\u63a7\u5236\u9762\u677f\", padding=\"10\")\n        self.control_frame.pack(fill=tk.X, pady=5)\n        \n        # \u8cc7\u6599\u593e\u9078\u64c7\u5340\u57df\n        self.folder_frame = ttk.Frame(self.control_frame)\n        self.folder_frame.pack(fill=tk.X, pady=5)\n        \n        self.folder_label = ttk.Label(self.folder_frame, text=\"\u76ee\u6a19\u8cc7\u6599\u593e:\")\n        self.folder_label.pack(side=tk.LEFT, padx=5)\n        \n        self.folder_path = tk.StringVar()\n        self.folder_entry = ttk.Entry(self.folder_frame, textvariable=self.folder_path, width=50)\n        self.folder_entry.pack(side=tk.LEFT, padx=5, fill=tk.X, expand=True)\n        \n        self.browse_button = ttk.Button(self.folder_frame, text=\"\u700f\u89bd...\", command=self.browse_folder)\n        self.browse_button.pack(side=tk.LEFT, padx=5)\n        \n        # \u81ea\u52d5\u6a19\u8a18\u9078\u9805\n        self.auto_mark_frame = ttk.Frame(self.control_frame)\n        self.auto_mark_frame.pack(fill=tk.X, pady=5)\n        \n        self.auto_mark_var = tk.BooleanVar(value=True)\n        self.auto_mark_check = ttk.Checkbutton(\n            self.auto_mark_frame, \n            text=\"\u81ea\u52d5\u6a19\u8a18\u91cd\u8907\u6a94\u6848\uff08\u4fdd\u7559\u6700\u820a\u7684\u6a94\u6848\uff09\", \n            variable=self.auto_mark_var\n        )\n        self.auto_mark_check.pack(side=tk.LEFT, padx=5)\n        \n        # \u529f\u80fd\u6309\u9215\u5340\u57df\n        self.button_frame = ttk.Frame(self.control_frame)\n        self.button_frame.pack(fill=tk.X, pady=5)\n        \n        self.scan_button = ttk.Button(self.button_frame, text=\"\u6383\u63cf\u91cd\u8907\u6a94\u6848\", command=self.start_scan)\n        self.scan_button.pack(side=tk.LEFT, padx=5)\n        \n        self.delete_button = ttk.Button(self.button_frame, text=\"\u522a\u9664\u6a19\u8a18\u7684\u6a94\u6848\", command=self.delete_selected, state=tk.DISABLED)\n        self.delete_button.pack(side=tk.LEFT, padx=5)\n        \n        # \u9032\u5ea6\u689d\n        self.progress_var = tk.DoubleVar()\n        self.progress_bar = ttk.Progressbar(self.control_frame, variable=self.progress_var, maximum=100)\n        self.progress_bar.pack(fill=tk.X, pady=5)\n        \n        self.status_var = tk.StringVar(value=\"\u5c31\u7dd2\")\n        self.status_label = ttk.Label(self.control_frame, textvariable=self.status_var, anchor=tk.W)\n        self.status_label.pack(fill=tk.X, pady=5)\n        \n        # \u7d50\u679c\u5c55\u793a\u5340\u57df\n        self.result_frame = ttk.LabelFrame(self.main_frame, text=\"\u91cd\u8907\u6a94\u6848\u6e05\u55ae\", padding=\"10\")\n        self.result_frame.pack(fill=tk.BOTH, expand=True, pady=5)\n        \n        # \u5efa\u7acb\u6a39\u72c0\u8996\u5716\u5c55\u793a\u7d50\u679c\n        self.tree = ttk.Treeview(self.result_frame, columns=(\"size\", \"path\"), show=\"tree headings\")\n        self.tree.heading(\"#0\", text=\"\u7fa4\u7d44\/\u6a94\u6848\")\n        self.tree.heading(\"size\", text=\"\u6a94\u6848\u5927\u5c0f\")\n        self.tree.heading(\"path\", text=\"\u6a94\u6848\u8def\u5f91\")\n        self.tree.column(\"#0\", width=120)\n        self.tree.column(\"size\", width=100)\n        self.tree.column(\"path\", width=600)\n        \n        self.tree_scroll_y = ttk.Scrollbar(self.result_frame, orient=tk.VERTICAL, command=self.tree.yview)\n        self.tree_scroll_x = ttk.Scrollbar(self.result_frame, orient=tk.HORIZONTAL, command=self.tree.xview)\n        self.tree.configure(yscrollcommand=self.tree_scroll_y.set, xscrollcommand=self.tree_scroll_x.set)\n        \n        self.tree_scroll_y.pack(side=tk.RIGHT, fill=tk.Y)\n        self.tree.pack(side=tk.TOP, fill=tk.BOTH, expand=True)\n        self.tree_scroll_x.pack(side=tk.BOTTOM, fill=tk.X)\n        \n        # \u5efa\u7acb\u53f3\u9375\u9078\u55ae\n        self.context_menu = tk.Menu(self.tree, tearoff=0)\n        self.context_menu.add_command(label=\"\u6a19\u8a18\u70ba\u8981\u522a\u9664\", command=lambda: self.mark_for_deletion(True))\n        self.context_menu.add_command(label=\"\u53d6\u6d88\u6a19\u8a18\", command=lambda: self.mark_for_deletion(False))\n        self.context_menu.add_separator()\n        self.context_menu.add_command(label=\"\u9664\u4e86\u9019\u500b\u90fd\u6a19\u8a18\", command=self.mark_all_except)\n        \n        self.tree.bind(\"&lt;Button-3>\", self.show_context_menu)\n        \n        # \u5b58\u5132\u6383\u63cf\u7d50\u679c\n        self.duplicate_groups = {}\n        self.files_to_delete = set()\n        \n    def browse_folder(self):\n        folder_path = filedialog.askdirectory(title=\"\u9078\u64c7\u8981\u6383\u63cf\u7684\u8cc7\u6599\u593e\")\n        if folder_path:\n            self.folder_path.set(folder_path)\n    \n    def start_scan(self):\n        folder_path = self.folder_path.get()\n        if not folder_path or not os.path.isdir(folder_path):\n            messagebox.showerror(\"\u932f\u8aa4\", \"\u8acb\u9078\u64c7\u6709\u6548\u7684\u8cc7\u6599\u593e\u8def\u5f91\")\n            return\n        \n        # \u91cd\u7f6eUI\n        self.tree.delete(*self.tree.get_children())\n        self.duplicate_groups = {}\n        self.files_to_delete = set()\n        self.delete_button.config(state=tk.DISABLED)\n        \n        # \u958b\u59cb\u6383\u63cf\u7dda\u7a0b\n        threading.Thread(target=self.scan_for_duplicates, args=(folder_path,), daemon=True).start()\n    \n    def scan_for_duplicates(self, folder_path):\n        self.status_var.set(\"\u6b63\u5728\u6383\u63cf\u4e2d...\")\n        self.progress_var.set(0)\n        \n        # \u6e05\u9664\u5148\u524d\u7684\u72c0\u614b\n        self.files_to_delete = set()\n        \n        # \u968e\u6bb5 1: \u6309\u5927\u5c0f\u5206\u7d44\u6a94\u6848\n        self.update_status(\"\u7b2c\u4e00\u968e\u6bb5: \u6309\u6a94\u6848\u5927\u5c0f\u5206\u7d44...\")\n        size_groups = {}\n        total_files = 0\n        \n        for root, _, files in os.walk(folder_path):\n            total_files += len(files)\n        \n        processed_files = 0\n        for root, _, files in os.walk(folder_path):\n            for file in files:\n                file_path = os.path.join(root, file)\n                try:\n                    size = os.path.getsize(file_path)\n                    if size > 0:  # \u5ffd\u7565\u7a7a\u6a94\u6848\n                        if size not in size_groups:\n                            size_groups[size] = []\n                        size_groups[size].append(file_path)\n                except (IOError, OSError):\n                    pass  # \u5ffd\u7565\u7121\u6cd5\u8a2a\u554f\u7684\u6a94\u6848\n                \n                processed_files += 1\n                self.progress_var.set((processed_files \/ total_files) * 50)  # \u7b2c\u4e00\u968e\u6bb5\u4f5450%\n                self.root.update_idletasks()\n        \n        # \u968e\u6bb5 2: \u8a08\u7b97MD5\u4e26\u5c0b\u627e\u91cd\u8907\u9805\n        self.update_status(\"\u7b2c\u4e8c\u968e\u6bb5: \u8a08\u7b97\u96dc\u6e4a\u503c\u6bd4\u5c0d\u91cd\u8907\u6a94\u6848...\")\n        potential_duplicates = {size: paths for size, paths in size_groups.items() if len(paths) > 1}\n        total_potential_dups = sum(len(paths) for paths in potential_duplicates.values())\n        \n        if total_potential_dups == 0:\n            self.root.after(0, lambda: self.update_status(\"\u5b8c\u6210\uff01\u672a\u767c\u73fe\u91cd\u8907\u6a94\u6848\"))\n            self.progress_var.set(100)\n            return\n        \n        processed_files = 0\n        for size, file_paths in potential_duplicates.items():\n            hash_groups = {}\n            \n            for file_path in file_paths:\n                try:\n                    file_hash = self.calculate_md5(file_path)\n                    if file_hash not in hash_groups:\n                        hash_groups[file_hash] = []\n                    hash_groups[file_hash].append(file_path)\n                except (IOError, OSError):\n                    pass  # \u5ffd\u7565\u7121\u6cd5\u8b80\u53d6\u7684\u6a94\u6848\n                \n                processed_files += 1\n                progress = 50 + (processed_files \/ total_potential_dups) * 50  # \u7b2c\u4e8c\u968e\u6bb5\u4f5450%\n                self.progress_var.set(progress)\n                self.root.update_idletasks()\n            \n            # \u5132\u5b58\u91cd\u8907\u6a94\u6848\u7d44\n            for file_hash, paths in hash_groups.items():\n                if len(paths) > 1:\n                    self.duplicate_groups[file_hash] = paths\n        \n        # \u5728\u4e3b\u7dda\u7a0b\u4e2d\u66f4\u65b0UI\n        self.root.after(0, self.update_ui_with_results)\n    \n    def calculate_md5(self, file_path, block_size=8192):\n        md5 = hashlib.md5()\n        with open(file_path, 'rb') as f:\n            for block in iter(lambda: f.read(block_size), b''):\n                md5.update(block)\n        return md5.hexdigest()\n    \n    def update_status(self, message):\n        self.status_var.set(message)\n        self.root.update_idletasks()\n    \n    def update_ui_with_results(self):\n        if not self.duplicate_groups:\n            self.update_status(\"\u5b8c\u6210\uff01\u672a\u767c\u73fe\u91cd\u8907\u6a94\u6848\")\n            return\n        \n        # \u586b\u5145\u6a39\u72c0\u8996\u5716\n        for idx, (file_hash, file_paths) in enumerate(self.duplicate_groups.items()):\n            size = os.path.getsize(file_paths[0])\n            size_str = self.format_size(size)\n            \n            group_id = f\"group_{idx}\"\n            group_text = f\"\u7fa4\u7d44 {idx+1} ({len(file_paths)} \u500b\u6a94\u6848)\"\n            \n            self.tree.insert(\"\", \"end\", group_id, text=group_text, values=(size_str, f\"MD5: {file_hash}\"))\n            \n            # \u6309\u5275\u5efa\u65e5\u671f\u6392\u5e8f\u6a94\u6848\uff08\u6700\u820a\u7684\u512a\u5148\uff09\n            sorted_paths = sorted(file_paths, key=lambda p: os.path.getctime(p))\n            \n            # \u986f\u793a\u6a94\u6848\u4e26\u81ea\u52d5\u6a19\u8a18\n            for i, path in enumerate(sorted_paths):\n                # \u70ba\u6bcf\u500b\u6a94\u6848\u5275\u5efa\u4e00\u500b\u552f\u4e00\u7684\u5b89\u5168ID\n                file_id = f\"file_{idx}_{i}\"\n                file_name = os.path.basename(path)\n                creation_time = os.path.getctime(path)\n                creation_time_str = self.format_time(creation_time)\n                \n                # \u5206\u958b\u986f\u793a\u8def\u5f91\u548c\u5275\u5efa\u6642\u9593\uff0c\u907f\u514d\u5b57\u4e32\u904e\u9577\n                display_path = path\n                display_time = f\"\u5efa\u7acb\u6642\u9593: {creation_time_str}\"\n                \n                self.tree.insert(group_id, \"end\", file_id, text=file_name, \n                                values=(size_str, display_path))\n                \n                # \u81ea\u52d5\u6a19\u8a18\u9664\u4e86\u6700\u820a\u6a94\u6848\u4ee5\u5916\u7684\u6240\u6709\u6a94\u6848\n                if self.auto_mark_var.get() and i > 0:\n                    self.mark_item(file_id, True, path)\n        \n        self.update_status(f\"\u6383\u63cf\u5b8c\u6210\uff01\u767c\u73fe {len(self.duplicate_groups)} \u7d44\u91cd\u8907\u6a94\u6848\")\n        self.delete_button.config(state=tk.NORMAL)\n    \n    def format_size(self, size):\n        for unit in ['B', 'KB', 'MB', 'GB', 'TB']:\n            if size &lt; 1024.0:\n                return f\"{size:.2f} {unit}\"\n            size \/= 1024.0\n        return f\"{size:.2f} PB\"\n    \n    def format_time(self, timestamp):\n        return time.strftime(\"%Y-%m-%d %H:%M:%S\", time.localtime(timestamp))\n    \n    def show_context_menu(self, event):\n        item = self.tree.identify_row(event.y)\n        if item:\n            self.tree.selection_set(item)\n            self.context_menu.post(event.x_root, event.y_root)\n    \n    def mark_for_deletion(self, to_delete):\n        selected_item = self.tree.selection()[0] if self.tree.selection() else None\n        if not selected_item:\n            return\n        \n        if selected_item.startswith(\"group_\"):\n            # \u5982\u679c\u9078\u64c7\u4e86\u4e00\u500b\u7fa4\u7d44\uff0c\u6a19\u8a18\u6240\u6709\u5b50\u9805\u76ee\n            for child in self.tree.get_children(selected_item):\n                file_path = self.tree.item(child, \"values\")[1]\n                self.mark_item(child, to_delete, file_path)\n        else:\n            # \u6a19\u8a18\u55ae\u500b\u6a94\u6848\n            file_path = self.tree.item(selected_item, \"values\")[1]\n            self.mark_item(selected_item, to_delete, file_path)\n    \n    def mark_item(self, item_id, to_delete, file_path):\n        if to_delete:\n            self.files_to_delete.add(file_path)\n            self.tree.item(item_id, tags=(\"delete\",))\n            self.tree.tag_configure(\"delete\", foreground=\"red\")\n        else:\n            if file_path in self.files_to_delete:\n                self.files_to_delete.remove(file_path)\n            self.tree.item(item_id, tags=())\n    \n    def mark_all_except(self):\n        selected_item = self.tree.selection()[0] if self.tree.selection() else None\n        if not selected_item or selected_item.startswith(\"group_\"):\n            return\n        \n        # \u7372\u53d6\u7236\u7fa4\u7d44\n        parent = self.tree.parent(selected_item)\n        \n        # \u6a19\u8a18\u9664\u4e86\u6240\u9078\u6a94\u6848\u4ee5\u5916\u7684\u6240\u6709\u6a94\u6848\n        for child in self.tree.get_children(parent):\n            file_path = self.tree.item(child, \"values\")[1]\n            if child != selected_item:\n                self.mark_item(child, True, file_path)\n            else:\n                self.mark_item(child, False, file_path)\n    \n    def delete_selected(self):\n        if not self.files_to_delete:\n            messagebox.showinfo(\"\u63d0\u793a\", \"\u6c92\u6709\u6a19\u8a18\u8981\u522a\u9664\u7684\u6a94\u6848\")\n            return\n        \n        confirm = messagebox.askyesno(\n            \"\u78ba\u8a8d\u522a\u9664\", \n            f\"\u78ba\u5b9a\u8981\u522a\u9664 {len(self.files_to_delete)} \u500b\u6a19\u8a18\u7684\u6a94\u6848\u55ce\uff1f\\n\u6b64\u64cd\u4f5c\u7121\u6cd5\u5fa9\u539f\uff01\",\n            icon=\"warning\"\n        )\n        \n        if not confirm:\n            return\n        \n        deleted = 0\n        errors = 0\n        for file_path in self.files_to_delete:\n            try:\n                os.remove(file_path)\n                deleted += 1\n            except Exception:\n                errors += 1\n        \n        # \u66f4\u65b0UI\uff0c\u79fb\u9664\u5df2\u522a\u9664\u7684\u9805\u76ee\n        for item_id in self.tree.tag_has(\"delete\"):\n            self.tree.delete(item_id)\n        \n        # \u6aa2\u67e5\u7fa4\u7d44\u662f\u5426\u53ea\u5269\u4e00\u500b\u6a94\u6848\uff0c\u5982\u679c\u662f\u5247\u79fb\u9664\u7fa4\u7d44\u7d50\u69cb\n        for group_id in [x for x in self.tree.get_children() if x.startswith(\"group_\")]:\n            children = self.tree.get_children(group_id)\n            if len(children) &lt;= 1:\n                for child in children:\n                    self.tree.detach(child)\n                self.tree.delete(group_id)\n        \n        self.files_to_delete.clear()\n        \n        if errors > 0:\n            messagebox.showwarning(\"\u8b66\u544a\", f\"\u5df2\u522a\u9664 {deleted} \u500b\u6a94\u6848\uff0c\u4f46\u6709 {errors} \u500b\u6a94\u6848\u7121\u6cd5\u522a\u9664\")\n        else:\n            messagebox.showinfo(\"\u5b8c\u6210\", f\"\u6210\u529f\u522a\u9664 {deleted} \u500b\u6a94\u6848\")\n\ndef main():\n    root = tk.Tk()\n    app = DuplicateFileFinder(root)\n    root.mainloop()\n\nif __name__ == \"__main__\":\n    main()\n<\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">\u7a0b\u5f0f\u78bc\u89e3\u6790<\/h2>\n\n\n\n<p>\u8b93\u6211\u5011\u4f86\u5206\u89e3\u9019\u500b\u7a0b\u5f0f\u7684\u4e3b\u8981\u7d44\u4ef6\u548c\u529f\u80fd\uff1a<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">1. \u754c\u9762\u8a2d\u8a08<\/h3>\n\n\n\n<p>\u7a0b\u5f0f\u4f7f\u7528<code>tkinter<\/code>\u548c<code>ttk<\/code>\u5efa\u7acb\u4e86\u4e00\u500b\u5206\u5340\u660e\u78ba\u7684\u7528\u6236\u754c\u9762\uff1a<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"># \u5efa\u7acb\u4e3b\u6846\u67b6\nself.main_frame = ttk.Frame(root, padding=\"10\")\nself.main_frame.pack(fill=tk.BOTH, expand=True)\n<\/pre>\n\n\n\n<p>\u754c\u9762\u4e3b\u8981\u5206\u70ba\u4e09\u500b\u90e8\u5206\uff1a<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u63a7\u5236\u9762\u677f\uff1a\u5305\u542b\u8cc7\u6599\u593e\u9078\u64c7\u3001\u9078\u9805\u8a2d\u5b9a\u548c\u64cd\u4f5c\u6309\u9215<\/li>\n\n\n\n<li>\u9032\u5ea6\u6307\u793a\u5340\uff1a\u986f\u793a\u9032\u5ea6\u689d\u548c\u72c0\u614b\u6d88\u606f<\/li>\n\n\n\n<li>\u7d50\u679c\u986f\u793a\u5340\uff1a\u4f7f\u7528\u6a39\u72c0\u8996\u5716\u5c55\u793a\u91cd\u8907\u6a94\u6848\u7d44<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">2. \u6a94\u6848\u6383\u63cf\u8207\u6bd4\u5c0d<\/h3>\n\n\n\n<p>\u6a94\u6848\u6bd4\u5c0d\u5206\u70ba\u5169\u500b\u968e\u6bb5\uff0c\u63d0\u9ad8\u4e86\u6548\u7387\uff1a<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"># \u968e\u6bb5 1: \u6309\u5927\u5c0f\u5206\u7d44\u6a94\u6848\nsize_groups = {}\n# ... \u4ee3\u78bc\u5be6\u73fe ...\n\n# \u968e\u6bb5 2: \u8a08\u7b97MD5\u4e26\u5c0b\u627e\u91cd\u8907\u9805\nfor size, file_paths in potential_duplicates.items():\n    hash_groups = {}\n    # ... \u4ee3\u78bc\u5be6\u73fe ...\n<\/pre>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>\u7b2c\u4e00\u968e\u6bb5<\/strong>\uff1a\u6309\u6a94\u6848\u5927\u5c0f\u5206\u7d44\uff0c\u56e0\u70ba\u5927\u5c0f\u4e0d\u540c\u7684\u6a94\u6848\u5fc5\u7136\u4e0d\u662f\u91cd\u8907\u7684<\/li>\n\n\n\n<li><strong>\u7b2c\u4e8c\u968e\u6bb5<\/strong>\uff1a\u53ea\u5c0d\u76f8\u540c\u5927\u5c0f\u7684\u6a94\u6848\u8a08\u7b97MD5\u96dc\u6e4a\u503c\uff0c\u7bc0\u7701\u6642\u9593\u548c\u8cc7\u6e90<\/li>\n<\/ol>\n\n\n\n<h3 class=\"wp-block-heading\">3. \u81ea\u52d5\u4fdd\u7559\u6700\u820a\u6a94\u6848<\/h3>\n\n\n\n<p>\u9019\u662f\u672c\u5de5\u5177\u7684\u6838\u5fc3\u529f\u80fd\uff0c\u5be6\u73fe\u5982\u4e0b\uff1a<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"># \u6309\u5275\u5efa\u65e5\u671f\u6392\u5e8f\u6a94\u6848\uff08\u6700\u820a\u7684\u512a\u5148\uff09\nsorted_paths = sorted(file_paths, key=lambda p: os.path.getctime(p))\n\n# \u986f\u793a\u6a94\u6848\u4e26\u81ea\u52d5\u6a19\u8a18\nfor i, path in enumerate(sorted_paths):\n    # ... \u986f\u793a\u6a94\u6848 ...\n    \n    # \u81ea\u52d5\u6a19\u8a18\u9664\u4e86\u6700\u820a\u6a94\u6848\u4ee5\u5916\u7684\u6240\u6709\u6a94\u6848\n    if self.auto_mark_var.get() and i > 0:\n        self.mark_item(file_id, True, path)\n<\/pre>\n\n\n\n<p>\u7a0b\u5f0f\u4f7f\u7528<code>os.path.getctime()<\/code>\u7372\u53d6\u6a94\u6848\u7684\u5275\u5efa\u6642\u9593\uff0c\u7136\u5f8c\u6309\u6642\u9593\u5347\u5e8f\u6392\u5217\uff0c\u4fdd\u7559\u7b2c\u4e00\u500b\uff08\u6700\u820a\u7684\uff09\u6a94\u6848\u3002<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">4. \u591a\u7dda\u7a0b\u8655\u7406<\/h3>\n\n\n\n<p>\u70ba\u4e86\u907f\u514d\u6383\u63cf\u904e\u7a0b\u4e2d\u754c\u9762\u51cd\u7d50\uff0c\u7a0b\u5f0f\u4f7f\u7528\u4e86\u591a\u7dda\u7a0b\uff1a<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"># \u958b\u59cb\u6383\u63cf\u7dda\u7a0b\nthreading.Thread(target=self.scan_for_duplicates, args=(folder_path,), daemon=True).start()\n<\/pre>\n\n\n\n<p>\u9019\u78ba\u4fdd\u4e86\u5373\u4f7f\u5728\u6383\u63cf\u5927\u91cf\u6a94\u6848\u6642\uff0c\u7528\u6236\u754c\u9762\u4ecd\u7136\u4fdd\u6301\u97ff\u61c9\u3002<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">\u4f7f\u7528\u6307\u5357<\/h2>\n\n\n\n<p>\u4f7f\u7528\u9019\u500b\u5de5\u5177\u975e\u5e38\u7c21\u55ae\uff0c\u53ea\u9700\u5e7e\u500b\u6b65\u9a5f\uff1a<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">\u6b65\u9a5f 1\uff1a\u9078\u64c7\u76ee\u6a19\u8cc7\u6599\u593e<\/h3>\n\n\n\n<p>\u9ede\u64ca\u300c\u700f\u89bd&#8230;\u300d\u6309\u9215\uff0c\u9078\u64c7\u8981\u6383\u63cf\u7684\u8cc7\u6599\u593e\u3002<\/p>\n\n\n\n<p><\/p>\n\n\n\n<h3 class=\"wp-block-heading\">\u6b65\u9a5f 2\uff1a\u958b\u59cb\u6383\u63cf<\/h3>\n\n\n\n<p>\u78ba\u8a8d\u300c\u81ea\u52d5\u6a19\u8a18\u91cd\u8907\u6a94\u6848\u300d\u9078\u9805\u5df2\u52fe\u9078\uff08\u9810\u8a2d\u52fe\u9078\uff09\uff0c\u7136\u5f8c\u9ede\u64ca\u300c\u6383\u63cf\u91cd\u8907\u6a94\u6848\u300d\u6309\u9215\u3002<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">\u6b65\u9a5f 3\uff1a\u6aa2\u67e5\u6383\u63cf\u7d50\u679c<\/h3>\n\n\n\n<p>\u6383\u63cf\u5b8c\u6210\u5f8c\uff0c\u91cd\u8907\u6a94\u6848\u6703\u6309\u7fa4\u7d44\u986f\u793a\u5728\u6a39\u72c0\u8996\u5716\u4e2d\uff1a<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u6bcf\u500b\u7fa4\u7d44\u4ee3\u8868\u4e00\u7d44\u5167\u5bb9\u76f8\u540c\u7684\u6a94\u6848<\/li>\n\n\n\n<li>\u7d05\u8272\u6a19\u8a18\u7684\u6a94\u6848\u5c07\u88ab\u522a\u9664\uff08\u9810\u8a2d\u662f\u4fdd\u7559\u6700\u820a\u7684\u6a94\u6848\uff0c\u5176\u9918\u6a19\u8a18\u70ba\u522a\u9664\uff09<\/li>\n<\/ul>\n\n\n\n<p><\/p>\n\n\n\n<h3 class=\"wp-block-heading\">\u6b65\u9a5f 4\uff1a\u8abf\u6574\u9078\u64c7\uff08\u53ef\u9078\uff09<\/h3>\n\n\n\n<p>\u5982\u679c\u9700\u8981\u624b\u52d5\u8abf\u6574\u8981\u522a\u9664\u7684\u6a94\u6848\uff1a<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>\u53f3\u9375\u9ede\u64ca\u6a94\u6848\uff0c\u5f9e\u9078\u55ae\u4e2d\u9078\u64c7\uff1a\n<ul class=\"wp-block-list\">\n<li>\u300c\u6a19\u8a18\u70ba\u8981\u522a\u9664\u300d\uff1a\u5c07\u6a94\u6848\u6a19\u8a18\u70ba\u522a\u9664<\/li>\n\n\n\n<li>\u300c\u53d6\u6d88\u6a19\u8a18\u300d\uff1a\u53d6\u6d88\u6a94\u6848\u7684\u522a\u9664\u6a19\u8a18<\/li>\n\n\n\n<li>\u300c\u9664\u4e86\u9019\u500b\u90fd\u6a19\u8a18\u300d\uff1a\u4fdd\u7559\u9078\u4e2d\u6a94\u6848\uff0c\u6a19\u8a18\u540c\u7d44\u7684\u5176\u4ed6\u6a94\u6848<\/li>\n<\/ul>\n<\/li>\n<\/ol>\n\n\n\n<p><\/p>\n\n\n\n<h3 class=\"wp-block-heading\">\u6b65\u9a5f 5\uff1a\u522a\u9664\u91cd\u8907\u6a94\u6848<\/h3>\n\n\n\n<p>\u78ba\u8a8d\u6a19\u8a18\u7121\u8aa4\u5f8c\uff0c\u9ede\u64ca\u300c\u522a\u9664\u6a19\u8a18\u7684\u6a94\u6848\u300d\u6309\u9215\u3002\u7cfb\u7d71\u6703\u986f\u793a\u78ba\u8a8d\u5c0d\u8a71\u6846\uff0c\u78ba\u8a8d\u5f8c\u5c07\u6c38\u4e45\u522a\u9664\u6a19\u8a18\u7684\u6a94\u6848\u3002<\/p>\n\n\n\n<p><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">\u81ea\u5b9a\u7fa9\u8207\u64f4\u5c55<\/h2>\n\n\n\n<p>\u9019\u500b\u7a0b\u5f0f\u53ef\u4ee5\u6839\u64da\u60a8\u7684\u9700\u6c42\u8f15\u9b06\u64f4\u5c55\u3002\u4ee5\u4e0b\u662f\u4e00\u4e9b\u53ef\u80fd\u7684\u81ea\u5b9a\u7fa9\u65b9\u5411\uff1a<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">\u4fee\u6539\u4fdd\u7559\u7b56\u7565<\/h3>\n\n\n\n<p>\u5982\u679c\u60a8\u60f3\u6309\u5176\u4ed6\u6a19\u6e96\u9078\u64c7\u8981\u4fdd\u7559\u7684\u6a94\u6848\uff08\u4f8b\u5982\u4fdd\u7559\u6700\u65b0\u7684\u800c\u975e\u6700\u820a\u7684\uff09\uff0c\u53ea\u9700\u4fee\u6539\u6392\u5e8f\u51fd\u6578\uff1a<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"># \u4fdd\u7559\u6700\u65b0\u6a94\u6848\u800c\u975e\u6700\u820a\u6a94\u6848\nsorted_paths = sorted(file_paths, key=lambda p: os.path.getctime(p), reverse=True)\n<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">\u6dfb\u52a0\u66f4\u591a\u6a94\u6848\u8a0a\u606f<\/h3>\n\n\n\n<p>\u60a8\u53ef\u4ee5\u5728\u6a94\u6848\u5217\u8868\u4e2d\u986f\u793a\u66f4\u591a\u4fe1\u606f\uff0c\u4f8b\u5982\u4fee\u6539\u6642\u9593\u3001\u6a94\u6848\u985e\u578b\u7b49\uff1a<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"># \u5728update_ui_with_results\u65b9\u6cd5\u4e2d\u4fee\u6539\nmodification_time = os.path.getmtime(path)\nmodification_time_str = self.format_time(modification_time)\nfile_extension = os.path.splitext(path)[1]\n\n# \u7136\u5f8c\u5728display_path\u4e2d\u6dfb\u52a0\u9019\u4e9b\u4fe1\u606f\n<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">\u6dfb\u52a0\u532f\u51fa\u5831\u544a\u529f\u80fd<\/h3>\n\n\n\n<p>\u60a8\u53ef\u4ee5\u6dfb\u52a0\u4e00\u500b\u6309\u9215\u4f86\u5c07\u91cd\u8907\u6a94\u6848\u7684\u6e05\u55ae\u532f\u51fa\u70baCSV\u6216TXT\u683c\u5f0f\uff1a<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">def export_report(self):\n    export_path = filedialog.asksaveasfilename(\n        defaultextension=\".csv\",\n        filetypes=[(\"CSV\u6a94\u6848\", \"*.csv\"), (\"\u6587\u5b57\u6a94\u6848\", \"*.txt\")]\n    )\n    if not export_path:\n        return\n        \n    with open(export_path, 'w', encoding='utf-8') as f:\n        # \u5beb\u5165\u5831\u544a\u5167\u5bb9\n        f.write(\"\u7fa4\u7d44,\u6a94\u6848\u540d\u7a31,\u6a94\u6848\u5927\u5c0f,\u6a94\u6848\u8def\u5f91,\u5275\u5efa\u6642\u9593,\u662f\u5426\u6a19\u8a18\u522a\u9664\\n\")\n        # \u904d\u6b77\u6240\u6709\u7fa4\u7d44\u548c\u6a94\u6848...\n<\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">\u5e38\u898b\u554f\u984c<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Q1: \u70ba\u4ec0\u9ebc\u7a0b\u5f0f\u6383\u63cf\u5927\u578b\u6a94\u6848\u5f88\u6162\uff1f<\/h3>\n\n\n\n<p>A: \u8a08\u7b97MD5\u96dc\u6e4a\u503c\u9700\u8981\u8b80\u53d6\u6574\u500b\u6a94\u6848\u5167\u5bb9\uff0c\u5c0d\u65bc\u5927\u578b\u6a94\u6848\u53ef\u80fd\u9700\u8981\u8f03\u9577\u6642\u9593\u3002\u7a0b\u5f0f\u5df2\u7d93\u512a\u5316\u4e86\u6383\u63cf\u6d41\u7a0b\uff0c\u9996\u5148\u6309\u6a94\u6848\u5927\u5c0f\u904e\u6ffe\uff0c\u53ea\u5c0d\u53ef\u80fd\u91cd\u8907\u7684\u6a94\u6848\u8a08\u7b97\u96dc\u6e4a\u503c\u3002<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Q2: \u5982\u4f55\u5224\u65b7\u6a94\u6848\u662f\u5426\u771f\u7684\u91cd\u8907\uff1f<\/h3>\n\n\n\n<p>A: \u7a0b\u5f0f\u4f7f\u7528MD5\u96dc\u6e4a\u7b97\u6cd5\u6bd4\u5c0d\u6a94\u6848\u5167\u5bb9\uff0c\u9019\u63d0\u4f9b\u4e86\u6975\u9ad8\u7684\u6e96\u78ba\u7387\u3002\u5169\u500b\u4e0d\u540c\u5167\u5bb9\u7684\u6a94\u6848\u7522\u751f\u76f8\u540cMD5\u503c\u7684\u6a5f\u7387\u6975\u4f4e\uff08hash\u78b0\u649e\uff09\uff0c\u56e0\u6b64\u53ef\u4ee5\u78ba\u4fdd\u88ab\u8b58\u5225\u70ba\u91cd\u8907\u7684\u6a94\u6848\u771f\u7684\u5167\u5bb9\u76f8\u540c\u3002<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Q3: \u522a\u9664\u5f8c\u53ef\u4ee5\u6062\u5fa9\u55ce\uff1f<\/h3>\n\n\n\n<p>A: \u4e0d\u53ef\u4ee5\u3002\u7a0b\u5f0f\u76f4\u63a5\u4f7f\u7528<code>os.remove()<\/code>\u522a\u9664\u6a94\u6848\uff0c\u9019\u662f\u6c38\u4e45\u6027\u7684\u64cd\u4f5c\uff0c\u4e0d\u6703\u5c07\u6a94\u6848\u79fb\u81f3\u56de\u6536\u7ad9\u3002\u8acb\u5728\u522a\u9664\u524d\u4ed4\u7d30\u6aa2\u67e5\u6a19\u8a18\u7684\u6a94\u6848\u3002<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Q4: \u7a0b\u5f0f\u5728\u6bd4\u5c0d\u54ea\u4e9b\u985e\u578b\u7684\u6a94\u6848\uff1f<\/h3>\n\n\n\n<p>A: \u7a0b\u5f0f\u6703\u6bd4\u5c0d\u6240\u6709\u975e\u7a7a\u6a94\u6848\uff0c\u4e0d\u9650\u5236\u6a94\u6848\u985e\u578b\u3002\u7121\u8ad6\u662f\u6587\u6a94\u3001\u5716\u7247\u3001\u97f3\u983b\u9084\u662f\u8996\u983b\u6a94\u6848\uff0c\u53ea\u8981\u5167\u5bb9\u76f8\u540c\u5c31\u6703\u88ab\u8b58\u5225\u70ba\u91cd\u8907\u3002<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Q5: \u6211\u53ef\u4ee5\u4fdd\u7559\u7279\u5b9a\u4f4d\u7f6e\u7684\u6a94\u6848\u55ce\uff1f<\/h3>\n\n\n\n<p>A: \u9810\u8a2d\u60c5\u6cc1\u4e0b\uff0c\u7a0b\u5f0f\u6703\u4fdd\u7559\u6700\u820a\u7684\u6a94\u6848\u3002\u5982\u679c\u60a8\u60f3\u4fdd\u7559\u7279\u5b9a\u4f4d\u7f6e\u7684\u6a94\u6848\uff0c\u53ef\u4ee5\u5728\u6383\u63cf\u5b8c\u6210\u5f8c\u4f7f\u7528\u53f3\u9375\u9078\u55ae\u624b\u52d5\u8abf\u6574\u6a19\u8a18\u3002<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">\u7d50\u8a9e<\/h2>\n\n\n\n<p>\u9019\u500bPython\u91cd\u8907\u6a94\u6848\u6e05\u7406\u5de5\u5177\u4e0d\u50c5\u80fd\u5e6b\u60a8\u7bc0\u7701\u5bf6\u8cb4\u7684\u786c\u789f\u7a7a\u9593\uff0c\u9084\u901a\u904e\u81ea\u52d5\u4fdd\u7559\u6700\u820a\u6a94\u6848\u7684\u529f\u80fd\uff0c\u8b93\u6a94\u6848\u6574\u7406\u5de5\u4f5c\u8b8a\u5f97\u66f4\u52a0\u667a\u80fd\u548c\u9ad8\u6548\u3002\u7121\u8ad6\u60a8\u662f\u6574\u7406\u7167\u7247\u3001\u97f3\u6a02\u3001\u6587\u6a94\u9084\u662f\u5176\u4ed6\u985e\u578b\u7684\u6a94\u6848\uff0c\u5b83\u90fd\u80fd\u5e6b\u60a8\u5feb\u901f\u627e\u51fa\u4e26\u5b89\u5168\u5730\u79fb\u9664\u91cd\u8907\u5167\u5bb9\u3002<\/p>\n\n\n\n<p>\u60a8\u53ef\u4ee5\u6839\u64da\u81ea\u5df1\u7684\u9700\u8981\u81ea\u7531\u4fee\u6539\u548c\u64f4\u5c55\u9019\u500b\u7a0b\u5f0f\uff0c\u6dfb\u52a0\u66f4\u591a\u529f\u80fd\u6216\u8abf\u6574\u73fe\u6709\u529f\u80fd\u7684\u884c\u70ba\u3002\u5e0c\u671b\u9019\u500b\u5de5\u5177\u80fd\u5920\u5e6b\u52a9\u60a8\u66f4\u597d\u5730\u7ba1\u7406\u6578\u4f4d\u8cc7\u7522\uff01<\/p>\n\n\n\n<p><em>\u5982\u6709\u4efb\u4f55\u554f\u984c\u6216\u5efa\u8b70\uff0c\u6b61\u8fce\u5728\u8a55\u8ad6\u5340\u7559\u8a00\u4ea4\u6d41\u3002<\/em><\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<p><strong>\u6ce8\u610f\u4e8b\u9805<\/strong>\uff1a\u5728\u4f7f\u7528\u6b64\u5de5\u5177\u522a\u9664\u6a94\u6848\u524d\uff0c\u5f37\u70c8\u5efa\u8b70\u5148\u5099\u4efd\u91cd\u8981\u8cc7\u6599\u3002\u7a0b\u5f0f\u4f7f\u7528\u7684\u522a\u9664\u64cd\u4f5c\u662f\u6c38\u4e45\u6027\u7684\uff0c\u7121\u6cd5\u6062\u5fa9\u3002<\/p>\n\n\n\n<p><\/p>","protected":false},"excerpt":{"rendered":"<p>\u5728\u9019\u500b\u6578\u4f4d\u6642\u4ee3\uff0c\u6211\u5011\u7684\u786c\u789f\u7e3d\u662f\u88ab\u5404\u7a2e\u6a94\u6848\u585e\u6eff\uff0c\u5176\u4e2d\u6709\u8a31\u591a\u662f\u91cd\u8907\u7684\u6a94\u6848\u5360\u7528\u4e86\u5bf6\u8cb4\u7684\u7a7a\u9593\u3002\u672c\u6559\u5b78\u5c07\u5e36\u60a8\u88fd\u4f5c\u4e00\u500b\u5f37\u5927 &#8230; <a title=\"[Python]\u91cd\u8907\u6a94\u6848\u6e05\u7406\u5de5\u5177\uff1a\u81ea\u52d5\u4fdd\u7559\u6700\u820a\u6a94\u6848\u7684\u5b8c\u6574\u6559\u5b78\" class=\"read-more\" href=\"https:\/\/zechs.taipei\/?p=457\" aria-label=\"\u95b1\u8b80\u3008[Python]\u91cd\u8907\u6a94\u6848\u6e05\u7406\u5de5\u5177\uff1a\u81ea\u52d5\u4fdd\u7559\u6700\u820a\u6a94\u6848\u7684\u5b8c\u6574\u6559\u5b78\u3009\u5168\u6587\">\u95b1\u8b80\u5168\u6587<\/a><\/p>","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"class_list":["post-457","post","type-post","status-publish","format-standard","hentry","category-uncategorized"],"_links":{"self":[{"href":"https:\/\/zechs.taipei\/index.php?rest_route=\/wp\/v2\/posts\/457","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/zechs.taipei\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/zechs.taipei\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/zechs.taipei\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/zechs.taipei\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=457"}],"version-history":[{"count":2,"href":"https:\/\/zechs.taipei\/index.php?rest_route=\/wp\/v2\/posts\/457\/revisions"}],"predecessor-version":[{"id":461,"href":"https:\/\/zechs.taipei\/index.php?rest_route=\/wp\/v2\/posts\/457\/revisions\/461"}],"wp:attachment":[{"href":"https:\/\/zechs.taipei\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=457"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/zechs.taipei\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=457"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/zechs.taipei\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=457"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}