一. 常量和表达式
下面先用Python写几个最简单的算术运算:
print(2 + 3) print(2 * 3) print(2 / 3)
运行得到结果:
说明:
运算符和数字之间,可以没有空格,也可以有多个空。但是一般习惯上写一个空格(比较美观)。
形如 2 + 3 这样的算式,在编程语言中称为表达式,表达式的运算结果,称为表达式的返回值。
其中2、3这样的数字,称为字面值常量。
+、-、*、/ 这种称为运算符或者操作符。
补充:
在 C/Java 中 2/3 结果为 0 (小数部分被截断)。但是在 Python 中得到的结果则是一个小数,这更符合日常使用的直觉。
在编程中,一般是没有“四舍五入”这样的规则的。且浮点数的存储和表示通常遵循 IEEE754 标准,这套规则下,在内存中表示浮点数的时候,可能会存在微小误差。
常量包括字符常量、字符串常量和数值常量:
字符型常量:‘a’
字符串型常量:‘abcd’
数值型常量:0、1、2、3.14、2.16 等
二. 变量和类型
1. 什么是变量
有的时候,我们需要进行的计算可能更复杂一些,需要把一些计算的中间结果保存起来。这个时候就需要用到变量,比如下面这个例子:
给定四个分数, 67.5, 89.0, 12.9, 32.2 ,编写代码,求这四个分数的方差。
PS:方差的计算过程:取每一项,减去平均值,计算平方,再求和,最后除以 (项数 - 1)
在这个代码中,就需要先计算这四个数字的平均值,然后再计算方差。这就需要把计算的平均值使用一个变量保存起来,具体计算代码如下:
avg = (67.5 + 89.0 + 12.9 + 32.2) / 4 total = (avg - 67.5)**2 + (avg - 89.0)**2 + (avg - 12.9)**2 + (avg - 32.2)**2 print(total / 3) # 输出:1173.2866666666666
说明:
avg、total 均为变量
** 在 Python 中表示乘方运算。** 2 即为求平方
变量可以视为是一块能够容纳数据的空间(可以把变量想象成一个盒子)。这个空间往往对应到 “内存” 这样的硬件设备上:
2. 变量的语法
2.1 定义变量
a = 10
创建变量的语句非常简单,其中:
a 为变量名。当我们创建很多个变量的时候,就可以用名字来进行区分。
= 为赋值运算符(不是数学中的等价),表示把 = 右侧的数据放到 = 左侧的空间中。
Note1:Python中定义变量不需要显示指定变量类型,编译器会根据初始值自动识别变量的类型。不过要显示指定类型也行,语法格式如下:
// 在变量名后加个冒号然后跟上变量的类型 a:int = 10
Note2:Python中不要求每条语句结束后末尾必须加上分号。不过加了也没影响,在Python中分隔一行中的多条语句时才会使用分号,向下图这个场景:
Note3:变量的名字要遵守一定规则
数学上,变量通常使用 x, y, z 这种简单的英文字母或者拉丁字母表示。但是在编程中不建议这样使用。
原因是编程中,一个程序里通常会同时创建出很多个变量。如果只是使用单个字母表示,在变量多了的时候,就很难记住哪个变量是干啥的,从而给维护程序带来了一定的困难。因此我们更建议使用带有明确描述性的名字,来表示变量的用途。
硬性规则(务必遵守)
变量名必须由数字、字母、下划线构成,不能包括其他特殊符号且不能以数字开头:
像 a1 是合法的变量名
像 1a, a*a 都是非法的变量名
变量名不能和 Python 的“关键字”重复,比如:if、while、else 等都不行,它们在语法中有特殊含义。
Python 中的变量名是大小写敏感的。像 num = 1 和 Num = 1 它们就是两个不同的变量。
软性规则(建议遵守)
给变量命名的时候,尽量使用描述性的单词来表示,做到通过变量名来表现出变量的作用。
当我们使用一个单词描述不清楚的时候,可以使用多个单词来命名(不怕变量名字长,而怕变量的含义表达不清楚)。
当包含多个单词的时候,推荐使用小驼峰命名法给变量命名,即除了第一个单词的首字母外,后继每个单词的首字母都大写,例如:personName、personAge。
2.2 使用变量
3. 变量的类型
变量里面存储的不仅仅是数字,还可以存储其它种类的数据。为了区分不同种类的数据,我们引入了 "类型"这样的概念。
PS:和 C++ / Java 等语言不同,Python 变量的类型不需要显式指定,而是在赋值的时候确定的。
3.1 整数
# type 和 print 类似,也是 python 内置的函数,可以使用 type 来查看一个变量的类型 a = 10 print(type(a)) # <class 'int'>
在 Python 中,int 能够表示的数据范围是无“无穷”的,即 int 类型的变量可以根据要表示数据的大小,自动扩容。这里要区别 C++ / Java 中的 int 只能表示到大约 -21亿 ~ 21亿。
因此 Python 中就没有 long 和 long long 这样的类型了,像 byte、short 这些也不存在。
这里可以看出 Python 的设计哲学:大道至简,即一类问题只提供一种解决方法。
3.2 浮点数
a = 0.5 print(type(a)) # <class 'float'>
和 C++ / Java 等语言不同,Python 的小数只有 float 一种类型,没有 double 类型。但是实际上Python 的 float 就等同于 C++ / Java 中的 double,表示双精度浮点数。
3.3 字符串
a = 'hello' print(type(a)) # <class 'str'>
在 Python 中没有字符类型,只有字符串类型,像 ‘a’ 这算是长度为 1 的字符串。
正是因为 Python 中没有字符类型,所以 ’ 和 " 都能表示字符串;而 C++/Java 中有单独的字符类型:单引号表示字符,双引号表示字符串。
# 二者没有区别,都表示字符串 s1 = 'hello' s2 = "hello"
补充说明
1、单引号、双引号和三引号
如果字符串里面包含了 双引号,则表示整个字符串就可以用 单引号 引起来。
如果字符串里面包含了 单引号,则表示整个字符串就可以用 双引号 引起来
如果同时有单、双引号,则使用三引号(‘’’ 或 “”")去引起来整个句子
例1:My name is “zhangsan”
# 整体使用单引号 s = 'My name is "zhangsan"'
例2:My name ‘is’ “zhangsan”
# 整体使用三单引号 s = '''My name 'is' "zhangsan"''' # 整体使用三双引号 s = """My name 'is' "zhangsan"""""
2、可以使用 len 函数来获取字符串的长度
s = "hello world" print(len(s)) # 11
3、可以使用 + 针对两个字符串进行拼接
s1 = "hello" s2 = "world" print(s1 + s2) # helloworld
形入上面这样的代码,就是字符串拼接。也就是把后面的字符串拼接到前一个字符串的末尾,得到一个更大的字符串,而对于原来的 s1 和 s2 是没有影响的。
要注意的是字符串只能和字符串相加,不能拿字符串和整数/浮点数相加:
3.4 布尔
布尔类型的取值只有两种:True(真)和 False(假)。注意它们的首字母是大写。
a = True b = False print(type(a), type(b)) # <class 'bool'> <class 'bool'>
如果把 bool 类型的值和 整数 进行算术运算,此时 Python 解释器就会把 True 当做 1,把 False 当做 0。
但是这样的操作是没有意义的,C/C++ 也有类似的行为。但是 Java 的做法是更合理的:如果出现 整数 和 布尔 混合运算,直接编译器报错。
3.5 关于变量类型的几点补充
变量类型的意义
1、不同的类型,占用的内存空间是不同的。
int 默认是 4 个字节,如果不够表示则动态扩容
float 固定八个字节
bool 一个字节就足够了
str 是变长的
2、不同的类型,对于能够进行的操作也是不同的。
int 和 float 可以进行 + - * / 四则运算,但不能使用len
str 可以使用 + ,但不能 - 不能 * 也不能 / ,可以使用len
动态类型特性
在 Python 中,一个变量是什么类型,是可以在 “程序运行” 过程中发生变化的,这个特性称为 “动态类型”。下面我们对比动、静态类型:
PS:一个语言是否是静态类型语言和该语言在定义变量时是否要加上类型无关!
Python 作为一个动态类型语言,在定义变量的时候也是可以写类型的:
动态类型特性是一把双刃剑:
对于中小型程序,可以大大地缩小代码量(比如写一段代码就可以同时支持多种类型)
对于大型程序,则提高了模块之间的交互成本(程序猿 A 提供的代码难以被 B 理解)
动态类型写起来比较灵活,然而在编程中,“灵活”这个词儿往往是“贬义”,这会使我们的代码更加复杂和容易出错。
相比之下,静态类型的语言还是更好一些的,尤其是在大型、多人协作开发的程序中。因此很多公司在创业之初,喜欢使用 Python 来开发,这时候产品规模小,也就是一两个程序员很快就能搞出来的。但是当项目达到一定的规模之后,动态类型的劣势就逐渐体现出来了,这时候很多公司就会针对之前的 Python 写的代码用其他语言进行重构。
三. 注释
1. 什么是注释?
注释是一种特殊的代码,它不会影响到程序的执行,但是能够起到解释说明的作用,能够帮助程序猿理解程序代码的执行逻辑。
其实写代码是一件比较烧脑的事情,读代码同样也非常烧脑。相比于一板一眼的代码,一些口语化的描述能更好的帮助程序猿理解程序。
# 计算 4 个数字 67.5, 89.0, 12.9, 32.2 的方差 avg = (67.5 + 89.0 + 12.9 + 32.2) / 4 total = (67.5 - avg) ** 2 + (89.0 - avg) ** 2 + (12.9 - avg) ** 2 + (32.2 - avg) ** 2 result = total / 3 print(result)
形如上述代码,如果没有注释,直接阅读,是不容易 get 到代码的含义是计算方差的。但是通过加了一行注释解释一下,就让人一目了然了。
2. 为什么要有注释?
代码的第一目标是容易理解,第二目标才是执行正确;写注释不光是为了方便别人来理解,也是方便三个月之后的自己理解。
3. 如何写注释?
3.1 注释的语法
Python 中有两种风格的注释:
(1)行注释
使用 # 开头的注释,一般要写在注释代码的上方。也可以写在要注释代码的右侧,但很少会写在代码下方,更不会写到代码左侧(这不把写的代码都给注释掉了嘛?)
# 记录数据总和 sum = 0
(2) 文档字符串
使用三引号引起来的称为 “文档字符串”,也可以视为是一种注释。
可以包含多行内容
一般放在 文件/函数/类 的开头
“”" 或者 ‘’’ 均可 (等价)
""" 这是文档字符串 这是文档字符串 """
3.2 注释的规范
给代码写注释时要注意以下几点:
保证注释的内容是准确的,起到误导作用的注释,还不如不写。
篇幅合理,既不要太精简,也不要长篇大论。
内容要积极向上,不要有负能量。
四. 输入输出
1. 控制台
程序通常需要和用户进行交互,交互包括输入和输出:
用户把信息传递给程序的过程,称为 “输入”
程序把结果展示给用户的过程,称为 “输出”
输入输出的最基本的方法就是控制台。以前对于机械的操作面板,就叫做控制台。控制台上会有一些按钮,开关,操作杆等器件。后来有了计算机,控制台的概念也就延续下来了,只不过计算机是通过键盘鼠标等进行操作,把操作结果显示到屏幕上。现在计算机中就把能够输入命令,向计算机安排工作,计算机再把结果显示出来这样的程序就称为控制台了。
PyCharm 运行程序,下方弹出的窗口就可以视为是控制台:
windows 自带的 cmd 程序,也可以视为是控制台:
控制台是一种人和计算机交互的最基础方式,但是日常生活中却不常用。更常见的交互方式是图形化界面,如我们平时用到的 QQ、浏览器、steam 等,都不需要用户输入命令,而只是通过鼠标点击窗口、按钮的方式来操作。
2. 基于控制台输出
Python 使用 print 函数可以直接将字面字符串输出到控制台:
print("hello") # hello
不仅能输出字面字符串,还可以输出其他类型的变量:
a = 10 print(a) # 10 b = True print(b) # True
更多的时候,我们希望能够输出的内容是混合了字符串和变量的:
# 输出 num = 10 num = 10 print(f'num = {num}')
使用 f 作为前缀的字符串,称为 f-string,即“格式化字符串”,此处的 f 表示 “format”
此时就可以用 { } 这样的语法,往字符串里嵌入变量或表达式的值。
像格式化字符串,很多编程语言都进行了各自的探索:
C语言使用 printf 配合 %d, %s 这样的占位符
C++ 使用了 std::cout,配合 << 来输出
Java 采用了字符串拼接,允许字符串和其他类型的值进行拼接
Python 最早支持的格式化字符串,也是效仿 C 的 printf 当做出了一些改进。Python 3.6 版本才开始支持 f - string
3. 基于控制台输入
python 使用 input 函数,从控制台读取用户的输入:
num = input("请输入一个数字:") print(num)
注意:
input 的参数相当于一个 “提示信息”,也可以什么都不写。
input 的返回值就是用户输入的内容(字符串类型)。
如果需要根据用户输入的内容进行算术运算,此时就需要先把读到的字符串转为数字。
字符串转为整数,可以使用 int()
整数转为字符串,可以使用 str()
字符串转为浮点数,可以使用 float()
代码示例:从控制台读入两个整数,并完成相加。
a = input("请输入第一个整数:") b = input("请输入第二个整数:") print(f'a + b = {a + b}')
注意上面的结果是字符串拼接,不是算术运算。如果要想进行算术运算,需要先转换类型:
a = int(input("请输入第一个整数:")) b = int(input("请输入第二个整数:")) print(f'a + b = {a + b}')
五. 运算符
1. 算术运算符
在 Python 中像 + - * / % ** // 这种进行算术运算的运算符,称为算术运算符
Note1:% 不是 “百分数”,而是求余数,且余数统一都是正数。
Note2:** 是求乘方。不光能算整数次方,还能算小数次方。
print(4 ** 2) # 16 print(4 ** 0.5) # 2.0
Note3:// 是取整除法(也叫地板除)。整数除以整数,结果还是整数(舍弃小数部分,并向下取整。不是四舍五入)
# 地板除法(取整除法),会针对计算的结果进行“向下取整” print(7 // 2) # 3 print(-7 // 2) # -4
Python 中除法需要注意到的两个点
第一点:/ 中不能用 0 作为除数。否则会抛出异常
运行时出现的错误,也叫作“抛出异常”。异常是编程语言中的一种常见机制,如果程序运行过程中抛出异常,程序直接就终止了,异常后面的代码也就不会执行了。
在有些编程语言中:
/ 整数 0 会抛出异常
/ 浮点数 0 会得到无穷大
而 Python 都认为除 0 都是异常。
第二点:在 Python 中 整数 / 整数,如果不能整除,得到的就是小数,不会出现截断的情况。
print(1 / 2) # 0.5
1
除了 Python 之外,大部分的编程语言都是 整数 / 整数,结果还是整数,小数部分统一都被截断了。
2. 关系运算符
像 < <= > >= == != 这一系列的运算符称为关系运算符,它们是在比较操作数之间的关系。关系运算符表达式返回值是 bool 类型,如果关系符合,则表达式返回 True;如果关系不符合,则表达式返回 False。
字符串关系的比较
Note1:字符串比较大小,规则是 “字典序”。
字典序是一种比较规则。在英文词典上,单词都是按照一定的顺序来排列的:
先看首字母在字母表上的顺序,谁小,谁就排在前面
如果首字母相同,依次比较第二个字母、第三个字母…
字符串在词典上越靠前,就越小;越靠后,就越大。典型的比如 abandon 就是一个小单词。
Note2:直接使用 == 或者 != 即可对字符串内容判定相等。(这一点和 C / Java 不同)
Note3:对于浮点数来说,不要使用 == 判定相等
看下面这个例子:
print(0.1 + 0.2 == 0.3) # False
对于浮点数来说,使用 == 比较相等,存在一定的风险!因为浮点数在内存中的存储方式决定了其值的表示不能做到绝对精确,是可能存在误差的,这样的误差在进行算术运算时候可能被放大,从而导致等于的判定出现误判。
不止是 Python 如此,主流编程语言都是如此。这个是 IEEE754 标准规定的浮点数格式所引入的问题,此处我们不做过多讨论。
针对浮点数正确地比较方式:做差,看差值是否小于预期的误差范围:
实际工程实践中,误差在所难免,只要保证误差在合理范围内即可:
2. 逻辑运算符
像 and or not 这一系列的运算符都称为逻辑运算符。
and:两侧操作数均为 True,表达式的值为 True;否则为 False(一假则假,全真才真)
or:两侧操作数均为 False,表达式的值为 False;否则为 True(一真则真,全假才假)
not:逻辑取反。只有一个操作数,操作数为 True,则返回 False;操作数为 False,则返回 True。
一种特殊写法
a < b and b < c 这个操作等价于 a < b < c 。这个设定和大部分编程语言都不相同,两种写法 Python 都支持。
关于短路求值
和其他编程语言类似,Python 也存在短路求值的规则:
对于 and 操作来说,如果左侧表达式为 False,那么整体的值一定是 False,右侧表达式不必求值。
对于 or 操作来说,如果左侧表达式为 True,那么整体的值一定是 True,右侧表达式不必求值。
下面两行代码,一旦右侧求值了,是能够看到代码出现异常的。但运行之后并没有抛出异常,因为短路了所以右侧没有求值。
print(10 > 20 and 10 / 0 == 1) # False print(10 < 20 or 10 / 0 == 1) # True
3. 赋值运算符
3.1 = 的使用
= 表示赋值,这个我们已经用过很多次了。注意和 == 区分:
= 表示赋值,意思是把 右侧的值 填充到 左侧的空间(变量) 中。
== 用来比较是否相等,是一个关系运算符。
链式赋值
# 先把 20 赋值给 b,再把 b 赋值给 a a = b = 20
先把 20 赋值给 b,再把 b 赋值给 a。一般不建议使用链式赋值,尽量一行代码就只是包含一个操作。
多元赋值
# 把 10 赋值给了 a;把 20 赋值给了 b a, b = 10, 20
多元赋值能够帮助我们解决一些特殊的问题,比如完成两个变量值的交换。
常规写法:
a = 10 b = 20 tmp = a a = b b = tmp
基于多元赋值的写法:
a = 10 b = 20 a, b = b, a
3.2 复合赋值运算符
Python 还有一些复合赋值运算,例如: += -= *= /= %=
其中 a += 1 等价于 a = a + 1 。其他复合赋值运算符也是同理。
注意:像 C++ / Java 中,存在 ++ – 这样的自增/自减运算符。Python 中则不支持这种运算,如果有需求,则直接使用 += 1 或者 -= 1 即可。
看下面这个例子:
a = 10 ++a a++
结果:
++a 之所以没有语法报错,是 Python 解释器把 + 当成了正号。
–a 也不会报错,把 - 当成了负号,负负得正,最终的值仍然不变。
但是 后置++ 和 后置-- 都是语法上会报错的。
那么Python 为何不支持自增、自减操作呢?可以从以下两点考虑:
前面说过,Python 的设计哲学是大道至简,一类问题只提供一种解决方案。有 += 1 和 -= 1 就够了。
自增、自减的最大的问题就是容易分不清前置和后置的区别。这对初学者来说是非常不友好的,考虑到这一点 Python 语法在设计的时候就进行了规避,避免出现这种不直观,并且容易混淆的语法。
PS:其实 前置++ 和 后置++,都是C语言开的头 。Go语言的作者也是其中一个C语言之父:肯汤姆逊。他设计的Go语言相当于C的升级版本,在 Golang 里就废除了 前置++,只保留了 后置++。而且这个 后置++ 不能取表达式的返回值。