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翻译软件实现代码(使用google api完成)
Nov 26 Python
Python实现字符串匹配算法代码示例
Dec 05 Python
python登录并爬取淘宝信息代码示例
Dec 09 Python
python 获取指定文件夹下所有文件名称并写入列表的实例
Apr 23 Python
详解python中@的用法
Mar 27 Python
对Python 检查文件名是否规范的实例详解
Jun 10 Python
python处理RSTP视频流过程解析
Jan 11 Python
Scrapy爬虫文件批量运行的实现
Sep 30 Python
python中watchdog文件监控与检测上传功能
Oct 30 Python
Python headers请求头如何实现快速添加
Nov 03 Python
python 基于opencv去除图片阴影
Jan 26 Python
在 Python 中利用 Pool 进行多线程
Apr 24 Python
浅谈Python数学建模之固定费用问题
浅谈Python数学建模之整数规划
浅谈Python数学建模之数据导入
Jun 23 #Python
python四种出行路线规划的实现
浅谈Python数学建模之线性规划
Jun 23 #Python
教你如何用Python实现人脸识别(含源代码)
python 对图片进行简单的处理
You might like
PHP session有效期session.gc_maxlifetime
2011/04/20 PHP
PHP中读写文件实现代码
2011/10/20 PHP
destoon实现VIP排名一直在前面排序的方法
2014/08/21 PHP
php 模拟 asp.net webFrom 按钮提交事件实例
2014/10/13 PHP
php计算title标题相似比的方法
2015/07/29 PHP
TP(thinkPHP)框架多层控制器和多级控制器的使用示例
2018/06/13 PHP
JavaScript RegExp方法获取地址栏参数(面向对象)
2009/03/10 Javascript
nodejs的require模块(文件模块/核心模块)及路径介绍
2013/01/14 NodeJs
jQuery中使用each处理json数据
2015/04/23 Javascript
wangEditor编辑器失去焦点后仍然可以在原位置插入图片分析
2015/05/06 Javascript
javascript实现表格增删改操作实例详解
2015/05/15 Javascript
如何根据百度地图计算出两地之间的驾驶距离(两种语言js和C#)
2015/10/29 Javascript
使用struts2+Ajax+jquery验证用户名是否已被注册
2016/03/22 Javascript
深入理解js promise chain
2016/05/05 Javascript
页面间固定参数,通过cookie传值的实现方法
2017/05/31 Javascript
vue项目中引入noVNC远程桌面的方法
2018/03/05 Javascript
详解vue项目中如何引入全局sass/less变量、function、mixin
2018/06/02 Javascript
layui实现左侧菜单点击右侧内容区显示
2019/07/26 Javascript
JS面向对象编程实现的Tab选项卡案例详解
2020/03/03 Javascript
Vue在H5 项目中使用融云进行实时个人单聊通讯
2020/12/14 Vue.js
python获得图片base64编码示例
2014/01/16 Python
Django中的“惰性翻译”方法的相关使用
2015/07/27 Python
python+opencv轮廓检测代码解析
2018/01/05 Python
django允许外部访问的实例讲解
2018/05/14 Python
Python数据正态性检验实现过程
2020/04/18 Python
Clarria化妆品官方网站:购买天然和有机化妆品系列
2018/04/08 全球购物
数学与统计学院学生个人职业生涯规划书
2014/02/10 职场文书
学生手册评语
2014/05/05 职场文书
职业规划实施方案
2014/06/10 职场文书
公司股东合作协议书
2014/09/14 职场文书
2015年安全教育月活动总结
2015/03/26 职场文书
调研报告的主要写法
2019/04/18 职场文书
2019年教师节:送给所有老师的祝福语
2019/09/05 职场文书
励志语录:你若不勇敢,谁替你坚强
2019/11/08 职场文书
Mysql中常用的join连接方式
2022/05/11 MySQL
新的CSS 伪类函数 :is() 和 :where()示例详解
2022/08/05 HTML / CSS