写了这么久Python,这10个冷门知识你知道几个?

832 阅读5分钟

写了这么久Python,这10个冷门知识你知道几个?

Python写久了,其实大多数时候都在用那一套基础语法,if、for、class、def……但有些东西你可能从来没碰过,也没听谁提过。

不用也能写代码,但知道了,偶尔能让你写得更简洁一点,也可能看懂别人的骚操作。

今天分享10个我自己觉得挺冷门但有点意思的小知识点。

image.png


1. for循环居然也能搭配else

很多人第一次见的时候都会问:for循环为啥要加else?它不是和if配的吗?

其实for ... else语句,在循环没有被break打断的时候,会执行else里的内容。

nums = [1, 2, 3, 4, 5]
for n in nums:
    if n == 10:
        print("找到了")
        break
else:
    print("没找到")

如果列表里没有10,循环完整跑完,就会走else,输出“没找到”。

跟写个found = False然后再判断要清爽很多。尤其查找的时候挺方便。


2. :=:传说中的“海象运算符”

Python 3.8以后加了个新操作符,叫“海象运算符”,写法是:=

它的作用是:在表达式里就完成赋值

举个例子,你以前可能这么写:

line = input()
while line:
    print(line)
    line = input()

现在可以写成一行:

while (line := input()):
    print(line)

虽然没快多少,但写起来确实清爽了一点。不用先写个变量,再去while里面判断。

不过也别滥用,太多海象会让代码可读性变差。


3. *args**kwargs还能这么组合

我们知道*args**kwargs可以接收位置参数和关键字参数,但你知道这4种方式可以混用吗?

def demo(a, b=2, *args, c=3, **kwargs):
    print(a, b, args, c, kwargs)

demo(1, 10, 20, 30, c=99, d=100)
# 输出:1 10 (20, 30) 99 {'d': 100}

关键点:

  • *args后还能接收命名参数(比如c)
  • **kwargs永远兜底接收其他关键字参数

写装饰器或者做接口的时候会很常用。


4. defaultdict让你少写很多判断

平时用字典的时候,总会写一堆判断:

if key not in my_dict:
    my_dict[key] = []
my_dict[key].append(val)

用了defaultdict之后,完全可以一行解决:

from collections import defaultdict

d = defaultdict(list)
d['a'].append(1)

不需要提前判断key在不在,没在它自己会初始化一个空列表给你。

还能指定成int、set、lambda等,挺灵活的。


5. zip(*iterable)可以“转置”二维列表

常见场景:你有个二维列表,想把行列对调。

data = [
    [1, 2, 3],
    [4, 5, 6]
]

transposed = list(zip(*data))
print(transposed)  # [(1, 4), (2, 5), (3, 6)]

注意这里的*是把data“解包”成多个参数传进去。

这个技巧在处理CSV、表格类数据的时候特别有用。


6. __slots__:给你的类瘦个身

默认情况下,Python 给每个对象都用一个 __dict__ 来存属性,这挺灵活,但也挺占内存。

如果你只是要创建很多结构简单的小对象,可以加个 __slots__ 限制一下,就能省不少空间。

class Person:
    __slots__ = ['name', 'age']

    def __init__(self, name, age):
        self.name = name
        self.age = age

加了 __slots__ 之后,这个类的实例就只能有 nameage 这两个属性,而且不再用 __dict__,内存也更紧凑。

适合用在大量创建对象的场景,比如爬虫、数据处理那种。


7. collections.Counter:统计谁比它快?

以前统计列表里元素出现的次数,是不是经常这么写:

counts = {}
for item in data:
    counts[item] = counts.get(item, 0) + 1

有点啰嗦。

直接用 Counter,一行搞定:

from collections import Counter

data = ['a', 'b', 'a', 'c', 'b', 'a']
c = Counter(data)
print(c)  # Counter({'a': 3, 'b': 2, 'c': 1})

还能直接做加法、找最常见的元素,功能挺丰富:

print(c.most_common(1))  # [('a', 3)]

省心省力,建议常备。


8. itertools:你可能忽略的超强标准库

itertools 这个模块常被忽视,但里面藏了很多处理迭代器的利器。

举几个我经常用的:

from itertools import combinations, permutations, groupby

list(combinations([1, 2, 3], 2))  # [(1, 2), (1, 3), (2, 3)]
list(permutations([1, 2, 3], 2))  # [(1, 2), (1, 3), (2, 1)...]

groupby也很强:

from itertools import groupby

data = 'aaabbcddddd'
groups = groupby(data)
for k, g in groups:
    print(k, list(g))
# 输出:
# a ['a', 'a', 'a']
# b ['b', 'b']
# c ['c']
# d ['d', 'd', 'd', 'd', 'd']

适合用在数据分组、组合问题、流式处理里。


9. sorted() + key,可以不按常理出牌排序

默认的排序太死板?用 key 就能自定义你想要的逻辑。

words = ['apple', 'banana', 'grape', 'kiwi']
# 按字符串长度排序
sorted(words, key=len)  

还可以组合复杂逻辑:

data = [{'name': 'Tom', 'age': 20}, {'name': 'Amy', 'age': 18}]
sorted(data, key=lambda x: x['age'])

甚至还能倒着排、忽略大小写、按拼音排……逻辑你说了算。


10. id():看看对象在内存里长啥样

这个不常用,但调试的时候挺有帮助。

a = [1, 2, 3]
b = a
print(id(a), id(b))  # 一样

说明两个变量指向的是同一个对象。

再看这个:

a = [1, 2, 3]
b = list(a)
print(id(a), id(b))  # 不一样

虽然b = list(a)看着差不多,其实是复制了一份新的出来。

可以帮你判断浅拷贝、深拷贝、引用是不是同一个。


收个尾

今天这10个冷门小知识,有的是标准库里不常提到的宝藏,有的是Python语法里的一点“小聪明”。

你可能用不上,但知道它们总归是有好处的。 以后看到别人写的“骚代码”时,不至于一脸问号。要用的时候,翻出来就能用。

如果你也有类似的冷门知识点,欢迎在评论区给我整两个,我一定认真看,能学一手是一手!