use*_*217 172 python mutable immutability
我对不可变类型是多么困惑.我知道该float对象被认为是不可变的,我的书中有这种类型的例子:
class RoundFloat(float):
def __new__(cls, val):
return float.__new__(cls, round(val, 2))
Run Code Online (Sandbox Code Playgroud)
这是因为类结构/层次结构被认为是不可变的吗?意思float是在类的顶部并且是它自己的方法调用.类似于这种类型的例子(即使我的书说dict是可变的):
class SortedKeyDict(dict):
def __new__(cls, val):
return dict.__new__(cls, val.clear())
Run Code Online (Sandbox Code Playgroud)
虽然可变的东西在类中有方法,但是这种类型的例子:
class SortedKeyDict_a(dict):
def example(self):
return self.keys()
Run Code Online (Sandbox Code Playgroud)
另外,对于最后一个class(SortedKeyDict_a),如果我将这种类型的集合传递给它:
d = (('zheng-cai', 67), ('hui-jun', 68),('xin-yi', 2))
Run Code Online (Sandbox Code Playgroud)
在不调用example方法的情况下,它返回一个字典.SortedKeyDictwith 将其__new__标记为错误.我尝试将整数传递给RoundFloat类,__new__并且它没有标记错误.
mor*_*tar 223
什么?浮动是不可改变的?但我不能这样做
x = 5.0
x += 7.0
print x # 12.0
Run Code Online (Sandbox Code Playgroud)
那不是"mut"x吗?
你同意字符串是不可变的吗?但你可以做同样的事情.
s = 'foo'
s += 'bar'
print s # foobar
Run Code Online (Sandbox Code Playgroud)
变量的值会发生变化,但会通过更改变量引用的内容来更改.可变类型可以改变这种方式,它也可以 "就地"改变.
这是区别.
x = something # immutable type
print x
func(x)
print x # prints the same thing
x = something # mutable type
print x
func(x)
print x # might print something different
x = something # immutable type
y = x
print x
# some statement that operates on y
print x # prints the same thing
x = something # mutable type
y = x
print x
# some statement that operates on y
print x # might print something different
Run Code Online (Sandbox Code Playgroud)
具体例子
x = 'foo'
y = x
print x # foo
y += 'bar'
print x # foo
x = [1, 2, 3]
y = x
print x # [1, 2, 3]
y += [3, 2, 1]
print x # [1, 2, 3, 3, 2, 1]
def func(val):
val += 'bar'
x = 'foo'
print x # foo
func(x)
print x # foo
def func(val):
val += [3, 2, 1]
x = [1, 2, 3]
print x # [1, 2, 3]
func(x)
print x # [1, 2, 3, 3, 2, 1]
Run Code Online (Sandbox Code Playgroud)
seb*_*ebs 180
您必须了解Python将其所有数据表示为对象.列表和词典中的一些对象是可变的,这意味着您可以在不改变其身份的情况下更改其内容.整数,浮点数,字符串和元组等其他对象是无法更改的对象.理解这一点的简单方法是,如果您查看对象ID.
下面你看到一个不可变的字符串.您无法更改其内容.TypeError如果你试图改变它,它会引发一个.此外,如果我们分配新内容,则会创建一个新对象而不是正在修改的内容.
>>> s = "abc"
>>>id(s)
4702124
>>> s[0]
'a'
>>> s[0] = "o"
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'str' object does not support item assignment
>>> s = "xyz"
>>>id(s)
4800100
>>> s += "uvw"
>>>id(s)
4800500
Run Code Online (Sandbox Code Playgroud)
您可以使用列表执行此操作,但不会更改对象标识
>>> i = [1,2,3]
>>>id(i)
2146718700
>>> i[0]
1
>>> i[0] = 7
>>> id(i)
2146718700
Run Code Online (Sandbox Code Playgroud)
要阅读有关Python数据模型的更多信息,您可以查看Python语言参考:
Mr.*_*Pei 102
常见的不可变类型:
int(),float(),complex()str(),tuple(),frozenset(),bytes()常见的可变类型(几乎所有其他):
list(),bytearray()set()dict()快速测试类型是否可变的一个技巧是使用id()内置函数.
示例,使用整数,
>>> i = 1
>>> id(i)
***704
>>> i += 1
>>> i
2
>>> id(i)
***736 (different from ***704)
Run Code Online (Sandbox Code Playgroud)
使用列表,
>>> a = [1]
>>> id(a)
***416
>>> a.append(2)
>>> a
[1, 2]
>>> id(a)
***416 (same with the above id)
Run Code Online (Sandbox Code Playgroud)
agf*_*agf 33
首先,类是否具有方法或类的结构与可变性无关.
ints和floats是不可变的.如果我做
a = 1
a += 5
Run Code Online (Sandbox Code Playgroud)
它将名称指向第一行内存a中的1某个位置.在第二行,它查找的是1,添加5,获取6,然后指向a那个6内存-它并没有改变的1一个6以任何方式.相同的逻辑适用于以下示例,使用其他不可变类型:
b = 'some string'
b += 'some other string'
c = ('some', 'tuple')
c += ('some', 'other', 'tuple')
Run Code Online (Sandbox Code Playgroud)
对于可变类型,我可以做一些事情,它可以改变存储在内存中的值.附:
d = [1, 2, 3]
Run Code Online (Sandbox Code Playgroud)
我创建的位置的列表1,2以及3在内存中.如果我那么做
e = d
Run Code Online (Sandbox Code Playgroud)
我只是指出e了相同的list d观点.然后我可以这样做:
e += [4, 5]
Run Code Online (Sandbox Code Playgroud)
并且将更新两者e和d指向的列表也具有内存4和5内存的位置.
如果我回到不可变类型并使用以下内容执行此操作tuple:
f = (1, 2, 3)
g = f
g += (4, 5)
Run Code Online (Sandbox Code Playgroud)
然后f仍然只指向原始tuple - 你已经指出g了一个全新的tuple.
现在,用你的例子
class SortedKeyDict(dict):
def __new__(cls, val):
return dict.__new__(cls, val.clear())
Run Code Online (Sandbox Code Playgroud)
你通过的地方
d = (('zheng-cai', 67), ('hui-jun', 68),('xin-yi', 2))
Run Code Online (Sandbox Code Playgroud)
(这是一个tuple的tuples)的val,你得到一个错误,因为tuple都不具备的一个.clear()方法-你必须通过dict(d)为val它的工作,在这种情况下,你会得到一个空的SortedKeyDict结果.
aba*_*ert 23
如果你是从另一种语言(除了像Python这样很像Python的Python)来使用Python,并且坚持用其他语言理解它,那么人们通常会把这种语言弄糊涂:
>>> a = 1
>>> a = 2 # I thought int was immutable, but I just changed it?!
Run Code Online (Sandbox Code Playgroud)
在Python中,赋值不是Python中的变异.
在C++中,如果你写a = 2,你正在调用a.operator=(2),这会改变存储的对象a.(如果有是存储在没有对象a,这是一个错误.)
在Python中,a = 2对存储的内容不做任何事情a; 它只是意味着2现在存储在其中a.(如果有是没有存储在对象a,这很好.)
最终,这是更深层次区分的一部分.
像C++这样的语言中的变量是内存中的类型位置.如果a是int,那意味着编译器知道的某个地方有4个字节应该被解释为int.所以,当你这样做时a = 2,它会改变存储在那4个字节的内存0, 0, 0, 1中的内容0, 0, 0, 2.如果在其他地方有另一个int变量,它有自己的4个字节.
像Python这样的语言中的变量是具有自己生命的对象的名称.数字有一个对象,数字有1另一个对象2.并且a不是4个字节的内存表示为int,它只是指向1对象的名称.a = 2将数字1转换为数字2 是没有意义的(这将使任何Python程序员都有太多的权力来改变宇宙的基本运作方式); 它所做的只是a忘记1对象并指向2对象.
那么,如果赋值不是变异,那么什么是变异呢?
a.append(b).(注意,这些方法几乎总是返回None).不可变类型没有任何这样的方法,通常可变类型.a.spam = b或a[0] = b.不可变类型不允许赋值给属性或元素,可变类型通常允许一个或另一个.a += b,有时候不使用.可变类型通常会改变该值; 不可变类型永远不会做,而是给你一个副本(他们计算a + b,然后分配结果a).但如果赋值不是变异,那么如何分配对象变异的一部分呢?这就是它变得棘手的地方.a[0] = b不会不发生变异a[0](再次,与C++),但它不发生变异a(不像C++,除了间接地).
所有这一切都是为什么最好不要尝试将Python的语义放在你习惯的语言中,而是以自己的方式学习Python的语义.
tal*_*nat 18
对象是否可变取决于其类型.这不取决于它是否具有某些方法,也不依赖于类层次结构.
用户定义的类型(即类)通常是可变的.有一些例外,例如不可变类型的简单子类.其他不变类型包括一些内置的类型,如int,float,tuple并str用C实现的,以及一些Python类
某些对象的值可以更改.值可以改变的对象被认为是可变的; 一旦创建它们的值不可更改的对象称为不可变.
(包含对可变对象的引用的不可变容器对象的值可以在后者的值更改时更改;但是容器仍然被认为是不可变的,因为它包含的对象集合无法更改.因此,不变性不严格与具有不可改变的价值相同,它更加微妙.)
对象的可变性由其类型决定; 例如,数字,字符串和元组是不可变的,而字典和列表是可变的.
ana*_*thi 14
可变对象:创建后可以更改的对象.
不可变对象:创建后无法更改的对象.
在python中将尝试更改它将赋予新对象的不可变对象的值.
以下是python中可变类型的列表对象:
listDictionarySetbytearrayuser defined classes以下是python中不可变类型的列表对象:
intfloatdecimalcomplexboolstringtuplerangefrozensetbytes问题:字符串是不可变类型吗?
答:是 的,但你可以解释一下:
证明1:
a = "Hello"
a +=" World"
print a
Run Code Online (Sandbox Code Playgroud)
产量
"Hello World"
Run Code Online (Sandbox Code Playgroud)
在上面的例子中,一旦创建为"Hello",字符串最终变为"Hello World".这意味着字符串是可变类型.但我们不能检查它的身份并检查它是否具有可变类型.
a = "Hello"
identity_a = id(a)
a += " World"
new_identity_a = id(a)
if identity_a != new_identity_a:
print "String is Immutable"
Run Code Online (Sandbox Code Playgroud)
产量
String is Immutable
Run Code Online (Sandbox Code Playgroud)
证明2:
a = "Hello World"
a[0] = "M"
Run Code Online (Sandbox Code Playgroud)
产量
TypeError 'str' object does not support item assignment
Run Code Online (Sandbox Code Playgroud)
问题:元组是不可变的类型吗?
答:是的, 这是
证明1:
tuple_a = (1,)
tuple_a[0] = (2,)
print a
Run Code Online (Sandbox Code Playgroud)
产量
'tuple' object does not support item assignment
Run Code Online (Sandbox Code Playgroud)
nad*_*pez 11
可变对象必须至少具有能够改变对象的方法.例如,list对象具有append实际改变对象的方法:
>>> a = [1,2,3]
>>> a.append('hello') # `a` has mutated but is still the same object
>>> a
[1, 2, 3, 'hello']
Run Code Online (Sandbox Code Playgroud)
但该类float没有方法来改变浮动对象.你可以做:
>>> b = 5.0
>>> b = b + 0.1
>>> b
5.1
Run Code Online (Sandbox Code Playgroud)
但=操作数不是一种方法.它只是在变量和它右边的任何东西之间建立一个绑定,没有别的.它永远不会改变或创建对象.从现在开始,这是变量将指向的声明.
当你b = b + 0.1的=操作结合的变量,以一个新的浮动,至极用的TE结果产生 5 + 0.1.
将变量分配给现有对象(可变或不可变)时,=操作数会将变量绑定到该对象.没有更多的事情发生
在任何一种情况下,=只需进行绑定.它不会更改或创建对象.
当你这样做时a = 1.0,=操作数不会创建浮点数,而是行的1.0一部分.实际上,当你编写1.0它时,它是一个float(1.0)返回浮动对象的构造函数调用的简写.(这就是为什么如果你键入1.0并按回车键,你得到1.0下面打印的"echo" ;这是你调用的构造函数的返回值)
现在,如果b是float和分配a = b,这两个变量都指向同一个对象,但实际上该变量不能comunicate betweem自己,因为对象是inmutable,如果你这样做b += 1,现在b指向一个新的对象,而a被仍指着老人,不知道b指的是什么.
但是,如果c是,让我们说,a list,你分配a = c,现在a,c可以"通信",因为list是可变的,如果你这样做c.append('msg'),那么只需检查a你得到的消息.
(顺便说一句,每个对象都有一个唯一的ID号,你可以使用它id(x).所以你可以检查一个对象是否相同,或者检查它的唯一ID是否已经改变.)
如果该类的每个对象在实例化时具有固定值且无法后续更改,则该类是不可变的
换句话说,更改该变量的整个值(name)或不理会它。
例:
my_string = "Hello world"
my_string[0] = "h"
print my_string
Run Code Online (Sandbox Code Playgroud)
您希望它能正常工作并打印问候世界,但这会引发以下错误:
Traceback (most recent call last):
File "test.py", line 4, in <module>
my_string[0] = "h"
TypeError: 'str' object does not support item assignment
Run Code Online (Sandbox Code Playgroud)
口译员说:我无法更改此字符串的第一个字符
您必须更改整体string以使其起作用:
my_string = "Hello World"
my_string = "hello world"
print my_string #hello world
Run Code Online (Sandbox Code Playgroud)
检查此表:
小智 5
在我看来,您正在与可变或不可变实际上意味着什么的问题作斗争。所以这是一个简单的例子:
首先,我们需要一个基础来进行解释。
因此,请考虑将您编程为虚拟对象的任何事物,以二进制序列形式存储在计算机内存中的事物。(不过,不要试图想像的太难。^^)现在,在大多数计算机语言中,您将不会直接使用这些二进制数,而是更多地使用二进制数的解释。
例如,您不考虑数字0x110、0xaf0278297319或类似数字,而是考虑诸如6的数字或诸如“ Hello,world”之类的字符串。这些数字或字符串永远都不能解释计算机内存中的二进制数。对于变量的任何值也是如此。
简而言之:我们不使用实际值编程,而是使用实际二进制值的解释。
现在,我们确实有一些为了逻辑和其他“精巧的东西”而不能更改的解释,而有些解释很可能会更改。例如,考虑一个城市的模拟,换句话说就是一个程序,其中有许多虚拟对象,其中一些是房屋。现在可以更改这些虚拟对象(房屋),并且仍可以将它们视为相同的房屋吗?当然可以。因此它们是可变的:可以对其进行更改而不会成为“完全”不同的对象。
现在考虑整数:它们也是虚拟对象(计算机内存中的二进制数的序列)。因此,如果我们更改其中之一,就像将值六增加一,它是否仍是六?当然不是。因此,任何整数都是不可变的。
因此:如果虚拟对象中的任何更改意味着它实际上变成了另一个虚拟对象,则称为不可变。
最后说明:
(1)切勿将使用可变语言和不变语言的真实世界经验与某种语言的编程混为一谈:
每种编程语言都有自己的定义,哪些对象可以被静音,哪些对象不能被静音。
因此,尽管您现在可能已经理解了含义上的差异,但仍然必须学习每种编程语言的实际实现。...确实,某种语言的目的可能是使6变为7而使其静音。然后这又将是相当疯狂或有趣的事情,例如对平行宇宙的模拟。^^
(2)这种表述当然不是科学的,它旨在帮助您掌握可变与不变之间的区别。
这个答案的目的是创建一个单一的地方,以找到所有好的主意,这些主意是关于如何判断您是否正在处理变异/非变异(不可变/可变),以及在可能的情况下怎么做?有时候,突变是不受欢迎的,而python在这方面的行为可能与其他语言的编码人员的直觉相反。
根据@ mina-gabriel的有用文章:
分析以上内容并结合@arrakëën的帖子:
什么不能意外改变?
什么可以?
“意料之外”是指其他语言的程序员可能不会期望这种行为(Ruby或Ruby,以及其他一些“ Python一样”的语言除外)。
添加到此讨论中:
当它防止您不小心用占用内存的大型数据结构的多份副本填充代码时,此行为是一个优点。但是,当这种情况不受欢迎时,我们如何解决呢?
使用列表,简单的解决方案是像这样构建一个新的列表:
list2 =列表(list1)
与其他结构一起使用...解决方案可能会更加棘手。一种方法是遍历元素并将其添加到新的(相同类型的)空数据结构中。
当您传入可变结构时,函数会改变原始函数。怎么说呢?
非标准方法(在有帮助的情况下):在MIT许可下发布的github上找到了此方法:
对于自定义类,@ semicolon建议检查是否存在__hash__函数,因为可变对象通常不应具有__hash__()函数。
这是我目前在该主题上积累的所有内容。欢迎其他想法,更正等。谢谢。
| 归档时间: |
|
| 查看次数: |
249935 次 |
| 最近记录: |