就像我们小学学习英语会从基础的单词开始一样,现在我们学习Python这门高级语言也是如此,让我们先来认识一下Python里的“单词”。

一、抽象的单词——变量

        在之前的例子中我们有这样一句代码:

y = 94/2-35

        在这一句中“y”就是所谓的变量。变量具体是什么?以这段代码为例,从形象的角度来讲,可以理解为:当我们运行完这一句代码后内存里会指定一块空间,该空间的名字叫y,内容则是运算的结果12。在这过程中发生了两件事,一份内存空间被单独划分出来以及数字12被写入了内存。在这里,变量y就是指这份被划定出来的空间本身。当计算机去寻找变量y时就会导向这个空间并读取其内容,数字12仅仅是这个空间里一个临时的“租客”。

        也可以说y所指向的空间里事实上可以存放任意的值,概念上较为类似于数学里的变量,于是在Python里我们也称之为变量。

        在这里我们要学习的就是变量的命名规则:

                                            1、可以使用字母,数字、下划线和汉字;

                                             2、变量名开头不能为数字,中间不能存在空格;

                                             3、不能跟保留字(也称关键字)相同;

                                             4、不能跟Python内置函数名相同;

# print()其实就是Python内置函数。

二、具体的单词——三种原子数据类型

        我们明白了变量的概念后,就来到下一步了:在这空间里,在等号的右边究竟可以住进那些“租客”,即Python中有那些数据类型。

        需要说明的是,在Python中数据类型繁多,涵盖数值、序列、映射、集合、二进制。每一个类型又有诸多子类。如果要从头到尾一步一步梳理挨个陈述就有些过于臃肿,不好理解。在这里我们只叙述常用的数据类型,并分为基本的原子数据类型和复杂的数据结构。

       原子数据类型我们主要讲述三类。

        第一类:整数型(int)。什么是整数型呢?比如在前文的代码中,数字12就是一种整数型数值。顾名思义,不带有小数部分的数字即是整数型,与数学中的定义类似;

        第二类:浮点型(float),虽然名字较为抽象但是实际上就是带有小数部分的数字。诸如12.1、12.2这样既有整数部分又有小数部分的数字是浮点型,或者0.1,0.2这样仅有小数部分的数字也是浮点型,即便是12.0这种在数学里不太常见的写法,在Python中也是合法的并且同样归属于浮点型;

        第三类:布尔值。布尔值仅取两个值True(正确)或False(错误)。这个数据类型有些特殊。从根源上,在Python的官方文档中布尔值事实上是整数型的子类,它的两个取值True或False在实际上就是与整数型类别中的两个数字1或0。所以有时它们会被互相混用,毕竟1和0的书写比两个英文单词会快捷。但是各位应该也能根据这两个单词看出来,布尔值事实上是进行正负判断的,即便在原理上他们等价于1和0,但由于他们有代表着正负判断的特殊含义,出于代码的方便理解需要它们还是被划分出来单独作为了一个类。

        在这里,True和False显然在Python中代表着特定含义,所以事实上它们就是我们在前文变量的命名规则“不能跟保留字(也称关键字)相同”里面提到的保留字。在官方定义里:保留字(也称为关键字)是指被Python语言内部定义并保留使用的标识符。这里True和False在Python中内部定义为布尔值的两个取值,对于Python而言有着特定的用途,所以他们就是Python的保留字。在后文我们学习Python语法的时候会学到if,else,while之类的单词在Python里的指定用法,他们也是Python的保留字。

三、具体的单词——五种数据结构

        当我们要批量处理数据的时候,比如AI训练中基础的矩阵运算,往往需要同时对大量的数据进行操作。如果继续只使用以上三种数据类型显然效率会极其低下。一个简单的3*3的矩阵就需要计算机去创建9次变量并9次存入值。更重要的是这种情况下进行矩阵的乘法运算将会是一个灾难,为了实现一行乘以一列用户需要去反复的书写冗长的代码。而为了迎合这种批量处理数据的需求,Python里便有了可以堆叠复数类型数据的数据结构,各自具有不同的特点,这里主要介绍以下五种:

