数据STUDIO

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

Image

Image
编程中的理解,有时只需一个好例子

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

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

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

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

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

defadd_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. 默认可变参数

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

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

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

解决方法:

defadd_to_list(value, items=None):
if items isNone:
        items = []
    items.append(value)
return items

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

3. Python的对象引用传递

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

  • 变量只是对象的标签
  • 函数参数是同一对象的新名字
defmodify(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

生成器简化了这一过程:

defcountdown(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 的强大之处

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

defdemo(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. 装饰器

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

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

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

greet("Python")

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

10. __name__ == "__main__"的用途

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

defmain():
    print("Running as script!")

if __name__ == "__main__":
    main()

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

写在最后

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

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

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

你还遇到过哪些让你豁然开朗的Python概念?欢迎在评论区分享!

🏴‍☠️宝藏级🏴‍☠️ 原创公众号『数据STUDIO』内容超级硬核。公众号以Python为核心语言,垂直于数据科学领域,包括可戳👉Python|MySQL|数据分析|数据可视化|机器学习与数据挖掘|爬虫等,从入门到进阶!

长按👇关注- 数据STUDIO -设为星标,干货速递ImageImage