1. 程式人生 > >python 可變變數與不可變變數區分

python 可變變數與不可變變數區分

轉:https://blog.csdn.net/god_wen/article/details/78423621

不可變變數

我們可以通過下面的例子來感受上面的話

 

<span style="color:#000000"><code>52454056
52454032
</code></span>

id()是一個內建函式,可以返回物件的唯一標識 
同樣的變數x進過經過加操作後地址改變了。

<span style="color:#000000"><code>x=<span style="color:#006666">1</span>
y=<span style="color:#006666">1</span>
z=<span style="color:#006666">1</span>
<span style="color:#4f4f4f">print</span> id(x)
<span style="color:#4f4f4f">print</span> id(y)
<span style="color:#4f4f4f">print</span> id(z)</code></span>
<span style="color:#000000"><code>56976040
56976040
56976040
</code></span>

可以發現雖然變數名不同但是地址確實一樣的。

這種特性是不是很想別的語言中的字串。我們稱有這種特性的變數為不可變變數(值一旦改變就會佔據新的記憶體空間)。python中的不可變變數有如下

不可變(immutable):int、字串(string)、float、(數值型number)、元組(tuple),None

優點: 
這樣可以減少重複的值對記憶體空間的佔用。 
缺點: 
操作之後就開闢記憶體的特性。執行效率會有一定程度的降低。

可變變數

我再來看看可變變數

<span style="color:#000000"><code>x=[]
<span style="color:#4f4f4f">print</span> id(x)
x.<span style="color:#4f4f4f">append</span><span style="color:#006666">(1</span>)
<span style="color:#4f4f4f">print</span> id(x)</code></span>
<span style="color:#000000"><code>49918024
49918024
</code></span>

可以看見可變數和別的語言是一樣的。記憶體是不會改變的

<span style="color:#000000"><code>x=[]
y=[]
z=[]
<span style="color:#4f4f4f">print</span> id(x)
<span style="color:#4f4f4f">print</span> id(y)
<span style="color:#4f4f4f">print</span> id(z)</code></span>
<span style="color:#000000"><code>62566472
62669960
62671368
</code></span>

可以看見對於可變變數 ,建立一次就開闢一次地址,不會引用同一塊記憶體

可變(mutable)變數:字典型(dictionary)、列表型(list)

預設引數時注意事項

python 中不可變數為按值傳遞,可變變數按引用傳遞

<span style="color:#000000"><code><span style="color:#000088">def</span> <span style="color:#009900">f</span><span style="color:#4f4f4f">(l=[])</span>:
    l.append(<span style="color:#006666">1</span>)
    <span style="color:#000088">return</span> l
<span style="color:#000088">print</span> f()
<span style="color:#000088">print</span> f()
<span style="color:#000088">print</span> f()</code></span>
<span style="color:#000000"><code>[1]
[1, 1]
[1, 1, 1]
</code></span>

這是為什麼呢?首先我們要知道python 你看見的任何東西都是物件。函式也不例外,預設引數有如下特點

  • 一個函式引數的預設值,僅僅在該函式定義的時候,被賦值一次。
  • 函式預設引數引數在每次呼叫時指向那個定義時建立的引數

現在我們來驗證上上面的結果。 
引數的預設值就存在__defaults__中。

<span style="color:#000000"><code><span style="color:#000088">def</span> <span style="color:#009900">f</span><span style="color:#4f4f4f">(l=[])</span>:

    l.append(<span style="color:#006666">1</span>)
    <span style="color:#000088">return</span> l
f()
<span style="color:#000088">print</span> f.__defaults__
f()
<span style="color:#000088">print</span> f.__defaults__
f()
<span style="color:#000088">print</span> f.__defaults__
</code></span>
<span style="color:#000000"><code>([1],)
([1, 1],)
([1, 1, 1],)
</code></span>

可以看見引數的預設值就在其中,但是是不是同一個呢,現在我們來看看地址

<span style="color:#000000"><code><span style="color:#000088">def</span> <span style="color:#009900">f</span><span style="color:#4f4f4f">(i=<span style="color:#006666">1</span>)</span>:
    <span style="color:#000088">print</span> id(i)
    i+=<span style="color:#006666">1</span>
    <span style="color:#000088">print</span> id(i)
    <span style="color:#000088">return</span> i

f()
<span style="color:#000088">print</span> <span style="color:#009900">'預設值地址:%s'</span>%id(f.__defaults__[<span style="color:#006666">0</span>])
f()
<span style="color:#000088">print</span> <span style="color:#009900">'預設值地址:%s'</span>%+id(f.__defaults__[<span style="color:#006666">0</span>])
f()
<span style="color:#000088">print</span> <span style="color:#009900">'預設值地址:%s'</span>%+id(f.__defaults__[<span style="color:#006666">0</span>])</code></span>
<span style="color:#000000"><code>56976040
56976016
預設值地址:56976040
56976040
56976016
預設值地址:56976040
56976040
56976016
預設值地址:56976040
</code></span>

可以發現函式定義時預設值就已近存在在__defaults__中了。在呼叫函式時我們的引數變數就會被賦值為這個__defaults__[0]地址。當.__defaults__[0]是不可變變數時 
我們對引數變數進行修改時我們的引數變數地址會變化。這個和我們的邏輯思維一致

可變變數就不一樣了!

