今天我们不从头讲语法,而是顺着这份笔记的脉络,挑出几个高频使用但极易踩坑 的细节,结合真实场景重新梳理一遍。读完你可能会发现:原来我一直用的 sort() 居然会改原列表?replace() 竟然不生效?*args 和 **kwargs 到底怎么优雅传参?
一、注释:除了"解释代码",还能做文档
笔记里简单写了三种形式:
bash
# 单行注释
"""
多行注释(三双引号)
'''
多行注释(三单引号)
'''
必须说清楚 :""" 和 ''' 本质上是字符串字面量,只是当它们孤立存在、不被赋值或操作时,解释器会忽略它们,起到了"注释"的效果。但如果你这样写:
ini
x = """我是字符串"""
那它就不是注释了。
实用场景 :函数内部的文档字符串(docstring)通常用 """ 包裹,help() 能读取它:
python
def add(a, b):
"""返回两个数的和"""
return a + b
help(add) # 会显示 """返回两个数的和"""
踩坑提醒 :如果你用 """ 注释掉一大段代码,而这段代码本身又包含 """,会触发 SyntaxError。此时要么改用 # 逐行注释,要么用编辑器快捷键批量加 #。
二、数据类型:一张表理清 Python 内置九大类
笔记列出了:整数、浮点数、字符串、布尔值、列表、元组、字典、集合、None。
| 类型 | 例子 | 是否可变 | 是否有序 |
|---|---|---|---|
| 整数 | 10 |
不可变 | - |
| 浮点数 | 3.14 |
不可变 | - |
| 字符串 | 'hello' |
不可变 | 有序(索引) |
| 布尔值 | True / False |
不可变 | - |
| 列表 | [1,2,3] |
可变 | 有序 |
| 元组 | (1,2,3) |
不可变 | 有序 |
| 字典 | {'name':'lgl','age':18} |
可变 | 有序(3.7+) |
| 集合 | {1,2,3} |
可变 | 无序 |
| None | None |
不可变 | - |
重点 :元组的"不可变"是指元素的指向不可变,但如果元组内包含列表,列表内容可以变:
ini
t = (1, [2,3])
t[1].append(4) # 合法,t 变成 (1, [2,3,4])
但你不能 t[0] = 5,这会报错。
None 常被用作"空值"占位,比如函数默认返回值就是 None。判断时推荐 if x is None 而非 if not x,因为后者会把 0、[] 等也判为 True。
三、输出与输入:那些你可能没玩过的花样
1. print() 的进阶用法
python
print(100) # 直接输出值
print('hello world') # 输出字符串
a = 10
print(f'a={a}') # f-string,推荐!清晰高效
笔记还提到 \t(制表符)、\n(换行)和 end=''。实际项目里,end 常用来构建动态刷新效果:
python
import time
for i in range(101):
print(f'\r进度:{i}%', end='', flush=True)
time.sleep(0.05)
print() # 换行
\r 让光标回到行首,end='' 阻止自动换行,flush=True 强制立即输出------这样你就看到一行进度条在变化。
2. input() 的"类型陷阱"
ini
a = int(input('请输入一个整数:'))
这样写没问题,但若用户输入 'abc' 会抛出 ValueError。实战中务必加异常处理:
python
try:
a = int(input('请输入整数:'))
except ValueError:
print('这不是整数,请重试')
另外,input() 返回的一律是字符串,别忘转型。
四、随机数:别再死记硬背了
scss
import random
print(random.randint(1, 100)) # 闭区间 [1,100],包含两端
print(random.uniform(1, 100)) # 浮点数,范围 [1,100)?文档是 [a,b] 或 [a,b)?
注意 :random.uniform(a,b) 的端点取决于 a 和 b 的大小关系,但返回的是 a <= N <= b(若 a<=b)或 b <= N <= a,总之是闭区间 。笔记说"不包含1和100"是错误的------实际上 uniform 是包含两端(理论上浮点数精度可能碰巧等于端点,但概率极低)。大家使用时以官方文档为准。
更常用的随机选择:random.choice(['红','蓝','绿'])、random.shuffle(list) 等,这些笔记没提,但实战频繁。
五、运算符与布尔逻辑:短路效应是关键
ini
print(2 ** 3) # 8,指数运算
a = False
b = not a # True
c = a and b # False
d = a or b # True
and 和 or 返回的不是布尔值 ,而是最后一个被求值的操作数:
bash
print(0 and 10) # 0,因为 0 为假,短路返回 0
print(3 or 0) # 3,因为 3 为真,短路返回 3
这个特性常用于简化逻辑,比如 x = y or '默认值',但小心 y=0 时也会被当成假。
字符串拼接:
ini
c = 'Hello' + ' ' + 'World' # 用 +
c = 'Ha' * 3 # 'HaHaHa'
* 可以快速重复字符串,但不要用 + 在循环里拼接大量字符串 ,因为字符串不可变,每次 + 都会创建新对象,效率极低。推荐用 ''.join(list_of_str)。
六、控制流:if、elif 与 for 的细节
yaml
if condition:
# 缩进 4 个空格
elif other:
# ...
else:
# ...
缩进是 Python 的灵魂,混用 Tab 和空格会报错,现代编辑器都能自动转空格。
for 循环笔记里写了 range(10) 表示 09, 9。range(1,10) 表示 1range 是惰性序列,节省内存,常与 list() 搭配转成列表。
但 for 循环可以直接迭代字符串、列表、元组、字典、集合:
bash
for ch in 'hello':
print(ch) # 迭代字符
for key in d:
print(key, d[key]) # 迭代字典键
for value in d.values():
print(value) # 迭代值
for k, v in d.items():
print(k, v) # 同时迭代键值对
七、字符串操作:不可变,别忘赋值
笔记里写了切片、replace、split、join。
ini
s = 'hello world'
print(s[0]) # 'h'
print(s[1:5]) # 'ello'(左闭右开)
print(s[1:5:2]) # 'el'(步长2)
new_s = s.replace('l', 'L') # 返回新字符串 'heLLo worLd'
print(s) # 原字符串不变!
parts = s.split(' ') # ['hello', 'world']
joined = ' '.join(parts) # 'hello world'
金句:字符串是"钢铁直男"------任何操作都生成新对象,绝不改变自己。永远记得把结果赋值回去。
实战场景 :清洗文本时,常用 strip() 去首尾空白,replace 去除换行符,split 按分隔符切分 CSV 数据。
八、列表:增删改查,排序有坑
ini
lst = [1,2,3]
lst.append(4) # 末尾追加
lst.insert(1, 99) # 在下标 1 前插入 99 -> [1,99,2,3,4]
lst.pop(2) # 删除下标 2 的元素并返回
lst.remove(99) # 删除第一个值为 99 的元素
lst.clear() # 清空
排序的陷阱:
ini
lst = [3,1,2]
lst.sort() # 修改原列表,返回 None
# 如果想保留原列表,用 sorted()
new_lst = sorted(lst) # 返回新列表
排序支持 key 和 reverse,比如按字符串长度排序:
ini
words = ['apple', 'pie', 'banana']
words.sort(key=len, reverse=True)
取下标 :lst[0] 获取第一项,lst[-1] 获取最后一项。如果下标越界会报 IndexError,安全方式是用 try 或先判断长度。
九、元组:不可变的"列表",用在哪?
ini
t = (1, 2, 3)
# t[0] = 100 # 报错
元组不可变,因此哈希可计算,可作为字典的键(而列表不行)。另外,函数返回多个值时,本质就是元组:
ruby
def get_stats():
return 1, 2, 3 # 自动打包为元组
x, y, z = get_stats() # 解包
注意 :单元素元组必须加逗号 (1,),否则被当作整数。
十、集合:去重利器,但别指望顺序
ini
s = {1,2,3,3} # 自动去重 -> {1,2,3}
s.add(4)
s.remove(2)
# 集合支持交集、并集等
a = {1,2,3}
b = {2,3,4}
print(a & b) # {2,3}
print(a | b) # {1,2,3,4}
集合无序,因此不能通过下标访问。如果需要有序去重,用 dict.fromkeys() 或 sorted(set(lst))。
十一、字典:键值对的"瑞士军刀"
scss
d = {'name': 'lgl', 'age': 18}
print(d.keys()) # dict_keys(['name','age'])
print(d.values()) # dict_values(['lgl',18])
print(d.items()) # dict_items([('name','lgl'), ('age',18)])
del d['age'] # 删除键值对
d.clear() # 清空字典
迭代时删除会导致运行时错误,安全做法:
ini
for key in list(d.keys()):
if key == 'age':
del d[key]
或者用字典推导式创建新字典:
css
d = {k:v for k,v in d.items() if k != 'age'}
默认值 :d.get('gender', '未知') 如果键不存在返回默认值,不报错。
十二、函数:定义、参数、作用域
python
def func(p1, p2, p3):
# 函数体
return 结果
pass # 占位,什么都不做
全局变量 :在函数内修改全局变量需 global 声明:
ini
x = 10
def change():
global x
x = 20
change()
print(x) # 20
但滥用全局变量会破坏封装,建议尽量通过参数和返回值传递。
1. *args 和 **kwargs 的正确使用姿势
笔记提到了"可变参数",但实战中它们能极大提升灵活性。
*args接收任意个位置参数,打包成元组。**kwargs接收任意个关键字参数,打包成字典。
css
def demo(*args, **kwargs):
print('位置参数:', args)
print('关键字参数:', kwargs)
demo(1,2,3, name='lgl', age=18)
# 输出:
# 位置参数: (1,2,3)
# 关键字参数: {'name':'lgl','age':18}
高阶用法:将已有的列表/字典"解包"传递:
python
params = {'name':'lgl', 'age':18}
def show(name, age):
print(f'{name} is {age}')
show(**params) # 等价于 show(name='lgl', age=18)
nums = [1,2,3]
def add(a,b,c):
return a+b+c
add(*nums) # 等价于 add(1,2,3)
注意 :参数名必须匹配,否则 TypeError。
十三、综合实战:把笔记里的代码串起来
笔记末尾有一段示例代码:
scss
a = 10
print(a)
print(f'a={a}')
a = int(input('请输入一个整数:'))
print(a)
print(type(a))
import random
print(random.randint(1,100))
print(random.uniform(1,100))
我们可以把这些揉进一个随机数猜谜游戏,涵盖输入、类型转换、条件、循环、随机数和函数:
python
import random
def guess_game():
target = random.randint(1, 100)
attempts = 0
while True:
try:
guess = int(input('猜一个 1~100 的整数:'))
except ValueError:
print('请输入有效整数!')
continue
attempts += 1
if guess < target:
print('太小了')
elif guess > target:
print('太大了')
else:
print(f'恭喜!用了 {attempts} 次猜中')
break
if __name__ == '__main__':
guess_game()
这个例子用到了 random.randint、input、int 转型、try-except、if-elif-else、while、break、函数定义、if __name__ 保护------几乎覆盖了笔记所有核心语法。
最后:你的基础,是否经得起"拷问"?
我们重新走了一遍笔记,但重点始终放在那些"你以为你知道,实际可能错"的细节上 。下面整理一份快速自查清单,你可以逐条过一遍:
- 我是否清楚
"""是字符串而非真正的注释? - 我是否会在
input后立即转型而不做异常处理? - 我是否知道
random.uniform是闭区间(理论)? - 我是否明白
and/or返回的是操作数而非布尔值? - 我是否习惯用
''.join()而不是+拼接大量字符串? - 我是否把
s.replace()的结果重新赋值了? - 我是否知道
sort()返回None且修改原列表,而sorted()返回新列表? - 我是否能在循环中安全删除字典元素?
- 我是否会用
*args和**kwargs解包传参? - 我是否清楚元组不可变但内部列表可变的特性?
如果每一项你都能自信点头,那么恭喜------你的基础真正"毕业"了。如果有一些模棱两可,不妨回头再翻翻对应章节。
技术之路,不怕慢,就怕"我以为"。 笔记是死的,但代码是活的。多写、多调试、多复盘,把每一个"知道"变成"做到",才是进阶的唯一捷径。