python中的可变对象和不变对象类型,python对象下的可变类型与不可变类型

前言

python对象对于修改这个行为,大家的操作方式都是不一样的。有些对象是可变类型,所以他就可以直接修改。其它的都是不可变类型,那我们要修改他时怎么办呢,这个时候就新生成的一个对象,然后将变量名指向这个新对象,修改动作就完成了。关于可变类型与不可变类型的一个特点,我们可以先看看一段奇怪的代码

In [1]: a = 'a'

In [2]: b = 'a'

In [3]: a is b

Out[3]: True

In [4]: a == b

Out[4]: True

In [5]: c = [1]

In [6]: d = [1]

In [7]: c is d

Out[7]: False

In [8]: c == d

Out[8]: True

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

In[1]:a='a'

In[2]:b='a'

In[3]:aisb

Out[3]:True

In[4]:a==b

Out[4]:True

In[5]:c=[1]

In[6]:d=[1]

In[7]:cisd

Out[7]:False

In[8]:c==d

Out[8]:True

你有没有觉得,a b 明明都是不同的对象,但是使用a is b 的时候 竟然返回了true ,这里面就会深入到对象的别名,标识与相等性,先说回到可变类型与不可变类型

分类

不可变类型

int

float

decimal

complex

bool

string

tuple

range

frozenset

bytes

可变类型

list

dict

set

bytearray

user-defined classes (unless specifically made immutable)

有个简单的方式来区分这些类型,类似的容器类型的数据就是可变类型(list,set,dict)—我们就想像一个容器可以不停的注入内容,其它的基本都是不可变的类型,这里有几个特殊的

元组 tuple

frozenset

str

区分类型?有这个必要吗?

当然是显然的,虽然我们在实际的工程环节都会围绕整体的业务问题,很少去了解底层的原理,但是我们一旦涉及到优化,涉及到底层的时候就很有必要了解技术细节。举个例子,如何高效的拼接字符串,我想下面的代码大部分人都写过,包括我

In [13]: str_build = ''

In [14]: for p in 'aaabbbccc':

...: str_build = str_build + p

1

2

3

4

In[13]:str_build=''

In[14]:forpin'aaabbbccc':

...:str_build=str_build+p

事实上,这样的效率并不是很高。如上所言,我们知道str 是不可变类型,我们在拼接字符串的时候,实际上是生成了一个新的对象,然后在变量名指向他,随着拼接的次数越来越多,我们生成对象的操作次数也会越来越多,这样就很没有这个必要(C# 里面的StringBuilder 就是为了解决字符串拼接的),那有效率的写法是怎样的呢

builder_list = []

for data in container:

builder_list.append(str(data))

"".join(builder_list)

### Another way is to use a list comprehension

"".join([str(data) for data in container])

### or use the map function

"".join(map(str, container))

1

2

3

4

5

6

7

8

9

10

11

12

builder_list=[]

fordataincontainer:

builder_list.append(str(data))

"".join(builder_list)

### Another way is to use a list comprehension

"".join([str(data)fordataincontainer])

### or use the map function

"".join(map(str,container))

我们通过使用列表,可以轻松的在本身对象的基础上修改内容,不会生成新对象来处理操作,这样就会节省内存

另外的一个我们会遇到关于可变类型的一个坑

def my_function(param=[]):

param.append("thing")

return param

my_function() # returns ["thing"]

my_function() # returns ["thing", "thing"]

1

2

3

4

5

6

7

defmy_function(param=[]):

param.append("thing")

returnparam

my_function()# returns ["thing"]

my_function()# returns ["thing", "thing"]

你会不会觉得,我去,不管我调用多少次,这丫不是应该就返回一个[‘thing’]回来吗?但是事实上你也看到了,因为可变对象一直都会引用同一段内容,我们在操作列表的时候都会对同一个列表操作,比如我们在函数体里面操作了一个set ,list ,或者 dict ,虽然我们没有返回内容,但是外部的内容还是会变的,所以我们要记得,不要给可变对象做为参数的时候加上默认值!

def my_function2(param=None):

if param is None:

param = []

param.append("thing")

return param

1

2

3

4

5

6

defmy_function2(param=None):

ifparamisNone:

param=[]

param.append("thing")

returnparam

结论

正确的区分可变类型与不可变类型,对于我们深入了解python是非常有帮助的,更多的关注技术细节,才能更好的拔高我们的技术水平

番外

我们在前言里面有提到的那个奇怪的问题,很显然,那是共享了字符串的字面量,这种Cpython里面一个优化策略,叫驻留(interning).CPython 还会在小的整数上使用这个优化措施,防止重复的创建”热门“数字,比如0,-1,和42等等,但是CPython不会驻留所有的字符串和整数。比如如下的代码

In [5]: c = 1

In [6]: d = 1

In [7]: id(c)

Out[7]: 4297514912

In [8]: id(d)

Out[8]: 4297514912

In [9]: e = 22569

In [10]: f = 22569

In [11]: e is f

Out[11]: False

In [12]: c is d

Out[12]: True

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

In[5]:c=1

In[6]:d=1

In[7]:id(c)

Out[7]:4297514912

In[8]:id(d)

Out[8]:4297514912

In[9]:e=22569

In[10]:f=22569

In[11]:eisf

Out[11]:False

In[12]:cisd

Out[12]:True

所以千万不要依赖字符串或者整数的驻留!比较字符串或者整数是否相等的时,应该使用== 而不是is. 驻留是python 解释器内部使用的一个特性。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值