嘿,各位写代码的朋友们,咱们今天聊聊一个特基础,但又无处不在的事儿——拼接。你写 Python,哪怕是最简单的脚本,十有八九都得跟“拼接”打交道。说白了,就是把一堆零零散散的东西,比如字符串、列表里的元素、字典里的键值对,给“粘”一块儿,变成一个新的整体。
这事儿听起来挺简单,不就是个加号(+
)的事儿嘛?刚开始学 Python 的时候,我也这么想,觉得 +
真是个万能灵药。两个字符串 "hello" + " world"
,得了,"hello world"
出来了。两个列表 [1, 2] + [3, 4]
,好家伙,[1, 2, 3, 4]
也齐活儿。那时候,我感觉自己掌握了宇宙真理,写啥都想着用 +
去拼。
但写着写着,就遇上事儿了。有一次我要处理一大堆从文件里读出来的文本行,大概几十万行吧,想把它们拼成一个大字符串。我理所当然地用了个循环,然后一行一行地用 +
往一个空字符串里加。刚开始还好,等行数一多,那速度简直了……就像蜗牛爬,眼瞅着程序在那里吭哧吭哧地跑,半天没反应。我当时都懵了,难道是我的电脑太烂?
后来才明白,原来 字符串拼接,尤其是用 +
在循环里反复操作,效率贼低!为啥?因为字符串在 Python 里是不可变的。你每次用 +
拼接,Python 都得在内存里重新创建一个新的字符串对象,把原来两个字符串的内容复制进去。你想啊,几十万次甚至更多次地创建、复制、再创建、再复制,那内存和CPU不就跟跑马拉松似的,累趴下了嘛。
这时候,我就学到了字符串拼接的第一个“神器”—— join()
方法。这玩意儿是字符串对象自带的方法,用法是 separator.join(sequence)
。这里的 separator
是分隔符,sequence
是一个可迭代对象,比如列表、元组,里面装的是你要拼接的各个小字符串。join()
的厉害之处在于,它会先计算出最终拼接后字符串的总长度,然后一次性分配好内存,再把序列里的元素一股脑儿地复制进去。这效率,跟用 +
在循环里一点点加,简直是天壤之别!
举个例子:
“`python
lines = [‘第一行文本’, ‘第二行内容’, ‘第三行结束’]
如果用 + 在循环里拼,大概这样(别学!)
big_string = ”
for line in lines:
big_string += line + ‘\n’ # 每次都创建新字符串!
用 join() 就优雅多了
big_string = ‘\n’.join(lines) # 用换行符把列表里的字符串串起来
print(big_string)
输出:
第一行文本
第二行内容
第三行结束
“`
你看,用 join()
,一行代码搞定,而且速度飞快。所以,以后碰到要拼接大量字符串,尤其是把列表、元组里的字符串拼起来,第一个想到的就应该是 .join()
!这就像串珠子,不是一颗一颗地拿线穿(+
),而是先把线准备好,然后把所有珠子一次性地串进去(join
)。那感觉,舒坦!
除了 +
和 join()
,Python 还有几种更“讲究”的 字符串拼接 方式,与其说是拼接,不如说是格式化,也就是把一些变量的值“塞”到指定的字符串模板里。
最早接触的是 %
操作符,有点像 C 语言的 printf:
“`python
name = ‘张三’
age = 30
greeting = “你好,我是 %s,今年 %d 岁了。” % (name, age)
print(greeting)
输出:你好,我是 张三,今年 30 岁了。
“`
这个 %
操作符用起来也行,但格式符(%s, %d 等)记起来有点烦,而且参数多了得用元组包起来,有时候对不上号还容易出错。
接着出现了 str.format()
方法。这个就好用多了,用花括号 {}
作为占位符:
“`python
item = ‘苹果’
price = 5.5
message = “我买了一个 {},花了 {} 元。”.format(item, price)
print(message)
还可以指定位置或者用关键字参数
message_kw = “我买了一个 {0},花了 {1} 元。或者反过来:{1} 元买了 {0}。”.format(item, price)
print(message_kw)
message_named = “物品:{item_name},价格:{item_price} 元。”.format(item_name=item, item_price=price)
print(message_named)
“`
format()
方法比 %
灵活不少,可读性也好很多。但我得说,当我第一次看到 f-strings(格式化字符串字面量,Python 3.6+)的时候,简直惊为天人!这玩意儿太方便了,直接在字符串前面加个 f
或 F
,然后在花括号里写变量名或者表达式就行,写起来跟读起来一样自然:
“`python
name = ‘李四’
age = 25
f-string
greeting_f = f”你好,我是 {name},今年 {age} 岁了。”
print(greeting_f)
花括号里还能放表达式!
x = 10
y = 20
result = f”计算结果是:{x * y + 5}”
print(result)
甚至还能调用函数!
print(f”名字是:{name.upper()}”)
“`
用 f-strings,写需要包含变量的字符串简直是飞一般的体验,简洁、直观、高效(它在底层其实也做了优化)。现在我写 Python 3.6+ 的代码,几乎所有的字符串格式化和拼接都优先用 f-strings。强烈推荐!它不像 %
那么古老,也不像 format()
稍微有点啰嗦,f-string 就是那种“所见即所得”的畅快感。
好了, 字符串拼接 咱们大概就聊到这里。但“拼接”这事儿,可不光是字符串的专利。各种 数据结构 也经常需要“拼接”或“合并”。
比如 列表(list)。前面说了,可以用 +
:list1 + list2
。但这会创建一个新的列表,如果列表很大,频繁这么做也会有性能开销。更常见的,如果你想把一个列表的内容“追加”到另一个列表的末尾,而不想创建新列表,可以用 extend()
方法:
“`python
list_a = [1, 2, 3]
list_b = [4, 5, 6]
用 + (创建新列表)
list_c = list_a + list_b
print(f”用 + 拼接后:{list_c}”) # [1, 2, 3, 4, 5, 6]
print(f”原列表a:{list_a}”) # [1, 2, 3] 原来的没变
用 extend() (原地修改)
list_a.extend(list_b)
print(f”用 extend() 后:{list_a}”) # [1, 2, 3, 4, 5, 6] list_a 被修改了!
“`
extend()
的效率通常比 +
高,因为它是在原列表的内存空间后面直接追加元素(如果空间够的话),而不是新建一个。
还有一种“拼接”或者说“插入”的方式,是用切片赋值。你可以把另一个列表的内容插入到当前列表的任意位置:
python
list_nums = [1, 2, 5, 6]
list_middle = [3, 4]
list_nums[2:2] = list_middle # 在索引2的位置插入 list_middle 的内容
print(f"用切片赋值插入后:{list_nums}") # [1, 2, 3, 4, 5, 6]
这种方式非常灵活,能做到 extend()
做不到的中间插入。
至于 元组(tuple) 呢?因为它和字符串一样是不可变的,所以想要“拼接”两个元组,唯一的办法就是用 +
操作符,这同样会创建一个新的元组:
python
tuple_a = (10, 20)
tuple_b = (30, 40)
tuple_c = tuple_a + tuple_b
print(f"元组拼接:{tuple_c}") # (10, 20, 30, 40)
没别的招,不可变就是这样,要变只能重新造一个。
再看看 集合(set)。集合是用来装一堆不重复元素的“袋子”。拼接两个集合,其实就是取它们的并集。你可以用 union()
方法 或者 |
运算符 来创建一个新集合,包含两个集合的所有不重复元素:
“`python
set_a = {1, 2, 3}
set_b = {3, 4, 5}
用 union() (创建新集合)
set_c = set_a.union(set_b)
print(f”集合 union 后:{set_c}”) # {1, 2, 3, 4, 5}
用 | (创建新集合)
set_d = set_a | set_b
print(f”集合 | 后:{set_d}”) # {1, 2, 3, 4, 5}
“`
如果你想把一个集合的内容加到另一个集合里,原地修改原来的集合,可以用 update()
方法 或者 |=
运算符:
“`python
set_x = {1, 2}
set_y = {2, 3, 4}
set_x.update(set_y)
print(f”集合 update() 后:{set_x}”) # {1, 2, 3, 4} set_x 被修改了!
set_p = {5, 6}
set_q = {6, 7, 8}
set_p |= set_q
print(f”集合 |= 后:{set_p}”) # {5, 6, 7, 8} set_p 被修改了!
“`
最后来说说 字典(dict) 的“拼接”,或者说合并。字典是一系列键值对的集合。合并字典时,如果遇到相同的键,后面的字典的值会覆盖前面字典的值。最常用的方法是 update()
方法:
“`python
dict_a = {‘name’: ‘张三’, ‘age’: 30}
dict_b = {‘age’: 31, ‘city’: ‘北京’}
用 update() (原地修改 dict_a)
dict_a.update(dict_b)
print(f”字典 update() 后:{dict_a}”) # {‘name’: ‘张三’, ‘age’: 31, ‘city’: ‘北京’}
“`
update()
会把另一个字典的键值对添加到当前字典里,如果键冲突就覆盖。
在 Python 3.5+ 版本,还有一种很简洁的字典合并方式,用 **
解包运算符:
“`python
dict_x = {‘a’: 1, ‘b’: 2}
dict_y = {‘b’: 3, ‘c’: 4}
用 ** 解包 (创建新字典)
dict_z = {dict_x, dict_y}
print(f”字典 ** 解包合并后:{dict_z}”) # {‘a’: 1, ‘b’: 3, ‘c’: 4}
“`
这种方式特别好用,尤其当你需要合并多个字典,或者在创建一个新字典时同时从别的字典里“拷贝”一些内容过来。它会创建一个新的字典,非常“Pythonic”。
哎呀,说起来,还有一个特别特别重要的“拼接”场景,是很多人容易忽略或者做错的,那就是 文件路径的拼接!不同操作系统(Windows、macOS、Linux)的文件路径分隔符是不一样的(Windows 是 \
,其他是 /
)。如果你直接用字符串的 +
去拼路径,写死的 /
或 \
,那你的代码一换个操作系统就可能完蛋。
正确的姿势是使用 os.path.join()
函数!
“`python
import os
比如要拼接目录 ‘my_folder’ 和文件名 ‘my_file.txt’
folder = ‘my_folder’
file_name = ‘my_file.txt’
错误示范 (依赖特定操作系统)
path_wrong = folder + ‘/’ + file_name # 在Windows上就错了
path_wrong = folder + ‘\’ + file_name # 在Linux/macOS上就错了
正确姿势
path_correct = os.path.join(folder, file_name)
print(f”正确拼接的文件路径:{path_correct}”) # 会根据当前系统自动使用正确的分隔符
“`
os.path.join()
会自动根据当前操作系统来选择合适的分隔符,太省心了。这是个小细节,但能避免不少跨平台的麻烦。
总结一下, 怎么拼接 python 里的各种东西,真不是一个 +
号就能包打天下的。字符串拼接有高效的 join()
和方便的 f-strings;列表有 extend()
和切片赋值;集合有 union()
/|
和 update()
/|=
;字典有 update()
和 **
解包。还有处理文件路径的救星 os.path.join()
。
在我看来,选择哪种方法,取决于你的具体需求:
1. 你要拼接的是什么类型?字符串?列表?字典?
2. 你想要创建新的对象,还是原地修改原来的对象?(可变 vs 不可变在这里是关键!)
3. 你关不关心效率?大量操作时,join()
、extend()
、update()
通常比反复用 +
创建新对象要快得多。
4. 你追求代码的简洁性和可读性吗?f-strings 在格式化字符串方面简直无敌。
编程这事儿,很多时候就像做手工活儿,拼接就是把不同的零件组装起来。了解并熟练使用这些不同的“工具”(不同的拼接方法),能让你把代码写得更有效率、更健壮,也更漂亮。别怕一开始用错了或者选了低效的方法,多写多练,自然就有感觉了。毕竟,从踩过的坑里学到的,才是最扎实的经验嘛!
评论(0)