ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

用Python从零打造简易音乐播放器:循环模式与图形界面实战

用Python从零打造简易音乐播放器:循环模式与图形界面实战 如果你搜过“python练手项目”大概率看到过计算器、猜数字、爬豆瓣这些。做的人多但能坚持做下去的人少。我见过太多朋友从计算器开始又从计算器放弃——因为做完以后除了“我大概学会了if/for”之外没有真正能拿得出手的东西。这篇文章要聊的是一个我反复推荐给新手的小项目用Python写一个简易音乐播放器支持单曲循环、列表循环还能直接加载整个文件夹来播放。它足够简单不涉及复杂的Web框架和数据库它又足够有用写完以后你手里就多了一个真正能天天打开的桌面工具。更重要的是它会顺手带你打通几个特别关键的基础概念文件遍历、事件绑定、界面布局、状态切换。哪怕你目前只会写“hello world”照着这篇内容一步步来也能在几小时内把播放器跑起来。我会把所有代码拆开讲清楚连环境怎么装、打包成exe怎么处理都会说到。文章偏长建议先收藏再动手。1. 为什么0基础学Python要先做这样一个音乐播放器1.1 这个项目到底用了哪些核心技术先说结论一个看似简单的音乐播放器背后其实是四块能力的组合。第一块是文件系统操作。你要让播放器支持“文件夹播放”就得自己遍历目录、过滤音频后缀名、整理出播放列表。这部分用到的是os模块属于Python里最基础的IO操作。把这块跑通了你以后做任何批量文件处理脚本都会非常顺手。第二块是音频播放能力。Python标准库本身不带播放音频的模块必须借助第三方库。我用的是pygame它虽然是做游戏出身的但它的mixer模块对MP3、WAV、OGG的支持非常稳定初始化两行代码就能播放音乐比自己去调底层的音频API要省太多事。对新手来说pygame是学习和做事之间的最佳平衡点。第三块是图形界面。播放器不能只黑乎乎的跑在终端里所以需要一个GUI界面。tkinter是Python自带的GUI库Windows上装了Python就自带它不需要额外安装。虽然它的界面风格确实谈不上“现代感”但在一个练手项目里它能让你精准理解“控件”“事件”“布局”这些桌面应用的基本概念。第四块是状态管理。播放器运行过程中至少存在这几个状态播放中、暂停中、已停止、单曲循环、列表循环。按钮上的文字、列表里的高亮、下一首到底跳到哪全部由这些状态驱动。这一块如果不理顺代码写出来很容易出现“点了暂停没反应”“切歌后状态错乱”之类的问题。1.2 三个核心需求背后的设计思考项目标题里明确写了三个功能点单曲循环、列表循环、文件夹播放。这三个功能单独看都不难但组合在一起就有意思了。文件夹播放的本质是“把磁盘里的文件变成内存中的播放列表”。这里有一个容易出现认知偏差的地方很多人以为文件夹播放是“直接播放整个目录”实际上程序必须先扫描目录把支持的后缀名过滤出来排好顺序然后才能交给播放器逐首处理。也就是说文件夹这个动作只是一种“加载列表”的手段真正播放的还是列表。单曲循环和列表循环本质上是两个播放结束后的跳转策略。单曲循环就是当前歌曲播完后回到自身列表循环就是当前歌曲播完后跳到下一首到末尾时再回到第一首。这两个逻辑写起来都只需要三四行代码但它们会引出一个关键的设计问题怎么判断一首歌播完了很多人第一反应是“sleep歌曲时长再触发下一首”这个方案非常不可靠。实际项目中一般有两种做法一种是向pygame注册一个播放结束事件另一种是用轮询的机制定期检查播放状态。我后面会详细讲这里先卖个关子。我想强调的是做这个项目真正的价值不在于“写出一个能用的播放器”而在于你在构思这几个功能时必须像一个真正的开发者那样把需求拆成逻辑、把逻辑拆成状态、把状态落到代码里。这个思维过程是你看再多教程都学不来的。2. 开发环境准备与依赖安装2.1 Python环境与pygame/tkinter的安装如果你是完全零基础第一步不是写代码而是把运行环境装好。Windows用户去Python官网下载3.10以上版本的安装包安装时一定记得勾选“Add Python to PATH”。这一步很多人会漏掉导致后面在命令行里输入python提示“不是内部或外部命令”那基本就没法继续了。安装完成后打开命令行winR输入cmd依次输入下面两条命令验证环境python --version pip --version两个都能输出版本号说明环境没问题。接下来安装pygamepip install pygame如果你在国内网络下载慢可以加镜像源比如用清华的源pip install pygame -i https://pypi.tuna.tsinghua.edu.cn/simpletkinter不需要额外安装。Windows官方安装包里已经包含了。但有些精简版Linux系统不一定自带如果你在Linux上跑先确认tkinter是否可用python -c import tkinter如果没有报错说明tkinter是好的。另外一个建议是给项目单独建一个虚拟环境避免把依赖装到全局环境里搞得一团乱。对零基础同学来说这一步可以先不做等以后项目多了再回来补。没有虚拟环境项目一样能跑。2.2 项目目录结构与代码文件规划这个播放器说到底就是一个单文件项目不需要特别复杂的目录结构。我的建议是新建一个文件夹叫music_player里面至少放两个文件music_player/ ├── player.py └── requirements.txtplayer.py是主程序所有代码都写在这个文件里。requirements.txt用来记录依赖内容只有一行pygame2.0.0不要小看requirements.txt这个文件。以后你接触Python项目多了会发现几乎所有开源项目都有它它是用来让别人快速复现环境的标准方式。别人拿到你的项目只需要在终端里执行pip install -r requirements.txt就能把依赖一次性装齐。把好习惯从一开始就养起来这个比多写几十行代码还重要。如果你愿意也可以把图标文件、音频资源放在一个assets目录里但现阶段完全没必要等后续扩展功能了再调整不迟。3. 播放器核心逻辑循环模式的设计与实现3.1 播放列表的数据结构在设计播放器内部逻辑时最重要的事情是决定播放列表用什么数据结构来保存。最简单的方案是Python列表list里面每一项是歌曲文件的完整路径。在这个列表之外我们再维护一个当前播放索引current_index表示现在正选中列表里的第几首歌。这样“上一首”“下一首”的操作本质上都只是对索引做加减法然后从列表里取对应的路径交出去播放。这里有个细节值得注意播放列表里保存的应该是文件的完整路径而不是仅仅一个文件名。因为pygame加载音乐时会把字符串当成路径去磁盘上找文件如果你只存文件名而程序所在的当前目录又不在音乐文件夹里那就会报文件找不到的错误。用os.path.join把文件夹路径和文件名拼起来是最稳妥的。如果你扫描的子文件夹里歌曲非常多比如几万首用list其实也不会有太大问题。Python的list在内存占用和索引访问方面都足够高效不需要为了这个项目去引入更复杂的数据结构。过早优化是新手比较容易踩的坑先把简单方案跑通再说。3.2 单曲循环与列表循环的切换逻辑循环模式其实就是一个字符串变量我习惯用loop_mode来表示它取两个值single和list。界面上用两个单选按钮来绑定这个变量用户点一下变量就跟着变。真正的“循环”不是一开始就让pygame无限循环播放同一首歌而是在“一首歌播放结束”这个事件触发后决定下一首到底是谁。整个逻辑非常清晰如果模式是single就把当前这首歌再播放一遍。如果模式是list就把索引加一如果索引越过了列表尾巴就回卷到0。这么设计的好处是如果你以后想加“随机播放”模式只需要再增加一个mode的取值并在结束事件的处理函数里新增一条分支。扩展成本非常低。还有一个细节是“上一首”在列表循环模式下通常也遵循回卷规则。也就是说如果当前是第一首歌那么点击“上一首”应该跳到最后一首。这符合大多数播放器的行为习惯。3.3 文件夹扫描与音频文件过滤文件夹扫描的核心代码很少但要注意的点不少。第一size要过滤。不要直接把文件夹里的所有文件都加入播放列表要判断文件后缀名是否是支持的音频格式。我一般定义一个小元组SUPPORTED_TYPES (.mp3, .wav, .ogg)然后用endswith()去匹配。这里我强烈建议把文件后缀转成小写再判断因为你无法确定用户拿到的文件是MP3还是mp3。第二列表要排序。os.listdir返回的文件顺序是不固定的直接拿来播会出现首曲目不稳定的情况。用sorted()包一层至少保证按文件名排序体验会好很多。第三是否递归子文件夹需要想清楚。如果播放器只支持扫描当前文件夹代码会简单很多。但如果你的音乐目录里还分了“华语”“欧美“这样的子文件夹那一次性把子文件夹里的歌也加载进来会更好用。递归遍历用os.walk可以很方便地实现。为了控制篇幅我这里先做单层扫描后面扩展建议里再提递归方案。4. 界面搭建tkinter实现简易播放器UI4.1 整体布局设计tkinter的界面布局有几个常用方式pack、grid和place。新手最容易上手的是pack它把所有控件按“从上到下、从左到右”的顺序排列。我这个播放器的界面结构很简单从上到下分四块顶部是一个工具栏左边显示当前歌曲信息右边是“打开文件夹”按钮。中间是一个列表框Listbox把整个播放列表展示出来支持用户点击选中和双击播放。再往下是控制按钮区一排四个按钮上一首、播放/暂停、停止、下一首。最底部是循环模式选择区两个单选按钮单曲循环和列表循环。用pack布局的话每个区域分别放进一个Frame容器里然后给顶部和底部Frame设定固定高度中间Listbox设定fillBOTH和expandTrue这样窗口拉伸时歌曲列表会跟着变大这是桌面应用最基本的体验要求。4.2 按钮功能与事件绑定的实现tkinter按钮绑定事件有两种常见方式一种是直接在Button里用command参数指定回调函数另一种是用bind方法绑定鼠标事件。按钮点击用command就够了简洁直白。比如“打开文件夹”按钮对应open_folder方法“下一首”对应next_song方法。双击播放列表中的某首歌则必须用bind方法绑定 事件。这里有一个容易踩的坑bind绑定的事件回调会接收一个event参数而command绑定的回调不接收参数。很多人第一次写的时候把这两者搞混结果代码一运行就报“takes 1 positional argument but 0 were given”之类的错误。所以我在play_selected方法里会加一个event参数哪怕这个参数用不到。4.3 当前歌曲信息与列表联动播放器最基本的反馈就是“当前正在播放哪首歌”。我用一个Label来显示歌曲名同时在播放列表里高亮当前曲目。更新高亮需要三步操作先把所有选中状态清空再设置当前索引为选中最后用see方法让列表滚动到可见位置。这个“界面与数据同步”的思路在以后做复杂应用时经常会碰到。一次状态更新可能要触发好几处UI的刷新。如果逻辑写得很乱状态就很容易产生不一致比如播放器已经在放第二首了界面上还显示第一首被选中。我的办法是把“刷新UI”的动作统一收敛进一个方法里凡是切换到新歌曲都走同一个方法。这样无论你是点击列表触发的播放还是上一首下一首触发的播放界面状态都是由同一段代码维护的不会出现各种奇怪的错位。5. 完整代码实现与关键模块讲解5.1 初始化与全局变量下面直接给出一个完整可运行的播放器代码。我会把重点部分拆开讲解。先看整体结构import os import tkinter as tk from tkinter import filedialog, messagebox import pygame class MusicPlayer: def __init__(self, root): self.root root self.root.title(简易音乐播放器) self.root.geometry(520x560) pygame.mixer.init() self.playlist [] self.current_index -1 self.loop_mode list self.is_playing False self.is_paused False self._build_ui() self.check_end_task self.root.after(500, self.check_music_end)初始化部分有几个关键点要说明。playlist和current_index在前面已经讲过这里重点提is_playing和is_paused这两个标志位。它们维护着播放器的状态也是后面判断按钮行为和自动切歌的依据。root.after是tkinter的定时器机制。我让它每隔500毫秒调用一次check_music_end方法用来检查音乐是否播放结束。这个方案在桌面小工具里非常常见它不是最高效的手段但胜在逻辑直白零基础也很容易理解。如果以后觉得自己水平上来了再改用pygame的MUSIC_END事件也不晚。5.2 播放控制核心函数播放控制主要涉及四个方法play_by_index、toggle_play、stop_music、prev_song、next_song。play_by_index是一切的中心所有“开始播放某首歌”的操作都会汇集到这里def play_by_index(self, index): if index 0 or index len(self.playlist): return self.current_index index pygame.mixer.music.load(self.playlist[index]) pygame.mixer.music.play() self.is_playing True self.is_paused False self.btn_play.config(text暂停) self.lab_song.config(text正在播放: os.path.basename(self.playlist[index])) self.listbox.selection_clear(0, tk.END) self.listbox.selection_set(index) self.listbox.see(index)这个方法做了三件事加载并播放音乐更新内部状态刷新整个界面的显示。toggle_play是播放/暂停按钮的逻辑。很多新手会把“暂停”和“停止”混为一谈其实它们在音频播放里是两个完全不同的操作。暂停是不销毁加载的音乐暂停后可以恢复停止是彻底清掉当前播放的曲目想再听就得重新加载。在pygame里暂停对应pause和unpause停止对应stop。我建议在代码里把这两类操作分清楚尽量不要混用。5.3 自动切歌与循环模式落地的关键细节自动切歌是这个项目里最有含金量的部分我单独拿出来讲。def check_music_end(self): if self.is_playing and not self.is_paused and not pygame.mixer.music.get_busy(): if self.loop_mode single: self.play_by_index(self.current_index) else: self.next_song() self.check_end_task self.root.after(500, self.check_music_end)逻辑就四个判断步骤。第一只有在确实处于播放状态时才去检查。第二暂停状态下不能触发切歌否则你暂停一会儿程序会误以为“播完了”悄悄切到下一首这是我在实际使用中踩过的坑。第三用pygame.mixer.music.get_busy()判断当前有没有声音在播放。如果这几个条件都满足说明前一首歌确实放完了。循环模式的处理就这么短短几行。single模式就重新播放当前索引list模式就调用next_song去跳下一首。这个设计让单曲循环和列表循环没有过多业务纠缠各走各的分支以后要加随机模式也只是再加一个elif分支的事。5.4 完整代码清单与运行下面给出整个代码文件内容和上面讲解的一致你可以直接新建player.py并复制进去import os import tkinter as tk from tkinter import filedialog, messagebox import pygame class MusicPlayer: def __init__(self, root): self.root root self.root.title(简易音乐播放器) self.root.geometry(520x560) pygame.mixer.init() self.playlist [] self.current_index -1 self.loop_mode list self.is_playing False self.is_paused False self._build_ui() self.check_end_task self.root.after(500, self.check_music_end) def _build_ui(self): top_frame tk.Frame(self.root) top_frame.pack(filltk.X, padx10, pady8) self.lab_song tk.Label(top_frame, text请选择音乐文件夹, font(微软雅黑, 12)) self.lab_song.pack(sidetk.LEFT) btn_folder tk.Button(top_frame, text打开文件夹, commandself.open_folder) btn_folder.pack(sidetk.RIGHT) self.listbox tk.Listbox(self.root, font(微软雅黑, 11)) self.listbox.pack(filltk.BOTH, expandTrue, padx10, pady8) self.listbox.bind(Double-Button-1, self.play_selected) control_frame tk.Frame(self.root) control_frame.pack(filltk.X, padx10, pady8) self.btn_prev tk.Button(control_frame, text上一首, commandself.prev_song) self.btn_prev.pack(sidetk.LEFT, filltk.X, expandTrue) self.btn_play tk.Button(control_frame, text播放, commandself.toggle_play) self.btn_play.pack(sidetk.LEFT, filltk.X, expandTrue) self.btn_stop tk.Button(control_frame, text停止, commandself.stop_music) self.btn_stop.pack(sidetk.LEFT, filltk.X, expandTrue) self.btn_next tk.Button(control_frame, text下一首, commandself.next_song) self.btn_next.pack(sidetk.LEFT, filltk.X, expandTrue) mode_frame tk.Frame(self.root) mode_frame.pack(filltk.X, padx10, pady8) self.loop_var tk.StringVar(valuelist) rb_single tk.Radiobutton(mode_frame, text单曲循环, variableself.loop_var, valuesingle) rb_single.pack(sidetk.LEFT) rb_list tk.Radiobutton(mode_frame, text列表循环, variableself.loop_var, valuelist) rb_list.pack(sidetk.LEFT) def scan_folder(self, folder): supported (.mp3, .wav, .ogg) files [] for f in os.listdir(folder): full os.path.join(folder, f) if os.path.isfile(full) and f.lower().endswith(supported): files.append(full) return sorted(files) def open_folder(self): folder filedialog.askdirectory() if not folder: return songs self.scan_folder(folder) if not songs: messagebox.showinfo(提示, 该文件夹没有找到支持的音频文件) return self.playlist songs self.listbox.delete(0, tk.END) for s in songs: self.listbox.insert(tk.END, os.path.basename(s)) self.current_index -1 self.lab_song.config(text共加载 {} 首歌曲.format(len(songs))) def play_by_index(self, index): if index 0 or index len(self.playlist): return self.current_index index pygame.mixer.music.load(self.playlist[index]) pygame.mixer.music.play() self.is_playing True self.is_paused False self.btn_play.config(text暂停) self.lab_song.config(text正在播放: os.path.basename(self.playlist[index])) self.listbox.selection_clear(0, tk.END) self.listbox.selection_set(index) self.listbox.see(index) def play_selected(self, event): index self.listbox.curselection() if index: self.play_by_index(index[0]) def toggle_play(self): if not self.playlist: return if not self.is_playing and not self.is_paused: self.play_by_index(0) elif self.is_playing and not self.is_paused: pygame.mixer.music.pause() self.is_playing False self.is_paused True self.btn_play.config(text继续) else: pygame.mixer.music.unpause() self.is_playing True self.is_paused False self.btn_play.config(text暂停) def stop_music(self): pygame.mixer.music.stop() self.is_playing False self.is_paused False self.btn_play.config(text播放) self.lab_song.config(text已停止) def prev_song(self): if not self.playlist: return if self.current_index 0: nxt len(self.playlist) - 1 else: nxt self.current_index - 1 self.play_by_index(nxt) def next_song(self): if not self.playlist: return nxt (self.current_index 1) % len(self.playlist) self.play_by_index(nxt) def check_music_end(self): if self.is_playing and not self.is_paused and not pygame.mixer.music.get_busy(): if self.loop_mode single: self.play_by_index(self.current_index) else: self.next_song() self.check_end_task self.root.after(500, self.check_music_end) if __name__ __main__: root tk.Tk() app MusicPlayer(root) root.mainloop()保存好后在命令行进到项目目录执行python player.py弹出窗口后点“打开文件夹”选一个放有mp3音乐的目录列表就会加载出来双击歌曲即可播放。6. 新手最容易踩的坑与排查技巧6.1 首次初始化失败pygame.mixer.init的音频设备问题有时候执行python player.py会在pygame.mixer.init()这行报错错误提示类似于“ALSA: Couldnt open audio device”或者“Failed to initialize audio device”。这种情况在Windows桌面上比较少见但在虚拟机、远程桌面、Linux服务器上经常出现。核心原因是没有可用的音频输出设备。虚拟机里没设置声卡或者远程桌面会话没有音频重定向都会触发这个问题。排查思路很简单先确认操作系统本身能不能出声然后用一段最简单的小脚本单独测试import pygame pygame.mixer.init() pygame.mixer.music.load(test.mp3) pygame.mixer.music.play()如果这段都无法运行那就说明问题不在播放器代码上而在环境上。在Windows下建议把声卡驱动更新一下在Linux下可以检查是否有音频服务在运行在虚拟机里则要调整虚拟声卡配置。这个排错顺序很重要不要一上来就怀疑自己的代码。6.2 中文文件名和路径乱码播放器界面上显示的中文歌名出现乱码通常有两种情况。第一种是代码文件本身保存的编码不是UTF-8。如果你在Windows上用的编辑软件默认保存成GBK那么代码里的中文字符串就会在运行时解析错乱。解决方法是把代码文件另存为UTF-8编码如果你的代码编辑器支持尽量把“文件编码设置为UTF-8”这个选项勾上。第二种情况是终端打印中文时乱码这类问题实际上不影响GUI显示因为tkinter本身对中文支持很好。我建议在播放器项目里不要依赖print输出调试信息直接在界面上用Label展示状态这样大多数中文显示问题自然就不存在了。还有一个容易被忽略的细节如果你的音乐文件夹路径里有中文比如“D:\音乐\我的歌单”在pygame加载时会遇到某些编码兼容性问题。Windows下一般没问题但遇到异常时可以尝试把路径规范化为绝对路径再传入。使用os.path.abspath或os.path.realpath都可以。6.3 mp3文件无法播放怎么办pygame.mixer对mp3的解码依赖系统的音频编解码器。你在自己电脑上开发测试时mp3正常播放但换个环境运行就突然无声或直接报错这种场景我遇到过太多次。解决办法有几个方向。先把音乐文件转成wav或ogg格式试一下如果转格式后能播放那基本可以确定是mp3解码器问题。pygame官方文档里也标注过它对mp3的兼容性并不是100%编码率过高或者包含特殊ID3标签的mp3都有可能加载失败。然后是确认pygame版本建议使用2.0以上版本老版本对mp3支持更差。pip install --upgrade pygame另外需要特别提醒的是pygame.mixer.music.load()只能在文件存在且格式受支持时成功。如果你在加载时捕获到了异常但没打印任何信息播放器会毫无反应。建议在load外面包一层异常捕获至少把错误信息弹出来这样排查起来轻松得多。6.4 打包成exe后运行出错项目做完以后很多人希望做成一个双击就可以运行的exe文件发给朋友用。这一步用PyInstaller就能搞定pip install pyinstaller pyinstaller -F -w player.py-F表示打包成单文件-w表示运行时不弹出黑色控制台窗口。生成的exe在dist目录下。但这里有几个常见的坑。第一tkinter有时候会被PyInstaller漏掉需要手动指定数据文件。解决办法是在打包命令中加上--hidden-import tkinter。第二pygame的一些文件也可能会被漏掉同样可以用--hidden-import pygame来强制带入。第三如果你以后在代码里用了图片、图标等外部文件单文件模式运行时会遇到临时文件释放问题需要把资源文件放到一个独立目录里或者用sys._MEIPASS处理。现阶段先记住前两条就够用了。打包完以后双击exe如果提示“Failed to execute script player”那就是打包过程中资源没带全。建议在控制台不添加-w参数重新打包一次运行exe时终端窗口里会输出真正的报错信息比满屏找答案要快得多。7. 从0开始学Python这个项目还能怎么扩展7.1 功能增强方向当你把基础版跑通以后可以试着往上加东西。我列几个从易到难的扩展方向每个都能让你学到新东西。音量控制是最简单也最实用的。pygame里音量设置就一行代码pygame.mixer.music.set_volume(0.5)取值范围在0到1之间。你只需要在界面上加一个Scale滑块再把滑块变化绑定到这个方法即可。这能让你快速掌握tkinter的常见控件。播放进度条稍微难一点。pygame.mixer.music.get_pos()可以返回当前播放位置的毫秒数但前提是你知道整首歌的时长。时长信息可以从mutagen这个库读取它是一个专门处理音频元数据的库安装方式和pygame一样然后用mutagen.mp3.MP3可以很方便拿到时长。把当前位置和总时长换算成进度百分比再画到Canvas控件上一个简易进度条就有了。记住上次播放的文件夹也很有用。实现方式是把当前文件夹路径存到一个json文件里程序启动时读取如果有记录就直接自动加载。这一块涉及json读写和程序配置管理正好把Python的文件写入操作也一并练了。再往后你还可以尝试做随机播放、拖拽文件进窗口添加歌曲、递归扫描子文件夹、支持flac格式。每一项扩展都会让你对播放器的理解更深一层但这里我想重点提醒一句扩展功能的顺序必须是一个功能完全稳定后再加下一个。否则所有功能搅在一起出了问题你连定位都困难。7.2 我对新手做项目的三点建议第一个建议是不要对着网上的代码照抄一遍就完事。抄一遍只能练打字你要做到能脱离代码逐行讲清楚每一段是干什么的。可以在代码里加注释然后隔几个小时后再关上代码默写一遍核心逻辑。写不出来的地方记录下来这会成为你真正的知识盲区。第二个建议是充分利用print和异常信息做调试。很多新手遇到代码没反应时第一反应是“代码错了重写”。其实大多数bug都不需要重写你只要在关键位置加几行print把变量的值打出来看看程序到底跑到了哪一步问题基本就浮出水面了。学会定位问题是比写新代码更重要的一种能力。第三个建议是这个播放器不要做完就扔。把它当成你自己的长期项目隔一段时间就打开加一个小功能。你会发现自己看几个月前的代码能挑出一堆毛病这恰恰说明你的水平在进步。等你有一天能把这段代码重写得又短又清楚的时候你的Python基础就已经非常扎实了。按照我个人的经验这个播放器项目是我带过的零基础学员里完成率最高的一个。因为它给的反馈足够即时——代码运行起来界面出现音乐响起那份成就感是计算器项目完全给不了的。希望你也能把它真正跑通再根据自己的需要一点点改造成属于你自己的工具。
返回列表