-------------------------------写在开头--------------------------
最近两周会浅学一下Python。借此机会记录一下自己的学习过程,同时也是一个上手写博客的机会。
本人是本科计算机专业在读,所以有一定的计算机基础,但是水平有限,而且也是第一次写博客,所以肯定会有说的不对或者不好的地方,如果有人刷到这些文章,请您带着批判,指导,或者是参考的心态浏览,不要尽听尽信,保持独立思考,感谢。
第二篇文章正式开始进入Python的学习,主要学习一下Python的基础数据类型和运算符。
Python的注释
开始前先学一下Python的注释方法,第一种是 # 加注释(大部分脚本语言都是用#作为注释的,Python最初也是最主要的影响就是来自ABC语言)
# 单行注释,一般用来做行注释
第二种是 """ """" 注释或者 ''' '''' 注释,具体如下:
"""
多行注释,一般用来做文件、类、方法、函数的注释
"""
'''
这也是多行注释
'''
Python的数据类型
Python中常见的数据类型大概有七种,分别是: Number(数字) String(字符串) bool(布尔类型) List(列表) Tuple(元组) Set(集合) Dictionary(字典)
Number包含int(整型),float(双精度浮点型),bool(布尔型),complex(复数型)(此处的bool型特指 0(False)和1(True))
String,Python中的字符串用单引号 ' 或双引号 " 括起来,同时使用反斜杠 \ 转义特殊字符。
Bool,布尔类型就是 True 或 False。在 Python 中,True 和 False 都是关键字,表示布尔值。
其他类型等之后再详细学习。
不像C/C++/Java那样,Python定义变量时不需要为它指定数据类型,但需要为它赋值,它会自己根据值的类型判断它自己应该是什么类型。比如说下面这样:
a = 1 # 句子可以用;结尾也可以不用 #整形默认为64位int型(64x操作系统)
print(type(a)) # 打印a的类型
print(hash(a)) # 打印a的地址
输出的结果为:
如果此时改变a的值,相当于重新赋值,它会再次判断值的类型,从而改变自己的类型,如下所示:
a = 1
print(type(a))
print(hash(a))
a = "ab" # 字符串默认是str类型,python中没有char类型,str的底层是单字节列表(数组)
print(type(a))
print(hash(a))
此时的结果为:
观察结果,很明显a的类型已经从 'int' 类型变成了 'str'类型,同时还能发现a的地址改变了。这看起来就像C语言中的指针交换数据。打个比方就好像有两栋房子,一个里面住着 1 ,另一个住着 "ab" ,a本来是第一栋房子的门牌号,所以一开始寻找a(打印a)的时候,你会在房子里见到 1 ,而后来a的值发生改变,可能对于一些语言来说,应该是把 1 踢出去,换成 "ab" 来住。但是Python的选择是直接把a这个门牌号放到 "ab" 的房子上,这样寻找a的时候也能找到 "ab" 。
当然实际情况并不是这么简单,具体的原因需要继续学习Python的可变数据类型和不可变数据类型,只是我目前不太需要了解得这么清楚,有需要的朋友请自行查阅资料学习。
Python的变量命名规则
- 变量名只能包含字母、数字和下划线。 变量名能以字母或下划线开头,但不能以数字开头。例如 grade_1 是合法的,但是 1_grade 是不合法的。这么规定主要是防止出现只有数字的情况,如果允许数字开头,就会有变量名为 1 这样的情况出现,计算机无法分辨这是变量还是常量。
- 变量名不能包含空格,一般都是用下划线来连接单词。 例如 grade_one 是合法的,但是变量名 grade one 就是不合法的。
- 不能将Python关键字和函数名用作变量名,Python需要保留这些单词用于特殊用途。(可以在cmd中查看这些关键字)
- 另外需要注意的是,¥ 符号和 $ 符号也是不可以用来命名的,这也是受到脚本语言的影响,这两个符号在脚本语言中都有特殊含义。
- 最后就是变量名最好要有意义,否则在代码的可读性上会很差。
Python的运算符
Python的运算符包括:算术运算符,比较运算符,逻辑运算符,位运算符,赋值运算符
- 算术运算符用来进行算术运算,主要使用的类型是整型和浮点型
- 比较运算符一般用来进行判断
- 逻辑运算符一般在判断的基础上进行复合运算
- 位运算符一般是计算机底层使用,效率较高,但可读性较差
- 赋值运算符是从右到左运算
接下来我们具体看一下这些运算符。
算术运算符
x = 11
y = 5
print(x + y) # 结果为16
print(x - y) # 结果为6
print(x * y) # 结果为55
print(x / y) # 会保留小数 结果为2.2
print(x // y) # 不会保留小数,不会进位 结果为2
print(x % y) # 取余 结果为1
print(x ** y) # 幂运算,x的y次方 结果为161051
算术运算符就是简单的加减乘除等符号,很好理解。
比较运算符
x = 11
y = 5
print(x > y) # x大于y? 结果为True
print(x < y) # x小于y? 结果为False
print(x == y) # x等于y? 结果为False
print(x != y) # x不等于y? 结果为True
print(x >= y) # x大于等于y? 结果为True
print(x <= y) # x小于等于y? 结果为False
比较运算的结果只有 True 和 False 两种,就是比较符号两边是否满足条件。
逻辑运算符
x = 11
y = 5
print(x > y and x == y) # 真 与 假 = 假(结果为False)
print(x > y or x == y) # 真 或 假 = 真(结果为True)
print(not (x > y and x == y)) # 非 (真 与 假) = 非 假 = 真(结果为True)
上面展示的是逻辑运算符中的与(and),或(or),非(not)。
and 要求左右两边都为 真(True) 则整个表达式才为 真。or 要求左右两边只要有一个为 真 那么整个表达式就是 真。not 则是将后面表达式的结果取反,如果后面表达式结果为 真,那么整个表达式结果为 假(False) ,相反则是 假 变 真。
逻辑运算符还有一种是 in 和 not in,这是判断一个值或者多个值是否在一个序列中(列表、元组、字典或字符串),如下所示:
x = 11
y = 5
print(x in [1, 2, 3, 4, 5]) # 11不在这个序列中,结果为False
print(y not in [1, 2, 3, 4, 5]) # 5在这个序列中,结果为True
print(not y in [1, 2, 3, 4, 5]) # y not in 和 not y in 结果上相同,但y not in效率更高一点
这里提一下,y not in 表示 y 不在序列中,则为 真 ,所以直接判断 y 在不在序列中就行。但是not y in 表示的是先给出 y 在序列中则为真的结果然后取反,也就是说要先判断 y 在不在序列中,然后再对结果取反,所以相当于比 y not in多了一步,效率上会低一点,所以一般用第一种写法。
赋值运算符
x = 11
y = 5
z = x + y # 先完成=右边的+运算,再将结果赋值给z
x += y # x = x + y,此时x的值变成了16
x *= y # x = x * y,此时x的值变成了80
print(type(x))
x /= y # x = x / y,此时x的值变成了16.0
print(type(x))
x -= y # x = x - y,此时x的值变成了11.0
print(z, x) #所以最后的结果为 16 11.0
注意在进行除法运算后 x 的值不是 16 而是 16.0 ,那是因为 / 运算总是返回一个 float 类型。而 // 运算如果除数和被除数都是整型那么返回的就是整型,但是只要有一个是 float 型,最后就会返回 float 型。而加减乘也是这样,左右两边有一个是 float 结果就是 float,所以最后x的结果是11.0,可以看下终端的输出:
位运算符
位运算的对象是计算机底层的二进制编码,它是对左右两边数据的每一位进行计算。位运算符主要包括:&(按位与),每位都为1才为1;|(按位或),只要有一位为1就为1;^(异或),两位不同才为1;~(按位取反)。具体举例如下:
x = 11 # 0000 1011
y = 5 # 0000 0101
print(x & y) # 按位与 两位都为1才为1 0000 1011 & 0000 0101 = 0000 0001 结果为1
print(x | y) # 按位或 只要有一位为1就为1 0000 1011 | 0000 0101 = 0000 1111 结果为15
print(x ^ y) # 按位异或 两位不同则为1 0000 1011 ^ 0000 0101 = 0000 1110 结果为14
print(~ -3) # 结果为2
按位取反的实现原理略微有些不一样,文章最后一部分会稍微讨论一下,现在只需要记住 ~x=-x-1。
位运算还有两个比较重要的运算符:<< (左移运算符) 和 >>(右移运算符)。
# 左移可以看作是×,左移两位相当于×2的2次方
print(11 << 2) # 0000 1011 << 2 = 0010 1100 = 44
# 右移也可以看作是÷,右移三位相当于÷2的三次方 右移时会出现低位丢弃的情况,类似于下面这个例子
print(44 >> 3) # 0010 1100 >>3 = 0000 0101 = 5
右移运算就是把二进制编码整体向右移动若干位,低位被移出去的就会直接被舍弃,而高位空出来的位置则自动补0,整体看起来就像是除(÷)上了2的n次方,而且没有余数,也不会进位(相当于 // 这个除,而不是 / 这个除),上方的举例可以看出来。
而左移运算和右移运算相反,二进制编码整体向左移,低位空出来的位置补0,理论上高位会进行舍弃,但是Python3之后int型的数值是越移越大,好像很难发生高位丢弃的情况,可能和一些底层的改动有关,这方面我也不懂,反正只要记住左移相当于乘(×)2的n次方就行。
Python位运算的取反问题
我们想要得到一个数取反后的值,首先要知道取反(~)的定义,菜鸟上的定义是:对数据的每个二进制位取反,即把1变为0,把0变为1。
此时假设有一个数为7,它的八位二进制为0000 0111,它的第一位是符号位,0表示正数(+),1则表示负数(-)。按照定义对它取反,就变成了1111 1000,第一位是符号位,那么这个数就是-8。
看到这肯定有人觉得奇怪,为什么1111 1000表示的是-8,而不是-120呢?
这是因为计算机中的负数是用补码表示的。0000 0111按位取反后得到的1111 1000不是我们需要的数值的原码,而是它的补码表示。补码转换成原码的规则是除了符号位的每一位取反,再加1,所以 [1111 1000]补 = 1000 0111 + 0000 0001 = [1000 1000]原。 (各种码之间的转换请自行查阅资料。),1000 1000 就是-8。
当然还可以举个负数的例子,假设一个数是-7,它的八位二进制是 1000 0111,这是它的原码,不是它在计算机中的表示,我们需要先把它转变成补码 [1000 0111]原 =(先减1) 1000 0111 - 0000 0001 = 1000 0110 =(再将除符号位的其他位取反) [1111 1001]补。此时再进行 ~ 运算,也就是全部取反,结果就是 0000 0110,也就是6。
因此,根据以上的计算,我们可以总结出 ~x=-x-1这样一个方便计算的结论。