专栏文章

Python 从入门到入土之奇技淫巧(二)

科技·工程参与者 1已保存评论 0

文章操作

快速查看文章及其快照的属性,并进行相关操作。

当前评论
0 条
当前快照
1 份
快照标识符
@mio8a44w
此快照首次捕获于
2025/12/02 14:59
3 个月前
此快照最后确认于
2025/12/02 14:59
3 个月前
查看原文
在绝大多数情况下,内置函数能实现的,就不要用手写(除非你想学习它的原理)。也许你也能写出来,但效率往往会差一些——因为它们往往已经被一群天才优化到了极致。
不过在 Python 这里,手写也许会慢非常多,为什么呢?
众所周知,Python 是一门高级的解释性语言,例如 CPython 解释器使用 C 语言实现的。显然,高级语言的效率远低于低级语言,而 Python 的内置函数正是由解释器实现的,所以会比手写快得多。
因此,学习内置函数还是挺重要的,好比学 C++ 要学 STL,你也不想为了一个 map 手写红黑树吧
Python 中内置的函数、类、常量等等,一共有 161 个 *(3.13.1 版本),但我们这一期只讲常用的函数。
* 由 import builtins; print(len(dir(builtins))) 得到

all & any

all 和 any 都接受一个可迭代对象,并返回一个 bool 值。
函数判断条件
all可迭代对象中不存在False
any可迭代对象中存在True
注意
所谓的 TrueFalse,是指将元素转为 bool 值(bool(x))的结果。
特别地,当可迭代对象没有任何元素时,all 和 any 分别返回 TrueFalse

进制转换

进制转换有 bin、oct 和 hex 函数,它们接受一个整数,返回一个对应进制下这个整数的表示,包含前缀。
函数进制前缀
bin二进制0b
oct八进制0o
hex十六进制0x
int 函数(准确来说是 int 类的构造函数)单列,它比较特殊,有多种用法,但均返回一个整数。
  • int(str) \longrightarrowstr 视为十进制数。
  • int(str, base) \longrightarrowstr 视为 base 进制数,base 默认为 1010,在 2362\sim36 范围内,str 不包含前缀。
  • int(str, base=0) \longrightarrow 自动识别进制,要求 str 包含前缀。

chr & ord

chr 接受一个整数,返回这个码点在 Unicode 中对应的字符。
ord 则相反,将一个字符转换为 Unicode 码点。

divmod

