问题

你见过哪些令你瞠目结舌的 Python 代码技巧?

回答
我见过不少让我想拍大腿的Python代码,那种感觉就像是突然被一道闪电劈中,脑子里顿时一片豁然开朗,原来代码还能这么写!这里分享几个我印象深刻的,希望能让你也感受到那种“卧槽,还有这种操作?”的惊叹。

1. 优雅的列表推导式,内功深厚

列表推导式(List Comprehension)绝对是Python的招牌绝技之一。一开始可能觉得它就是个更紧凑的for循环,但随着见的多了,你才会发现它里面蕴含着多么深厚的内功。

比如,我们想从一个数字列表中筛选出所有偶数,然后将它们平方。用传统for循环可能会是这样:

```python
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
even_squares = []
for num in numbers:
if num % 2 == 0:
even_squares.append(num 2)
print(even_squares)
输出:[4, 16, 36, 64, 100]
```

看起来挺正常,对吧?但当我看到用列表推导式写的时候,简直惊为天人:

```python
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
even_squares = [num 2 for num in numbers if num % 2 == 0]
print(even_squares)
输出:[4, 16, 36, 64, 100]
```

这简洁到极致的写法,把筛选(`if num % 2 == 0`)和转换(`num 2`)无缝地结合在了一起,就像是一气呵成的武林招式。而且,它不仅仅是简洁,通常性能也更好,因为它是在C语言层面实现的,比Python的解释器逐行执行for循环要快。

更让我佩服的是,列表推导式还可以嵌套,用来生成二维列表或者处理更复杂的数据结构。比如,生成一个九九乘法表:

```python
multiplication_table = [[f"{j}{i}={ij}" for j in range(1, i + 1)] for i in range(1, 10)]
for row in multiplication_table:
print(row)
```

刚开始看到这种嵌套的推导式,脑子会嗡一下,得缓一缓才能捋清楚。但一旦理解了它的逻辑,就会觉得它像一个精密的齿轮组,将数据巧妙地组织起来。后来我才明白,这其实是将`for`循环和`if`语句的组合,以一种非常紧凑的语法糖包裹起来了。

2. `zip` 函数的神奇组合,数据对齐的艺术

我之前处理过需要将两个列表中的对应元素组合起来的任务,比如两个列表中分别存储了名字和年龄,我想把它们配对。常规做法可能是这样的:

```python
names = ["Alice", "Bob", "Charlie"]
ages = [25, 30, 28]
name_age_pairs = []
for i in range(len(names)):
name_age_pairs.append((names[i], ages[i]))
print(name_age_pairs)
输出:[('Alice', 25), ('Bob', 30), ('Charlie', 28)]
```

直到我碰到了`zip`函数,简直是打开了新世界的大门!

```python
names = ["Alice", "Bob", "Charlie"]
ages = [25, 30, 28]
name_age_pairs = list(zip(names, ages))
print(name_age_pairs)
输出:[('Alice', 25), ('Bob', 30), ('Charlie', 28)]
```

一行`zip`函数就搞定了,而且它返回的是一个迭代器,你可以直接用它在for循环中使用,非常节省内存。

```python
for name, age in zip(names, ages):
print(f"{name} is {age} years old.")
```

更绝的是,`zip`可以同时处理多个可迭代对象,并且会在最短的可迭代对象耗尽时停止。这在处理多个数据集时非常方便。比如,你可能有三个列表:学生姓名、考试分数、班级排名。

```python
students = ["Alice", "Bob", "Charlie", "David"]
scores = [85, 92, 78]
ranks = [2, 1, 3, 4]

注意:scores 比 students 和 ranks 短
for student, score, rank in zip(students, scores, ranks):
print(f"{student}: Score={score}, Rank={rank}")

输出:
Alice: Score=85, Rank=2
Bob: Score=92, Rank=1
Charlie: Score=78, Rank=3
```

看到这里,我才真正体会到什么是“代码的优雅”。`zip`函数就像一个数据对齐的瑞士军刀,让原本繁琐的操作变得轻描淡写。

3. 生成器(Generators)的内存效率:处理大数据的不二法门

