资讯专栏INFORMATION COLUMN

PyTips 0x0c - Python 知之深浅

LoftySoul / 840人阅读

摘要:不可变对象包括,,,,等,可变对象包括,,等。在中,赋值的过程仅仅是创建一个某个值的对象将变量名指向引用这个对象。这就像语言中指针的概念,只不过更灵活地是中的变量随时可以指向其它对象不分类型,其它变量也可以指向这一对象。

项目地址:https://git.io/pytips

Python 中的对象分为两种:可变对象(mutable)和不可变对象(immutable)。不可变对象包括int,float,long,str,tuple等,可变对象包括list,set,dict等。在 Python 中,赋值(assignment, =)的过程仅仅是:

创建一个(某个值的)对象;

将变量名指向(引用)这个对象。

这就像 C 语言中指针的概念,只不过更灵活地是 Python 中的变量随时可以指向其它对象(不分类型),其它变量也可以指向这一对象。如果这一对象是可变的,那么对其中一个引用变量的改变会影响其它变量:

lst = [1, 2, 3]
s = lst
s.pop()
print(lst)

d = {"a": 0}
e = d
e["b"] = 1
print(d)
[1, 2]
{"b": 1, "a": 0}

如果你不是刻意想要这样做(实际也很少会要这样操作),那么就可能导致一些意想不到的错误(尤其是在传递参数给函数的时候)。为了解决这一麻烦,最简单的方法就是不直接变量指向现有的对象,而是生成一份新的 copy 赋值给新的变量,有很多种语法可以实现:

lst = [1,2,3]

llst = [lst,
        lst[:],
        lst.copy(),
        [*lst]] # invalid in 2.7
for i, v in enumerate(llst):
    v.append("#{}".format(i))
print(lst)

d = {"a": 0}
dd = [d,
      d.copy(),
      {**d}] # invalid in 2.7
for i, v in enumerate(dd):
    v["dd"] = "#{}".format(i)
print(d)
[1, 2, 3, "#0"]
{"dd": "#0", "a": 0}
deep vs shallow

上面给出的这些 copy 的例子比较简单,都没有嵌套的情况出现,如果这里的可变对象中还包含其它可变对象,结果会怎样呢:

lst = [0, 1, [2, 3]]

llst = [lst,
        lst[:],
        lst.copy(),
        [*lst]]
for i, v in enumerate(llst):
    v[2].append("#{}".format(i))
print(lst)

d = {"a": {"b": [0]}}
dd = [d,
      d.copy(),
      {**d}]
for i, v in enumerate(dd):
    v["a"]["b"].append("#{}".format(i))
print(d)
[0, 1, [2, 3, "#0", "#1", "#2", "#3"]]
{"a": {"b": [0, "#0", "#1", "#2"]}}

这些 copy 的方法称为浅拷贝(shallow copy),它相比直接赋值更进了一步生成了新的对象,但是对于嵌套的对象仍然采用了赋值的方法来创建;如果要再进一步,则需要深拷贝(deep copy),由标准库 copy 提供:

from copy import deepcopy

lst = [0, 1, [2, 3]]
lst2 = deepcopy(lst)
lst2[2].append(4)
print(lst2)
print(lst)

d = {"a": {"b": [0]}}
d2 = deepcopy(d)
d2["a"]["b"].append(1)
print(d2)
print(d)
[0, 1, [2, 3, 4]]
[0, 1, [2, 3]]
{"a": {"b": [0, 1]}}
{"a": {"b": [0]}}

清楚了赋值(引用)、copy 还是 deepcopy 之间的区别才能更好地避免意想不到的错误,同样也可以利用它们的特性去实现一些 little tricks,例如我们在 0x04 闭包与作用域 中利用可变对象的特性实现 nonlocal 的功能。关于可变对象的引用、传递等既是 Python 的基本属性,同时又因为隐藏在背后的“暗箱操作”而容易引起误解,想要深入了解可以进一步阅读参考链接的文章,我也会在后面的文章中继续一边学习、一边补充更多这方面的知识。


欢迎关注公众号 PyHub!

参考

python基础(5):深入理解 python 中的赋值、引用、拷贝、作用域

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/37822.html

相关文章

  • PyTips 0x07 - Python 字符串

    摘要:项目地址所有用过的人应该都看过下面两行错误信息这就是界的锟斤拷今天和接下来几期的内容将主要关注中的字符串字节及两者之间的相互转换。 项目地址:https://git.io/pytips 所有用过 Python (2&3)的人应该都看过下面两行错误信息: UnicodeEncodeError: ascii codec cant encode characters in position...

    go4it 评论0 收藏0
  • PyTips 0x0b - Python 无处不在的else

    摘要:可以通过一个简单的例子来展示当然,也可以用状态变量的做法来替代总结有人觉得的这些用法违反直觉或者是而非,不值得提倡。 项目地址:https://git.io/pytips 我们都知道 Python 中 else 的基本用法是在条件控制语句中的 if...elif...else...,但是 else 还有两个其它的用途,一是用于循环的结尾,另一个是用在错误处理的 try 中。这原本是 P...

    jaysun 评论0 收藏0
  • PyTips 0x06 - Python 开发命令行工具

    摘要:项目地址作为一种脚本语言,可以非常方便地用于系统尤其是系统命令行工具的开发。自身也集成了一些标准库,专门用于处理命令行相关的问题。命令行工具的一般结构标准输入输出系统中,一切皆为文件,因此标准输入输出可以完全可以看做是对文件的操作。 项目地址:https://git.io/pytips Python 作为一种脚本语言,可以非常方便地用于系统(尤其是*nix系统)命令行工具的开发。Pyt...

    Shihira 评论0 收藏0
  • PyTips 0x0e - Python 内置排序方法

    摘要:项目地址提供两种内置排序方法,一个是只针对的原地排序方法,另一个是针对所有可迭代对象的非原地排序方法。 项目地址:https://git.io/pytips Python 提供两种内置排序方法,一个是只针对 List 的原地(in-place)排序方法 list.sort(),另一个是针对所有可迭代对象的非原地排序方法 sorted()。 所谓原地排序是指会立即改变被排序的列表对象,就...

    Baoyuan 评论0 收藏0
  • PyTips 0x03 - Python 列表推导

    摘要:项目地址列表推导中提到的方法可以通过简化的语法快速构建我们需要的列表或其它可迭代对象,与它们功能相似的,还提供列表推导的语法。 项目地址:https://git.io/pytips 0x03 - Python 列表推导 0x02 中提到的 map/filter 方法可以通过简化的语法快速构建我们需要的列表(或其它可迭代对象),与它们功能相似的,Python 还提供列表推导(List C...

    sugarmo 评论0 收藏0

发表评论

0条评论

LoftySoul

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<