小学学的带余除法,接受两个数,返回一个元组,包含它们相除的商和余数,即 (x // y, x % y)

enumerate

enumerate 是一个可迭代对象,构造函数接受一个可迭代对象,可以在遍历时返回一个包含下标和值的元组(这点比 C++ 方便多了)。
PYTHON
l = 'abcde'
for i, val in enumerate(l):
    print(i, val)
# 0 a
# 1 b
# 2 c
# 3 d
# 4 e

eval & exec

eval 和 exec 均接受一个字符串或字节串(bytes),还有一种用法较罕见故不提及。
eval,即 evaluate,就是将字符串看作表达式,返回它的值,可以水一些表达式计算。
而 exec,就是当作正常代码执行,无返回值(None)。
eval:
PYTHON
print(eval('1+1'))  # 2
print(eval('[1, 2, 3]'))  # [1, 2, 3]
eval('print("Hello World")')  # Hello World

print(eval('print("Hello World")'))
# Hello World
# None

# 别忘了 eval 是表达式求值
eval('a = 114514') # SyntaxError: invalid syntax
print(a)  # NameError: name 'a' is not defined
exec:
PYTHON
print(exec('1+1'))  # None
print(exec('[1, 2, 3]'))  # None
exec('print("Hello World")')  # Hello World

print(exec('print("Hello World")'))
# Hello World
# None

exec('a = 114514')
print(a)  # 114514

exit

与 C++ 类似,接受一个整数作为返回值,并退出程序。

filter

filter 实际上是一个类,顾名思义它是一个过滤器,它的构造函数有且仅有两个参数:一个可调用对象(callable)和一个可迭代对象(iterable)。它会保留可调用对象处理后为 True 的元素。
PYTHON
l = [i for i in range(10)]
f = filter(lambda x: x & 1, l)
print(f)  # <filter object at 十六进制地址>
print(list(f))  # [1, 3, 5, 7, 9]

hash

哈希函数,接受一个不可变对象,返回整数哈希值。
PYTHON
print(hash(114514))  # 114514
print(hash(114514.0))  # 114514
print(hash(114514.1919810))  # 442678046743445330
print(hash('114514'))  # 2469581522774740131

print(hash([1, 2, 3, 4, 5]))  # TypeError: unhashable type: 'list'
print(hash((1, 2, 3, 4, 5)))  # -5659871693760987716

print(hash({1, 2, 3, 4, 5}))  # TypeError: unhashable type: 'set'
print(hash(frozenset({1, 2, 3, 4, 5})))  # -3779889356588604112
# frozenset 是 set 的不可变版本,除了不能修改以外与 set 均相同。

id

获取对象的地址。
上期中我们讲到 Python 中对象常常以引用形式传递,那么我们怎么验证一个对象是否是另一个对象的引用呢?
PYTHON
a = [1, 2, 3]
b = a
c = [1, 2, 3]
print(id(a) == id(b))  # True,说明b是a的引用
print(id(a) == id(c))  # False,说明c不是a的引用
另外,Python 会将小整数存储到一个池子里,所以你会发现:
PYTHON
a = 1
b = 1
print(id(a) == id(b))  # True
PYTHON
a = 114514
b = 114514
print(id(a) == id(b))  # False
PYTHON
print((id(256) - id(0)) / 32)  # 256.0
print((id(257) - id(0)) / 32)  # -4319257184724.75(因人而异)
print((id(0) - id(-5)) / 32)  # 5.0
print((id(0) - id(-6)) / 32)  # 4348471359369.75(因人而异)
PYTHON
t = 114514, 114514
print(id(t[0]) == id(t[1]))  # True
# ?
t = (1, 2), (1, 2)
print(id(t[0]) == id(t[1]))  # True
# ??
t = [1, 2], [1, 2]
print(id(t[0]) == id(t[1]))  # False
# ???

iter & next

iter 有两种用法,第一种是获取可迭代对象的一个迭代器。
PYTHON
i = iter([1, 2, 3])
print(next(i))  # 1
print(next(i))  # 2
print(next(i))  # 3
print(next(i))  # StopIteration
print(next(i))  # StopIteration
第二种接受一个可调用对象(callable)和一个值(sentinel),可以一直 next,直到可调用对象的返回值等于 sentinel。
PYTHON
i = 0
def f():
    global i
    i += 1
    print(i)
    return i
it = iter(f, 5)
print(next(it))  # 1
print(next(it))  # 2
print(next(it))  # 3
print(next(it))  # 4
print(next(it))  # StopIteration
print(next(it))  # StopIteration
print(i)  # 5
实际上,for 的本质就是一个 while 循环在不断地执行 next,直到遇到 StopIteration。
PYTHON
for i in l:
    print(i)
# 等价于
it = iter(l)
while 1:
    try:
        i = next(it)
        print(i)
    except StopIteration:
        break
小寄巧PYTHON
for i in iter(input, ''):
    print(i)
这段代码会不断读取输入,直到输入为空行。

map

上一期中我们多次看到了 map,这东西到底有啥用呢?
首先,我们需要澄清一下,map 不是 C++ 中的有序键值对容器(map),也不是字典(dict),它就是「映射」,而且它看起来像函数,实则是一个类
map 的构造函数有且仅有两个参数:一个可调用对象(callable)和一个可迭代对象(iterable)。它会将可迭代对象中的每个元素都映射到经过可调用对象处理的元素
Warning
不要忘了 map 是一个类,也是可迭代对象,它的类型并不与你扔进去的可迭代对象相同,必要时需要进行转换
举个例子:
PYTHON
l = [i for i in range(1, 6)]
l = map(lambda x: x * 2, l)
print(l)
# <map object at 十六进制地址>
# ???
print(list(l))
# [2, 4, 6, 8, 10]
# Wonderful Answer!
再举个更经典的例子:
PYTHON
l = list(map(int, input().split()))
上一期中讲到,input 函数只能读入整行字符串,所以遇到一行多个整数时,我们需要先使用 str.split 方法将读入的字符串按空格分隔,得到一个包含多个字符串的列表,再使用 map 将每个字符串都映射到它的整数形式,最后用 list 将其转换为列表。

max & min

This does what you think it does. —— C++ STL 中 max / min 函数的文档
max / min 可以传入一个可迭代对象或者多个参数,与 sort 类似,可以加上一个 key 函数。
PYTHON
print(max(1, 2, 3, 4, 5))  # 5
print(max('abcdefg'))  # g
print(max('abc', 'abcdef'))  # abcdef

print(hash('abc'))  # -2837500452362813077
print(hash('abcdef'))  # 6448430210802371289
print(max('abc', 'abcdef', key=lambda x: hash(x)))  # abcdef

pow

通常情况下等效于 ** 运算符,但不同的是 pow 函数可以传入一个模数。
PYTHON
P = 998244353
# 乘法逆元
def inverse(x):
    return pow(x, P - 2, P)
print(114514 * inverse(114514) % P)  # 1

range

每次循环你几乎都会用到 range,但你真的了解它吗?
PYTHON
print(list(range(5)))  # [0, 1, 2, 3, 4]
print(list(range(1, 6)))  # [1, 2, 3, 4, 5]
print(list(range(0, 10, 2)))  # [0, 2, 4, 6, 8]

print(list(range(10, 0)))
# []
# ???
print(list(range(10, 0, -1)))
# [10, 9, 8, 7, 6, 5, 4, 3, 2, 1]
# Wonderful Answer!
range(start, stop, step) 相当于 C++ 中的:
CPP
template<typename T>
vector<T> range(T start, T stop, T step = 1) {
  vector<T> result;
  for (T i = start; i < stop; i += step) {
    result.push_back(i);
  }
  return result;
}
警钟撅烂PYTHON
for i in range(5):
    print(i)
    i += 114514
    print(i)
# 0
# 114514
# 1
# 114515
# 2
# 114516
# 3
# 114517
# 4
# 114518
不要学其他语言学傻了,range 是一个生成器。上文中有讲过,i 只是对其调用 next 的结果,对 i 的修改只持续到这一轮循环结束。

repr

将对象转换为开发者友好的字符串。
PYTHON
print(repr('Hello World\n'))
# 'Hello World\n'
print('Hello World\n')
# Hello World
# [滚木]

reversed

这是一个普普通通的可迭代对象。
PYTHON
a = [1, 2, 3, 4, 5]
别急,有反转。
PYTHON
print(reversed(a))
# <list_reverseiterator object at 十六进制地址>
print(list(reversed(a)))
# [5, 4, 3, 2, 1]
for i in reversed(a):
    print(i)
# 5
# 4
# 3
# 2
# 1

round

顾名思义,四舍五入。但 Python 很有良心的一点在于它可以自定义保留几位小数。
PYTHON
print(round(1.14514))  # 1
print(round(1.919810))  # 2
print(round(1.14514, 2))  # 1.15
print(round(1.919810, 2))  # 1.92

zip

PYTHON
print(list(
    zip('abcdefg', range(3), range(4))
))
# [('a', 0, 0), ('b', 1, 1), ('c', 2, 2)]
你问我这有什么用?……它可以用来压缩键值对。
PYTHON
print(dict(zip('abcde', range(5))))
# {'a': 0, 'b': 1, 'c': 2, 'd': 3, 'e': 4}

__import__

PYTHON
import numpy as np
相当于
PYTHON
np = __import__('numpy')

结语

这一期,我们一共讲了 23 个内置函数,接下来还会讲内置的类以及标准库。但看过 Python 官方文档的都知道,内置及标准库的目录整整有有 12 页(相当于按 11 下 PageDown)。这么多内容我讲不完,你们也记不住,但事实上你只要学会其中的 20%20\%,就能实现 80%80\% 的功能。至于剩下 20%20\% 的功能?管他嘞,真用到了再说吧。
由于这个系列属于梦到什么说什么的那种,大家有什么想听的内容,也欢迎在评论区提出,希望这个系列能真正地呼应标题。

评论

0 条评论,欢迎与作者交流。

正在加载评论...