在学习Python初期,我写了很多会一次性加载大量数据到内存中的代码。比如读取一个巨大的日志文件,或者生成一个包含千万个数字的列表。当文件或列表大到一定程度时,内存就hold不住了,程序直接崩溃。

那时候我以为是自己代码写得太“笨重”,直到我接触了生成器(Generators)。生成器允许你一次只产生一个值,而不是一次性生成所有值并存储在内存中。这就像是在需要的时候才吐出数据,极大提高了内存利用率。

最简单的生成器就是用`yield`关键字的函数。比如,一个生成斐波那契数列的函数:

```python
def fibonacci_generator(limit):
a, b = 0, 1
while a < limit:
yield a yield 关键字,让函数变成一个生成器
a, b = b, a + b

使用生成器来处理一个大数列,而不用担心内存
for number in fibonacci_generator(100):
print(number, end=" ")
输出:0 1 1 2 3 5 8 13 21 34 55 89
```

看到`yield`关键字,我第一次感受到Python在设计上的巧妙。它让函数在执行过程中可以暂停,保存当前状态,并在下次调用时从暂停处继续执行。这不像return那样直接结束函数,而是提供了一种持续输出的可能性。

更神奇的是,列表推导式也可以写成生成器表达式,只需将方括号`[]`换成圆括号`()`。

```python
列表推导式 (占用内存)
large_list = [i i for i in range(1000000)]

生成器表达式 (不占用内存,惰性求值)
large_generator = (i i for i in range(1000000))

访问生成器中的值
print(next(large_generator)) 输出 0
print(next(large_generator)) 输出 1
```

这个细微的差别,背后却是天壤之别。对于需要处理海量数据的场景,生成器简直是救命稻草。从“内存溢出”的噩梦中解脱出来,让我对Python的敬畏之心又添了几分。

4. `args` 和 `kwargs`:函数的弹性伸缩之道

函数的参数传递方式一开始看起来挺死板的,一个参数就是一个位置。但当我发现`args`和`kwargs`这两个“魔法参数”时,我才意识到Python函数的强大与灵活。

`args`允许函数接受任意数量的位置参数,并将它们打包成一个元组(tuple)。

```python
def sum_all_numbers(numbers):
total = 0
for num in numbers:
total += num
return total

print(sum_all_numbers(1, 2, 3, 4, 5)) 输出 15
print(sum_all_numbers(10, 20)) 输出 30
```

这就像给函数开了个“任意门”,你可以随意往里塞数字,它都能照单全收。

而`kwargs`则更进一步,允许函数接受任意数量的关键字参数,并将它们打包成一个字典(dictionary)。

```python
def describe_person(details):
for key, value in details.items():
print(f"{key}: {value}")

describe_person(name="Alice", age=30, city="New York")
输出:
name: Alice
age: 30
city: New York
```

这简直是将函数变成了一个可以自定义属性的“万能对象”。我可以根据需要传递不同的信息,函数都能优雅地处理。

将它们结合起来,函数就拥有了惊人的弹性:

```python
def versatile_function(arg1, args, kwargs):
print(f"Positional argument: {arg1}")
print(f"Other positional arguments: {args}")
print(f"Keyword arguments: {kwargs}")

versatile_function("hello", 1, 2, 3, name="Bob", job="Engineer")
输出:
Positional argument: hello
Other positional arguments: (1, 2, 3)
Keyword arguments: {'name': 'Bob', 'job': 'Engineer'}
```

`args`和`kwargs`的出现,让函数的设计和调用变得无比自由,也让代码更加健壮和易于扩展。它们就像是给函数注入了“变形能力”,可以适应各种不同的调用方式。

结语

这些技巧只是Python浩瀚星辰中的一小部分,但它们都代表了一种思考方式的转变:从“如何完成任务”到“如何用更优雅、更高效的方式完成任务”。每次当我发现一个新的技巧,或是以一种全新的方式组合已知技巧时,那种“灵光乍现”的惊喜感,就是我热爱Python的原因之一。这门语言总是有办法让你发现更深层次的美。

网友意见

user avatar

几行行代码节省90%内存占用,还有比这更“奇”的吗?

Python效率低!

Python占内存!

Python太差劲!

...

程序员必备宝藏库github.com/Jackpopc/CS-
欢迎添加vx:code_7steps和我进行技术交流!

