Python Base(one)
Python编程的基本概念
1-1 Python程序构成
欢迎来到 Python 的核心!对于初学者来说,理解 Python 的程序结构就像学习建筑的框架。虽然 Python 看起来很像英语,但它有一套严格的“组件布局”规则。
为了让你看清全貌,我们来看一个基础但完整的“薪资计算器” Demo。
1. 入门级 Demo:薪资核算小程序
这个程序虽然简单,但包含了 Python 程序的所有核心组成部分。
# -*- coding: utf-8 -*-
"""
程序名称:简易薪资计算器
功能:根据工时计算周薪,并判断等级。
作者:edward
"""
import math # 1. 导入模块
TAX_RATE = 0.2 # 2. 全局变量(常量)
def calculate_pay(hours, rate): # 3. 函数定义
"""计算扣税后的实际薪资"""
gross_pay = hours * rate
net_pay = gross_pay * (1 - TAX_RATE)
return round(net_pay, 2)
def main(): # 4. 主逻辑函数
print("--- 欢迎使用薪资系统 ---")
# 5. 输入与数据转换
try:
user_name = input("请输入员工姓名: ")
work_hours = float(input("请输入本周工时: "))
hourly_rate = float(input("请输入时薪: "))
# 6. 逻辑调用
final_salary = calculate_pay(work_hours, hourly_rate)
# 7. 条件判断
if final_salary > 1000:
level = "高级"
else:
level = "标准"
# 8. 格式化输出
print(f"\n员工: {user_name}")
print(f"实际到手薪资 (${level}): {final_salary}")
except ValueError:
print("错误:请输入有效的数字!")
# 9. 入口判断
if __name__ == "__main__":
main()
2. 详细拆解:Python 程序的 9 个组成部分
① 文档注释 (Docstrings & Comments)
#开头的是单行注释,解释代码意图。""" ... """是文档字符串。放在文件开头是程序说明,放在函数内部是功能说明。- 全栈视角:在团队协作中,好的注释能节省 80% 的沟通成本。
② 导入模块 (Imports)
- 使用
import引入 Python 自带的工具箱(标准库)或第三方库。 - Python 的强大在于其丰富的“插件”,你需要什么功能,通常先找对应的模块。
③ 全局变量与常量 (Global Variables/Constants)
- 在 Python 中,通常用全大写字母表示常量(虽然 Python 语法上不强制禁止修改它,但这是开发者之间的约定)。
④ 函数定义 (Functions)
- 关键字:使用
def定义。 - 结构:
def 函数名(参数):。 - 返回值:使用
return返回结果。
⑤ 缩进 (Indentation) —— 最重要的一点!
- 在 Java 或 C 中,我们用
{}。在 Python 中,我们使用缩进(通常是 4 个空格)。 - 注意:同一个代码块的缩进必须完全一致,否则由于 Python 的解释特性,程序会直接报错。
⑥ 输入与输出 (Input & Output)
input():从控制台获取用户输入,返回的是字符串(String)。print():将结果输出到控制台。f-string:如f"姓名: {name}",这是 Python 3.6+ 最推荐的字符串格式化方式,非常简洁。
⑦ 数据类型转换 (Type Casting)
float()、int()、str():由于input()得到的是文本,如果要进行数学计算,必须强制转换类型。
⑧ 异常处理 (Exception Handling)
try...except:为了防止用户乱输入(比如在工时里输入“打工人”而不是数字)导致程序崩溃,我们要用异常处理来捕捉错误。
⑨ 程序入口 (if __name__ == "__main__":)
- 这是 Python 程序的“启动开关”。
- 它确保了当你直接运行这个文件时,
main()函数才会执行。如果你把这个文件当做插件导入到别的程序里,它就不会自动运行,避免了干扰。
3.给初学者的特别提醒:
- 冒号 (😃 的存在:你会发现
if、def、for之后都有个冒号,这代表“下面是一段缩进的代码块”,千万别漏掉。 - 变量不需要定义类型:你不需要写
int a = 1,直接写a = 1。Python 会自动识别。 - 空行:虽然 Python 不强制,但在函数之间留 2 行空格,在逻辑块之间留 1 行空格,会让你的代码看起来像出自大厂专家之手。
1-2 Python之对象和内存
在 Python 的世界里,有一句名言:“万物皆对象 (Everything is an object)”。
-
Python中,一切皆对象。
-
每个对象由:标识(identity)、类型(type)、value(值)组成
-
标识用于唯一标识对象,通常对应于对象在计算机内存中的地址。使用内置函数id(obj)可返回对象obj的标识。
-
类型用于表示对象存储的“数据”的类型。类型可以限制对象的取值范围以及可执行的操作。可以使用type(obj)获得对象的所属类型。
-
值表示对象所存储的数据的信息。使用print(obj)可以直接打印出值。
对象的本质就是:
一个内存块,拥有特定的值,支持特定类型的相关操作
1. 核心概念:变量是“标签”,不是“盒子”
在 C++ 或 Java 等语言中,变量像是一个盒子,你把值放进盒子里。
在 Python 中,变量更像是贴在对象上的“标签”(引用/指针)。对象存在于内存中,变量只是指向它的名字。
示例代码:
a = [1, 2, 3]
b = a
b.append(4)
print(a) # 输出 [1, 2, 3, 4]
解释:这里并没有两个列表。a 和 b 这两个“标签”都贴在了内存中同一个列表对象上。修改 b,a 也会同步变化。
2. 一个 Python 对象的内部结构
每个 Python 对象在内存中被创建时,至少包含三个核心部分:
- 标识 (Identity / ID):对象的唯一行政代号(可以理解为内存地址)。使用
id(obj)查看。 - 类型 (Type):告诉 Python 这个对象能做什么。使用
type(obj)查看。 - 值 (Value):对象存储的实际数据。
x = 100
print(id(x)) # 内存中的地址,如 1407312345678
print(type(x)) # <class 'int'>
print(x) # 100
a=3
print(a)
print(id(a))
print(type(a))
b='我爱你'
print(b)
print(id(b))
print(type(b))
3. 不可变对象 vs 可变对象 (核心坑点)
这是全栈开发中最常遇到 Bug 的地方。
- 不可变对象 (Immutable):一旦创建,值就不能更改。如果要改,Python 会创建一个新对象。
- 代表:
int,float,str,tuple,bool
- 代表:
- 可变对象 (Mutable):可以直接在原地修改对象的内容。
- 代表:
list,dict,set
- 代表:
实验对比:
# 不可变对象测试
s = "hello"
old_id = id(s)
s = s + " world"
print(id(s) == old_id) # False!s 现在指向了一个全新的字符串对象
# 可变对象测试
lst = [1]
old_id = id(lst)
lst.append(2)
print(id(lst) == old_id) # True!lst 还在原来的位置,只是内容变了
4. 内存管理机制:Python 是如何“扫地”的?
作为解释型语言,Python 自动管理内存,主要靠两套机制:
A. 引用计数 (Reference Counting) —— 主要手段
每个对象都有一个计数器,记录有多少个变量指向它。
- 当
b = a时,引用计数 +1。 - 当
del a时,引用计数 -1。 - 当引用计数降为 0 时: 对象会被立即回收,内存释放。
B. 垃圾回收 (Garbage Collection) —— 辅助手段
有时会出现“循环引用”(A 引用 B,B 引用 A,但没人用它们了)。
Python 的垃圾回收器会定期扫描,发现并清理这种“孤岛”内存,防止内存泄漏。
5. 小整数池与字符串驻留 (进阶优化)
为了性能,Python 做了一些“耍赖”的技巧:
- 小整数池:Python 启动时会预先创建
-5 到 256之间的整数对象。无论你写多少次a=10,b=10,它们都指向同一个内存地址。 - 字符串驻留 (Interning):短的字符串有时也会被复用。
a = 10
b = 10
print(a is b) # True,因为 10 在小整数池里
x = 500
y = 500
print(x is b) # False(通常),因为 500 超出了预设范围,创建了两个独立对象
1-3 Python之引用
在Python中,变量也称为:对象的引用(reference)。变量存储的就是对象的地址。
变量通过地址引用了“对象”。
变量位于:栈内存(压栈出栈等细节,后续再介绍)。
对象位于:堆内存。
如下源代码对应的内存图:
a=3
b="我爱你"
⚠️Python是动态类型语言
变量不需要显式声明类型。根据变量引用的对象,Python解释器自动确定数据类型
你的直觉非常敏锐!作为一个全栈开发人员,如果你有 C、C++ 或 Java 的背景,这个结论可能会让你感到既惊讶又合理:
是的,在 CPython(最常用的 Python 实现)中,所有的对象都存储在“堆”(Heap)上。
为了让你透彻理解这一点,我们需要拆解一下这句话背后的深层含义。
1. 为什么“万物皆对象”?
在 C 语言中,一个 int 变量只占用 4 个字节,直接存放在栈(Stack)里。
但在 Python 中,即使是最简单的 a = 1,Python 也会在内存中创建一个完整的 PyObject 结构体。
这个结构体至少包含:
- 引用计数 (ob_refcnt):用于垃圾回收。
- 类型指针 (ob_type):指向
int类型对象的描述。 - 值 (ob_size / ob_digit):存储实际的数字。
因为每个对象都有这些额外的信息,它们的大小是不固定的,所以必须存放在私有堆(Private Heap)中以便动态管理。
2. 栈(Stack)里存的是什么?
既然对象都在堆里,那栈里还有东西吗?有,但栈里只存“引用”和“执行上下文”。
当我们执行一个函数时,Python 会在栈上创建一个 “栈帧”(Stack Frame)。栈帧里包含:
- 变量名(也就是引用/指针):这些名字(如
x,y,my_list)存放在栈帧里,但它们存储的值是堆中对象的内存地址。 - 执行状态:当前执行到了哪一行代码。
形象比喻:
- 堆(Heap):像是超市的货架,所有的货物(对象:数字、字符串、列表)都堆在这里。
- 栈(Stack):像是你手里的购物清单(变量名)。清单上只写着“苹果在 A3 货架”、“牛奶在 B1 货架”。你手里拿的是位置信息,而不是实物。
3. 这个设计对性能的影响(全栈开发者的必知点)
为什么 Python 通常比 C 或 Java 慢?“全员堆对象”是原因之一:
- 内存开销:在 C 语言中存 1000 个整数几乎不占额外空间;在 Python 中,1000 个整数就是 1000 个
PyObject,每个都要几十个字节。 - 寻址开销:每次你要计算
a + b,Python 必须先去栈里找a的地址,再去堆里访问那个地址,读取里面的值。这比直接从栈里取数要慢。 - 垃圾回收压力:因为所有东西都在堆里,引用计数和垃圾回收器必须时刻盯着堆内存,增加了 CPU 负担。
4. 极端例子:函数和类也在堆里
在 Python 中,def 定义的函数和 class 定义的类本身也是对象。
def say_hi():
print("Hi")
# say_hi 这个名字也是一个标签,指向堆里的一个“函数对象”
func_ptr = say_hi
print(id(say_hi)) # 获取这个函数在堆里的地址
这意味着你可以像传递数字一样传递函数(这也就是为什么 Python 支持高阶函数和装饰器的根本原因)。
5. 总结:你的理解是正确的
- 所有的值(对象)都在堆中。
- 变量名仅仅是栈里的一个指针。
- 这种设计牺牲了性能,但换取了极大的灵活性(动态类型、反射、元编程等)。
1-4 Python基础之标识符
在 Python 的编程世界里,标识符(Identifier) 就是由于程序员定义的、用来指代变量、函数、类、模块或其他对象的“名字”。
以下是关于 Python 标识符的详细技术文档:
1. 什么是标识符
简单来说,标识符就是起名字。无论你是定义一个变量 x = 10,还是定义一个函数 def login():,其中的 x 和 login 就是标识符。
2. 强制性命名规则
Python 解释器对标识符有严格的语法要求,不符合以下规则的代码将无法运行(报 SyntaxError):
- 字符组成:可以由 字母 (A-Z, a-z)、数字 (0-9) 和 下划线 (
_) 组成。 - 数字开头限制:标识符不能以数字开头。
user1(✅ 合法)1user(❌ 非法)
- 大小写敏感 (Case Sensitive):Python 严格区分大小写。
username和UserName是两个完全不同的变量。
- 特殊字符限制:不能包含
@、$、%等特殊字符。 - 禁止使用关键字:不能使用 Python 预留的关键字(如
if,while,class等)。
3. Python 关键字 (Reserved Keywords)
你可以通过以下代码查看当前版本 Python 的所有关键字:
import keyword
print(keyword.kwlist)
常见关键字举例:
False, None, True, and, as, assert, break, class, continue, def, del, elif, else, except, finally, for, from, global, if, import, in, is, lambda, nonlocal, not, or, pass, raise, return, try, while, with, yield
4. 行业约定的命名规范 (PEP 8)
虽然语法允许你乱起名字,但作为全栈开发人员,遵循 PEP 8(Python 官方代码风格指南)是专业素质的体现。
| 类型 | 命名方式 | 示例 |
|---|---|---|
| 项目/包名 | 全小写,尽量简短 | my_project |
| 模块名 | 全小写,下划线分隔 | auth_service.py |
| 变量/函数名 | 蛇形命名法 (snake_case) | user_id, calculate_total() |
| 类名 | 大驼峰命名法 (PascalCase) | UserProfile, DatabaseClient |
| 常量 | 全大写,下划线分隔 | MAX_RETRIES, API_KEY |
5. 下划线的特殊含义
在 Python 中,标识符前后的下划线往往带有特定含义,这与内存管理和封装有关:
- 单下划线开头 (
_variable):暗示这是一个“私有变量”,不希望被外部直接访问(虽然语法上仍可访问,这是一种约定)。 - 双下划线开头 (
__variable):触发“名称修饰”,在类中用于更强的私有化。 - 双下划线开头和结尾 (
__init__):魔法方法 (Magic Methods)。这些是 Python 内置的特殊方法,开发者不应当随便起类似的名字。 - 单下划线 (
_):- 在解释器交互模式下,表示最后一个表达式的结果。
- 在代码中,常作为丢弃变量(不需要使用的临时变量)。
for _ in range(5): print("Hello") # 只循环5次,不关心循环变量的具体值
6. 与 Web 全栈开发的对比总结
如果你有前端经验,可以对比记忆:
- JavaScript:习惯用小驼峰 (
camelCase),如userId。 - Python:习惯用蛇形 (
snake_case),如user_id。 - HTML/CSS:习惯用中划线 (
kebab-case),如user-profile。
核心提示:在编写 Python 后端 API 时,虽然 JSON 传输可能使用
camelCase,但在 Python 代码内部逻辑中,应坚持使用snake_case以保持风格地道。
1-5 Python基础之变量的声明、初始化与垃圾回收机制
1. 变量的声明与初始化 (Declaration & Initialization)
在 Python 中,声明与初始化是同时发生的。你不需要(也不能)提前声明变量的类型。
1.1 动态类型特性
Python 是动态强类型语言:
- 动态:变量不需要指定类型,类型在运行时根据对象确定。
- 强类型:除非进行显式转换,否则不同类型之间不会自动发生“隐式转换”(例如字符串和数字不会自动相加)。
1.2 语法实现
# 声明并初始化
user_id = 101 # Python 在堆中创建一个 int 对象,并让 user_id 指向它
user_name = "Alice" # 创建一个 str 对象
# 链式赋值
a = b = c = 1
# 解包赋值 (Multiple Assignment)
x, y = 5, 10
1.3 底层原理
当你执行 a = 1000 时,Python 内部经历了三个步骤:
- 创建对象:在内存(堆)中创建一个对象,存储值
1000。 - 创建变量:在当前作用域(栈/命名空间)中创建变量名
a。 - 连接(绑定):将变量名
a存储的地址指向对象的内存地址。
2. 垃圾回收机制 (Garbage Collection, GC)
Python 不需要手动释放内存(如 C 语言的 free)。它主要依靠 “引用计数” 机制,并辅助以 “标记-清除” 和 “分代回收”。
2.1 引用计数 (Reference Counting) —— 核心机制
每个对象内部都有一个 ob_refcnt 字段,记录当前有多少个变量指向它。
- 计数增加:
- 变量赋值:
b = a - 作为参数传递给函数:
func(a) - 放入容器中:
list1 = [a]
- 变量赋值:
- 计数减少:
- 变量被显式销毁:
del a - 变量被重新赋值:
a = 200 - 离开作用域(如函数执行完毕,局部变量销毁)
- 变量被显式销毁:
当引用计数降为 0 时,对象占用的内存会被立即回收。
import sys
x = [1, 2, 3]
print(sys.getrefcount(x)) # 查看引用计数(结果通常比预期多1,因为函数调用本身也持有引用)
2.2 循环引用问题 (Circular References)
引用计数有一个致命缺陷:无法处理循环引用。
a = []
b = []
a.append(b)
b.append(a)
del a
del b
# 此时 a 和 b 的计数仍为 1,但外部已无法访问,造成内存泄漏。
2.3 标记-清除 (Mark and Sweep)
为了解决循环引用,Python 会定期扫描内存:
- 标记:从根对象(全局变量等)出发,遍历所有可达对象并打上标记。
- 清除:遍历堆内存,如果某个对象没有标记且存在循环引用,则强制回收。
2.4 分代回收 (Generational Collection) —— 效率优化
这是基于一种经验假说:新创建的对象往往死得快,而老的对象存活时间更长。
Python 将内存中的对象分为三代:
- 第 0 代:新创建的对象。当第 0 代对象达到一定阈值时,触发 GC。
- 第 1 代:在一次 GC 中幸存下来的第 0 代对象。
- 第 2 代:在多次 GC 中幸存下来的老旧对象。
规则:GC 对第 0 代扫描频率最高,第 2 代扫描频率最低。这样可以显著减少垃圾回收时的 CPU 消耗。
3. 全栈开发的避坑总结
| 关注点 | 建议 |
|---|---|
| 局部变量 | 尽量使用局部变量,函数结束后会自动触发引用计数减 1。 |
| 大数据处理 | 处理几 GB 的文件时,及时使用 del variable 手动解除引用。 |
| 可变默认参数 | def func(lst=[]) 会导致 lst 对象常驻在第 2 代内存中,造成意外逻辑和内存占用。 |
| 监控工具 | 复杂应用可使用 import gc 模块手动触发 gc.collect() 或监控回收状态。 |
1-6 Python基础之常量
在 Web 全栈开发中,常量通常用于存储配置信息(如 API 密钥、数据库连接字符串、分页大小等)。与 JavaScript 的 const 或 Java 的 final 不同,Python 并没有语法层面上的“真常量”。
1. 核心概念:约定优于配置
在 Python 中,常量是通过命名约定(Naming Convention)来实现的。
- 语法本质:Python 的变量即使大写,在运行时依然可以被修改。
- 文化契约:Python 社区遵循“成年人原则”(We are all consenting adults),即:“既然你把这个变量写成了全大写,我就默认为你不希望我去改动它。”
2. 命名规范 (PEP 8)
根据 Python 官方风格指南 PEP 8:
- 格式:全部大写字母,单词之间用下划线
_分隔。 - 位置:通常定义在模块(文件)的顶部,或专门的
config.py文件中。
示例:
# 基础常量定义
API_TIMEOUT = 30
MAX_CONNECTIONS = 100
DEFAULT_THEME_COLOR = "#3498db"
# 即使你之后写了 API_TIMEOUT = 10,代码能运行,但 IDE 会发出警告,且同事会想打你。
3. 进阶:如何实现“强约束”常量?
作为全栈开发人员,如果你希望代码更健壮,防止意外修改,可以采用以下几种方式:
3.1 使用 typing.Final (Python 3.10+)
这是类型提示(Type Hinting)层面的约束。虽然它不能阻止修改,但 Mypy 等静态检查工具和主流 IDE(如 PyCharm/VS Code)会直接报错。
from typing import Final
DATABASE_URL: Final = "postgresql://localhost:5432/mydb"
# 如果你尝试修改:
DATABASE_URL = "mysql://something_else"
# IDE 会显示错误:Cannot assign to final name "DATABASE_URL"
3.2 使用类与 @property (只读属性)
如果你想在运行时严格禁止修改,可以利用类和属性装饰器:
class _Config:
@property
def VERSION(self):
return "1.0.0"
settings = _Config()
# 尝试操作:
print(settings.VERSION) # 1.0.0
settings.VERSION = "2.0.0" # 报错:AttributeError: can't set attribute
4. 全栈开发中的最佳实践
4.1 抽离配置文件
在 Web 框架(如 Django, Flask, FastAPI)中,通常将常量统一放在一个独立文件中。
# config.py
import os
DEBUG = os.getenv("DEBUG", "False") == "True"
DB_PASSWORD = os.getenv("DB_PASSWORD", "secret_pass")
4.2 使用枚举 (Enum)
对于“分类”性质的常量(如 HTTP 状态码、用户角色、订单状态),推荐使用内置的 enum 模块。
from enum import Enum
class UserRole(Enum):
ADMIN = 1
EDITOR = 2
GUEST = 3
# 使用时更具语义化
current_role = UserRole.ADMIN
if current_role == UserRole.ADMIN:
pass
5. 对比总结
| 特性 | Python 常量 | JavaScript const |
|---|---|---|
| 强制性 | 弱(依靠约定,除非用特殊技巧) | 强(运行时直接报错) |
| 命名风格 | SNAKE_CASE_ALL_CAPS |
camelCase 或 UPPER_CASE |
| 修改后果 | 逻辑混乱,破坏维护性 | 程序由于语法错误无法运行 |
1-8 Python基础之最基本内置数据类型
在 Python 中,“万物皆对象”。即使是最基础的数据类型,在内存中也是作为 PyObject 结构体存在于堆(Heap)中的。
作为全栈开发人员,了解这些类型不仅是为了写代码,更是为了理解它们与数据库(SQL)、前端(JavaScript)之间的数据映射关系。
1 类型分类总览
Python 的内置数据类型主要可以分为以下几大类:
| 分类 | 类型名称 (type) |
例子 | 特点 |
|---|---|---|---|
| 数值型 | int, float, complex |
42, 3.14, 1+2j |
不可变 |
| 布尔型 | bool |
True, False |
int 的子类 |
| 字符串 | str |
"Hello" |
不可变 Unicode 序列 |
| 列表 | list |
[1, 2, 2] |
可变,有序序列 |
| 元组 | tuple |
(1, 2, 2) |
不可变,有序序列 |
| 集合 | set |
{1, 2, 3} |
可变,无序且唯一 |
| 字典 | dict |
{"id": 1} |
可变,键值对(Key-Value) |
| 空值 | NoneType |
None |
表示“无”或“空” |
2 数值型 (Numeric Types)
2.1 整数 (int)
- 无限精度:Python 3 的
int并不像 C 或 Java 那样有 32 位或 64 位的限制。只要内存足够,它可以表示任意大的整数。 - 内存特性:对于较小的整数(-5 到 256),Python 内部会进行缓存(Integer Interning),以提高性能。
2.2 浮点数 (float)
- 双精度:类似于 C 语言中的
double,遵循 IEEE 754 标准。 - 注意:由于二进制浮点数的特性,
0.1 + 0.2不等于0.3。在处理金融业务(如订单金额)时,建议使用decimal模块。
3. 布尔型 (Boolean Type)
- 本质:
bool是int的子类。 - 对应关系:
True == 1,False == 0。 - 布尔测试:在 Python 中,以下值被视为 “假” (Falsy):
None,False- 数值中的
0,0.0 - 空容器:
"",[],(),{},set()
4. 字符串 (String Type) —— Web 开发的核心
Python 3 的 str 是 Unicode 序列。
- 不可变性 (Immutability):一旦创建,字符串的内容就不能修改。每次拼接字符串(如
s += "!")实际上是在堆中创建了一个全新的字符串对象。 - 驻留机制 (Inlining/Interning):为了节省内存,短小且符合规则的字符串字面量会被重用内存地址。
s = "Python"
# s[0] = "p" # ❌ 报错:TypeError
new_s = "p" + s[1:] # ✅ 正确方案:创建一个新对象
5. 序列类型(列表与元组)
- 列表 (
list):就像 JavaScript 的Array。它是动态数组,支持 O(1) 的随机访问。 - 元组 (
tuple):不可变的列表。通常用于存储结构化数据(如数据库的一行记录(id, name, email)),由于其不可变性,元组的内存开销比列表小。
6. 全栈视角:类型映射表
当你作为一个全栈开发人员进行前后端交互时,类型的对应关系如下:
| Python 类型 | JSON 类型 | JavaScript 类型 | SQL 类型 (常见) |
|---|---|---|---|
int |
number |
Number / BigInt |
INT, BIGINT |
float |
number |
Number |
FLOAT, DOUBLE |
str |
string |
String |
VARCHAR, TEXT |
bool |
boolean |
Boolean |
BOOLEAN / TINYINT |
list |
array |
Array |
(通常通过中间表表示) |
dict |
object |
Object |
JSON, JSONB |
None |
null |
null |
NULL |
7. 实用工具函数
在调试阶段,这两个函数是你了解对象“本质”的神器:
x = [1, 2, 3]
print(type(x)) # <class 'list'> - 查看对象类型
print(id(x)) # 140712345678 - 查看对象在堆中的内存地址
print(isinstance(x, list)) # True - 判断是否为指定类型
1-9 Python基础之整型
作为全栈开发人员,虽然我们在业务逻辑中大多使用十进制,但在处理底层协议、颜色值、加密算法或性能优化时,二进制、八进制、十六进制以及你提到的三进制会经常出现。
1. 常见的进制表示法
Python 内部原生支持以下四种进制的直接书写:
| 进制 | 英文 | 前缀 | 字符集 | 示例 (表示十进制 10) |
|---|---|---|---|---|
| 二进制 | Binary | 0b 或 0B |
0-1 | 0b1010 |
| 八进制 | Octal | 0o 或 0O |
0-7 | 0o12 |
| 十进制 | Decimal | (无) | 0-9 | 10 |
| 十六进制 | Hexadecimal | 0x 或 0X |
0-9, A-F | 0xa |
示例代码:
a = 0b1010 # 二进制
b = 0o12 # 八进制
c = 10 # 十进制
d = 0xa # 十六进制
print(a == b == c == d) # 输出: True
2. 关于“三进制” (Ternary)
Python 虽然没有像 0b 那样为三进制(Base-3)提供内置的字面量前缀,但我们可以通过内置函数进行转换和处理。
2.1 将三进制字符串转为十进制
使用 int(string, base) 函数。
# 将三进制字符串 "121" 转换为十进制
# 计算过程:1*(3^2) + 2*(3^1) + 1*(3^0) = 9 + 6 + 1 = 16
val = int("121", 3)
print(val) # 输出: 16
2.2 将十进制转为三进制
Python 没有内置 ternary() 函数,但可以通过简单的递归或循环实现:
def to_ternary(n):
if n == 0: return "0"
res = ""
while n > 0:
res = str(n % 3) + res
n //= 3
return res
print(to_ternary(16)) # 输出: "121"
3. 进制转换内置函数
在日常 Web 开发(如权限控制、颜色转换)中,以下函数最为常用:
| 函数 | 功能 | 示例 |
|---|---|---|
bin(n) |
将十进制转为二进制字符串 | bin(10) -> '0b1010' |
oct(n) |
将十进制转为八进制字符串 | oct(10) -> '0o12' |
hex(n) |
将十进制转为十六进制字符串 | hex(255) -> '0xff' |
int(s, base) |
将任意进制字符串转为十进制 | int('ff', 16) -> 255 |
4. 全栈开发中的应用场景
4.1 前端:颜色值 (Hex)
在 CSS 中我们常用 #FFFFFF。在 Python 后端(如 Pillow 库处理图片)时,常需要转换:
# 提取 RGB
color = 0xFF5733
r = (color >> 16) & 0xFF
g = (color >> 8) & 0xFF
b = color & 0xFF
print(f"R:{r}, G:{g}, B:{b}") # R:255, G:87, B:51
4.2 Linux/后端:文件权限 (Octal)
在给文件设权限时,我们常说 777 或 644,这其实是八进制。
import os
# 设置文件为只读 (八进制 0o444)
os.chmod('test.txt', 0o444)
4.3 状态位与权限管理 (Binary)
在复杂的 Web 系统中,常用二进制位(Bitmask)来存储用户权限。
READ = 0b001
WRITE = 0b010
EXEC = 0b100
user_permission = READ | WRITE # 用户拥有读写权限 (0b011)
总结
- 二进制 (
0b)、八进制 (0o)、十六进制 (0x) 是 Python 的一等公民。 - 三进制或任意进制可以通过
int(str, base)进行解析。 - 对全栈开发而言,十六进制(颜色、加密)和二进制(位运算、权限)的权重最高。
1-10 Python基础之Float
作为一名全栈Web开发人员,理解浮点数及其在Python中的处理逻辑,对于无论是处理后端接口的数值计算,还是前端数据的呈现都至关重要。
以下是关于 Python 浮点数的系统性讲解:
1. 基本知识 (Basic Knowledge)
浮点数(Floating-point numbers)在Python中用于表示带有小数点的数字,或者使用科学计数法表示的数字。
-
定义方式:
直接写出带小数点的数字即可。x = 3.14 y = -0.01 z = 10.0 # 即使是整数,加了 .0 也会变成浮点数 -
科学计数法:
使用e或E表示 10 的幂。a = 1.2e3 # 等于 1.2 * 10^3 = 1200.0 b = 1.2e-3 # 等于 1.2 * 10^-3 = 0.0012 -
底层实现:
Python 的float类型通常对应于 C 语言中的double(双精度浮点数),遵循 IEEE 754 标准。这意味着它占用 64 位内存(8字节)。 -
关键陷阱:精度问题:
这是全栈开发中常遇到的坑(尤其是处理电商价格或金融计算时)。print(0.1 + 0.2 == 0.3) # 输出 False print(0.1 + 0.2) # 输出 0.30000000000000004原因:计算机内部使用二进制表示浮点数,某些十进制小数(如0.1)无法被精确表示为二进制,从而产生微小的舍入误差。
全栈建议:涉及财务计算时,请务必使用 Python 的decimal模块。
2. 自动转换、强制转换与增强赋值运算符
① 自动类型转换 (Implicit Type Conversion)
在算术运算中,Python 会自动将“窄”类型转换为“宽”类型以防止精度丢失。
-
整数与浮点数运算:
只要运算中有一个操作数是浮点数,结果就是浮点数。a = 10 + 3.0 # 结果:13.0 (float) b = 5 * 2.0 # 结果:10.0 (float) -
除法运算:
在 Python 3 中,使用/运算符的结果始终是浮点数,即使能整除。print(10 / 2) # 输出 5.0
② 强制类型转换 (Explicit Type Conversion / Casting)
使用内置函数手动转换数据类型。
-
转为浮点数
float():float(10) # 10.0 float("3.14") # 3.14 (字符串转浮点数,常用于解析 API 数据) -
浮点数转整数
int():
注意:int()会直接截断小数部分(向零取整),而不是四舍五入。int(3.9) # 3 int(-3.9) # -3 -
四舍五入
round():
如果需要精确控制小数位数。round(3.567, 2) # 3.57
③ 增强赋值运算符 (Augmented Assignment Operators)
增强赋值运算符(如 +=, -=, *=, /=)同样遵循自动转换规则。
| 运算符 | 例子 | 等价 |
|---|---|---|
| += | a += 2 | a = a + 2 |
| -= | a -= 2 | a = a-2 |
| *= | a *= 2 | a = a * 2 |
| /= | a /= 2 | a = a / 2 |
| //= | a //= 2 | a = a//2 |
| **= | a **= 2 | a = a**2 |
| %= | a %= 2 | a = a % 2 |
- 语法:
x op= y等价于x = x op y。 - 示例与转换逻辑:
score = 100 # 此时是 int score += 5.5 # 等价于 score = 100 + 5.5 print(score) # 输出 105.5,score 自动转为了 float count = 10 count /= 2 # 即使能整除,/= 也会使 count 变为 float print(count) # 输出 5.0
⚠️注意:
“+=”中间不能加空格!
结合的是右侧整个表达式:
y *= x+2 # 相当于:y = y*(x+2) 而不是:y = y*x+2
1-11 Python基础之时间的表示
在 Web 开发中,时间处理是及其核心的一环。无论是数据库的 created_at 字段、前端展示的倒计时,还是 API 的缓存失效时间,都离不开对时间的精确掌控。
以下是关于 Python 时间表示的基础知识:
1. 时间的表示:Unix 时间戳与精度
在计算机科学中,时间通常不是以“年月日”存储的,而是以一个连续的数字。
① Unix 时间戳 (Unix Timestamp)
- 定义:指从 1970年1月1日 00:00:00 UTC(协调世界时)开始所经过的秒数。这被称为“Unix 纪元(Epoch)”。
- 特点:它是一个随时间推移而增大的实数,不受时区影响,全球统一。
- Python 获取方式:
import time print(time.time()) # 输出类似于:1715836800.123456
② 毫秒与微秒 (Milliseconds & Microseconds)
Python 的 time.time() 返回的是一个浮点数,小数部分代表了比秒更小的单位:
- 1 秒 (Second)
- 1 毫秒 (Millisecond, ms) = $10^{-3}$ 秒(常用于 Web 前端 JS 中的
Date.now()) - 1 微秒 (Microsecond, μs) = $10^{-6}$ 秒(计算机底层及高频交易常用)
- 1 纳秒 (Nanosecond, ns) = $10^{-9}$ 秒(Python 3.7+ 通过
time.time_ns()获取整数纳秒)
全栈开发注意:
- Python 的时间戳通常以秒为单位(浮点数)。
- JavaScript 的
new Date().getTime()返回的是毫秒(整数)。 - 建议:前后端通过 API 交互时间戳时,需统一单位。
2. time 模块的核心用法
time 模块是 Python 内置的处理底层时间的模块。
① 获取当前时间
import time
# 获取当前 Unix 时间戳 (float)
seconds = time.time()
print(f"当前时间戳: {seconds}")
# 获取人类可读的字符串 (本地时区)
print(f"本地时间串: {time.ctime()}")
# 输出示例: Thu May 16 10:00:00 2024
② 暂停程序执行 (sleep)
在爬虫、限流或自动化测试中非常常用。
print("开始任务...")
time.sleep(2.5) # 暂停 2.5 秒,支持浮点数
print("任务完成")
③ 性能分析 (高精度计时)
如果你想测试某段 Python 代码运行多快,不要用 time.time(),而应该用 time.perf_counter()。
start = time.perf_counter()
# 执行一段耗时代码
sum(range(1000000))
end = time.perf_counter()
print(f"代码耗时: {end - start} 秒")
④ 结构化时间 (struct_time)
如果你想单独获取“年”或“月”,可以使用 localtime()。
local_now = time.localtime()
print(f"年份: {local_now.tm_year}")
print(f"月份: {local_now.tm_mon}")
3. 全栈视角下的时间处理建议
在实际的 Web 项目(如 Django, Flask, FastAPI)中,你会发现单纯用 time 模块是不够的:
-
存储与显示分离:
- 数据库:建议统一存储为 UTC 时间(或者 Unix 时间戳)。
- 前端:根据用户的浏览器时区,利用 JavaScript 将 UTC 转换为本地时间展示。
-
timevsdatetime:time模块主要用于获取时间戳和延迟控制。- 对于日期的加减(如:计算 7 天后的日期),你应该使用
datetime模块:
from datetime import datetime, timedelta future_date = datetime.now() + timedelta(days=7) -
格式化输出:
如果你需要输出特定的字符串格式(如2024-05-16 14:00),常用strftime:import time print(time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()))
总结:
time.time()是底层工具,给机器读。datetime是高级封装,给人读和做日期运算。- 做全栈开发,请务必建立时区意识(Timezone Aware)。
1-12 实战demo
import turtle
import math
# 1. 定义多点坐标 (List of Tuples)
coords = [(0, 0), (100, 100), (200, 0), (100, -100), (0, 0)]
# 2. 定义计算两点间距离的函数
def calculate_distance(p1, p2):
"""
计算两点 (x1, y1) 和 (x2, y2) 之间的欧几里得距离
"""
x1, y1 = p1
x2, y2 = p2
distance = math.sqrt((x2 - x1)**2 + (y2 - y1)**2)
return distance
# 3. 设置海龟作图
# 提高速度,设置画笔颜色和粗细
turtle.speed(3)
turtle.pensize(2)
turtle.pencolor("blue")
# 4. 绘制折线并计算总路程
total_distance = 0
for i in range(len(coords)):
x, y = coords[i]
# 移动到该点(如果是起点则只移动不画线,后续点开始画线)
if i == 0:
turtle.penup()
turtle.goto(x, y)
turtle.pendown()
turtle.dot(5, "red") # 在起始点打一个红点
else:
# 计算当前点与前一个点的距离
dist = calculate_distance(coords[i-1], coords[i])
total_distance += dist
# 绘图
turtle.goto(x, y)
turtle.dot(5, "red") # 在每个节点打点
# 在两点中间位置写上距离(可选,增强可视化)
mid_x = (coords[i-1][0] + x) / 2
mid_y = (coords[i-1][1] + y) / 2
turtle.penup()
turtle.goto(mid_x, mid_y)
turtle.write(f"{dist:.2f}", align="center", font=("Arial", 10, "normal"))
turtle.goto(x, y) # 回到当前坐标继续绘制
turtle.pendown()
# 5. 显示结果
turtle.penup()
turtle.goto(0, -150)
turtle.color("black")
turtle.write(f"绘制完成!总折线长度: {total_distance:.2f}",
align="center", font=("Microsoft YaHei", 12, "bold"))
# 保持窗口不关闭
turtle.done()
1-13 Python基础之运算符全解
作为一名全栈开发人员,掌握运算符是构建逻辑、处理数据和控制程序流程的基础。在 Python 中,运算符的设计非常符合人类语言直觉(如 and, or, in),这与 JavaScript 或 C 系列语言略有不同。
以下是针对 Python 基础运算符的系统性总结、
1. 算术运算符 (Arithmetic Operators)
用于执行最基本的数学运算。
| 运算符 | 名称 | 描述 | 示例 ($a=10, b=3$) |
|---|---|---|---|
+ |
加 | 相加 | $a + b = 13$ |
- |
减 | 相减 | $a - b = 7$ |
* |
乘 | 相乘 | $a * b = 30$ |
/ |
除 | 结果总是浮点数 | $a / b = 3.33...$ |
// |
整除 | 向下取整,去掉小数部分 | $10 // 3 = 3$ |
% |
取模 | 返回除法的余数 | $10 % 3 = 1$ |
** |
幂 | 计算次方 | $2 ** 3 = 8$ |
加法补充
- 数字相加
3+2结果是5 - 字符串拼接
“3”+“2”结果是“32” - 列表、元组等合并
[10,20,30]+[5,10,100]结果是[10,20,30,5,10,100]
乘法操作补充
- 数字相乘
3*2结果是6 - 字符串复制
“sxt”*3结果是”sxtsxtsxt” - 列表、元组等复制
[10,20,30]*3结果是[10,20,30,10,20,30,10,20,30]
增强赋值型运算符补充
复合赋值可以让程序更加精炼,提高效率。
| 运算符 | 描述 | 示例 | 等价于 |
|---|---|---|---|
| += | 加法赋值字符串拼接 | sum += na += “sxt” | sum = sum + na = a + “sxt” |
| -= | 减法赋值 | num1 -= n | num = num - n |
| *= | 乘法赋值 | a *= b | a = a * b |
| /= | 浮点除赋值 | a/=b | a = a / b |
| //= | 整数除赋值 | a//=b | a = a//b |
| %= | 取余赋值 | a%=b | a = a % b |
| **= | 幂运算赋值 | a**=2 | a = a**2 |
| <<= | 左移赋值 | a<<=2 | a = a<<2 |
| >>= | 右移赋值 | a>>=2 | a = a>>2 |
| &= | 按位与赋值 | a&=b | a = a&b |
| |= | 按位或赋值 | a|=b | a=a|b |
| ^= | 按位异或赋值 | a^=b | a = a^b |
⚠️与C和JAVA不一样,Python不支持自增(++)和自减(--)
2. 比较运算符 (Comparison Operators)
用于比较两个值,返回布尔值 (True 或 False)。
| 运算符 | 描述 | 示例 |
|---|---|---|
== |
等于 (检查值) | 5 == 5.0 -> True |
!= |
不等于 | 5 != 3 -> True |
> |
大于 | 10 > 5 -> True |
< |
小于 | 5 < 10 -> True |
>= |
大于等于 | 5 >= 5 -> True |
<= |
小于等于 | 3 <= 5 -> True |
3. 逻辑运算符 (Logical Operators)
全栈开发中处理多重逻辑判断的核心。
| 运算符 | 逻辑 | 规则 | 短路特性 (Short-circuit) |
|---|---|---|---|
and |
与 | 两者皆为真才为真 | 若左侧为假,不再计算右侧 |
or |
或 | 只要有一个为真即为真 | 若左侧为真,不再计算右侧 |
not |
非 | 取反 | not True 变为 False |
4. 赋值运算符 (Assignment Operators)
用于给变量赋值及进行复合运算。
| 运算符 | 示例 | 等价于 |
|---|---|---|
= |
x = 5 |
- |
+= |
x += 3 |
x = x + 3 |
-= |
x -= 2 |
x = x - 2 |
*= |
x *= 2 |
x = x * 2 |
/= |
x /= 2 |
x = x / 2 |
//= |
x //= 2 |
x = x // 2 |
%= |
x %= 2 |
x = x % 2 |
5. 成员运算符 (Membership Operators)
Python 特有的强大工具,用于检查对象是否在序列中(如列表、字符串、字典)。
| 运算符 | 描述 | 示例 |
|---|---|---|
in |
在序列中找到值则返回 True |
'a' in 'apple' -> True |
not in |
不在序列中则返回 True |
1 in [2, 3] -> False |
6. 身份/同一运算符 (Identity Operators)
用于比较两个对象的内存地址。
| 运算符 | 描述 | 区别于 == |
|---|---|---|
is |
判断是否为同一个对象 | x is y 等同于 id(x) == id(y) |
is not |
判断是否不是同一个对象 | x is not None 是最常用的用法 |
7. 位运算符 (Bitwise Operators)
在处理底层数据、图像处理或权限系统时非常有用。
| 运算符 | 描述 | 二进制逻辑示例 |
|---|---|---|
& |
按位与 | 只有对应位都为 1 才得 1 |
| |
按位或 | 只要有一个为 1 就得 1 |
^ |
按位异或 | 位不同则得 1 |
~ |
按位取反 | 翻转二进制位 |
<< |
左移 | 相当于乘以 $2^n$ |
>> |
右移 | 相当于除以 $2^n$ |
8. 运算符优先级速查表 (从高到低)
- 括号
()—— 强制最高优先级 - 幂运算
** - 计算类
*,/,//,%然后再+,- - 位移类
<<,>> - 位逻辑
&>^>| - 比较/身份/成员
==,!=,>,is,in等 - 逻辑类
not>and>or—— 逻辑或优先级最低
9.基本运算符总结
| 运算符 | 说明 |
|---|---|
| and , or , not | 布尔与、布尔或、布尔非 |
| is , is not | 同一性判断,判断是否为同一个对象 |
| in , not in | 判断是否在指定序列中 |
| <,<=,>,>=,!=,== | 比较值是否相当,可以连用 |
| | ^ & | 按位或,按位异或、按位与 |
| <<, >> | 移位 |
| ~ | 按位翻转 |
| +,-,*,/,//,% | 加,减,乘,浮点除、整数除、取余 |
| ** | 幂运算 |
1-14 Python基础之同一运算符和成员运算符
作为全栈开发人员,在处理复杂的后端逻辑、数据库查询结果或 API 数据验证时,同一运算符与成员运算符是编写高效、安全代码的关键。
以下是关于这两个运算符的深度解析。
1. 同一运算符 (Identity Operators)
同一运算符用于比较两个对象的内存地址(ID)是否相同。它告诉我们这两个变量是否指向内存中的同一个对象。
| 运算符 | 描述 | 核心原理 |
|---|---|---|
is |
判断两个变量是否引用同一个对象 | id(a) == id(b) |
is not |
判断两个变量是否引用不同对象 | id(a) != id(b) |
① is 与 == 的本质区别
这是程序员最容易混淆的地方:
-
==(等值运算符):侧重于“长得是否一样”(内容/值)。 -
is(同一运算符):侧重于“是不是同一个实体”(内存地址)。 -
is用于判断两个变量引用对象是否为同一个,既比较对象的地址。==用于判断引用变量引用对象的值是否相等,默认调用对象的__eq__()方法。
代码示例:
list1 = [1, 2, 3]
list2 = [1, 2, 3]
list3 = list1
print(list1 == list2) # True (值相等)
print(list1 is list2) # False (内存地址不同,是两个独立的列表)
print(list1 is list3) # True (list3 指向 list1 的内存)
② 在 Web 开发中的最佳实践
在全栈开发中,判断一个变量是否为空(None)时,必须且只能使用 is。因为 None 在 Python 中是一个单例对象。
# API 参数校验示例
user_input = request.json.get('username')
if user_input is None:
return {"error": "用户名不能为空"}, 400
2. 成员运算符 (Membership Operators)
成员运算符用于测试一个值是否存在于某个序列(如字符串、列表、元组、字典或集合)中。
| 运算符 | 描述 | 示例 |
|---|---|---|
in |
如果在序列中找到值,返回 True |
'a' in 'apple' |
not in |
如果在序列中没找到值,返回 True |
'guest' not in ['admin', 'editor'] |
① 针对不同数据类型的行为
- 字符串:检查是否包含子字符串。
- 列表/元组:检查是否包含某个元素。
- 字典:检查键 (Key) 是否存在,而不是值 (Value)。
- 集合 (Set):检查元素是否存在。
代码示例:
# 字典成员检测
user_data = {"id": 1, "name": "Alex"}
print("id" in user_data) # True
print("Alex" in user_data) # False (只查键)
# 权限控制逻辑
allowed_roles = {"admin", "editor", "manager"}
current_user_role = "guest"
if current_user_role not in allowed_roles:
print("权限不足!")
3. 全栈视角下的性能与安全
作为开发人员,你需要关注运算符背后的性能差异:
① 成员运算的性能效率 (O(n) vs O(1))
- 列表 (
list) / 元组 (tuple):使用in是线性搜索。如果列表有 100 万个元素,Python 需要从头遍历,速度慢 ($O(n)$)。 - 集合 (
set) / 字典 (dict):使用in是哈希查找。无论数据量多大,查询时间几乎恒定 ($O(1)$)。 - 建议:在处理大规模黑名单过滤、权限列表比对时,优先将数据转换为
set再使用in运算符。
② 逻辑安全:is 的陷阱
不要对整数或字符串频繁使用 is 进行比较,因为 Python 内部有“驻留机制(Interning)”。
# 陷阱:小整数会被缓存,但大整数不会
a = 256
b = 256
print(a is b) # True
a = 1000
b = 1000
print(a is b) # False (在某些环境下,如交互式解释器)
结论:除了判断 is None、is True、is False 外,其余所有内容比较请一律使用 ==。
总结
- 同一性 (
is):查身份证 ID,用于单例检查(尤其是None)。 - 成员性 (
in):查容器里有没有。 - 性能点:在大数据集合中,
in set()远快于in list()。 - 业务逻辑:善用
not in可以写出更符合人类语言逻辑的鉴权代码。
1-15 Python基础之整数缓存
在 Python(特别是官方的 CPython 实现)中,整数缓存机制(也称为 Integer Interning 或 Small Integer Caching)是一个为了优化性能和节省内存而设计的底层策略。
作为全栈开发人员,理解这一点能帮你避开很多在后端逻辑处理(如 Django 的 ID 比较或数据处理)中的隐形 Bug。
1. 什么是整数缓存?
在 Python 程序运行时,某些整数会被频繁使用(如循环计数器、列表索引、状态码)。
为了避免重复创建和销毁这些常用的整数对象,Python 在解释器启动时,就会在内存中预先创建好一个整数对象池。当你的代码需要用到这些数字时,Python 直接指向池里的现有对象。
2. 缓存的范围:-5 到 256
CPython 将 -5 到 256(闭区间)之间的所有整数进行了缓存。
- 为什么是这个范围? 因为在大多数程序中,这个范围内的整数使用频率最高。
- 存储位置: 这些对象被存储在全局数组
small_ints中。
3. 代码演示:is vs ==
利用我们之前学到的同一运算符 (is),可以清晰看到缓存的效果:
# 在缓存范围内
a = 256
b = 256
print(a is b) # True (内存地址相同,指向同一个缓存对象)
# 超出缓存范围
x = 257
y = 257
print(x is y) # False (创建了两个不同的对象,地址不同)
# 比较内容永远正确
print(x == y) # True (值是一样的)
4. 为什么要这么做?(性能优势)
- 节省内存:不需要为成百上千个“1”或“100”分配独立的内存空间。
- 提升速度:
- 创建新对象需要申请内存(Malloc),销毁对象需要垃圾回收(GC)。
- 通过缓存,直接返回指针引用,极大地提高了执行效率。
5. 进阶陷阱:代码块优化 (Constant Folding)
你可能会在某些 IDE(如 PyCharm)或直接运行 .py 脚本时发现以下现象:
# 运行脚本 test.py
a = 1000
b = 1000
print(a is b) # 竟然返回了 True!
原因:
这不是因为整数缓存池扩大了,而是因为 Python 编译器的“常量折叠”优化。
- 当 Python 编译一个程序块(比如一个函数或一个模块)时,如果在同一代码块里出现了两个相同的长常量,编译器会尝试让它们指向同一个常量对象。
- 如果在 交互式解释器(REPL/Shell) 中一行一行输入,则不会有这种优化,结果依然是
False。
6. 全栈开发者的实践启示
① 永远不要用 is 比较业务中的数字
在判断订单状态、用户 ID、计数统计时,必须使用 ==。
# 危险写法 (如果 status 为 300 就会报错)
if status is 200:
pass
# 正确写法
if status == 200:
pass
② 类似的概念:字符串驻留 (String Interning)
Python 同样会对一些短的、仅含标识符字符的字符串进行缓存(Interning),原理与整数缓存类似。
③ JS 与 Python 的对比
在 JavaScript 中,基本类型(Primitive types)的长数值比较是用值比较的(即使是 ===),并没有类似 Python 这种明显的“对象身份”陷阱。但在 Python 这种“万物皆对象”的语言里,身份(Identity)和值(Value)的区分至关重要。
总结
- 范围:
-5到256是预加载的单例对象。 - 目的:空间和时间的平衡及优化。
- 准则:
is只用于None、True、False这类真正的单例,业务数值比较始终用==。
1-16 Python基础之字符串操作一
在 Python 全栈开发中,字符串(String)的处理是最核心的技能之一。无论是处理用户提交的表单、生成 HTML 模板,还是与后端数据库交互,字符串都扮演着至关重要的角色。
以下是关于 Python 字符串、Unicode 以及创建方式的深度总结:
1.字符串基本特点
- 字符串的本质是:字符序列。
- Python不支持单字符类型,单字符也是作为一个字符串使用的。
⚠️Python的字符串是不可变的,我们无法对原字符串做任何修改。但,可以将字符串的一部分复制到新创建的字符串,达到“看起来修改”的效果。
很多人初学编程时,总是担心自己数学不行,潜意识里认为数学好才能编程。实际上,大多数程序员打交道最多的是“字符串”而不是“数学”。因为,编程是用来解决现实问题的,因此逻辑思维的重要性远远超过数学能力。
2. Unicode 字符集
在 Python 3 中,字符串的所有字符都是 Unicode。
- 什么是 Unicode? 它是一个为世界上所有文字(包括汉字、希腊字母、甚至表情符号 Emoji)分配唯一编号的标准。
- 编码差异:Python 3 的
str类型在内存中是以 Unicode 表示的。而当你需要将字符串保存到文件或通过网络发送时,通常会将其编码(Encode)为UTF-8。 - 全栈视角:当你的前端页面(HTML)声明了
<meta charset="UTF-8">,Python 后端接收到的数据能完美兼容,不会出现乱码。
使用内置函数ord()可以把字符转换成对应的Unicode码;
使用内置函数chr()可以把十进制数字转换成对应的字符。
# Unicode 示例
s = "你好 Python 🐍"
print(s) # 正常打印,包含汉字和 Emoji
3. 三种创建字符串的方式
在 Python 中,你可以根据不同场景选择三类引号:
① 单引号 (' ')
最常用的方式,适合短字符串。
name = 'FullStack Dev'
② 双引号 (" ")
功能与单引号完全一致。其优势在于:如果字符串内部包含单引号,外层用双引号可以避免转义。
# 推荐做法
sentence = "It's a beautiful day"
# 对应的单引号做法(需要转义符号 \)
sentence_alt = 'It\'s a beautiful day'
③ 三引号 (''' ''' 或 """ """)
也称为“多行字符串”。
- 支持多行:会自动保留字符串中的回车换行。
- 文档字符串 (Docstrings):常用于函数或类的说明文档。
- 全栈应用:在 Python 代码中手写一段 HTML 模板或 SQL 语句时极其方便。
html_content = """
<div>
<h1>Hello World</h1>
<p>这是一个多行字符串</p>
</div>
"""
4. len() 函数
len() 用于返回字符串中字符的数量(注意:是物理字符数,而非字节数)。
- 计算规则:无论是一个字母、一个汉字,还是一个 Emoji,在 Python 3 的
len()中都计为 1。 - 全栈开发注意点:
- 在进行数据库字段设计时(如 MySQL 的
VARCHAR(20)),数据库存储的通常也是字符数,这与 Python 的len()是一致的。 - 但如果要计算网络传输大小,则需要将其转换为字节串(bytes)后计算
len(s.encode('utf-8'))。
- 在进行数据库字段设计时(如 MySQL 的
代码示例:
s1 = "ABC"
s2 = "中国"
s3 = "🐍"
print(len(s1)) # 结果:3
print(len(s2)) # 结果:2 (在 Python 2 中可能是 6,但在 Python 3 是 2)
print(len(s3)) # 结果:1
# 字节长度 vs 字符长度
print(len(s2.encode('utf-8'))) # 结果:6 (因为一个汉字在 UTF-8 中占 3 字节)
① 空字符串
Python允许空字符串的存在,不包含任何字符且长度为0。例如:
c = ''
print(len(c)) #结果:0
5. 字符串的不可变性(Immutable)—— 补充干货
作为全栈开发,必须理解这一点:Python 的字符串是不可变的。
一旦创建,你就不能修改其中的某个字符。
s = "Python"
# s[0] = "p" # 这行会报错! TypeError
# 只能创建一个新的字符串
s = "p" + s[1:]
print(s) # "python"
性能提示:如果你在循环中频繁用 + 拼接大量字符串,性能会很差。在处理大量数据拼接(如生成长的 HTML 列表)时,推荐使用 ''.join(list_of_strings)。
总结
- Unicode 保证了全球语言的兼容性,不用担心乱码。
- 单/双引号 灵活嵌套,三引号 处理多行文本和 SQL/HTML。
len()返回字符数量,符合直觉,但传输数据时需关注字节数。
1-17 Python基础之字符串操作二
在全栈开发中,字符串处理涉及前后端数据交互、日志记录以及动态 HTML/模板生成。掌握 Python 字符串的基础操作是处理这些任务的前提。
以下是关于转义字符、拼接、复制及 input() 输入的系统总结:
1. 转义字符 (Escape Characters)
当字符串中包含一些具有特殊含义的字符(如引号本身、换行符)时,需要使用反斜杠 \ 开启转义。
| 转义字符 | 说明 | 全栈应用场景 |
|---|---|---|
\' |
单引号 | 字符串内嵌套单引号 |
\" |
双引号 | 字符串内嵌套双引号 |
\n |
换行符 | 生成多行日志或格式化文本输出 |
\t |
制表符 (Tab) | 格式化控制台输出整齐的表格数据 |
\\ |
反斜杠本身 | 表示 Windows 文件路径(如 C:\\Users\\...) |
原始字符串 (Raw String) 技巧:
在字符串前加 r,其中的转义字符将不再生效。这在编写正则表达式 (Regex) 时非常有用。
# 路径处理
path = r"C:\nextjs\app\page.js" # 不会被识别为 \n 或 \a 等
2. 字符串拼接 (Concatenation)
Python 提供了多种方式将多个字符串组合在一起。
① 使用 + 号
最直观,但由于 Python 字符串不可变的特性,频繁使用 + 拼接会消耗较多内存。
first_name = "React"
last_name = "Framework"
full_name = first_name + " " + last_name
② 隐式拼接
仅限字符串常量放在一起时自动合并。
html_tag = "<div " "class='container'></div>" # 自动合并
③ f-string (推荐做法)
自 Python 3.6+ 引入。这是全栈开发中最标准、效率最高的方式。
# 将变量直接嵌入字符串
db_user = "admin"
port = 5432
connection_str = f"Connecting to {db_user} at port {port}..."
④ .join() 方法
当需要拼接列表中的大量字符串(如从数据库查出的多行纪录)时,性能远超 +。
tags = ["Python", "JavaScript", "SQL"]
html_list = ", ".join(tags) # "Python, JavaScript, SQL"
3. 字符串复制 (Replication)
使用乘法运算符 * 可以快速重复字符串。
- 全栈场景:常用于在控制台调试时生成分隔线。
separator = "-" * 50
print(separator)
print(" DEBUGGING API RESPONSE ")
print(separator)
4. 获取键盘输入 input()
input() 函数允许程序暂停并等待用户从键盘输入文本。
- 返回值类型:
input()永远返回 字符串 (str)。 - 类型转换:如果输入的是数字,必须手动转换(如
int()或float()),否则无法进行数学计算。
示例代码:
# 获取前端端口号并验证
user_port = input("请输入前端运行的端口号: ")
print(f"数据类型是: {type(user_port)}") # <class 'str'>
# 如果需要数学运算,必须转换
port_number = int(user_port)
if port_number == 3000:
print("这是 Next.js/React 的默认端口。")
5. 全栈开发避坑指南
-
拼接时的类型错误:
在 Python 中,你不能直接str + int(例如"Port: " + 3000会报错)。而在 JavaScript 中这会自动转为字符串。- Pythonic 方案:使用 f-string:
f"Port: {3000}"。
- Pythonic 方案:使用 f-string:
-
input()的安全性:
虽然在生产 Web 环境中我们不直接使用input()获取用户输入(而是通过 API 请求),但在编写 CLI (命令行工具) 或脚本(例如:自动化部署脚本、数据库迁移工具)时,它是最基础的交互手段。 -
转义与 SQL 注入:
虽然\能够转义字符,但在手动拼接 SQL 语句时,严禁通过字符串转义来防范 SQL 注入。应始终使用 Web 框架(Django/SQLAlchemy)提供的参数化查询。
总结
- 转义:让特殊字符“保持原样”或“发挥特权”。
- 拼接:日常用 f-string,批量处理用
.join()。 - 复制:
*快速生成冗余字符。 - 输入:记得转换返回的 字符串。
1-18 Python基础之字符串操作三
作为全栈开发人员,处理字符串不仅仅是简单的显示文本,还涉及到数据清洗、API 返回结果格式化以及高效的内存管理。
以下是关于 Python 字符串转换、提取、替换及底层内存机制的深度解析:
1. str() 函数:强制类型转换
在处理后端逻辑时,经常需要将数据库读取的数字、日期或对象转换为字符串,以便进行拼接或返回给前端(JSON 格式)。
- 功能:将其他数据类型(int, float, list, dict 等)转换为字符串。
- 示例:
user_id = 1024 print("User ID is: " + str(user_id)) # 必须转换,直接 + 会报错 data_list = [1, 2, 3] print(str(data_list)) # "[1, 2, 3]" (变成了一个外观是列表的字符串)
2. 字符提取(索引与切片)
Python 的字符串提取功能非常强大,类似于 JavaScript 的 slice(),但语法更简洁。
① 索引 (Indexing)
- 正向从
0开始。 - 反向从
-1开始(提取末尾字符极其方便)。
s = "Next.js"
print(s[0]) # 'N'
print(s[-1]) # 's'
② 切片 (Slicing) - [start : end : step]
- 规则:包头不包尾(包含
start,不包含end)。 - 全栈技巧:常用于处理文件后缀、URL 路径或隐藏敏感信息(如隐藏手机号中间位)。
url = "https://www.python.org"
# 提取协议
print(url[:5]) # "https" (省略 start 默认为 0)
# 提取域名(从索引 8 开始到最后)
print(url[8:]) # "www.python.org" (省略 end 默认为结尾)
# 步长 (step):逆序字符串(笔试高频题)
print(url[::-1]) # "gro.nohtyp.www//:sptth"
3. replace() 字符串替换
在处理用户提交的敏感词过滤、或是根据环境动态修改路径时,replace() 是首选。
- 语法:
s.replace(old, new, [count]) - 关键点:它不会修改原字符串,而是返回一个全新的字符串(因为字符串是不可变的)。
path = "api/v1/user/profile"
new_path = path.replace("v1", "v2")
print(path) # "api/v1/user/profile" (原词没变)
print(new_path) # "api/v2/user/profile" (新词产生)
4. 字符串的内存分析(核心面试点)
理解内存机制是区分高级开发人员与初学者的标准。
① 字符串的不可变性 (Immutability)
在 Python 中,一旦字符串在内存中被创建,它的内容就不能被改变。
当你执行 s = s.replace("a", "b") 时,内存中发生了以下过程:
- 在内存中寻找一块新空间。
- 计算并存放替换后的新字符串内容。
- 将变量
s指向这块新空间的地址。 - 原有的旧字符串如果没有其他引用,将被 Python 的垃圾回收 (GC) 自动处理。
② 内存驻留 (String Interning)
为了优化性能,对于一些短小的、符合标识符规则的字符串,Python 会尝试让它们共享同一块内存地址。
a = "hello"
b = "hello"
print(a is b) # True (它们指向内存中同一个地址)
③ 拼接的性能警告
虽然用 + 拼接很方便,但在循环中大量拼接性能极差:
# 差劲的做法:每次拼接都会在内存中开启新空间,复制老内容
result = ""
for i in range(1000):
result += str(i)
# 全栈优选方案:.join()
# join 预先计算总长度,一次性申请内存,效率极高
result = "".join([str(i) for i in range(1000)])
总结(全栈视角对比)
| 特性 | Python | JavaScript |
|---|---|---|
| 可变性 | 不可变 | 不可变 (Primitive String) |
| 提取 | s[start:end] (切片) |
s.substring() 或 s.slice() |
| 替换 | replace() 返回新串 |
replace() 返回新串 |
| 转换 | str(123) |
String(123) 或 123.toString() |
作为开发人员,记住一句话:在 Python 中,所有“修改”字符串的操作实质上都是在“创建”新字符串。 这种设计简化了多线程环境下的数据安全。
1-19 Python基础之字符串操作四
这一部分涵盖了 Python 字符串处理的进阶实战。作为全栈开发人员,理解这些不仅能提升代码效率,还能在处理大规模文本数据(如日志分析、爬虫、报表生成)时避免性能瓶颈。
1. 字符串切片 (Slicing) & 逆序
(简要回顾,补充底层逻辑)
- 原理:切片操作通过调用底层
__getitem__并传入slice对象来实现。 - 逆序应用:
s[::-1]。 - 应用场景:解析固定格式的文件(如旧式银行报文)、反转域名、处理文件路径。
典型操作(三个量为正数的情况)如下:
| 操作和说明 | 示例 | 结果 |
|---|---|---|
[:] 提取整个字符串 |
“abcdef”[:] | “abcdef” |
[start:]从start索引开始到结尾 |
“abcdef”[2:] | “cdef” |
[:end]从头开始知道end-1 |
“abcdef”[:2] | “ab” |
[start:end]从start到end-1 |
“abcdef”[2:4] | “cd” |
[start:end:step]从start提取到end-1,步长是step |
“abcdef”[1:5:2] | “bd” |
其他操作(三个量为负数)的情况:
| 示例 | 说明 | 结果 |
|---|---|---|
| "abcdefghijklmnopqrstuvwxyz"[-3:] | 倒数三个 | “xyz” |
| "abcdefghijklmnopqrstuvwxyz"[-8:-3] | 倒数第八个到倒数第三个(包头不包尾) | 'stuvw' |
| "abcdefghijklmnopqrstuvwxyz"[::-1] | 步长为负,从右到左反向提取 | 'zyxwvutsrqponmlkjihgfedcba' |
切片操作时,起始偏移量和终止偏移量不在[0,字符串长度-1]这个范围,也不会报错。起始偏移量小于0则会当做0,终止偏移量大于“长度-1”会被当成-1。例如:
>>> 'abcdefg'[3:50]
'defg'
2. split() 分割与 join() 合并:效率之王
这是全栈开发中处理数据格式转换(如 CSV 到 JSON)最频繁的操作。
-
split(sep, maxsplit):- 将字符串按分隔符拆分为列表。如果不传
sep,默认按任何空白字符(空格、换行、制表符)分割,并自动丢弃空结果。
- 将字符串按分隔符拆分为列表。如果不传
-
join(iterable):- 原理(非常重要):与
+不同,join()会先计算所有字符串的总长度,然后一次性申请内存并拷贝。
- 原理(非常重要):与
-
代码效率测试(全栈必知):
import time # 差评做法:频繁开辟内存空间,O(n^2) start = time.time() res = "" for i in range(100000): res += str(i) print("使用 + 拼接耗时:", time.time() - start) # 推荐做法:一次性计算内存,O(n) start = time.time() res = "".join([str(i) for i in range(100000)]) print("使用 join 拼接耗时:", time.time() - start) -
结论:在循环内拼接字符串时,永远优先使用
join()。
split()可以基于指定分隔符将字符串分隔成多个子字符串(存储到列表中)。如果不指定分隔符,则默认使用空白字符(换行符/空格/制表符)。示例代码如下:
拼接字符串要点: 使用字符串拼接符
+,会生成新的字符串对象,因此不推荐使用+来拼接字符串。推荐使用join函数,因为join函数在拼接字符串之前会计算所有字符串的长度,然后逐一拷贝,仅新建一次对象。
3. 驻留机制 (Interning) 与 字符串比较 与 同一性 与 成员操作符判断子字符串
① 驻留机制
- 驻留机制:为了优化寻址速度,对于符合“标识符规则”(字母、数字、下划线)的常量字符串,Python 会在程序启动时将其存入一个全局字典(Intern table),让值相同的串指向同一地址。
is(身份判断):比较内存地址id()。==(值判断):调用__eq__比较内容。- 应用注意:
- 绝不能用
is比较业务中的字符串(因为动态生成的、复杂的或包含特殊符号的字符串不会被驻留)。 is None是性能最高且标准的做法,因为None是全局单例。
- 绝不能用
字符串驻留:常量字符串只保留一份。
c = "dd#"
d = "dd#"
print(c is d) #True
② 字符串比较与同一性
我们可以直接使用== !=对字符串进行比较,是否含有相同的字符。
我们使用is is not,判断两个对象是否同一个对象。比较的是对象的地址,即id(obj1)是否和id(obj2)相等。
③ 成员操作符判断子字符串
in not in 关键字,判断某个字符(子字符串)是否存在于字符串中。
4. 常用方法:清理与检查
| 方法 | 应用场景 (全栈视角) |
|---|---|
find() / index() |
find() 找不到返回 -1,常用于判读 URL 中是否包含特定参数。 |
startswith() / endswith() |
校验 URL 协议 (https://) 或文件上传后缀名 (.png)。 |
strip() / lstrip() / rstrip() |
必用。处理用户表单或读取配置文件时,移除两端多余的空格或换行。 |
upper() / lower() |
忽略大小写的用户登录验证、搜索关键字归一化。 |
isdigit() / isalpha() |
前端输入在后端的二次基础验证(如验证码是否全数字)。 |
center() / ljust() |
编写控制台工具 (CLI) 时,美化输出排版。 |
常用查找方法
我们以一段文本作为测试:
a='''我是高淇,我在北京尚学堂科技上班。我的儿子叫高洛希,他6岁了。我是一个编程教育的普及者,希望影响6000万学习编程的中国人。我儿子现在也开始学习编程,希望他18岁的时候可以超过我'''
| 方法和使用示例 | 说明 | 结果 |
|---|---|---|
| len(a) | 字符串长度 | 96 |
| a.startswith('我是高淇') | 以指定字符串开头 | True |
| a.endswith('过我') | 以指定字符串结尾 | True |
| a.find('高') | 第一次出现指定字符串的位置 | 2 |
| a.rfind('高') | 最后一次出现指定字符串的位置 | 29 |
| a.count("编程") | 指定字符串出现了几次 | 3 |
| a.isalnum() | 所有字符全是字母或数字 | False |
① 去除首尾信息
我们可以通过strip()去除字符串首尾指定信息。通过lstrip()去除字符串左边指定信息,rstrip()去除字符串右边指定信息。
【操作】去除字符串首尾信息
>>> "*s*x*t*".strip("*")
's*x*t'
>>> "*s*x*t*".lstrip("*")
's*x*t*'
>>> "*s*x*t*".rstrip("*")
'*s*x*t'
>>> " s xt ".strip()
's xt'
②大小写转换
编程中关于字符串大小写转换的情况,经常遇到。我们将相关方法汇总到这里。为了方便学习,先设定一个测试变量:
a = "gaoqi love programming, love SXT"
| 示例 | 说明 | 结果 |
|---|---|---|
| a.capitalize() | 产生新的字符串,首字母大写 | 'Gaoqi love programming, love sxt' |
| a.title() | 产生新的字符串,每个单词都首字母大写 | 'Gaoqi Love Programming, Love Sxt' |
| a.upper() | 产生新的字符串,所有字符全转成大写 | 'GAOQI LOVE PROGRAMMING, LOVE SXT' |
| a.lower() | 产生新的字符串,所有字符全转成小写 | 'gaoqi love programming, love sxt' |
| a.swapcase() | 产生新的,所有字母大小写转换 | 'GAOQI LOVE PROGRAMMING, LOVE sxt' |
③格式排版
center()、ljust()、rjust()这三个函数用于对字符串实现排版。示例如下:
>>> a="SXT"
>>> a.center(10,"*")
'***SXT****'
>>> a.center(10)
' SXT '
>>> a.ljust(10,"*")
'SXT*******'
④特征判断方法
- isalnum() 是否为字母或数字
- isalpha() 检测字符串是否只由字母组成(含汉字)
- isdigit() 检测字符串是否只由数字组成
- isspace() 检测是否为空白符
- isupper() 是否为大写字母
- islower() 是否为小写字母
>>> "sxt100".isalnum()
True
>>> "sxt尚学堂".isalpha()
True
>>> "234.3".isdigit()
False
>>> "23423".isdigit()
True
>>> "aB".isupper()
False
>>> "A".isupper()
True
>>> "\t\n".isspace()
True
5. format 格式化:动态拼接的利器
除了最新的 f-string,format() 在处理模板数据或复杂数字格式时依然重要。
- 填充与对齐:
{:*>10}(右对齐,总宽10,星号填充)。 - 数字格式化:
- 精度控制:
{:.2f}(保留两位小数)。 - 千分位:
{:,}(打印金融金额:1,234,567.89)。 - 进制转换:
{:b}(二),{:x}(十六)。
- 精度控制:
- 示例:
# 财务报表格式化 balance = 12345.678 print(f"账户余额:{balance:,.2k}") # 报错,应为 {balance:,.2f} print(f"账户余额:{balance:*>15,.2f}") # ***12,345.68
① format()基本用法
基本语法是通过 {} 和:来代替以前的 % 。
format() 函数可以接受不限个数的参数,位置可以不按顺序。
我们通过示例进行格式化的学习。
>>> a = "名字是:{0},年龄是:{1}"
>>> a.format("高淇",18)
'名字是:高淇,年龄是:18'
>>> a.format("高希希",6)
'名字是:高希希,年龄是:6'
>>> b = "名字是:{0},年龄是{1}。{0}是个好小伙"
>>> b.format("高淇",18)
'名字是:高淇,年龄是18。高淇是个好小伙'
>>> c = "名字是{name},年龄是{age}"
>>> c.format(age=19,name='高淇')
'名字是高淇,年龄是19'
我们可以通过{索引}/{参数名},直接映射参数值,实现对字符串的格式化,非常方便。
② 填充与对齐
- 填充常跟对齐一起使用
^、<、>分别是居中、左对齐、右对齐,后面带宽度:号后面带填充的字符,只能是一个字符,不指定的话默认是用空格填充
>>> "{:*>8}".format("245")
'*****245'
>>> "我是{0},我喜欢数字{1:*^8}".format("高淇","666")
'我是高淇,我喜欢数字**666***'
③ 数字格式化
浮点数通过f,整数通过d进行需要的格式化。案例如下:
>>> a = "我是{0},我的存款有{1:.2f}"
>>> a.format("高淇",3888.234342)
'我是高淇,我的存款有3888.23'
其他格式,供大家参考:
| 数字 | 格式 | 输出 | 描述 |
|---|---|---|---|
| 3.1415926 | 3.14 | 保留小数点后两位 | |
| 3.1415926 | 3.14 | 带符号保留小数点后两位 | |
| 2.71828 | 3 | 不带小数 | |
| 5 | 05 | 数字补零 (填充左边, 宽度为2) | |
| 5 | 5xxx | 数字补x (填充右边, 宽度为4) | |
| 10 | 10xx | 数字补x (填充右边, 宽度为4) | |
| 1000000 | 1,000,000 | 以逗号分隔的数字格式 | |
| 0.25 | 25.00% | 百分比格式 | |
| 1000000000 | 1.00E+09 | 指数记法 | |
| 13 | 13 | 右对齐 (默认, 宽度为10) | |
| 13 | 13 | 左对齐 (宽度为10) | |
| 13 | 13 | 中间对齐 (宽度为10) |
6. 可变字符串:io.StringIO
Python 的 str 是不可变的。有时你需要一个“类似文件的对象”来动态构建超长文本,而不想频繁生成新的字符串对象。
- Python中,字符串属于不可变对象,不支持原地修改,如果需要修改其中的值,只能创建新的字符串对象。
- 确实需要原地修改字符串,可以使用io.StringIO对象或array模块
- 原理:
StringIO在内存中开辟一块缓冲区,像操作文件一样操作字符串。 - 应用场景:
- 大规模生成 CSV 导出文件内容。
- 单元测试:模拟一个文件对象传给需要处理文件的函数。
- 示例:
from io import StringIO f = StringIO() f.write("Line 1\n") f.write("Line 2\n") # 获取全部内容 content = f.getvalue() print(content) f.close()
7. 类型转换总结
全栈开发中,Python 与前端 (JSON/Strings) 交互后的转换至关重要:
str(x):通用的“转字符串”方法。int(str, base):将字符串转为整数。注意int("10", 16)是十六进制转十进制。float(str):转浮点数。list(str):将字符串打散成字符列表。ord(char)与chr(int):ord('a')-> 97 (Unicode 码点)。chr(97)-> 'a'。- 场景:简单加密算法、生成随机验证码、判断字符范围。
eval()(警告!):- 能将字符串当成 Python 表达式执行。
- 全栈陷阱:绝对不要对用户输入使用
eval(),这会导致严重的远程代码执行 (RCE) 安全漏洞。如果需要解析 JSON 格式的字符串,请使用json.loads()。
| 类型转换 | |
|---|---|
| int(x [,base]) | 将x转换为一个整数 |
| long(x [,base] ) | 将x转换为一个长整数 |
| float(x) | 将x转换到一个浮点数 |
| complex(real[,imag]) | 创建一个复数 |
| str(x) | 将对象 x 转换为字符串 |
| repr(x) | 将对象 x 转换为表达式字符串 |
| eval(str) | 用来计算在字符串中的有效Python表达式,并返回一个对象 |
| Complex(A) | 将参数转换为复数型 |
| tuple(s) | 将序列 s 转换为一个元组 |
| list(s) | 将序列 s 转换为一个列表 |
| set(s) | 转换为可变集合 |
| dict(d) | 创建一个字典。d 必须是一个序列 (key,value)元组 |
| frozenset(s) | 转换为不可变集合 |
| chr(x) | 将一个整数转换为一个字符 |
| unichr(x) | 将一个整数转换为Unicode字符 |
| ord(x) | 将一个字符转换为它的整数值 |
| hex(x) | 将一个整数转换为一个十六进制字符串 |
| oct(x) | 将一个整数转换为一个八进制字符串 |
给全栈开发者的核心建议:
- 性能优先:在大文本拼接前,养成把零件丢进
list最后再"".join(list)的习惯。 - 安全优先:始终对用户传入的内容进行
strip()处理,并警惕eval()的使用。 - 对齐一致:如果后端涉及生成日志或 CLI 工具,利用
ljust()和format()的对齐功能,会让你的后端的输出像前端页面一样优雅。

浙公网安备 33010602011771号