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实现的金山快盘的签到程序
Jan 17 Python
Python编程语言的35个与众不同之处(语言特征和使用技巧)
Jul 07 Python
Python和GO语言实现的消息摘要算法示例
Mar 10 Python
python实现的简单猜数字游戏
Apr 04 Python
Python中计算三角函数之cos()方法的使用简介
May 15 Python
python类的继承实例详解
Mar 30 Python
Python基于列表模拟堆栈和队列功能示例
Jan 05 Python
Python关于excel和shp的使用在matplotlib
Jan 03 Python
Python线程条件变量Condition原理解析
Jan 20 Python
有关Tensorflow梯度下降常用的优化方法分享
Feb 04 Python
利用Python的folium包绘制城市道路图的实现示例
Aug 24 Python
python中pymysql包操作数据库方法
Apr 19 Python
浅谈Python数学建模之固定费用问题
浅谈Python数学建模之整数规划
浅谈Python数学建模之数据导入
Jun 23 #Python
python四种出行路线规划的实现
浅谈Python数学建模之线性规划
Jun 23 #Python
教你如何用Python实现人脸识别(含源代码)
python 对图片进行简单的处理
You might like
php数组应用之比较两个时间的相减排序
2008/08/18 PHP
Yii的Srbac插件用法详解
2016/07/14 PHP
微信公众平台开发教程⑥ 微信开发集成类的使用图文详解
2019/04/10 PHP
laravel 时间格式转时间戳的例子
2019/10/11 PHP
ASP.NET MVC中EasyUI的datagrid跨域调用实现代码
2012/03/14 Javascript
基于javascript 闭包基础分享
2013/07/10 Javascript
在Python中使用glob模块查找文件路径的方法
2015/06/17 Javascript
JavaScript编程中window的location与history对象详解
2015/10/26 Javascript
浅析node连接数据库(express+mysql)
2015/11/30 Javascript
怎么限制input的text里输入的值只能是数字(正则、js)
2016/05/16 Javascript
微信小程序 条件渲染详解
2016/10/09 Javascript
JavaScript的变量声明提升问题浅析(Hoisting)
2016/11/30 Javascript
微信小程序 122100版本更新问题解决方案
2016/12/22 Javascript
利用原生JS与jQuery实现数字线性变化的动画
2017/02/24 Javascript
Js中将Long转换成日期格式的实现方法
2018/06/05 Javascript
基于D3.js实现时钟效果
2018/07/17 Javascript
[01:10]DOTA2次级职业联赛 - EP战队宣传片
2014/12/01 DOTA
Python 实现选择排序的算法步骤
2018/04/22 Python
python去除扩展名的实例讲解
2018/04/23 Python
利用Python实现Shp格式向GeoJSON的转换方法
2019/07/09 Python
Python实现最常见加密方式详解
2019/07/13 Python
Python 生成一个从0到n个数字的列表4种方法小结
2019/11/28 Python
python的json中方法及jsonpath模块用法分析
2019/12/06 Python
Python中求对数方法总结
2020/03/10 Python
pytorch 多分类问题,计算百分比操作
2020/07/09 Python
深入分析python 排序
2020/08/24 Python
python中常用的数据结构介绍
2021/01/12 Python
全球性的在线商店:Vogca
2019/05/10 全球购物
Huda Beauty官方商店:化妆和美容产品
2020/09/05 全球购物
介绍一些UNIX常用简单命令
2014/11/11 面试题
十佳班主任事迹材料
2014/01/18 职场文书
董事长助理岗位职责
2014/02/18 职场文书
端午节活动策划方案
2014/03/09 职场文书
党员政治学习材料
2014/05/14 职场文书
大学生个人简历自荐信
2015/03/06 职场文书
ajax请求前端跨域问题原因及解决方案
2021/10/16 Javascript