资讯动态

Python三种括号区别详解:()圆括号、[]方括号、{}花括号用法与避坑

发布时间:2026/10/9 21:45:58 来源:尧图企业网站定制
写Python代码的过程中我猜每个人都有过这种瞬间盯着屏幕上的代码突然拿不准这里到底该用圆括号还是方括号更别提花括号了。尤其是刚入门那阵子看到()、[]、{}在代码里来回出现好像每个都有点关系又好像随时都会写错。说句实话这个问题我当年也困扰过很久直到后来把三种括号在Python里各自主管的“业务”拆开看才发现这事一点都不玄乎。这篇内容就是专门讲清楚()、[]、{}三种括号的区别。不管你是零基础准备开始学Python还是已经写了一阵子但偶尔还会被括号绕晕都可以花几分钟把这篇过一遍。我会把每种括号的核心用途、容易踩的坑、以及报错之后的排查思路都讲透配合代码示例看完之后基本不会再搞混。1. 为什么说括号是Python的“语法路标”1.1 括号不是装饰是语法的一部分很多初学者容易把括号当成“辅助符号”觉得它只是把内容包起来方便阅读。这个理解在Python里是万万要不得的。Python解析器看到不同括号时会触发完全不同的语法规则括号本身就是在告诉解释器“接下来这段代码该按什么逻辑处理”。举个例子(1, 2, 3)是一个元组[1, 2, 3]是一个列表{1, 2, 3}是一个集合{a: 1}是一个字典。四个写法只差一个符号但内存布局、操作方法、可变性全都不一样。如果你随手写错一个括号轻则报语法错误重则程序不报错但逻辑完全跑偏——后一种情况才是最折磨人的。我见过不少朋友在调试的时候一行行看代码都找不到问题最后发现是把列表当成字典用了或者把元组当成列表去修改元素。这其实就是没把括号当成语法标记来看带来的后果。理解了“括号是语法的一部分”这个前提后面的区别就容易记了。1.2 先记住一个最简单的判断口诀如果只想记住一句话那就是圆括号用来调用和分组方括号用来定位和列表花括号用来映射和去重。这句话虽然不能覆盖所有细节但能帮你解决80%的日常困惑。圆括号()函数调用、表达式分组、元组方括号[]列表、索引、切片花括号{}字典、集合后面每一节我会把各自的细节展开讲同时也会点上那些“例外”和“边角情况”。先把大方向立住再慢慢补细节就不会乱。2. 圆括号()函数调用、元组与优先级2.1 最常用场景函数调用圆括号在Python里最暴露的存在感就是“调用”。定义一个函数用的是def关键字加函数名真正让函数跑起来靠的是后面的括号print(hello)、len(items)、range(10)这些都是调用。这里有一个特别容易和元组混淆的点print(a, b)里面虽然有圆括号但它是函数调用不是元组。括号里逗号隔开的是多个参数传给print函数依次打印。而(a, b)如果单独作为一个值出现才是一个包含两个元素的元组。同一个符号的不同身份确实容易让人迷糊所以判断的时候要看它们出现在什么位置、承担什么功能。我自己写代码会习惯先在脑子里问一句这里是想让一个对象“执行”起来还是想表达“一组数据”如果是前者用圆括号包裹参数去调用如果是后者再考虑是不是要创建元组。2.2 元组其实是“逗号说了算”这是一个值得刻在脑子里的冷知识元组的本质是逗号不是圆括号。也就是说1, 2, 3本身就是元组(1, 2, 3)只是为了让它在某些场景下可读性更好、或者消除歧义。你可以直接在交互环境里试一下a 1, 2, 3 print(type(a)) # class tuple这就是为什么a 1,能得到一个单元素元组而a (1)得到的是一个整数。区别就在那个逗号上。很多人想创建单元素元组时写成了(1)结果发现类型是int这就是没有理解“逗号才是元组的灵魂”这句话。所以创建单元素元组必须写成single (1,)顺便说一句元组是不可变序列没有列表的append、remove这类修改方法。如果发现程序报错说元组不支持某操作大概率是不小心用了圆括号来创建本应修改的数据结构。元组的不可变特性让它适合做“一次性打包”的数据比如函数返回多个值时Python就会自动把它们打包成元组。2.3 优先级与生成器表达式的存在感圆括号的另一个身份是“算数里的优先级标记”这一点和数学课上学的一致(a b) * c先算括号里的加法再和c相乘。虽然这个用途看起来简单但在写表达式的时候经常会被忽略尤其是混合了and、or、比较运算的复杂条件时不加括号常常让读代码的人猜优先级埋下逻辑隐患。再进阶一层圆括号还出现在生成器表达式里(x * x for x in range(10))。它长得和列表推导式很像但外层是圆括号而不是方括号结果是生成器而不是列表。生成器是惰性求值的不会一次性把所有元素放进内存适合处理大范围数据。很多人把生成器表达式拿去len()结果报错就是没意识到生成器没有长度这个概念。圆括号还有解包赋值的作用比如a, b (1, 2)可以同时给两个变量赋值只是这里更准确的说是元组解包圆括号可有可无。可以把a, b (1, 2)写成a, b 1, 2完全等价。3. 方括号[]列表、索引与切片3.1 列表的“可变”与“有序”方括号最标志性的用途是创建列表items [1, 2, 3]。列表是Python里用的最频繁的数据结构之一它有两个很重要的特点有序、可变。有序指的是元素保持你添加时的顺序可变指的是你可以直接修改某个位置的元素。items [1, 2, 3] items[0] 100 print(items) # [100, 2, 3]这个操作放在元组上就会直接报错因为元组不可变。如果你有一个需要不断增删改的数据集合存放它的容器就应该选择列表用方括号创建而不是用圆括号。另外列表里可以装任意类型的数据甚至可以嵌套列表形成二维乃至多维结构。比如表示矩阵时通常就是列表套列表matrix [[1, 2], [3, 4]]。很多做数据处理、写算法题的朋友会经常遇到这种结构外层方括号是行内层方括号是列访问matrix[0][1]就是第一行第二列的元素。3.2 索引和切片是方括号的高频操作方括号的另一个高频身份是索引操作符。不管这个对象是列表、元组还是字符串只要你想获取它内部的某个元素几乎都要用到方括号。text hello print(text[1]) # e print(items[-1]) # 最后一个元素这里有个容易踩的点方括号里的索引从0开始并且负数索引表示从末尾往前数。很多新手在写循环取数据时把索引当成从1开始导致第一个元素被跳过最后一个元素又越界访问报错。建议一旦发现索引类报错先检查是不是边界问题。切片同样依靠方括号格式是[start:stop:step]比如items[1:4]拿到索引1到3的元素items[::-1]反转整个序列。切片的左闭右开规则也经常让人困惑但我有一个经验stop位置是“取不到”的。如果不想记规则就干脆用[:n]取前n个元素用[-n:]取后n个元素用这两个就够了。3.3 列表推导式与被忽略的拷贝细节列表推导式是方括号和一个for表达式的组合可以用一行代码快速构造列表squares [x * x for x in range(10)]这行代码等价的写法是一个几行的循环加append。列表推导式让代码更紧凑但也要注意可读性不要把多个循环和条件判断挤在一行里否则别人包括未来的你自己读起来会很痛苦。还有一个细节很多人会踩用list2 list1并不是复制列表只是给同一个列表起了个新名字。如果修改list2list1也会变因为两个变量指向同一个内存对象。要想真正复制应该用list1[:]或者list2 list(list1)。这个坑在函数参数传递、循环里意外修改原数据时经常出现值得多留个心眼。4. 花括号{}字典与集合的“同形异义”4.1 字典是键值对的映射表花括号{}最经典的身份是创建字典。字典存储的是“键值对”之间的映射关系比如user {name: 张三, age: 18}花括号左边是键冒号右边是值键值对之间用逗号分隔。字典的查询速度非常快因为它内部是基于哈希表实现的哪怕数据量很大通过键取值也很迅速。日常写爬虫、处理接口返回的JSON数据、保存配置参数几乎离不开字典。一个很关键的区别是创建字典用花括号访问字典里的值用的是方括号。比如user[name]才能拿到名字写成user{name}那是语法错误。很多人第一次接触字典时会觉得“既然是花括号创建的那访问也该用花括号”这个直觉在Python里不成立要特别纠正。字典还有一些常用操作比如user.get(name)方法获取值键不存在时返回None而不是抛异常user.keys()拿到所有键user.items()同时拿到键和值。用in关键字判断键是否存在比直接访问再捕获异常要安全得多。4.2 集合是唯一元素的无序集合花括号还有一个身份是创建集合{1, 2, 3}。集合的特点是元素不重复、无序所以它最常用的场景就是去重。nums [1, 2, 2, 3, 3, 3] unique set(nums)集合和字典长得像但集合里只有元素没有冒号分隔的键值对。判断一个花括号包裹的内容“是字典还是集合”就看看里面有没有冒号。有冒号是字典没有冒号是集合。集合还支持数学上的交集、并集、差集操作a b取交集a | b取并集a - b取差集。这些操作在比较两个数组的共同元素、找出差异时非常高效比用循环一个一个判断快得多。需要注意集合本身是可变的可以添加或删除元素但集合里的元素必须是不可变类型。也就是说你不能把一个列表放进集合里因为列表可以修改它的哈希值不稳定。如果报错说unhashable type: list就是往集合里塞了不可哈希的容器。4.3 空{}和空set()的坑这是一个非常经典的细节空花括号{}创建的是字典不是集合。要创建空集合必须用set()empty_dict {} empty_set set() print(type(empty_dict)) # class dict print(type(empty_set)) # class set这个坑的麻烦之处在于代码不会直接报错但你的变量类型和预期完全不一样。比如你想创建一个空集合然后在循环里往里添加元素去重结果用的是{}添加时就会报错或者行为不符合预期。我在帮别人排查时见过好几次这种问题最后都是把{}改成set()就好了。与之类似的还有[]和()之间的选择创建空列表用[]创建空元组用()这个倒比较直观不太容易混淆。4.4 推导式与嵌套大括号里的花活字典和集合都支持推导式它们和列表推导式长得很像只是外层括号换成花括号。# 字典推导式 squares_dict {x: x * x for x in range(5)} # 集合推导式 squares_set {x * x for x in range(5)}注意字典推导式要把键和值用冒号分开集合推导式则直接写元素。这个区别和创建字典与集合的字面量规则一脉相承。花括号场景下的嵌套也很常见比如列表里套字典再比如字典里嵌套列表或字典用来表示复杂的数据结构。尤其是处理从接口返回的JSON数据时经常会遇到一层套一层的字典列表这时候要能分清楚每一层用的是什么括号、该怎么访问。我处理这类数据的方法是先把最外层的结构读出来判断它是列表还是字典然后一层一层往里剥不要想着一步到位算出结果否则很容易写错。5. 三种括号最容易搞混的场景与避坑清单5.1 用索引还是用调用先分清对象三位括号混用最典型就是一个错误想访问列表里的元素却写成了圆括号调用想调用函数却写成了方括号。比如func[0]会报错而不是拿到函数的第一个参数因为方括号在Python里表示索引而items()表示把变量当函数调用如果items其实是个列表就会报TypeError: list object is not callable。我在实际调试中遇到这种报错第一反应就是去查变量名是不是把函数名覆盖了或者是不是错把列表当成了可调用对象。有时候是因为命名问题比如一个变量叫list然后在后面调用list[0]或list()就把类型本身和该类型的方法搞混了。一个可靠的检查方法是看到括号的时候先问自己“这里我想做什么”。想拿容器里的数据用方括号想让一个可调用对象执行用圆括号。这样问一遍基本不会选错。5.2 字典访问用方括号而不是花括号这个我在前面写过一次但因为太常踩还是要单独拿出来说。字典用花括号创建但值的索引访问永远用方括号person {name: 李四} print(person[name])如果你天然地觉得“用花括号创建的容器访问里面东西也该用花括号”那这个直觉在Python里真不行。方括号是Python里通用的“取元素”操作符不管是从列表取、从元组取、从字典取键对应的值甚至从字符串取字符统统用方括号。就冲这一点方括号在心里的地位应该是最高的凡是“定位到某个位置”或者“取出某个值”优先想方括号。5.3 理解括号的“配对”与嵌套写复杂表达式时括号经常一层套一层比如列表里装字典字典里又装列表。这时候容易犯两个错要么少写一个括号导致语法错误要么逻辑上配对错了导致数据访问结果不对。我的建议是不要把嵌套全部写在一行里特别是新手阶段宁可多写几个中间变量把每一层结构化整为零。比如data {users: [{name: 张三}, {name: 李四}]}想拿到第一个用户的名字如果一眼看不出来就拆开users data[users] first_user users[0] name first_user[name]这样虽然多写了几行但每一步都好理解也不容易配错括号。等你熟练了再合并成data[users][0][name]也不迟。5.4 从报错信息反推括号错在哪Python的报错信息虽然有时候让人一头雾水但大多数情况下还是能提供线索的。归纳一下和括号相关的报错基本可以这样反推SyntaxError: invalid syntax且提示位置离某个花括号很近如果不是普通语法问题去看看是不是把中文括号当成英文括号了。TypeError: tuple object does not support item assignment说明你用方括号给元组元素赋值了把容器从元组改成列表。TypeError: list object is not callable说明你用圆括号调用了列表或者变量名覆盖了函数名。TypeError: unhashable type: list说明你把列表放进了集合或作为字典的键需要换成不可变类型。KeyError: xxx说明你访问字典时用的键不存在去检查键的实际拼写别用花括号去访问。报错信息看到了就去对照自己写的是哪种括号、想做什么操作大多数问题都能在几秒内定位出来。6. 常见问题与排查技巧实录6.1 把列表当函数调用有一次我的一个朋友写代码报错是TypeError: list object is not callable。他百思不得其解因为我们根本不会故意调用一个列表。后来检查发现他定义一个变量叫list然后在后面写list(x for x in ...)想把生成器转成列表但此时list已经被一个列表对象覆盖了解释器把它当成列表对象去调用自然报错。排查方法很简单不要用list、dict、set、str这类内建类型名作为变量名。一旦变量名覆盖了内建名代码的行为会变得非常诡异而且难排查。大家在写代码的时候可以顺手养成了一个习惯变量名加个后缀或说明性前缀比如list1、data_list有效避免这种问题。6.2 忘了逗号元组变成普通值还有一个很隐蔽的问题就是创建单元素元组时少了逗号a (1) print(type(a)) # class int这里得到的是整数1不是元组。只有写成(1,)才能得到一个单元素元组。这个问题在函数返回一个元素时尤其要小心比如你只想返回一个包含某个数字的元组结果少打了逗号函数返回类型变了接收方又按元组去解包就会报错。我的建议是如果你要返回“一个值的元组”写的时候一定带上逗号哪怕看起来不习惯也要写。另外可以用type(value)函数检查一下结果的类型花一秒钟确认能省很多时间。6.3 空{}到底该怎么用再强调一次{}是空字典不是空集合。我踩过这个坑的场景是想收集一段文本里出现过的所有不重复字符一开始写成了chars {}然后循环里chars.add(ch)一运行就报AttributeError: dict object has no attribute add。改成chars set()之后马上就通了。这个报错还算友好直接告诉你是字典没有add方法。怕的是你想做集合运算结果拿着一个空字典去做、|报错报得莫名其妙。所以规则很简单要一个可以往里放“唯一元素”的空容器就写set()。6.4 下标越界与KeyError的排查思路IndexError: list index out of range和KeyError是另外两个常见的索引类错误。前者是方括号下标超出长度后者是字典键不存在。排查前者时去打印列表的长度和下标值看看是不是边界判断写错了排查后者时去打印字典的keys()看你的访问键是不是拼错或者压根不存在。归纳一下排查顺序先确认变量类型用type()再确认操作符是索引还是调用再确认边界条件或键是否存在。这样三步走下来90%以上的括号相关问题都能定位。7. 最后分享两个我一直在用的小习惯括号问题说穿了是“语法符号和含义的映射”问题解决办法不复杂但需要形成习惯。我自己一直坚持两个小习惯受益很大一是写代码时随手加type()检查在拿不准变量是什么类型的时候打印一下几秒钟就能确认二是从内往外读括号嵌套遇到一层套一层的长表达式先找最内部的那个括号理解它返回什么再向外一层一层扩展。这两个习惯能让很多括号相关的坑提前暴露。另外如果你是刚开始学Python不要害怕报错。一次报错就是一个明确的问题信号它告诉你代码哪里不符合解释器的预期。把常见报错和对应的括号用法记下来下次遇到就能快速反应。括号这件事写多了自然就熟了千万别死记硬背写几段带头文件、数据结构嵌套的代码比看十遍理论都有用。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