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之不要红头文件(1)
Sep 28 Python
Python正则表达式实现截取成对括号的方法
Jan 06 Python
python绘制立方体的方法
Jul 02 Python
django用户登录和注销的实现方法
Jul 16 Python
uwsgi+nginx部署Django项目操作示例
Dec 04 Python
用Python+OpenCV对比图像质量的几种方法
Jul 15 Python
python利用7z批量解压rar的实现
Aug 07 Python
python循环输出三角形图案的例子
Nov 22 Python
Python打包模块wheel的使用方法与将python包发布到PyPI的方法详解
Feb 12 Python
Python将二维列表list的数据输出(TXT,Excel)
Apr 23 Python
python语言time库和datetime库基本使用详解
Dec 25 Python
jupyter 添加不同内核的操作
Feb 06 Python
浅谈Python数学建模之固定费用问题
浅谈Python数学建模之整数规划
浅谈Python数学建模之数据导入
Jun 23 #Python
python四种出行路线规划的实现
浅谈Python数学建模之线性规划
Jun 23 #Python
教你如何用Python实现人脸识别(含源代码)
python 对图片进行简单的处理
You might like
php基于mcrypt的加密解密实例
2014/10/27 PHP
WordPress中用于获取搜索表单的PHP函数使用解析
2016/01/05 PHP
php写app接口并返回json数据的实例(分享)
2017/05/20 PHP
JavaScript函数定义的常见注意事项小结
2014/09/16 Javascript
JavaScript点击按钮后弹出透明浮动层的方法
2015/05/11 Javascript
jquery分割字符串的方法
2015/06/24 Javascript
JavaScript中点击事件的写法
2016/06/28 Javascript
js创建对象几种方式的优缺点对比
2016/09/28 Javascript
JS自定义函数实现时间戳转换成date的方法示例
2017/08/27 Javascript
微信小程使用swiper组件实现图片轮播切换显示功能【附源码下载】
2017/12/12 Javascript
微信小程序实现MUI数字输入框效果
2018/01/31 Javascript
vue.js中npm安装教程图解
2018/04/10 Javascript
对vue 键盘回车事件的实例讲解
2018/08/25 Javascript
Vue+iview+webpack ie浏览器兼容简单处理
2019/09/20 Javascript
Vue中图片Src使用变量的方法
2019/10/30 Javascript
在vue中使用jsonp进行跨域请求接口操作
2020/10/29 Javascript
[02:04]2014DOTA2国际邀请赛 BBC小组赛第三天总结
2014/07/12 DOTA
Python使用pylab库实现绘制直方图功能示例
2018/06/01 Python
Python实现的序列化和反序列化二叉树算法示例
2019/03/02 Python
树莓派与PC端在局域网内运用python实现即时通讯
2019/06/22 Python
django settings.py 配置文件及介绍
2019/07/15 Python
python 有效的括号的实现代码示例
2019/11/11 Python
django 前端页面如何实现显示前N条数据
2020/03/16 Python
python 贪心算法的实现
2020/09/18 Python
Python 使用office365邮箱的示例
2020/10/29 Python
美国保健品专家:Life Extension
2018/05/04 全球购物
The North Face北面美国官网:美国著名户外品牌
2018/09/15 全球购物
澳大利亚最受欢迎的美发用品目的地:AMR
2019/08/28 全球购物
Java 中访问数据库的步骤?Statement 和PreparedStatement 之间的区别?
2012/06/05 面试题
大学生学习2014年全国两会心得体会
2014/03/12 职场文书
优秀党支部书记事迹材料
2014/05/29 职场文书
运动会加油稿100字
2014/09/19 职场文书
投标文件签署授权委托书范本
2014/10/12 职场文书
教师节主持词开场白
2015/05/29 职场文书
2015年治庸问责工作总结
2015/07/27 职场文书
2019年度行政文员工作计划范本!
2019/07/04 职场文书