page contents

10 个让我豁然开朗的Python概念

当我初次接触Python时,我以为一年时间就能掌握它。简单的语法、友好的社区、无限的教程——我似乎什么都不缺。但随着时间推移,我意识到自己在一些基础概念上存在误解。

attachments-2025-11-f9Vl0cre691d1edcc52f3.png当我初次接触Python时,我以为一年时间就能掌握它。简单的语法、友好的社区、无限的教程——我似乎什么都不缺。但随着时间推移,我意识到自己在一些基础概念上存在误解。

最困难的是什么?这些并非元类或编写Python编译器那样高大上的主题,而是我多年来一直理解错误或遗忘的基础知识。每次正确理解它们后,头脑中的障碍就消失了。

今天,我将分享花了数年才理解的10个Python概念,以及最终让我豁然开朗的简单示例。

1. 可变对象 vs 不可变对象

这是我遇到的第一个经典Python陷阱。我曾经无法理解为什么我的列表在函数内部会神奇地改变。

def add_item(items, value):
    items.append(value)
    return items

my_list = [1, 2, 3]
add_item(my_list, 4)
print(my_list)  # [1, 2, 3, 4] - 原列表被修改了!

关键在于:

不可变类型(int、float、str、tuple)→ 修改时会创建新对象

可变类型(list、dict、set)→ 修改时会改变原对象

理解这个区别后,调试代码变得容易多了。

2. 默认可变参数

这个问题我记了很多年。可变默认参数会导致"奇怪"的行为。

def add_to_list(value, items=[]):
    items.append(value)
    return items

print(add_to_list(1))  # [1]
print(add_to_list(2))  # [1, 2] - 不是新的列表!

解决方法:

def add_to_list(value, items=None):
    if items is None:
        items = []
    items.append(value)
    return items

解释是:默认参数在函数定义时被计算,而不是每次调用时。

3. Python的对象引用传递

我曾经长期争论Python是"传引用"还是"传值"。实际上是按对象引用传递。

变量只是对象的标签

函数参数是同一对象的新名字

def modify(num):
    num += 1
    print("Inside:", num)

x = 5
modify(x)
print("Outside:", x)  # 仍然是5

对于不可变值,表现像传值;对于可变对象,表现像传引用。这种细微差别让我困扰了很久。

4. is 与 == 的区别

我曾互换使用这两个操作符,直到因此出错。

a = [1, 2]
b = [1, 2]
print(a == b) # True (值相等)
print(a is b) # False (不同对象)

== → 比较值

is → 比较对象标识

这在处理None时尤其重要。**始终使用is None,而不是== None**。

5. 迭代器和生成器

我使用Python多年后才真正理解迭代器。关键在于__next__和__iter__方法。

my_iter = iter([1, 2, 3])
print(next(my_iter)) # 1
print(next(my_iter)) # 2

生成器简化了这一过程:

def countdown(n):
    while n > 0:
        yield n
        n -= 1

for i in countdown(3):
    print(i)  # 输出 3, 2, 1

理解了生成器后,我才明白为什么它们不将所有内容都保留在内存中。

6. 列表推导式 vs 生成器表达式

我最初以为它们是一样的,其实不是。

# 列表推导式:在内存中构建完整列表
squares = [x*x for x in range(5)]

# 生成器表达式:惰性计算,一次只产生一个值
squares_gen = (x*x for x in range(5))

处理大型数据集时,内存使用至关重要。

7. 上下文管理器(with语句)

我曾经这样写:

f = open("data.txt")
data = f.read()
f.close()

直到有人告诉我这很危险。如果出现错误,文件可能永远不会关闭。上下文管理器解决了这个问题:

with open("data.txt") as f:
    data = f.read()

好处是:即使发生错误,with语句也能保证执行清理工作。你还可以使用__enter__和__exit__创建自己的上下文管理器。

8. *args 和 **kwargs 的强大之处

我曾经因为觉得吓人而避免使用它们。但它们其实就是动态参数解包。

def demo(a, *args, **kwargs):
    print("a:", a)
    print("args:", args)
    print("kwargs:", kwargs)

demo(1, 2, 3, x=4, y=5)

输出:

a: 1
args: (2, 3)
kwargs: {'x': 4, 'y': 5}

一旦理解了这个概念,编写可重用函数就变得更容易了。

9. 装饰器

我长期模仿使用装饰器却不理解其原理,直到看到这个示例:

def log(func):
    def wrapper(*args, **kwargs):
        print(f"Calling {func.__name__}")
        return func(*args, **kwargs)
    return wrapper

@log
def greet(name):
    print(f"Hello, {name}")

greet("Python")

装饰器只是接受函数并返回函数的函数。理解了这一点,你就不再会觉得装饰器是"魔法"了。

10. __name__ == "__main__"的用途

我曾以为这行代码只是样板文件,直到理解了它的目的:

def main():
    print("Running as script!")

if __name__ == "__main__":
    main()

它让你能够将文件同时作为模块和脚本来运行。因此,任何严肃的Python项目都会包含这行代码。

写在最后

这些概念一旦理解后似乎很明显,但我保证——它们最初对我来说并不明显。每一个都代表了我Python经验中的"顿悟时刻",突然间对事物有了更深层次的理解。

如果你仍然对其中一些概念感到困惑,别担心——这很正常。关键是不断尝试示例,直到理解为止。因为在编程中,一个好的示例可以改变你一生的编码方式。

Python激发好奇心。你探索的特性越多,你的代码就越优雅。不要死记硬背语法——要去实验、破坏它并从中学到东西。

更多相关技术内容咨询欢迎前往并持续关注好学星城论坛了解详情。

想高效系统的学习Python编程语言,推荐大家关注一个微信公众号:Python编程学习圈。每天分享行业资讯、技术干货供大家阅读,关注即可免费领取整套Python入门到进阶的学习资料以及教程,感兴趣的小伙伴赶紧行动起来吧。

attachments-2022-05-rLS4AIF8628ee5f3b7e12.jpg

你可能感兴趣的文章

相关问题

0 条评论

请先 登录 后评论
Pack
Pack

1543 篇文章

作家榜 »

  1. 轩辕小不懂 2403 文章
  2. 小柒 2228 文章
  3. Pack 1543 文章
  4. Nen 576 文章
  5. 王昭君 209 文章
  6. 文双 71 文章
  7. 小威 64 文章
  8. Cara 36 文章