很多人都会说到Python是一门强类型、动态类型的语言,为什么这么说呢?
我们先来看一下什么是强类型的语言 百度答案:强类型指的是程序中表达的任何对象所从属的类型都必须能在编译时刻确定。 什么意思呢? 就是说,在编译的时候,变量的类型就可以被编译器确定,并且运行时该变量不经过强制转换将类型无法发生改变。 例如我在Python里面定义了两个不同类型的变量,然后对这两个变量进行以下操作
a = 1
b = "b"
a + b
就会报如下的类型错误(TypeError)
因为a和b是两个不同类型的变量,在不可以进行赋值操作,但是在C语言里面是可以的,因为会进行隐式转换,而在Python里面不会进行类型隐式转换。 对于Python来说,变量不通过int()或str()等方法进行转换的话,那么该变量的类型将无法发生改变 根据百度提供的信息可以了解到:强类型是针对类型检查的严格程度而言的,它指任何变量在使用的时候必须要指定这个变量的类型,而且在程序的运行过程中这个变量只能存储这个类型的数据。
但是我们在用Python的时候就会知道,我们是可以给变量赋其它类型的值的,例如我们可以这样写
a = 1
a = False
b = "b"
a = b
这不是和强类型的定义发生冲突了吗? 其实这涉及到我们要讲的另外一个问题,动态类型。
为什么说Python是动态类型呢?
因为Python的变量并不是指定了类型的,Python的变量在进行赋值的时候,是指向了对象的地址,在进行重新赋值的时候,Python变量并不关心值的类型,因为它只是改变了地址的指向。这种赋值方式报错导致的类型错误,都是在运行的时候才会发生,包括上面举的例子的。
在编译的时候不对变量类型进行识别,在运行的时候可以改变其结构的语言,我们称为动态语言。
因为Python的这个特性,所以可以抛开强类型语言对于赋值时类型的要求,但是在运行时会对变量指向地址的值的类型进行判别。 这就是Python既是动态类型语言,也是强类型语言的原因。
对于Python这种动态指向,在编写程序的时候,给我们带来了很多方便,但是稍不注意,也会让程序发生一些难以察觉的错误。 例如我们对列表操作的时候
当Python将一个列表赋给变量a,此时a并不等于[1,2,3],而是指向了这个列表的地址。 将a的值赋给b的时候,相当于将b的指向修改成a的指向,也就是b也是指向这个列表的地址。当你对b进行操作的时候,就是在对这个地址的值进行操作,所以a的值也会发生改变,当程序在递归或者用树遍历的时候,如果这样进行列表的赋值,将会导致重大的错误。 为了避免这种情况,Python也提供了一些方法给我们进行赋值使用,当我们希望将a和b不是指向同一个列表,但两个列表的值又要一样的时候,我们可以用list的copy()方法来对列表进行复制。这种情况下我们是复制出了一个新的列表赋给了b,所以对b操作的时候不会改变a的值。 另外一种方法就是直接对整个列表进行截取: 当我们使用a[x:y]时,可以截取到a中下标x到下标y的片段; 当x或y的参数放空时,表示从头开始截取或者截取到尾; 例:a[:y]就是截取下标0~y的片段,a[x:]就是从x开始截取到列表结束; 而a[:]就是从头截取到最后一个元素,也就是整个列表都截取下来。
或者是对多维列表的子列表进行修改
但是这两个方法对于多维列表操作单个元素来说,都不起作用!
原因是:列表里面保存的元素,也是地址的指向! 当对多维列表的下一级列表进行操作的时候,是让该下标指向了另一个列表的地址,但是对该子列表的元素进行修改时,是修改子列表的元素的指向。
附上图可能会更容易理解一点:
例如: 我将a复制给了b,虽然生成的是新列表,但是列表里面元素指向的地址是相同的,当我修改b[0]的时候,相当于是把b[0]指向了其它列表,此时a并不会收到影响。 但是如果我修改的是b[1][1],此时我修改的是b[1]指向的列表下标为1的元素,但是a[1]和b[1]指向的是同一列表,相当于也是在修改a[1]的元素。
这种情况下,也有应对的方法,我用的是for循环来进行重复制
a = [[1,1,1],[1,1,1],[1,1,1]]
b = []
for data in a:
b.append(data.copy())
但是这种办法对于对于n维列表,需要内嵌的for循环为n-1个,如果是维度高的列表,就不是很切实际了。 如果大佬们有更好的方法,还请在评论区留言^_^