Python基础之变量的相关知识总结


Posted in Python onJune 23, 2021

变量全都是引用

跟其他编程语言不同,Python的变量不是盒子,不会存储数据,它们只是引用,就像标签一样,贴在对象上面。

比如:

>>> a = [1, 2, 3]
>>> b = a
>>> a.append(4)
>>> b
[1, 2, 3, 4]
>>> b is a
True

a变量和b变量引用的是同一个列表[1, 2, 3]。b可以叫做a的别名。

比较来看:

>>> a = [1, 2, 3]
>>> c = [1, 2, 3]
>>> c == a
True
>>> c is a
False

c引用的是另外一个列表,虽然和a引用的列表的值相等,但是它们是不同的对象。

浅复制与深复制

浅复制是指只复制最外层容器,副本中的元素是源容器中元素的引用。如果所有元素都是不可变的,那么这样没有问题,还能节省内容。但是,如果有可变的元素,那么结果可能会出乎意料之外。构造方法或[:]做的都是浅复制。

示例:

>>> x1 = [3, [66, 55, 44], (7, 8, 9)]
# x2是x1的浅复制
>>> x2 = list(x1)

# 不可变元素没有影响
>>> x1.append(100)
>>> x1
[3, [66, 55, 44], (7, 8, 9), 100]
>>> x2
[3, [66, 55, 44], (7, 8, 9)]  

# x1[1]是列表,可变元素会影响x2
# 因为它们引用的是同一个对象
>>> x1[1].remove(55)
>>> x1
[3, [66, 44], (7, 8, 9), 100]
>>> x2
[3, [66, 44], (7, 8, 9)]  

# x2[1]也会反过来影响x1
>>> x2[1] += [33, 22]
>>> x1
[3, [66, 44, 33, 22], (7, 8, 9), 100]  
>>> x2
[3, [66, 44, 33, 22], (7, 8, 9)]

# 不可变元组也不会有影响
# +=运算符创建了一个新元组
>>> x2[2] += (10, 11)
>>> x1
[3, [66, 44, 33, 22], (7, 8, 9), 100]  
>>> x2
[3, [66, 44, 33, 22], (7, 8, 9, 10, 11)]

深复制是指我们常规理解的复制,副本不共享内部对象的引用,是完全独立的一个副本。这可以借助copy.deepcopy来实现。

示例:

>>> a = [10, 20]
>>> b = [a, 30]
>>> a.append(b)
>>> a
[10, 20, [[...], 30]]
>>> from copy import deepcopy
>>> c = deepcopy(a)
>>> c
[10, 20, [[...], 30]]

即使是有循环引用也能正确复制。

注意copy.copy()是浅复制,copy.deepcopy()是深复制。

函数传参

Python唯一支持的参数传递模式是共享传参,也就是指函数的各个形式参数获得实参中各个引用的副本。因为Python的变量全都是引用。对于不可变对象来说没有问题,但是对于可变对象就不一样了。

示例:

>>> def f(a, b):
...     a += b
...     return a
... 

# 数字不变
>>> x = 1
>>> y = 2
>>> f(x, y)
3
>>> x, y
(1, 2)

# 列表变了
>>> a = [1, 2]
>>> b = [3, 4]
>>> f(a, b)
[1, 2, 3, 4]
>>> a, b
([1, 2, 3, 4], [3, 4])

# 元组不变
>>> t = (10, 20)
>>> u = (30, 40)
>>> f(t, u)
(10, 20, 30, 40)
>>> t, u
((10, 20), (30, 40))

由此可以得出一条警示:函数参数尽量不要使用可变参数,如果非用不可,应该考虑在函数内部进行复制。

示例:

class TwilightBus:
    """A bus model that makes passengers vanish"""

    def __init__(self, passengers=None):
        if passengers is None:
            self.passengers = []
        else:
            self.passengers = passengers

    def pick(self, name):
        self.passengers.append(name)

    def drop(self, name):
        self.passengers.remove(name)