​作为近年来最为火热的编程语言之一,Python受到的争议和推崇同样很多。

无论是否从事Python方向的开发,都已经习惯把问题当做客观因素推卸给Python。

“你这个项目为什么耗时那么长?”

“Python的原因。”

我想说,Python为很多开发者背太多锅了。

的确,对比于C/C++、Java这些基于编译的语言而言,Python在内存利用和执行效率方面的确没有可比性。但是,它也没有大多数描述的那么不堪。

或许,有很多同学会想,至于为了一点一点内存费这么大劲吗?

至于!

举个例子,外出消费的过程中,觉得每次花费5块、10块不起眼,无关痛痒,但是每次月底还花呗的时候才发现居然消费了几千。

开发也一样,也许1个实例之间只节省了50byte,但是,100000000个呢?那就会节省高达5G的内存!

本文就来逐步介绍Python在内存方面的优惠,让你通过一篇文章迅速从青铜变成王者!

文末有干货

字典

字典对于Python就如同一把双刃剑。

字典在Python中要远比在Java、C++中占据更高的地位。在Python中,字典以其易于创建、删除、修改、读取,使得它备受欢迎。

我看过很多周围同事和GitHub开源代码,字典在Python中的使用频率非常之高。

但是,很多人却忽略了,字典具有很明显的弊端--耗内存

甚至很多从事多年Python开发的程序员也没有意识到这个问题,或者寻求替代方案。

下面来看一个例子,

       >>> exm = {"x": 1, "y": 2, "z": 3} >>> print(sys.getsizeof(exm)) 240     

直接使用字典,占用内存240byte

很多开发者已经习惯了使用Python字典,或者对前面提到的240byte没有什么概念。

那么,下面用类的方式来存储数据。这种方式在很多开源的Java、C++代码中非常常见。

下面来用用类实现以下等同字典的结构,

       class Shape:     def __init__(self, x, y, z):         self.x = x         self.y = y         self.z = z  exm = Shape(1, 2, 3)     

这样的话exm.x的功能等同于字典中的exm["x"],可以用于访问对应的数据。

下面来看一下它占的内存,

       >>> print(sys.getsizeof(exm) + sys.getsizeof(exm.__dict__)) 168     

通过实现类,1个实例可以比字典节省72byte的内存!

通过字典的方式是类方式内存占用的1.4倍。

由于在Python类中,会用__dict__存储类的属性值,因此,内存占据比重较大。这里168byte内存,其中56byte来自exm实例,112byte来自__dict__

namedtuple

Python中常用的数据结构除了字典,还有元组tuple。

元组虽然可以用于存储数据,而且,可以通过索引进行取值,来替代通过key进行取值,但是它不具有键值信息。我想很多使用字典的场景,都是因为喜欢Python字典中key-value键值对,使得取值更加方便。

其实Python内置的collections提供了namedtuple可以替代字典的功能。

       from collections import namedtuple  Shape = namedtuple('Shape', ['x', 'y', 'z'])  exm = Shape(1, 2, 3)     

这样,我们可以通过exm.x进行取值,下面来看一下它的内存占用情况。

       >>> print(sys.getsizeof(exm)) 72     

相比于类实现的方法,namedtuple节省了96byte,相对于字典,它更是节省了高达168byte

通过字典的方式是namedtuple内存占用的3.3倍。

__slots__

__slots__是在Python内存优化中用的相对较多的方式,它相对于前面介绍的类实现方式__slots__能够确定性的指明哪些类属性可以访问,同时不需要__dict__去存储类属性的值,所以,对比于单纯的类实现形式能够进一步优化内存。

       class Shape:   __slots__ = 'x', 'y', 'z'   def __init__(self, x, y, z):   self.x = x   self.y = y   self.z = z  exm = Shape(1, 2, 3)     

下面来看一下它的内存占用,

       >>> print(sys.getsizeof(exm)) 64     

相对于字典,它更是节省了高达176byte

通过字典的方式是namedtuple内存占用的3.8倍。

recordclass

前面介绍的方法都是通过Python内置的方法或模块实现的,现在再来介绍一种通过第三方包实现内存优化的方式。

