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多线程操作实例
Nov 21 Python
编写同时兼容Python2.x与Python3.x版本的代码的几个示例
Mar 30 Python
python动态加载包的方法小结
Apr 18 Python
Windows下搭建python开发环境详细步骤
Jul 20 Python
详解Python 2.6 升级至 Python 2.7 的实践心得
Apr 27 Python
基于Python中numpy数组的合并实例讲解
Apr 04 Python
numpy中实现ndarray数组返回符合特定条件的索引方法
Apr 17 Python
python查找重复图片并删除(图片去重)
Jul 16 Python
python3实现raspberry pi(树莓派)4驱小车控制程序
Feb 12 Python
解决pycharm下pyuic工具使用的问题
Apr 08 Python
Python安装并操作redis实现流程详解
Oct 13 Python
PyQt QMainWindow的使用示例
Mar 24 Python
浅谈Python数学建模之固定费用问题
浅谈Python数学建模之整数规划
浅谈Python数学建模之数据导入
Jun 23 #Python
python四种出行路线规划的实现
浅谈Python数学建模之线性规划
Jun 23 #Python
教你如何用Python实现人脸识别(含源代码)
python 对图片进行简单的处理
You might like
global.php
2006/12/09 PHP
关于Iframe如何跨域访问Cookie和Session的解决方法
2013/04/15 PHP
php实现多维数组中每个单元值(数字)翻倍的方法
2015/02/16 PHP
php的闭包(Closure)匿名函数详解
2015/02/22 PHP
php数字每三位加逗号的功能函数
2015/10/22 PHP
不错的新闻标题颜色效果
2006/12/10 Javascript
基于jquery的代码显示区域自动拉长效果
2011/12/07 Javascript
jquery给图片添加鼠标经过时的边框效果
2013/11/12 Javascript
JavaScript输入邮箱自动提示实例代码
2014/01/13 Javascript
js使用循环清空某个div中的input标签值
2014/09/29 Javascript
嵌入式iframe子页面与父页面js通信的方法
2015/01/20 Javascript
jQuery实现的经典竖向伸缩菜单效果代码
2015/09/24 Javascript
微信小程序ajax实现请求服务器数据及模版遍历数据功能示例
2017/12/15 Javascript
浅谈用Webpack路径压缩图片上传尺寸获取的问题
2018/02/22 Javascript
AngularJS 应用模块化的使用
2018/04/04 Javascript
详解JavaScript对数组操作(添加/删除/截取/排序/倒序)
2019/04/28 Javascript
vue中npm包全局安装和局部安装过程
2019/09/03 Javascript
jQuery 筛选器简单操作示例
2019/10/02 jQuery
[54:54]Newbee vs Serenity 2018国际邀请赛小组赛BO2 第一场 8.17
2018/08/18 DOTA
Python创建xml的方法
2015/03/10 Python
python在windows下实现ping操作并接收返回信息的方法
2015/03/20 Python
python实现读取大文件并逐行写入另外一个文件
2018/04/19 Python
利用Python进行数据可视化常见的9种方法!超实用!
2018/07/11 Python
python生成器与迭代器详解
2019/01/01 Python
使用Django和Postgres进行全文搜索的实例代码
2020/02/13 Python
matplotlib jupyter notebook 图像可视化 plt show操作
2020/04/24 Python
vscode+PyQt5安装详解步骤
2020/08/12 Python
python爬虫多次请求超时的几种重试方法(6种)
2020/12/01 Python
HTML5和以前HTML4的区别整理
2013/10/20 HTML / CSS
澳大利亚手表品牌:Time IV Change
2018/10/06 全球购物
西班牙土拨鼠床垫公司,感觉在云端:Marmota
2019/03/18 全球购物
xxx同志考察材料
2014/02/07 职场文书
科长竞聘演讲稿
2014/05/16 职场文书
货物运输服务质量承诺书
2014/05/29 职场文书
公司管理制度范本
2015/08/03 职场文书
pytorch中的model=model.to(device)使用说明
2021/05/24 Python