<span style="color:#000000"><code><span style="color:#000088">def</span> <span style="color:#009900">f</span><span style="color:#4f4f4f">(l=[])</span>:
    <span style="color:#000088">print</span> id(l)
    l.append(<span style="color:#006666">1</span>)
    <span style="color:#000088">print</span> id(l)
    <span style="color:#000088">return</span> l

f()
<span style="color:#000088">print</span> <span style="color:#009900">'預設值地址:%s'</span>%id(f.__defaults__[<span style="color:#006666">0</span>])
f()
<span style="color:#000088">print</span> <span style="color:#009900">'預設值地址:%s'</span>%id(f.__defaults__[<span style="color:#006666">0</span>])
f()
<span style="color:#000088">print</span> <span style="color:#009900">'預設值地址:%s'</span>%id(f.__defaults__[<span style="color:#006666">0</span>])</code></span>
<span style="color:#000000"><code>58904648
58904648
預設值地址:58904648
58904648
58904648
預設值地址:58904648
58904648
58904648
預設值地址:58904648
</code></span>

因為可變變數的特性。我們在更改時時不會換地址的,所以我們一直都是在更改__defaults__[0] 。這種特性在預設引數中不是我們想要的。我們用如下的程式碼完成任務

<span style="color:#000000"><code><span style="color:#000088">def</span> <span style="color:#009900">f</span><span style="color:#4f4f4f">(l=None)</span>:
    <span style="color:#000088">print</span> id(l)
    l=[]
    l.append(<span style="color:#006666">1</span>)
    <span style="color:#000088">print</span> id(l)
    <span style="color:#000088">return</span> l

<span style="color:#000088">print</span> f()
<span style="color:#000088">print</span> <span style="color:#009900">'預設值地址:%s'</span>%id(f.__defaults__[<span style="color:#006666">0</span>])
<span style="color:#000088">print</span> f()
<span style="color:#000088">print</span> <span style="color:#009900">'預設值地址:%s'</span>%id(f.__defaults__[<span style="color:#006666">0</span>])
<span style="color:#000088">print</span> f()
<span style="color:#000088">print</span> <span style="color:#009900">'預設值地址:%s'</span>%id(f.__defaults__[<span style="color:#006666">0</span>])
</code></span>
<span style="color:#000000"><code>1792584792
47296584
[1]
預設值地址:1792584792
1792584792
47296584
[1]
預設值地址:1792584792
1792584792
47296584
[1]
預設值地址:1792584792
</code></span>

因為None是不可變變數,所以我對他的所有操作都是在新的記憶體中完成的。這其實也就是None的作用

類中全域性變數的注意事項

<span style="color:#000000"><code><span style="color:#000088">class</span> <span style="color:#4f4f4f">b</span>:
    x = []
    <span style="color:#000088">def</span> <span style="color:#009900">set</span><span style="color:#4f4f4f">(self)</span>:
        self.x.append(<span style="color:#006666">1</span>)
    <span style="color:#000088">def</span> <span style="color:#009900">get</span><span style="color:#4f4f4f">(self)</span>:
        <span style="color:#000088">return</span> self.x
<span style="color:#000088">for</span> i <span style="color:#000088">in</span> range(<span style="color:#006666">3</span>):
    a = b()
    <span style="color:#000088">print</span> b.__dict__
    a.set()
    a.get()
</code></span>
<span style="color:#000000"><code>{'x': [], '__module__': '__main__', 'set': <function set at 0x0000000002DE5AC8>, '__doc__': None, 'get': <function get at 0x0000000002DE5B38>}
{'x': [1], '__module__': '__main__', 'set': <function set at 0x0000000002DE5AC8>, '__doc__': None, 'get': <function get at 0x0000000002DE5B38>}
{'x': [1, 1], '__module__': '__main__', 'set': <function set at 0x0000000002DE5AC8>, '__doc__': None, 'get': <function get at 0x0000000002DE5B38>}
</code></span>

可以發現每次建立類b時,其中的x都是不一樣的。這也是因為可變變數的原因

而對於不可變變數就不一樣了

<span style="color:#000000"><code>
<span style="color:#000088">class</span> <span style="color:#4f4f4f">b</span>:
    x = <span style="color:#006666">1</span>
    <span style="color:#000088">def</span> <span style="color:#009900">set</span><span style="color:#4f4f4f">(self)</span>:
        self.x+=<span style="color:#006666">1</span>
    <span style="color:#000088">def</span> <span style="color:#009900">get</span><span style="color:#4f4f4f">(self)</span>:
        <span style="color:#000088">return</span> self.x
<span style="color:#000088">for</span> i <span style="color:#000088">in</span> range(<span style="color:#006666">3</span>):
    a = b()

    <span style="color:#000088">print</span> b.__dict__
    a.set()
    <span style="color:#000088">print</span> a.get()
</code></span>
<span style="color:#000000"><code>{'x': 1, '__module__': '__main__', 'set': <function set at 0x000000000313AAC8>, '__doc__': None, 'get': <function get at 0x000000000313AB38>}
2
{'x': 1, '__module__': '__main__', 'set': <function set at 0x000000000313AAC8>, '__doc__': None, 'get': <function get at 0x000000000313AB38>}
2
{'x': 1, '__module__': '__main__', 'set': <function set at 0x000000000313AAC8>, '__doc__': None, 'get': <function get at 0x000000000313AB38>}
2
</code></span>

可以發現,原先的”全域性變數“現在已經不是全域性變量了