1)有序且不可变:字符串(str)

        这应该是Python里最易于理解的数据结构,顾名思义一串字符放在一起就是一个字符串,两端会以单引号或双引号括起做标识。

a = 94/2-35
print(type(a)) # a是算式运算出的整数型数据12

# type()查看变量的数据类型

b = "94/2-35"
print(type(b)) # b是内容为一串算式的文本

        引号的标识是必须的,在上面的例子中,如果没有引号的标识,计算机将无法确认用户输入的是一串需要被运算的式子还是一串内容为算式的字符串。显然,用户输入字符串时是需要的是这串字符本身,并不希望被执行计算变成其他内容。

        在Python中,字符串有着自己的特点:

        ①字符串是有序的数据结构。就像是列队报数的士兵一样,在Python中有序代表着每个字符都是按顺序排列,并有着独属于自己的序号。用户可以根据序号来查询字符串里指定的的内容。

'''
012-->
————————————
Hello World
————————————
    <-- -2 -1 
'''
# 单、双引号的括起用来标识字符串,而三引号括起则会变成注释内容,此外本行开头的“#”标识也是用作注释的。Python不运行注释内容

        在Python中,这个“序号”被称为索引。如上图代码所示索引有两种计数方式,正向计数从0开始,按照从左往右的正常阅读顺序依次递增,就像是列队报数。反向计数从-1开始,按照与正常阅读顺序相反的方向依次递减(从-1到-2到-3...)。正向计数存在的原因不必解释,这就是我们正常计数习惯。反向计数则是为了方便实际应用。当我们想访问字符串末尾的内容时我们不必知晓整个字符串的长度然后减一(因为索引从0开始计数)来算出它的索引号。只需要-1就能直接指向末尾的内容会较为方便。

txt = "Hello World"

print(txt[0])
print(txt[-1])

print(txt[1])
print(txt[-2])

print(txt[0: 2])
print(txt[-3: -1])

        索引号能让我们和计算机有同一套标准,可以让计算机确定我们需要的具体是什么。那么跟计算机有了同一套标准后,我们该如何让计算机读取出来指定的内容返回给我们呢?这就是Python里的切片操作。对于存储了字符串类型数据的变量,在变量名后面书写一对中括号并在中括号内指定索引号即可。如上面代码所示,txt[0]实现了对于"Hello World"这段字符串访问并返回索引为0的字符。

        当我们想要截取多个字符时可以使用:来操作,当我们在中括号内填入[strat: stop]格式的内容时,计算机会返回从索引号start对应的字符开始,stop对应的字符的前一个字符结束。这套规则对于反向索引同样适用,不会跟着索引号的计数方向一起反转。txt[-3: -1]的返回结果是"rl",在txt[-3: -1]里,start是-3,stop是-1,所以实际返回为-3对应的r开始直到-1对应的d的前一个字符l。

        ②字符串不可变。对字符串修改是非法的操作,只会引起报错。

txt = "Hello World"

txt[-1] = "!"
# TypeError: 'str' object does not support item assignment
# 类型错误:'str'(字符串)对象不支持元素赋值

2)有序且不可变:元组(tuple)

        两端用小括号括起以标识为元组类型

tp = (1, 1.0, '1')

print(tp[0])
print(type(tp))

        有序、不可变指的是像字符串一样,可以索引、切片但无法对其元素进行任何修改。不可修改的特性让元组多为储存重要数据用。

print(tp[0])
tp[2] = (1, 2, 3)

# TypeError: 'tuple' object does not support item assignment

3) 有序但可变:列表(list)

        两端用中括号括起以标识为列表类型。

ls = [1, 1.0, '1']

print(ls[0])
print(type(ls)) # type()查看变量的数据类型

        什么是有序、可变?有序指的是这个列表型变量可以像字符串那样索引。每一个元素都有对应的索引号。可变指的是与字符串相反,其中内容可随意变更,你甚至可以将其中一个元素替换为列表从而达成嵌套。