测试一下:

>>> basketball_team = ['Sue', 'Tina', 'Maya', 'Diana', 'Pat']
>>> bus = TwilightBus(basketball_team)
>>> bus.drop('Tina')
>>> bus.drop('Pat')
>>> basketball_team
['Sue', 'Maya', 'Diana']

TwilightBus下车的学生,竟然从basketball_team中消失了。这是因为self.passengers引用的是同一个列表对象。修改方法很简单,复制个副本:

def __init__(self, passengers=None):
        if passengers is None:
            self.passengers = []
        else:
            self.passengers = list(passengers)  # 使用构造函数复制副本

del和垃圾回收

del语句删除的是引用,而不是对象。但是del可能会导致对象没有引用,进而被当做垃圾回收。

示例:

>>> import weakref
>>> s1 = {1, 2, 3}
# s2和s1引用同一个对象
>>> s2 = s1
>>> def bye():
...     print("Gone")
...     
# 监控对象和调用回调
>>> ender = weakref.finalize(s1, bye)
>>> ender.alive
True
# 删除s1后还存在s2引用
>>> del s1
>>> ender.alive
True
# s2重新绑定导致{1, 2, 3}引用归零
>>> s2 = "spam"
Gone
# 对象被销毁了
>>> ender.alive
False

在CPython中,对象的引用数量归零后,对象会被立即销毁。如果除了循环引用之外没有其他引用,两个对象都会被销毁。

弱引用

某些情况下,可能需要保存对象的引用,但不留存对象本身。比如,有个类想要记录所有实例。这个需求可以使用弱引用实现。

比如上面示例中的weakref.finalize(s1, bye),finalize就持有{1, 2, 3}的弱引用,虽然有引用,但是不会影响对象被销毁。

其他使用弱引用的方式是WeakDictionary、WeakValueDictionary、WeakSet。

示例:

class Cheese:

    def __init__(self, kind):
        self.kind = kind

    def __repr__(self):
        return 'Cheese(%r)' % self.kind
>>> import weakref
>>> stock = weakref.WeakValueDictionary()
>>> catalog = [Cheese('Red Leicester'), Cheese('Tilsit'),
...                 Cheese('Brie'), Cheese('Parmesan')]
...
>>> for cheese in catalog:
        # 用作缓存
        # key是cheese.kind
        # value是cheese的弱引用
...     stock[cheese.kind] = cheese
...
>>> sorted(stock.keys())
['Brie', 'Parmesan', 'Red Leicester', 'Tilsit']

# 删除catalog引用,stock弱引用不影响垃圾回收
# WeakValueDictionary的值引用的对象被销毁后,对应的键也会自动删除
>>> del catalog
>>> sorted(stock.keys())  # 还存在一个cheese临时变量的引用
['Parmesan']

# 删除cheese临时变量的引用,stock就完全清空了
>>> del cheese
>>> sorted(stock.keys())
[]

注意不是每个Python对象都可以作为弱引用的目标,比如基本的list和dict就不可以,但是它们的子类是可以的:

class MyList(list):
    pass
a_list = MyList(range(10))
weakref_to_a_list = weakref.ref(a_list)

小结

本文首先阐述了Python变量全部都是引用的这个事实,这意味着在Python中,简单的赋值是不创建副本的。如果要创建副本,可以选择浅复制和深复制,浅复制使用构造方法、[:]copy.copy(),深复制使用copy.deepcopy()。del删除的是引用,但是会导致对象没有引用而被当做垃圾回收。有时候需要保留引用而不保留对象(比如缓存),这叫做弱引用,weakref库提供了相应的实现。

参考资料:

《流畅的Python》

到此这篇关于Python基础之变量的相关知识总结的文章就介绍到这了,更多相关Python变量内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

