刘旭昕 發表於 2021-7-30 20:57:00

Python - 可变和不可变对象

<h3><strong>前置知识</strong></h3>
<ul>
<li>在 Python 中,一切皆为对象</li>
<li>Python 中不存在值传递,<span style="color: rgba(210, 44, 74, 1)">一切传递的都是对象的引用</span>,也可以认为是传址</li>
</ul>
<p>&nbsp;</p>
<h3 id="hid-j3rSiZ"><strong>有哪些可变对象,哪些不可变对象?</strong></h3>
<ul>
<li>不可变对象:字符串、元组、数字(int、float)</li>
<li>可变对象:数组、字典、集合</li>
</ul>
<p>&nbsp;</p>
<h3><strong>不可变对象和可变对象的区别?</strong></h3>
<ul>
<li>可变对象:改变对象内容,对象在内存中的地址<span style="color: rgba(210, 44, 74, 1)">不会</span>被改变</li>
<li>不可变对象:改变对象内容,对象在内存中的地址<span style="color: rgba(210, 44, 74, 1)">会</span>被改变;如果必须存储一个不同的值,则必须创建新的对象</li>
</ul>
<p>&nbsp;</p>
<h4>不可变对象的应用场景</h4>
<p>它们在需要常量哈希值的地方起着重要作用,例如作为字典中的键</p>
<p>&nbsp;</p>
<h3 id="hid-iK3G5N"><strong>从内存角度出发说下有什么区别?</strong></h3>
<h4 id="hid-xkDzNC"><strong>不可变对象</strong></h4>
<p><strong><img src="https://img2020.cnblogs.com/blog/1896874/202103/1896874-20210301211549664-1358897679.png" alt="" class="medium-zoom-image" loading="lazy"></strong></p>
<ul>
<li>Python 中的变量有一个内存空间</li>
<li>具体的数据(对象)也有一个内存空间</li>
<li>而变量保存(指向)的是存储数据(对象)的内存地址,一般也叫<span style="color: rgba(210, 44, 74, 1)">对象引用</span></li>
<li>不可变对象是指对象内容本身不可变</li>
<li>变的是:改变了值,会创建新对象,然后变量改变了对象引用,指向了新对象,旧对象会被垃圾回收</li>
</ul>
<p>&nbsp;</p>
<h4 id="hid-Y5AK5w">可变对象</h4>
<p><img src="https://img2020.cnblogs.com/blog/1896874/202103/1896874-20210301214710421-312061590.png" alt="" class="medium-zoom-image" loading="lazy"></p>
<p>变的是:原来对象的内容,不会创建新对象,而变量也还是指向原对象</p>
<p>&nbsp;</p>
<h3>从代码角度看看区别&nbsp;</h3>
<h4>不可变对象-整型</h4>
<div class="cnblogs_code">
<pre>a = 123<span style="color: rgba(0, 0, 0, 1)">
b </span>=<span style="color: rgba(0, 0, 0, 1)"> a
</span><span style="color: rgba(0, 0, 255, 1)">print</span><span style="color: rgba(0, 0, 0, 1)">(id(a))
</span><span style="color: rgba(0, 0, 255, 1)">print</span><span style="color: rgba(0, 0, 0, 1)">(id(b))
</span><span style="color: rgba(0, 0, 255, 1)">print</span><span style="color: rgba(0, 0, 0, 1)">(a, b)

a </span>+= 2

<span style="color: rgba(0, 0, 255, 1)">print</span><span style="color: rgba(0, 0, 0, 1)">(id(a))
</span><span style="color: rgba(0, 0, 255, 1)">print</span><span style="color: rgba(0, 0, 0, 1)">(id(b))
</span><span style="color: rgba(0, 0, 255, 1)">print</span><span style="color: rgba(0, 0, 0, 1)">(a, b)