ls[2] = [1, 2, 3]

print(ls)
print(type(ls[2]))

4)不严格有序但可变:字典(dict)

        两端用大括号括起以标识为字典类型。其中每一个元素均为键值对,以“键(key):值(value)”的形式出现。键或者值均可以为任意类型的数据。

dt = {"01": "张三", 2: "李四", 3.0: "王五"}

print(dt["01"])
print(type(dt))

        字典是颇为特殊的有序数据结构,事实上,它在Python3.6之前和集合一样属于无序的数据结构。而即便是在版本更新后由于底层的一些变动,它变成了有序的数据结构也无法像其他有序数据结构一样通过索引来切片。取而代之的是我们以冒号左边的键(key)作为“索引”来确定要找的值。所以这里也对键提出了额外的要求:必须不能重复,就像每个人的身份证号一样。

5)无序可变:集合(set)

        两端以大括号括起以表示为集合类型。其中的元素不可以是可变的数据类型,也就是只能包括数值、字符串、元组等类型的数据。由于集合本身属于可变的数据结构,所以集合不能像列表那样自我嵌套。从语法上,集合与字典的不同就是其中元素并不需要以键值对的形式出现,一个数据即是一个元素。

s = {"red", "blue", "green", "yellow"}
print(s)
print(type(s))
# print(s[0])

s.add(4)
print(s)

s.remove("green")
print(s)

        或许各位疑惑,前文中的字典并不能以索引来切片,那么到底有序在哪?我们可以参考集合的无序性。在无序方面集合不仅不存在索引,无法通过切片操作获取内容,还具有其元素储存顺序与创建和添加顺序完全无关的特点,每次运行输出的内容顺序可能都会不一样。字典的有序性则体现在元素储存顺序与创建和添加顺序相等,并且稳定不变,不管运行多少次都是同样的结果。这是因为其底层实现随着版本革新产生了变化,字典的有序则是这种变化的副产物。所以字典即不像原来一样,跟集合同属无序数据结构完全的混乱,也不像字符串那样,有序到存在索引。

        相对的,由于该数据结构纯粹的无序特性,也不存在可以互相区分的索引,一个集合便不允许其中存在重复值。不过也由于这种特性,如果我们将其他类型的数据转化为集合类型就能完成去重的操作。从代码中就可以看出,数据之间的相互转化非常简单。Python已经提供了所需的函数,也就是对应元素类型的名字。

s = "1"
s_int = int(s)
s_float = float(s)
print(s, s_int, s_float)



ls = [1, 1.0, "1"]

ls_tp = tuple(ls)
ls_set = set(ls)

print(ls_tp, ls_set)

四、数据类型与数据结构说明

        前文提到过,数据有数值、序列、映射、集合、二进制等类型,但是后续我们的介绍却着重于原子数据类型和数据结构这两类,而数值、序列、映射、集合、二进制这些却没有叙述。这是因为这几个类型是Python从数据本身的种种特质来划分的。数据结构和原子数据类型则是计算机科学从数据的组织方式来将数据分为可以组织、存储其他对象的容器和原子型的不可拆分数据。就像字符串(str),在Python角度讲,根据其特质,字符串属于序列类型的数据。从数据结构的角度讲,它也属于有序的数结构而非。只是出于便于理解,我们虽然列出了Python的划分方法,但是讲解上是从计算机科学的角度上讲的。

        而同样的出于方便理解的目的,变量的具体实现方式并非如上文所述。实际上恰恰相反,是数据现在内存里组织起来,接着变量名被指向了这个数据,作为一个名称来使用。比如,假设内存空间里真有这么一个变量空间在,那我们不论给一个变量赋值多少次,那么空间本身在内存中的地址应当是不变的。但事实上,一旦变量被重新赋值,其地址就会改变,且变量的地址与数据的地址相同。

        以上这些是出于严谨性的补充说明,读者不必急于全部记下,慢慢理解即可。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