07-01结构与封装
解构与封装
解构
什么是结构?如下讲两个例子
解构:按照元素顺序, 把线性结构的元素赋值给变量。
简单一点的例子
In [26]: x = 1
In [27]: y = 2
In [28]: tmp = x
In [29]: x = y
In [30]: y = tmp
In [31]: print(x, y)
2 1
复杂一点的例子
In [32]: x, y = y, x
In [33]: print(x, y)
1 2
In [36]: lst = [1, 2]
In [37]: first = lst[0]
In [38]: second = lst[1]
In [39]: first, second = lst # 解构
In [40]: print(first, second)
1 2
封装
- 定义一个元组, 可以省略小括号
- 封装出来的一定是元组
In [43]: t = 1, 2
In [44]: t
Out[44]: (1, 2)
In [45]: type(t)
Out[45]: tuple
定义一个元组, 可以省略小括号
In [46]: t1 = (1, 2)
In [47]: t2 = 1, 2
t1和t2的效果是相等的。
封装出来的一定是元组
python3与python2解构不同之处
上述就是Python2和Python3解构的相同之处,Python3还带来一些新的解构变化
总结:
- 不加*号的解构
- 元素按照顺序赋值给变量。
- 变量和元素必须匹配。
- 加*号的解构
- 加星号变量, 可以接收任意个数的元素。
- 加星号的变量不能单独出现。在一个表达式也只能出现1次
- *号位置可以在表达式中任意地方出现。(左中右)
加*号的场景
依然用实例说话
场景1:*号参数位于中间
In [48]: lst = list(range(1000))
In [49]: head, *mid, tail = lst
In [50]: head
Out[50]: 0
In [51]: tail
Out[51]: 999
In [52]: mid
Out[52]:
[1,
2,
...
...
998]
In [67]: first, sceond, *other, last = lst # 这种情况也是允许的
In [68]: first
Out[68]: 0
In [69]: sceond
Out[69]: 1
In [70]: other
Out[70]: [2, 3]
In [71]: last
Out[71]: 4
场景2:*号位于最右边
In [53]: lst = list(range(5))
In [54]: head, *tail = lst
In [55]: head
Out[55]: 0
In [56]: tail
Out[56]: [1, 2, 3, 4]
In [63]: first, sceond, *other = lst # 这种情况下是允许的
In [64]: first
Out[64]: 0
In [65]: sceond
Out[65]: 1
In [66]: other
Out[66]: [2, 3, 4]
场景3:只有一个星号, 没有其他参数
In [58]: *lst2 = lst # 左边只有一个加星号的变量是不行的
File "<ipython-input-58-98211a44ccfb>", line 1
*lst2 = lst
^
SyntaxError: starred assignment target must be in a list or tuple
场景4:星号位于最左边
In [59]: *head, tail = lst # 这种是OK的
In [60]: head
Out[60]: [0, 1, 2, 3]
In [61]: tail
Out[61]: 4
场景5:多个星号
In [62]: head, *m1, *m2, tail = lst # 两个*号是不行的
File "<ipython-input-62-1fc1a52caa8e>", line 1
head, *m1, *m2, tail = lst
^
SyntaxError: two starred expressions in assignment
左右有多个加星号的变量也是不行的。
不加*号场景
场景1:左边变量数超过右边元素个数的时候
In [72]: v1, v2, v3, v4, v5, v6, v7 = lst # 左边变量数超过右边元素个数的时候, 是不行。
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
<ipython-input-72-9366cfb498a1> in <module>()
----> 1 v1, v2, v3, v4, v5, v6, v7 = lst
ValueError: not enough values to unpack (expected 7, got 5)
场景2:左边变量数小于右边元素个数
In [73]: v1, v2 = lst # 左边变量数小于右边元素个数, 且左边没有加星号的变量
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
<ipython-input-73-d7b0a4e7871e> in <module>()
----> 1 v1, v2 = lst
ValueError: too many values to unpack (expected 2)
解构返回的都是列表
In [83]: f, *t = (1, 2, 3, 4)
In [84]: t # 返回的都是一个列表
Out[84]: [2, 3, 4]
_下划线在解构中的作用
python的一个惯例, 使用单个下划线_表示丢弃该变量。
In [85]: head, *_, tial = lst
In [86]: head
Out[86]: 0
In [87]: tail
Out[87]: 4
In [88]: _ # python的一个惯例, 使用单个下划线_表示丢弃该变量。
Out[88]: 4
单个下划线也是python合法的标识符, 但是如果不是要丢弃一个变量, 通常不要用单个下划线表示一个有意义的变量。
在gettext, 多语言gettext('bbbb'), 通常会重命名_ _('dfasfdasd')
在gettext中的惯例, 会覆盖python中_丢弃的变量。
In [2]: head, *_ = 'I love Python'
In [3]: head
Out[3]: 'I'
In [4]: _
Out[4]: 'I'
ipython把返回值临时保存在_
In [5]: [1, 2, 3, 4]
Out[5]: [1, 2, 3, 4]
In [6]: _
Out[6]: [1, 2, 3, 4]
多层次的解构
解构也可以用在多层次(比如一个列表中还有一个元组)的语句上。
非常复杂的数据结构, 多层嵌套的线性结构的时候, 可以用结构快速提取其中的值。
解构用的多的地方在于函数的返回值, 函数返回值封装一个元组
In [11]: lst = [1, (2, 3), 5]
In [12]: _, v, *_ = lst
In [13]: _, val = v # 复杂的实现
In [15]: val
Out[15]: 3
In [16]: _, (_, val), *_ = lst # 直接的实现
In [17]: val
Out[17]: 3
解构是支持多层次的
In [25]: lst
Out[25]: [0, (1, 2), 2, 3, 4, 5, 6, 7, 8, 9]
In [26]: _, (*_, tail), *_ = lst
In [27]: tail
Out[27]: 2
In [28]: _, [*_, tail], *_ = lst # 可以以列表的形式
In [29]: tail
Out[29]: 2
非常复杂的数据结构, 多层嵌套的线性结构的时候, 可以用结构快速提取其中的值。
解构用的多的地方在于函数的返回值, 函数返回值封装一个元组
In [33]: key, _, value = 'env = prod'.partition('=') # 快速匹配key, value
In [34]: key
Out[34]: 'env '
In [35]: value
Out[35]: ' prod'
没有解构, 我们也可以活下来, 但是有了结构, 生活很美好
解构这个特性, 被很多的语言借鉴。