</span><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 输出结果</span>
4473956912
4473956912
123 123
4473956976
4473956912
125 123</pre>
</div>
<ul>
<li>从前两次打印可以看到,a、b 变量保存的内存地址是同一个,他们们都保存了 123 的内存地址(123 对象的引用)</li>
<li><span style="color: rgba(210, 44, 74, 1)">预期情况:</span>在 a 做了加法赋值运算之后,既然他们一开始都是指向同一个内存地址,按道理修改 123 后,他们也应该仍然指向同一个内存地址呀,但是并没有!</li>
<li><span style="color: rgba(210, 44, 74, 1)">实际情况:</span>a 指向了新的内存地址,而 b 仍然指向旧的内存地址,所以他们的值也不一样</li>
</ul>
<p>&nbsp;</p>
<h4>可以看看下面的图</h4>
<p>首先,这是一个内存区域</p>
<p><img src="https://img2020.cnblogs.com/blog/1896874/202107/1896874-20210729230249499-1141173869.png" alt="" loading="lazy"></p>
<p><span style="color: rgba(210, 44, 74, 1)"><strong>原理</strong></span></p>
<ul>
<li>因为数字(int、float) 是不可变对象,所以不能在 123 的内存地址上直接修改数据</li>
<li>加法赋值,实际上是将原来的 123 复制了一份到新的内存地址,然后再做加法,得到一个新的值 125,最后 a 再指向新的内存地址</li>
</ul>
<p>&nbsp;</p>
<h4>不可变对象-字符串</h4>
<div class="cnblogs_code">
<pre>a = <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">test</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">
b </span>=<span style="color: rgba(0, 0, 0, 1)"> a
</span><span style="color: rgba(0, 0, 255, 1)">print</span><span style="color: rgba(0, 0, 0, 1)">(id(a))
</span><span style="color: rgba(0, 0, 255, 1)">print</span><span style="color: rgba(0, 0, 0, 1)">(id(b))
</span><span style="color: rgba(0, 0, 255, 1)">print</span><span style="color: rgba(0, 0, 0, 1)">(a, b)

a </span>+= <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">123</span><span style="color: rgba(128, 0, 0, 1)">"</span>

<span style="color: rgba(0, 0, 255, 1)">print</span><span style="color: rgba(0, 0, 0, 1)">(id(a))
</span><span style="color: rgba(0, 0, 255, 1)">print</span><span style="color: rgba(0, 0, 0, 1)">(id(b))
</span><span style="color: rgba(0, 0, 255, 1)">print</span><span style="color: rgba(0, 0, 0, 1)">(a, b)


</span><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 输出结果</span>
4455345392
4455345392<span style="color: rgba(0, 0, 0, 1)">
test test
</span>4455818288
4455345392<span style="color: rgba(0, 0, 0, 1)">
test123 test</span></pre>
</div>
<p>&nbsp;</p>
<h4>不可变对象-元组</h4>
<div class="cnblogs_code">
<pre>a = (1, 2, 3<span style="color: rgba(0, 0, 0, 1)">)
b </span>=<span style="color: rgba(0, 0, 0, 1)"> a
</span><span style="color: rgba(0, 0, 255, 1)">print</span><span style="color: rgba(0, 0, 0, 1)">(id(a))
</span><span style="color: rgba(0, 0, 255, 1)">print</span><span style="color: rgba(0, 0, 0, 1)">(id(b))
</span><span style="color: rgba(0, 0, 255, 1)">print</span><span style="color: rgba(0, 0, 0, 1)">(a, b)

a </span>= a +<span style="color: rgba(0, 0, 0, 1)"> a
</span><span style="color: rgba(0, 0, 255, 1)">print</span><span style="color: rgba(0, 0, 0, 1)">(id(a))
</span><span style="color: rgba(0, 0, 255, 1)">print</span><span style="color: rgba(0, 0, 0, 1)">(id(b))
</span><span style="color: rgba(0, 0, 255, 1)">print</span><span style="color: rgba(0, 0, 0, 1)">(a, b)


</span><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 输出结果</span>
4455410240
4455410240<span style="color: rgba(0, 0, 0, 1)">
(</span>1, 2, 3) (1, 2, 3<span style="color: rgba(0, 0, 0, 1)">)
</span>4455359200
4455410240<span style="color: rgba(0, 0, 0, 1)">
(</span>1, 2, 3, 1, 2, 3) (1, 2, 3)</pre>
</div>
<p>&nbsp;&nbsp;</p>
<h4>可变对象列表</h4>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 列表</span>
a =
b </span>=<span style="color: rgba(0, 0, 0, 1)"> a

</span><span style="color: rgba(0, 0, 255, 1)">print</span><span style="color: rgba(0, 0, 0, 1)">(id(a))
</span><span style="color: rgba(0, 0, 255, 1)">print</span><span style="color: rgba(0, 0, 0, 1)">(id(b))
</span><span style="color: rgba(0, 0, 255, 1)">print</span><span style="color: rgba(0, 0, 0, 1)">(a, b)

a </span>+=

</span><span style="color: rgba(0, 0, 255, 1)">print</span><span style="color: rgba(0, 0, 0, 1)">(a, b)
</span><span style="color: rgba(0, 0, 255, 1)">print</span><span style="color: rgba(0, 0, 0, 1)">(id(a))
</span><span style="color: rgba(0, 0, 255, 1)">print</span><span style="color: rgba(0, 0, 0, 1)">(id(b))


