ARTICLE DETAIL

资讯详情

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

零基础学Python:从环境搭建到SQLite笔记检索工具

零基础学Python:从环境搭建到SQLite笔记检索工具 简介面向Python零基础学习者的中文笔记内容贴近最新版本Python从搭建开发环境到掌握核心语法层层展开帮助初学者降低入门门槛。压缩包共含860个文件整体大小118.77MB除PDF阅读版笔记外还收有HTML、CHM等帮助文档文件类型以C/C源文件、头文件、汇编源文件及嵌入式工程配置文件为主部分目录附带可执行工具与说明文本便于对照查阅。笔记系统梳理了Python环境搭建、语法基础、基本数据类型、运算符、分支结构、循环结构、流程控制语句、列表、元组等核心知识点章节由浅入深既适合自学入门也可作为日常复习的速查手册配套工程文件可配合动手实践帮助读者在操作中巩固理解。目前已有412人学习/下载适合希望获得完整中文Python学习资料并愿意动手实践的读者。1. 零基础学Python为什么总是卡在列表之后我见过太多人把Python教程当小说读看到列表、元组就翻页背完语法却写不出一个能跑的程序。问题不在记忆力而在学习顺序先建立“写小脚本解决问题”的反馈回路语法才留得住。这份《Python学习笔记》按“搭环境→写代码→调错→做小工具”串成完整链路从零开始一直带到能独立完成带界面的脚本。它适合零基础转行者也适合写了几段脚本却觉得根基不稳的初级工程师——比如分不清列表和元组的应用场景或者一遇到Traceback就不知道从哪下手的人。2. Python环境搭建解释器版本选择与虚拟环境隔离2.1 为什么不用系统自带的Python版本很多Linux发行版和macOS自带的Python要么是2.x时代的遗留产物要么是3.x的老版本。表面上看执行python --version一切正常实际上装第三方库时问题才会暴露缺少pip、缺少开发头文件、openssl和sqlite3版本过旧导致模块编译失败。我在一台自带Python 3.6的旧服务器上给项目装新版pandas折腾了半小时最后发现是glibc版本太老Python本身根本起不来。反过来如果你用自己装的Python覆盖了系统目录又可能影响系统自带工具的运行。所以第一步的结论非常明确用pyenv这类版本管理工具单独装一个用户级Python不碰系统目录。pyenv的原理是在PATH前面插入一层shim转发目录当你敲python时它根据当前目录下的.python-version文件或者全局配置把命令指到对应版本的Python上。好处是多版本共存、互不干扰环境变量也干净切换项目时不用重装解释器。2.2 pyenv的安装与版本切换# 拉取pyenv源码并初始化以bash为例 git clone https://github.com/pyenv/pyenv.git ~/.pyenv echo export PYENV_ROOT$HOME/.pyenv ~/.bashrc echo export PATH$PYENV_ROOT/bin:$PATH ~/.bashrc echo eval $(pyenv init -) ~/.bashrc source ~/.bashrc # 安装Python 3.12.x并设为全局默认 pyenv install 3.12.3 pyenv global 3.12.3 python --version第一段命令把pyenv的克隆路径和初始化逻辑写进shell配置核心是eval $(pyenv init -)它负责生成shim转发机制source ~/.bashrc让配置立即生效不用重开终端。第二段中pyenv install 3.12.3默认从源码编译耗时取决于机器性能编译期间需要系统提前装好zlib、readline、sqlite3、openssl等依赖库否则装完后运行import sqlite3会直接报ModuleNotFoundError。日常用得最多的pyenv命令我整理了一张表命令作用典型场景pyenv install 3.12.3安装指定Python版本需要新特性或新版编译环境pyenv global 3.12.3设置全局默认版本新机器初始化pyenv local 3.12.3为当前目录锁定版本项目各自用不同版本pyenv versions列出所有已安装版本确认切换是否成功pyenv uninstall 3.12.3删除某个版本清理旧版本释放磁盘Windows上pyenv原生支持不完整我一般直接装官方安装包再结合下一节说的venv在项目目录里做隔离效果等价。这里关键不是工具选哪个而是“解释器必须项目可控”这个意识。2.3 venv虚拟环境让项目依赖互不污染同一个机器上可能有多个项目一个要Django 3一个要Django 4全装进全局site-packages迟早出问题。Python 3自带的venv模块解决这件事不需要额外装virtualenv# 在项目根目录创建虚拟环境 python -m venv .venv source .venv/bin/activate pip install requests pip freeze requirements.txt第一条命令在当前目录生成.venv文件夹里面是一套独立的解释器和pip第二条的source把虚拟环境的bin目录插到PATH最前面此后python、pip都指向这个环境。pip install requests装的是项目依赖而非全局最后用pip freeze requirements.txt导出完整依赖清单换机器时执行pip install -r requirements.txt即可复现环境。注意.venv只是约定俗成的名字但不管叫什么都要写进.gitignore不然整个依赖目录会被提交进仓库。退出环境用deactivate。这套流程在写爬虫、写自动化脚本时能救命的点在于它保证了你在笔记里看到的每个pip安装命令都不会污染你日常工作的Python。2.4 VSCode和PyCharm里的解释器指向命令行环境配好之后编辑器经常不认这是初学者最容易卡住的地方。VSCode按CtrlShiftP打开命令面板输入Python: Select Interpreter然后选路径到.venv/bin/pythonPyCharm在Settings的Project Interpreter里Add Local Interpreter选Existing并指向同一个路径。这里最常见的坑是命令行里明明pip install成功了编辑器里运行却提示ModuleNotFoundError。原因就是编辑器选中的解释器和命令行用的不是同一个它们各自的site-packages完全隔离。判断方法很简单在编辑器自带终端里执行which python看输出路径里有没有.venv字样。没有说明PATH被编辑器覆写了去解释器设置里重新指定即可。3. Python语法地基动态类型、运算符与流程控制3.1 动态类型变量只是名字类型住在对象上Python是动态类型语言这句话初学者听过就忘直到遇到变量被篡改的诡异bug才回头来看。这里的本质是变量名只是一个指向对象的引用类型信息挂在对象上不在变量上。同一个名字可以先指向整数再指向字符串语法完全合法# 同一个名字先后绑定不同类型 x 42 print(type(x)) # class int x 42 print(type(x)) # class str # 三种最常用的类型转换 num int(42) # 字符串转整数 s str(3.14) # 浮点数转字符串 flag bool(1) # 非零整数转True第一段代码解释了“类型住在对象上”的含义type(x)返回的是x当前指向对象的类型而不是变量本身携带的类型。第二段是写爬虫时天天用的操作从HTML或者JSON里拿到的数字往往是字符串形式做运算前必须用int()转换注意字符串里带空格或换行时要先strip()否则会抛ValueError。实际项目里我习惯用isinstance()做类型收口而不是直接type(x) int因为前者能正确识别子类关系。比如在数据处理管线里isinstance(obj, (int, float))可以一次排除掉字符串和None避免后续运算炸掉。3.2 运算符优先级与布尔短路运算符优先级这个知识点写简单脚本时感受不到一旦表达式变长早晚会踩坑。比如not a b实际是not (a b)因为比较运算优先于not。下面这张表按从高到低列出最常见的几组优先级运算符说明高**幂运算右结合* / // %乘除取整取余 -加减 !比较运算低not and or逻辑运算or最低优先级之外更值得掌握的是布尔短路and左侧为False时右侧不再执行or左侧为真时右侧不再执行。这不仅是性能优化更是代码惯用法# or短路左侧为False时取默认值 user_input name user_input.strip() or anonymous print(name) # anonymous # 注意is与的区别 a 256 b 256 print(a is b) # True小整数缓存 c 1000 d 1000 print(c is d) # 不同解释器结果可能不同不要依赖 print(c d) # True比较值or返回的是第一个真值对象本身user_input.strip() or anonymous在空字符串时返回默认值可读性比三行if判断好得多。is比较的是身份比较的是值判断None用is None判断相等用这是代码审查时必查的一项。3.3 分支结构缩进即语法Python里缩进不是风格问题而是语法边界。if、for、while后面的冒号表示一个代码块的开始块内语句必须保持相同的缩进级别混用空格和Tab会直接报IndentationError。分支结构建议统一用4个空格score 78 if score 90: grade A elif score 60: grade B else: grade Celif按顺序自上而下判断某个条件命中后后面的分支不会再执行。所以写区间时要保证条件顺序和业务逻辑一致比如先判断score 90再判断score 60如果反过来90分的人也会落到60分那个分支里。边界值最好用表格列清楚90落在A89落在B60落在B59落在C。3.4 循环结构range、break、continue与elsefor循环是Python里遍历的主力配合range(start, stop, step)做有起始和步长的次数控制。range是左闭右开区间range(2, 10, 2)生成2、4、6、8不包含10。这里还有一个很少被讲透的语法for-else。循环体没有被break打断时else块会执行非常适合做查找失败的兜底# for-else循环未被break打断时执行else for n in range(2, 10): for x in range(2, n): if n % x 0: break else: print(n, 是质数)内层循环中只要发现n能被x整除就break此时内层的else不会执行如果从2到n-1都没有能整除它的数else执行打印质数。这个写法比用一个布尔标志位去记录“是否找到因数”要简洁也避免了标志位忘记重置的问题。3.5 流程控制的隐藏坑循环里改列表循环中边遍历边修改列表会得到反直觉的结果lst [1, 2, 3, 4] for item in lst: if item 2: lst.remove(item) print(lst) # 实际结果是 [1, 3, 4]问题在于for循环内部按索引递增取元素删除元素2之后原来的3补到索引1的位置循环索引指向下一个元素时恰好跳过3。正确做法是遍历副本for item in lst[:]或者在删除时改用列表推导式构造新列表。这个坑在日志清理、队列去重场景里经常出现值得记牢。4. 核心数据结构实战列表、元组、字典、集合4.1 列表可变序列的增删改查与切片列表是Python里用得最多的容器修改和查询都很灵活。它是有序的、可变的底层是动态数组按下标访问和末尾追加的时间复杂度都是O(1)但中间插入删除是O(n)。cities [北京, 上海, 广州] cities.append(深圳) # 尾部追加O(1) cities.insert(1, 杭州) # 中间插入后续元素后移 popped cities.pop() # 弹出尾部元素返回被弹出的值 cities[0] 重庆 # 按下标修改 print(cities[1:3]) # 左闭右开切片返回新列表 print(cities[::-1]) # 负数步长实现反转切片返回的是新列表原列表不受影响所以lst[::-1]是“返回反转副本”而list.reverse()是原地反转返回None。切片两端都可以省略lst[:2]取前两个lst[2:]取从第3个到末尾。边界要记住lst[a:b]包含a不包含b连续切片容易错一位的都在b上。4.2 元组不可变性与拆包语法元组和列表最大的区别是不可变元素不能增删改。这个限制换来的好处是可以作为字典的键也可以安全地在多线程环境里被共享读取。写代码时凡是“这一组数据不应该被改”的场景都应该用元组而不用列表。point (10, 20) x, y point # 拆包一次取多个值 # point[0] 99 # TypeError元组不可变 a, *rest [1, 2, 3, 4] # *rest吃掉剩余元素 print(a) # 1 print(rest) # [2, 3, 4] # 交换两个变量的惯用法 x, y y, x拆包语法在函数返回多个值、遍历items()时无处不在。a, *rest这种带星号的写法特别适合处理“首元素剩余元素”的场景比如解析配置时取第一行做标题剩下的做正文。互换变量那行底层做了“先打包成元组再拆包”比引入中间变量更清晰。4.3 字典哈希表的使用边界与get方法字典底层是哈希表查找速度是O(1)。它有两条硬性约束键必须是可哈希的因此列表、字典本身不能当键键是唯一的后插入的同名键会覆盖前面的值。Python 3.7起字典保持插入顺序这在做配置文件解析时能保证输出顺序与源文件一致。user {name: Tom, age: 25} # user[[1, 2]] x # TypeError: unhashable type: list age user.get(age, 0) # 键不存在时返回默认值 tags user.setdefault(tags, []) # 键不存在时插入空列表再返回 for key, value in user.items(): print(key, value)用get()而不是直接user[age]可以在键缺失时返回默认值避免KeyError。setdefault适合初始化嵌套结构比如在爬虫里统计每个网址的标签列表不需要先判断键是否存在再初始化空列表。判断键是否存在的更朴素的写法是if key in user它不会引发异常也不修改字典。4.4 集合去重、交集差集与顺序陷阱集合是哈希表实现的“只有键没有值”的数据结构元素唯一且无法按下标访问。日常用得最多的是去重和集合运算seen {1, 2, 3} seen.add(3) # 重复元素自动忽略 words1 {python, java} words2 {python, cpp} print(words1 words2) # 交集 {python} print(words1 | words2) # 并集 {python, java, cpp} print(words1 - words2) # 差集 {java}列表去重的常见做法是list(set(items))但结果顺序不固定因为set是无序的。如果要保留原顺序去重用Python 3.7字典保序的特性list(dict.fromkeys(items))它会保留每个元素第一次出现的位置。去重后还要比较集合大小判断是否有重复是数据质量校验的常用手段。4.5 四种结构怎么选结构可变性有序性底层实现典型场景list可变有序动态数组需要增删改的序列tuple不可变有序定长数组固定的一组数据、字典键dict可变插入有序哈希表键值映射、统计计数set可变无序哈希表去重、交集差集判断选型时先问三个问题数据要不要改要不要按下标访问要不要按名字查找需要按名字查找用字典需要去重用集合需要保证内容不被误改用元组其他大多数场景用列表。提醒一句不要为了变通把元组转列表、列表转元组转来转去结构选定之后保持类型一致代码的可维护性会好很多。5. 函数、作用域与异常处理从脚本到可复用模块5.1 参数传递的四种写法函数是Python里组织逻辑的最小单元。参数传递有四种形式位置参数、关键字参数、*args收集多余位置参数、**kwargs收集多余关键字参数。混用时要遵循位置参数在前、默认参数次之、*args再后、**kwargs最后的顺序def request(url, timeout3, *args, **kwargs): url是必填位置参数timeout有默认值 args收集多余位置参数kwargs收集多余关键字参数 print(url:, url) print(timeout:, timeout) print(args:, args) print(kwargs:, kwargs) request(http://example.com, 5, 1, 2, retry2)调用这行时http://example.com传给url5传给timeout1和2进入args元组retry2进入kwargs字典。这种写法在写HTTP封装时非常实用底层的requests库接收到的常见参数如headers、proxies都是通过kwargs透传的。注意默认参数只在函数定义时求值一次所以默认值不要用可变对象比如def f(x, lst[])是有问题的多次调用会共享同一个列表正确写法是def f(x, lstNone)。5.2 LEGB作用域与闭包陷阱Python找变量按LEGB顺序Local函数内、Enclosing外层函数、Global模块级、Built-in内置命名空间。理解这个顺序才能解释下面这段代码为什么能实现计数器def outer(): count 0 def inner(): nonlocal count count 1 return count return inner counter outer() print(counter()) # 1 print(counter()) # 2关键在nonlocal这一行。内层函数要修改外层函数的局部变量count必须声明nonlocal否则Python会认为你只是在inner内部新建了一个局部变量然后报UnboundLocalError。闭包把count的状态一直保存在内存里没有开全局变量这是装饰器、回调函数的基础。5.3 异常处理try/except/else/finally的完整语义异常处理不是简单地“捕获一下不崩溃”而是要让程序在异常时走预期的分支并释放资源。完整语法是try / except / else / finally四段式import json raw {name: Tom try: data json.loads(raw) except json.JSONDecodeError as e: print(JSON解析失败行号:, e.lineno) except AttributeError: pass else: print(解析成功姓名:, data[name]) finally: print(无论是否异常都执行适合释放资源)except可以有多个按异常类型从上到下匹配子类异常要写在父类前面。else只有在try块没有异常时才执行适合放那些不想被except误捕获的逻辑finally无论什么情况都执行做文件关闭、数据库连接释放的收尾。常见异常对照异常类型触发场景ValueError类型对但值不合法如int(abc)TypeError类型不匹配如数字加字符串KeyError字典键不存在IndexError列表下标越界AttributeError对象没有该属性5.4 排错看懂Traceback和print定位Traceback的阅读顺序是从下往上最后一行是异常类型和原因往上找第一个属于你自己代码的帧这才是错误真正的源头。中间跨过的第三方库的帧往往只是传递过程。定位阶段性bug时我习惯在关键分支处打print输出变量名和值比如print(debug: items , items[:5])看切片而不是整个列表避免日志爆炸。确认问题后删掉print换成日志模块或者断言。断言assert isinstance(result, list)可以在开发期快速暴露类型问题但要记得python -O运行时会跳过断言不要依赖它做线上校验。6. 把笔记变成工具用Python与SQLite实现笔记全文检索6.1 从PDF笔记提取正文文本学了前面的语法和数据结构最大的进步应该是能给自己做工具。这份《Python学习笔记》是PDF翻半天找“列表切片”在哪一页非常低效。我做的第一件事是把PDF转成纯文本建立本地索引。# Linux下用poppler-utils里的pdftotext pdftotext Python学习笔记.pdf notes.txt# 或用Python的pypdf库提取 from pypdf import PdfReader reader PdfReader(Python学习笔记.pdf) with open(notes.txt, w, encodingutf-8) as f: for page in reader.pages: f.write(page.extract_text() \n)pdftotext是命令行工具速度快但对图片型PDF无能为力pypdf在Windows和Linux都能跑可以嵌入脚本批量处理。提取出来的文本可能有乱码或多余空行先别管下一步直接进数据库。6.2 建表与参数化查询规避SQL注入的正确姿势文本索引用SQLite足够单文件、零配置、Python自带sqlite3模块不需要额外起服务。建表时给content建立索引会加快查询但SQLite的LIKE其实不走普通索引真正要提速用FTS5全文索引这里先建基础表import sqlite3 conn sqlite3.connect(notes.db) conn.execute( CREATE TABLE IF NOT EXISTS notes ( id INTEGER PRIMARY KEY, title TEXT, content TEXT ) ) # 把笔记按段落写入 current_title for line in open(notes.txt, encodingutf-8): line line.strip() if not line: continue if line.startswith(第) and len(line) 30: current_title line conn.execute( INSERT INTO notes (title, content) VALUES (?, ?), (current_title, line), ) conn.commit()注意INSERT语句里用的是?占位符参数通过第二个参数传入。这是参数化查询的标准写法sqlite3驱动会帮你转义特殊字符从根上杜绝SQL注入。不要用f-string或者字符串拼接把变量直接嵌进SQL比如fINSERT INTO notes VALUES ({line})一旦line里包含单引号或恶意构造的内容轻则语法错误重则执行了攻击者拼接的SQL。自己写的工具没有外部输入时问题不明显但只要有一天数据来自爬虫或用户输入拼接SQL就是高危写法参数化不是可选项。检索时同样用参数化keyword 列表 rows conn.execute( SELECT title FROM notes WHERE content LIKE ?, (f%{keyword}%,), ).fetchall() for row in rows: print(row[0])LIKE ?配合%关键字%实现包含匹配。参数里传入的是f%{keyword}%这个f-string用来构造通配符字符串不涉及SQL语句结构所以是安全的。这里区分一下占位符?只替换“值”不替换“表名、列名”如果你想动态拼接表名那部分无法参数化只能做白名单校验。6.3 检索效率验证与下一步查询写了先确认它是不是在扫全表sqlite3 notes.db EXPLAIN QUERY PLAN SELECT title FROM notes WHERE content LIKE %列表%;如果计划里出现SCAN notes说明是全表扫描。笔记文本量不大时无所谓到了几万条就明显卡顿。下一步是把表改成FTS5全文索引用MATCH语法做关键字搜索还能支持排序和相关度打分。这一步就是前面学到的SQLite封装、异常处理、命令行知识全部落地的结果工具不大但已经能把那份灰尘满满的PDF变成可以一秒定位答案的检索系统了。本文还有配套的精品资源点击获取
返回列表