Python 相关文章推荐
Python显示进度条的方法
Sep 20 Python
Python函数嵌套实例
Sep 23 Python
用实例详解Python中的Django框架中prefetch_related()函数对数据库查询的优化
Apr 01 Python
Python+django实现文件上传
Jan 17 Python
python 网络编程详解及简单实例
Apr 25 Python
Python3实现的简单三级菜单功能示例
Mar 12 Python
详解在Python中以绝对路径或者相对路径导入文件的方法
Aug 30 Python
Django 批量插入数据的实现方法
Jan 12 Python
python numpy--数组的组合和分割实例
Feb 24 Python
Python生成器常见问题及解决方案
Mar 21 Python
Python Django中的STATIC_URL 设置和使用方式
Mar 27 Python
如何利用python检测图片是否包含二维码
Oct 15 Python
浅谈Python数学建模之固定费用问题
浅谈Python数学建模之整数规划
浅谈Python数学建模之数据导入
Jun 23 #Python
python四种出行路线规划的实现
浅谈Python数学建模之线性规划
Jun 23 #Python
教你如何用Python实现人脸识别(含源代码)
python 对图片进行简单的处理
You might like
PHP+ajax 无刷新删除数据
2010/02/20 PHP
php实现Mysql简易操作类
2015/10/11 PHP
PHP数组去重比较快的实现方式
2016/01/19 PHP
thinkPHP5框架导出Excel文件简单操作示例
2018/08/03 PHP
PHP实现的简单留言板功能示例【基于thinkPHP框架】
2018/12/07 PHP
php的无刷新操作实现方法分析
2020/02/28 PHP
IE6/7/8/9不支持exec的简写方式
2011/05/25 Javascript
JavaScript异步调用定时方法并停止该方法实现代码
2012/03/16 Javascript
把文本中的URL地址转换为可点击链接的JavaScript、PHP自定义函数
2014/07/29 Javascript
谷歌地图打不开的解决办法
2014/08/07 Javascript
jQuery实现的一个tab切换效果内部还嵌有切换
2014/08/10 Javascript
浅析JavaScriptSerializer类的序列化与反序列化
2016/11/22 Javascript
jQuery实现字符串全部替换的方法
2016/12/12 Javascript
微信小程序实战之自定义抽屉菜单(7)
2017/04/18 Javascript
webpack开发跨域问题解决办法
2017/08/03 Javascript
vue实现简单的MVVM框架
2018/08/05 Javascript
vue 集成jTopo 处理方法
2019/08/07 Javascript
JavaScript实现单图片上传并预览功能
2019/09/30 Javascript
浅谈TypeScript的类型保护机制
2020/02/23 Javascript
VUE项目实现主题切换的多种方法
2020/11/26 Vue.js
python调用Delphi写的Dll代码示例
2017/12/05 Python
Python实现屏幕截图的两种方式
2018/02/05 Python
java中两个byte数组实现合并的示例
2018/05/09 Python
修复 Django migration 时遇到的问题解决
2018/06/14 Python
Python实现针对json中某个关键字段进行排序操作示例
2018/12/25 Python
对pandas通过索引提取dataframe的行方法详解
2019/02/01 Python
python 实现selenium断言和验证的方法
2019/02/13 Python
Python hmac模块使用实例解析
2019/12/24 Python
通过HTML5 Canvas API绘制弧线和圆形的教程
2016/03/14 HTML / CSS
加拿大奢华时装品牌:Mackage
2018/01/10 全球购物
安德玛加拿大官网:Under Armour加拿大
2019/10/02 全球购物
澳洲Chemist Direct药房中文网:澳洲大型线上直邮药房
2019/11/04 全球购物
四年级数学教学反思
2016/02/16 职场文书
智慧人生:永远不需要向任何人解释你自己
2019/08/20 职场文书
详解Python 3.10 中的新功能和变化
2021/04/28 Python
Windows安装Anaconda3的方法及使用过程详解
2021/06/11 Python