</span><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 输出结果</span>
4327665856
4327665856<span style="color: rgba(0, 0, 0, 1)">
[</span>1, 2, 3, 4, 5, 6]
</span>4327665856
4327665856</pre>
</div>
<p>能看到 a 变量修改值之后,b 的值也随之修改了</p>
<p>&nbsp;</p>
<h4>可以看看下面的图</h4>
<p><img src="https://img2020.cnblogs.com/blog/1896874/202107/1896874-20210730202130156-190699005.png" alt="" loading="lazy">&nbsp;</p>
<ul>
<li>因为 list 是可变对象,所以并不会将原来的值复制到新的内存地址再改变,而是直接在原来的内存地址上修改数据</li>
<li>因为 a、b 都是指向原来的内存地址的,所以 a、b 变量保存的内存地址是一致的(对象引用是一致的),当然值也是一样的啦</li>
</ul>
<p>&nbsp;</p>
<h3>Python 函数的参数传递</h3>
<p>这里先提前讲下函数的入门,因为参数传递是个挺重要的点</p>
<p>&nbsp;</p>
<h4>概念</h4>
<ul>
<li>开头有讲到,Python 的一切传递都是对象的引用,函数参数传递也不例外</li>
<li>当传递给函数的是一个变量,实际上传递的是变量保存的对象引用(变量指向的内存地址)</li>
<li>在函数内部修改变量时,会根据变量指向的内存地址,去修改对应的值才对,事实真是如此吗</li>
</ul>
<p>&nbsp;</p>
<h4>参数传递不可变对象</h4>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 函数</span>
<span style="color: rgba(0, 0, 255, 1)">def</span><span style="color: rgba(0, 0, 0, 1)"> test_no_define(age, name):
    age </span>= 123<span style="color: rgba(0, 0, 0, 1)">
    name </span>= <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">poloyy</span><span style="color: rgba(128, 0, 0, 1)">"</span>
    <span style="color: rgba(0, 0, 255, 1)">print</span><span style="color: rgba(0, 0, 0, 1)">(age, name)


age </span>= 1<span style="color: rgba(0, 0, 0, 1)">
name </span>= <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">yy</span><span style="color: rgba(128, 0, 0, 1)">"</span>
<span style="color: rgba(0, 0, 255, 1)">print</span><span style="color: rgba(0, 0, 0, 1)">(age, name)

test_no_define(age, name)
</span><span style="color: rgba(0, 0, 255, 1)">print</span><span style="color: rgba(0, 0, 0, 1)">(age, name)


</span><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 输出结果</span>
1<span style="color: rgba(0, 0, 0, 1)"> yy
</span>123<span style="color: rgba(0, 0, 0, 1)"> poloyy
</span>1 yy</pre>
</div>
<p>&nbsp;</p>
<h4>参数传递可变对象</h4>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 函数</span>
<span style="color: rgba(0, 0, 255, 1)">def</span><span style="color: rgba(0, 0, 0, 1)"> test_define(dicts, sets):
    dicts[</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">age</span><span style="color: rgba(128, 0, 0, 1)">'</span>] = 24<span style="color: rgba(0, 0, 0, 1)">
    sets.pop()
    </span><span style="color: rgba(0, 0, 255, 1)">print</span><span style="color: rgba(0, 0, 0, 1)">(dicts, sets)


dicts </span>= {<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">age</span><span style="color: rgba(128, 0, 0, 1)">"</span>: 123<span style="color: rgba(0, 0, 0, 1)">}
sets </span>= {1, 2<span style="color: rgba(0, 0, 0, 1)">}
</span><span style="color: rgba(0, 0, 255, 1)">print</span><span style="color: rgba(0, 0, 0, 1)">(dicts, sets)

test_define(dicts, sets)
</span><span style="color: rgba(0, 0, 255, 1)">print</span><span style="color: rgba(0, 0, 0, 1)">(dicts, sets)


</span><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 输出结果</span>
1<span style="color: rgba(0, 0, 0, 1)"> yy
{</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">age</span><span style="color: rgba(128, 0, 0, 1)">'</span>: 123} {1, 2<span style="color: rgba(0, 0, 0, 1)">}
{</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">age</span><span style="color: rgba(128, 0, 0, 1)">'</span>: 24} {2<span style="color: rgba(0, 0, 0, 1)">}
{</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">age</span><span style="color: rgba(128, 0, 0, 1)">'</span>: 24} {2}</pre>
</div>
<p>&nbsp;&nbsp;</p>
<h4>总结</h4>
<ul>
<li>当函数参数传递的变量是不可变对象的时候,函数内改变变量值,函数外的变量<span style="color: rgba(210, 44, 74, 1)">不会</span>随之改变</li>
<li>当函数参数传递的变量是可变对象的时候,函数内改变变量值,函数外的变量<span style="color: rgba(210, 44, 74, 1)">会</span>随之改变</li>
</ul>
<p>&nbsp;</p><br><br>
来源:https://www.cnblogs.com/poloyy/p/15073168.html
頁: [1]
查看完整版本: Python - 可变和不可变对象