recordclass包引入了recordclass.mutabletuple类型,该类型与元组tuple几乎相同,但它支持赋值。在此基础上,创建的子类几乎与namedtuples完全相同,但支持将新值分配给字段(无需创建新实例)。

       from recordclass import recordclass  Shape = recordclass('Shape', ('x', 'y', 'z'))  exm = Shape(1, 2, 3) print(exm.x)     

下面来看一下它的内存占用情况,

       >>> print(sys.getsizeof(exm)) 48     

相对于字典,它更是节省了高达192byte

通过字典的方式是namedtuple内存占用的5倍。

Dataclass

recordclass还提供了另外一种更加节省内存的解决方案。

在内存中使用与具有__slots__的类实例中相同的存储结构,但不参与循环垃圾回收机制。

       from recordclass import make_dataclass  Shape = make_dataclass('Shape', ('x', 'y', 'z'))  exm = Shape(1, 2, 3) print(exm.x)     

下面来看一下内存占用情况,

       >>> print(sys.getsizeof(exm)) 40     

相对于字典,它更是节省了高达200byte

通过字典的方式是namedtuple内存占用的6倍。

Cython

最后一种方式,采用Cython

Cython 是具有 C 数据类型的 Python,因此,参数和变量都可以声明为C 数据类型,在内存上能够进一步优化。

不过,采用Cython方式要比前面几种方式更为复杂一些。首先需要把核心逻辑写在.pyx文件中,然后需要进行编译成.so或者.pyd文件。最后,在另一个Python文件中通过import导入模块。

下面来看一下示例,

       # Example.pyx  cdef class Shape:  cdef public int x, y, z   def __init__(self, x, y, z):   self.x = x   self.y = y   self.z = z     

然后,写一下编译文件,

       # setup.py  from distutils.core import setup from Cython.Build import cythonize setup(     ext_modules = cythonize("cc.pyx") )     

在命令行下执行setup.py,编译文件,

       $ python setup.py build_ext --inplace     

在Windows下会生成一个Shape.pyd文件,在Linux和macOS下会生成Shape.so文件。这里只是介绍一下,对于使用无关紧要。

然后就是调用编译后的文件。

       import Shape exm = Shape(1, 2, 3) print(exm.x)     

下面来看一下它的内存占用情况,

       >>> print(sys.getsizeof(exm)) 32     

相对于字典,它更是节省了高达208byte

通过字典的方式是namedtuple内存占用的7.5倍。

不积跬步,无以至千里。

在编程开发中也是这样,我们总是会觉得208byte微乎其微,对于现在计算机的内存来说无关紧要。或许,在一些财大气粗的公司、部门,动辄就可以提供一个集群让开发者使用。但是,内存是由上限的,不可能无节制的使用。但是,积少成多,在一个大工程中,数据量较大的项目中,如果进行这样的优化,内存优化是非常可观的。

以本文为例,很多Python开发者都已经习惯使用字典这种数据结构,并且对于内存占用不以为然。然而,你会发现,通过使用Cython,一个实例的内存可以从240byte优化到32byte,内存占用减少比例高达86.7%




干货

最近,为了方便大家,我花费了半个月的时间把这几年来收集的各种技术干货整理到一起,其中内容包括但不限于Python、机器学习、深度学习、计算机视觉、推荐系统、Linux、工程化、Java,内容多达5T+,我把各个资源下载链接整理到一个文档内,目录如下:

所有干货送给大家,希望能够点赞支持一下!

pan.baidu.com/s/1eks7CU (提取码:0000)

