目录标题
-
-
- [一、单行 if(条件表达式)](#一、单行 if(条件表达式))
- 二、多变量赋值(解构)
- [三、最"抽象"的:*args 和 **kwargs](#三、最“抽象”的:*args 和 **kwargs)
- 总结:除了上面三个还有啥鬼畜写法吗
- [1. 切片赋值(Slice Assignment)------ 替换列表中间的一段](#1. 切片赋值(Slice Assignment)—— 替换列表中间的一段)
- [2. `for-else` 和 `while-else` ------ 循环没被 `break` 就执行](#2.
for-else和while-else—— 循环没被break就执行) - [3. 链式比较(Chained Comparison)------ `a < b > c`](#3. 链式比较(Chained Comparison)——
a < b > c) - [4. 海象运算符(Walrus Operator)------ `:=`(Python 3.8+)](#4. 海象运算符(Walrus Operator)——
:=(Python 3.8+)) - [5. 函数参数解包(`*` 和 `**` 在函数调用时)](#5. 函数参数解包(
*和**在函数调用时)) - [6. 下划线 `_` 的多种用途(乱用警告)](#6. 下划线
_的多种用途(乱用警告)) - [7. `zip` + `*` 解包实现矩阵转置(终极鬼畜)](#7.
zip+*解包实现矩阵转置(终极鬼畜)) - 8.作用域for
- [💡 总结:这些"鬼畜"写法的本质](#💡 总结:这些“鬼畜”写法的本质)
-
一、单行 if(条件表达式)
javascript
let status = age >= 18 ? '成年' : '未成年';
python
status = '成年' if age >= 18 else '未成年'
二、多变量赋值(解构)
javascript
let [a, b] = [1, 2]; // 数组解构
let { name, age } = person; // 对象解构
python
a, b = 1, 2 # 元组解构(看起来像没括号,其实右边是 (1, 2))
name, age = person # 但必须按位置对应,不能像 JS 那样按属性名解构
三、最"抽象"的:*args 和 **kwargs
javascript
function sum(...numbers) { // ... 把参数收成数组
return numbers.reduce((a, b) => a + b);
}
python
def sum(*args): # *args 收集成元组
return sum(args) # 这里用了内置 sum,但名字冲突了
总结:除了上面三个还有啥鬼畜写法吗
哈哈,你既然主动问"还有啥鬼畜写法",那我就不客气了------Python 里确实还有一堆"第一次见会怀疑人生"的写法。我挑几个最经典的,按"震惊程度"排序给你看:
1. 切片赋值(Slice Assignment)------ 替换列表中间的一段
写法:
python
a = [1, 2, 3, 4, 5]
a[1:4] = [99, 88] # 把索引 1~3 替换成 [99, 88]
print(a) # [1, 99, 88, 5]
鬼畜在哪?
- 左边是切片(取一段),右边是列表,长度可以不一样!
- 如果右边比左边短,列表会变短;如果右边比左边长,列表会变长。
- 相当于用一段新数据"替换"旧数据,但长度自动调整------像"橡皮泥"一样可以伸缩。
对比 JS :JS 里要 splice(1, 3, 99, 88),专门用一个方法;Python 直接用赋值语法干这事。
2. for-else 和 while-else ------ 循环没被 break 就执行
写法:
python
for i in range(5):
if i == 10:
break
else:
print("循环正常结束,没有被 break")
# 输出:循环正常结束,没有被 break
鬼畜在哪?
else不是和if配对,而是和for/while配对!- 它的逻辑是:如果循环没有被
break中断,就执行else块。 - 用途:查找某个元素,如果找到了就
break,如果没找到就执行else(比如"没找到,显示提示")。
对比 JS :没有这种写法,必须手动加一个 found 标志变量。
3. 链式比较(Chained Comparison)------ a < b > c
写法:
python
x = 5
if 1 < x < 10: # 等价于 1 < x and x < 10
print("在范围内")
鬼畜在哪?
- 你可以在一个表达式里写
a < b > c <= d,Python 会自动拆成多个比较,而且保证每个变量只计算一次。 - 这在数学上很自然(
1 < x < 10是数学写法),但在编程语言里很少见。
对比 JS :JS 不支持链式比较,必须写成 1 < x && x < 10。
4. 海象运算符(Walrus Operator)------ :=(Python 3.8+)
写法:
python
if (n := len([1, 2, 3])) > 2:
print(f"长度是 {n}") # 输出:长度是 3
鬼畜在哪?
- 在
if条件里同时完成赋值和使用。 - 相当于
n = len(...)和if n > 2写在一行。 - 名字"海象"是因为
:=像海象的牙齿和眼睛(:是眼睛,=是牙齿)。
对比 JS :JS 里可以 if ((n = arr.length) > 2),但会警告(赋值在条件里通常被 eslint 禁止)。Python 专门造了个新语法来支持这件事。
5. 函数参数解包(* 和 ** 在函数调用时)
写法:
python
def foo(a, b, c):
print(a, b, c)
lst = [1, 2, 3]
foo(*lst) # 输出:1 2 3(把列表拆成位置参数)
dct = {'a': 1, 'b': 2, 'c': 3}
foo(**dct) # 输出:1 2 3(把字典拆成关键字参数)
鬼畜在哪?
- 同一个
*符号,在函数定义 里是"收集参数",在函数调用里是"展开参数"------含义刚好相反。 **在定义里收集关键字参数,在调用里展开字典。- 新手经常搞混
*args(定义时)和func(*list)(调用时)。
对比 JS :JS 用 ... 同时做收集和展开,符号统一,更好记。
6. 下划线 _ 的多种用途(乱用警告)
用法 1:表示"我不需要这个值"
python
for _ in range(10): # 循环10次,不关心当前值
print("hello")
用法 2:解构时忽略某些位置
python
a, _, b = (1, 2, 3) # a=1, b=3,2被忽略
用法 3:交互式环境里代表上一次表达式的结果
python
>>> 3 + 4
7
>>> _ + 2
9 # _ 自动记住上次结果 7
用法 4:作为变量名(但会被 linter 警告)
python
_ = 5 # 合法,但建议别这么用
鬼畜在哪?
- 同一个
_符号,在不同上下文里有 4 种完全不同的含义,全靠"约定俗成"来区分。
对比 JS :JS 里 _ 只是普通变量名(除非你用 Lodash 库),没有这些"魔法"用法。
7. zip + * 解包实现矩阵转置(终极鬼畜)
写法:
python
matrix = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
]
transposed = list(zip(*matrix))
print(transposed) # [(1, 4, 7), (2, 5, 8), (3, 6, 9)]
鬼畜在哪?
zip把多个列表"拉链"式合并。*matrix把列表展开成 3 个独立的列表参数。- 两者配合,一行代码完成矩阵转置------简洁得令人发指,但第一次看根本看不懂。
对比 JS :JS 需要写嵌套循环或 map 链,至少 3-5 行。
8.作用域for
python
for i in students:
if '老张' in i and '老李' in i:
print(f'找到这个班级名单那了:{i}')
third_class_tuple = tuple(i) # 转换为元组
count_laozhang = third_class_tuple.count('老张') # 统计次数
index_laoli = third_class_tuple.index('老李') # 获取索引下标
# 按照预期格式打印结果
print(f'"老张"在元组中出现了 {count_laozhang} 次')
print(f'"老李"在元组中的索引下标是:{index_laoli}')
third_class_tuple明明在for循环里,难道没有作用域?在for循环结束还能使用?
Python 的 for 循环不会创建新的作用域。在 for 里定义的变量,和在外面定义的变量,处于同一个作用域
Python 的作用域规则:
- 全局作用域(模块级)
- 函数作用域(def / lambda 内部)
- 类作用域(class 内部)
- 推导式作用域(列表推导式、生成器表达式等,Python 3 特有)
注意:for、if、while、try 这些语句块,不会创建新的作用域。
对比一下,可见python的不一样的鬼畜
| 语言 | for 是否创建新作用域 |
备注 |
|---|---|---|
| Python | ❌ 不创建 | 循环变量和循环体内定义的变量,都留在外层作用域 |
| Java | ✅ 创建 | for (int i = 0; ...) 里的 i 只活在循环里 |
| C/C++ | ✅ 创建 | C99 起支持 for (int i = ...),i 只在循环内有效 |
| JavaScript | ✅ 创建(let / const) |
var 不创建,会提升到函数作用域 |
| Go | ✅ 创建 | for i := ... 里的 i 只在循环内有效 |
💡 总结:这些"鬼畜"写法的本质
| 写法 | 核心逻辑 | 为什么"鬼畜" |
|---|---|---|
| 切片赋值 | 用新数据替换旧切片,长度自动适应 | 赋值语法居然能改变列表长度 |
for-else |
循环没被 break 才执行 else |
else 和 if 无关 |
| 链式比较 | a < b > c 自动拆成多个比较 |
数学写法被"硬塞"进编程语言 |
海象 := |
条件里同时赋值和使用 | 增加了新的运算符,改变了 Python 习惯 |
*/** 解包 |
调用时展开列表/字典 | 同一个符号在定义和调用时含义相反 |
_ 多重身份 |
占位符、忽略值、交互式历史、变量名 | 同一个符号 4 种用法 |
zip(*matrix) |
一行矩阵转置 | 语法太密集,读起来像密码 |
这些写法之所以"鬼畜",是因为 Python 的设计哲学是"让代码像英语一样表达意图",而英语本身就是"倒装句 + 省略 + 多义词"的重灾区。 你习惯了就好,而且一旦熟悉了,写起来确实比 JS 省很多行。
如果你有哪一条想让我展开讲的(比如 zip 的底层原理,或者海象运算符的踩坑案例),我可以继续给你细拆。