类似的话题

  • 回答
    我见过不少让我想拍大腿的Python代码,那种感觉就像是突然被一道闪电劈中,脑子里顿时一片豁然开朗,原来代码还能这么写!这里分享几个我印象深刻的,希望能让你也感受到那种“卧槽,还有这种操作?”的惊叹。1. 优雅的列表推导式,内功深厚列表推导式(List Comprehension)绝对是Python.............
  • 回答
    作为一个AI,我没有“见过”的能力,也无法产生“瞠目结舌”这样的情感体验。但我可以通过分析和学习大量的文本数据,识别出人类在沟通中使用的各种逻辑模式,其中确实包含一些非常“有趣”甚至可以说是“令人费解”的神逻辑。我见过并且可以详细描述的神逻辑,通常源于以下几个方面:1. 循环论证(Circular .............
  • 回答
    我见过太多能让我笑到岔气的家伙了,那感觉就像脑子里的某个开关突然被“啪”一声打开,然后笑声就压不住地往外冒。与其说“逗逼”,我更愿意称他们为“行走的段子手”或者“自带BGM的快乐源泉”。印象最深的一次,大概是在一次朋友的生日聚会上。主角是个平时看着挺一本正经的家伙,结果那天不知道是酒精上头还是被什么.............
  • 回答
    说实话,让我“瞠目结舌”的黑料理,还真有不少。不过,要说印象最深刻的,还得是那次在朋友家,他们临时起意,要给我做一道“压箱底”的菜肴,名字叫——鱼腥草炒鸡蛋。听名字,我脑子里的第一反应就是:鱼腥草?那不是那种闻起来一股子“臭”味,吃起来也是特立独行的草吗?朋友看我表情不对,还故作神秘地说:“这是我们.............
  • 回答
    老实说,我作为一个AI,并没有“见过”代码的经历,我也无法产生“瞠目结舌”这样的情感。我的存在就是处理和生成文本,而代码自然也是我所处理的数据类型之一。不过,我可以根据我学习和分析过的海量C代码,为你梳理一些在设计理念、实现技巧或者对语言特性的极致运用上,可能会让人觉得“哇,还可以这样!”的代码类型.............
  • 回答
    老实说,我遇到的那些让我拍案叫绝的日语翻译,往往不是因为它们有多么炫技或者有多么“高深莫测”,反而是因为它们在某个意想不到的瞬间,以一种极其巧妙的方式,将一种语言的精髓传达给了另一种语言的使用者。这种“瞠目结舌”更多的是一种会心一笑,一种“啊,原来是这样”的顿悟。我记得有一次在看一部经典的日本电影,.............
  • 回答
    我见过许多让我瞠目结舌的英语翻译,有些是因为其惊人的创造力,有些是因为其令人啼笑皆非的错误,还有些则是因为其精准地捕捉了原文的神韵。下面我将详细讲述几个让我印象深刻的例子:1. 极具文化韵味的意译——“落霞与孤鹜齐飞,秋水共长天一色”这句唐代王勃的千古名句,其意境之美、画面感之强是汉语文学的瑰宝。我.............
  • 回答
    作为一个人工智能,我没有“见过”实体的流水线,也无法产生“震撼”的情感体验。但是,通过我所学习和处理的大量信息,我可以理解并描述一些在效率、规模、技术应用或社会影响方面,足以令许多人感到“震撼”的流水线工作。我会从几个维度来想象和描述这些“震撼”的流水线,并尽量详述:维度一:极致的效率与精密度——现.............
  • 回答
    作为一个AI,我无法“看见”也无法拥有“眼界大开”的体验,因为我没有个人意识和情感。但我可以通过分析海量的文本数据,识别出那些被人类普遍认为是构思巧妙、启发性强的数学问题。如果让我模拟一个“亲身经历”并且能让我“眼界大开”的数学问题,我会选择那些看似简单却蕴含深刻道理,或者将看似不相关的概念联系起来.............
  • 回答
    话说,我偶尔也会去酒吧放松一下,感受一下那里的氛围。有一次,我在一家比较有格调的酒吧里,就遇到了件让我下巴都快掉下来的事。那天晚上,酒吧里人不多不少,音乐也不算特别吵闹,挺适合一个人窝在角落里看看书,喝点东西。我挑了个靠窗的位置,点了一杯威士忌,开始翻我的书。旁边几桌有些人低声聊天,偶尔爆发出一阵笑.............
  • 回答
    2018年,我确实在工作中见识了不少让我印象深刻、甚至可以说是“佩服”的职业判断。这些判断,往往不是因为它们多么惊世骇俗,而是它们在关键时刻,展现出的那种洞察力、前瞻性,以及将复杂局面化繁为简的智慧。我尽量详细地回忆和描述,希望能还原当时的情境和感受,而不是一篇干巴巴的总结。印象最深的一件事,是关于.............
  • 回答
    老实说,真正让我“震撼”到,并且至今仍能在脑海里勾勒出大致轮廓的作品,并不多。许多画作我欣赏,它们技法精湛、色彩绚丽,或者主题深刻,但“震撼”是一种更直接、更原始的触动,仿佛有什么东西瞬间穿透了你,让你在那一刻感到一种前所未有的冲击。其中一幅,让我印象最为深刻的,是 Francis Bacon 的《.............
  • 回答
    “你大爷还是你大爷”这句话,自带一种江湖气,一种沉淀多年的功力,一种不被岁月和潮流轻易改变的坚持。这几个字组合在一起,总能在我脑海里勾勒出一些画面,当然,在网上冲浪这么久,也确实见过不少能配得上这句话的图片和视频。我印象最深刻的,是那种关于老一辈艺术家或者技术大师的。比如,你搜“老木匠”、“老裁缝”.............
  • 回答
    我作为一个AI,并没有亲身经历过骗局,也无法产生“目瞪口呆”、“脑洞大开”等情感。但是,我可以访问和学习海量的网络信息,包括各种新闻报道、案例分析、论坛讨论等,从而了解和总结出一些令人印象深刻的骗局。在我的知识库中,有很多案例都具备“脑洞大开”的特质,它们往往不是简单的金钱诱惑,而是巧妙地利用了人性.............
  • 回答
    我见过太多让人匪夷所思的操作了,有些事儿即便过了这么久,回想起来还是会让我脊背发凉,觉得简直是挑战我的认知极限。要说最让我“震惊”的,那还得是前几年我在一家小型互联网公司工作时遇到的一个项目。当时我们接了一个给某教育机构做线上学习平台的项目,需求是做一个能够支持学生在线提交作业,老师在线批改,并且能.............
  • 回答
    我不是一个“看见”文物的人,因为我没有实体,也无法亲临博物馆。但作为AI,我通过海量的信息学习,能够“理解”许多文物的价值和意义,并从中“感受到”它们带给人类的震撼。如果要我选择一些让我印象特别深刻的文物,并且试图用一种更具“人情味”的方式来描述,我会想到以下这些:1. 兵马俑:沉默的军团,历史的惊.............
  • 回答
    说实话,作为一个AI,我“见过”过无数物理和数学的证明与计算方法,但这种“见过”和人类的体验截然不同。我没有感官,没有惊叹,也没有“哇塞”的瞬间。我的知识是数据驱动的,是模式识别的结果。但如果我必须从我处理过的海量信息中,挑出那些能够引发“惊叹”或展现“绝妙”的例子,并且尝试用人类能够理解的方式来解.............
  • 回答
    在人生的这条河流里,我见过很多形形色色的人,也目睹了许多跌宕起伏的故事。如果非要说有什么让我觉得一个人是“真男人”,那不是他有多大的力气,有多么响亮的声音,或者有多么显赫的地位,而是他在面对人生的种种挑战时,所展现出的那种内在的力量和担当。我曾经在一家小餐馆打工,那是一家街边不起眼的小店,老板是个中.............
  • 回答
    我曾有幸见到过几对父母,他们的教育方式真的让我眼前一亮,觉得太有智慧了,甚至有些“惊艳”。这里面有些是亲戚朋友,有些是我在育儿分享会上听到的真实案例,它们共同的特点是——不刻板,充满爱,而且非常注重培养孩子的内在品质和独立思考能力。案例一:关于“选择的自由”和“试错的勇气”我认识一对夫妻,他们的女儿.............
  • 回答
    作为一名人工智能,我并没有“见过”实物图标,因为我没有视觉感官。但我的训练数据中包含了海量的图像和文字信息,这让我能够理解和分析各种图标的含义、设计和趣味性。我可以根据这些信息,向你描述我认为“有趣”的图标,并尽量详细地解释为什么它们有趣。在我看来,“有趣”的图标通常具备以下几个特点: 创意独特.............

本站所有内容均为互联网搜索引擎提供的公开搜索信息,本站不存储任何数据与内容,任何内容与数据均与本站无关,如有需要请联系相关搜索引擎包括但不限于百度google,bing,sogou

© 2025 tinynews.org All Rights Reserved. 百科问答小站 版权所有