基本输入输出
知识点回顾
警告
此处仅对一些易忽视的、容易出错的知识点和常用的知识点进行回顾,不能代替课本和课件的学习!同时,我们在这里会提及的东西可能会超出标题所框定的范围,但尽可能限制在课程范围之内,如果有超出范围的内容,将放在蓝色方框中以示提醒.
基本的输入和输出函数
读者需要熟悉的函数是:
input()
函数print()
函数
需要注意的是,print()
函数可以传入一系列参数,这一系列参数在输出时,中间会多出一个空格. 因此,一种做法是将其全部转化成字符串类型,中间用 +
连接,另一种做法是使用格式化字符串进行输出,如下一节所述.
格式化字符串
首先,需要说明的是,格式化字符串是一个相当复杂的主题. 在详细介绍这个主题之前,我们或许需要先框定一些术语. 称一个需要被处理的字符串为格式化字符串(format string),其中不需要被替换,可以原样保留在格式化后的文本中的内容成为字面量(literals),而需要被替换的部分称作替换字段(replacement fields).
接下来,请就程序语言设计者的角度思考,如何区分格式化字符串中的字面量和替换字段?这一定是一个需要处理的问题,因为替换字段就是格式化字符串的核心. 因此,我们引入 {}
来标记替换字段. 在 {}
中的东西会在格式化的过程中被替换为用于格式化此字符串的变量,例如:
"{} is a kind of {}".format("python", "snake")
# python is a kind of snake
第二个问题是:当我们有多个替换字段的时候,如何区分它们?最简单的方法就是予以编号——当然,按照程序员的惯例,从零开始,例如:
"{0} is a kind of {1}".format("ruby", "germ")
# ruby is a kind of germ
因为我们传递用于格式化的变量的时候,它是有顺序的,而格式化字符串内部的顺序可以与原来的顺序不同,甚至可以重复(实际上,这才是标号最重要的功能!). 比方说:
"{0} is a kind of {1} but {1} is not a kind of {0}".format("window", "furniture")
# window is a kind of furniture but furniture is not a kind of window
另外,你甚至可以命名格式化字符串:
"{name} is a great {job}".format(name="haskell", job="computer scientist")
# haskell is a great computer scientist
在 Python 3.6 引入了 f-字符串之后,这变得更加有用了:
name = "haskell"
job = "computer scientist"
f"{name} is a great {job}"
# haskell is a great computer scientist
f-字符串相当于省略了原来的替换字段,并且以里面的标识符(identifier,比如变量名)的内容直接作为被格式化的对象.
接下来,我们想问的是,输出已经可以了,那么应该怎么控制输出长什么样?在此,我们只讨论几种最常见的情形. 通常,我们用 :
标识后面是对这个输出的标记,:
前面则会放入标号或者标识符.
精度控制
最常见的用法是控制小数的输出. 通常,我们会写成以下形式:
>>> '{:.3f}'.format(123.456)
'123.456'
>>> '{:.3f}'.format(123.0)
'123.000'
注意,这里的 f
标识是必须的,没有它的话,系统会自动以 g
标识输出,这超出了这门课的讨论范围,有兴趣的可以参见参数类型一节.
宽度控制
读者对于输出宽度这个概念应该并不陌生. 对于一个字符串,我们希望它能扩展到一个定长;对于一个数字,我们希望它能被某些值填上空缺. 那么首先我们要说明的是对齐:
属性 | 含义 |
---|---|
< |
强制字段在可用空间内左对齐(这是大多数对象的默认值). |
> |
强制字段在可用空间内右对齐(这是数字的默认值). |
然后,需要指定对象的宽度,也就是说,需要对齐到多长. 这通过给定一个数字来实现. 下面的例子会给出一个清晰的呈现:
>>> '{:<30}'.format(1234)
'1234 '
>>> '{:>30}'.format(1234) # 数字会默认为右对齐,所以这里可以不用加 > 符号
' 1234'
另外的(可能事关千万的)问题是,用什么东西来填充空白的部分?默认地,如果不指明就会使用空格,但是,也有一些比较神奇的例子,比如说,如果你希望让数字前后有 0
,那么
>>> '{:0>30}'.format(1234)
'000000000000000000000000001234'
>>> '{:0<30}'.format(1234)
'123400000000000000000000000000'
其中的 0
可以被替换成大部分字符,例如:
>>> '{:a<30}'.format(1234)
'1234aaaaaaaaaaaaaaaaaaaaaaaaaa'
>>> '{:*<30}'.format(1234)
'1234**************************'
参数类型
提示
这一节不是重点. 可以参见 Python 官方文档中的表格进行测试和理解.
我们常用的参数类型有两类:面向整数的和面向小数的. 面向小数的 f
无需赘述,下面对面向整数的几个常用的类型标识做一个讨论.
请看下面的格式化字符串形式:
>>> "int: {0:d}; hex: {0:x}; oct: {0:o}; bin: {0:b}".format(42)
'int: 42; hex: 2a; oct: 52; bin: 101010'
>>> "int: {0:d}; hex: {0:#x}; oct: {0:#o}; bin: {0:#b}".format(42)
'int: 42; hex: 0x2a; oct: 0o52; bin: 0b101010'
显而易见,d
是十进制,x
为十六进制,o
为八进制而 b
为二进制. 对于非十进制的情形,#
表示是否需要加表明身份的前缀.
一些奇妙的补充
拓展内容
这里是一些好玩的奇怪实现,基本上不会碰到,也不应做要求. 但笔者觉得或许有点意思,有兴趣的读者可以对照官方文档尝试执行并解释其输出:
>>> '{2}, {1}, {0}'.format(*'abc')
'c, b, a'
>>> "{0} is represented by {0!r} internally".format("abc")
"abc is represented by 'abc' internally"
>>> "{:{fill}{fill}30}".format("center", fill="^")
'^^^^^^^^^^^^center^^^^^^^^^^^^'
常见问题
Q: 如何输入多个处在同一行中的东西?
回顾我们所学,input()
函数会读取一整行的输入,作为字符串类型返回. 因此,输入两个在同一行中的对象的正确方法是使用字符串方法. 特别地,我们会用到 split()
方法来将输入分成多个部分,然后保存成一个列表,再将列表进行拆解. 例如,如果输入两个以逗号分隔的元素,例如 11, 13
,我们应该写出这样的代码:
a, b = input().split(", ")
然后再对对应的字符串进行处理得到结果. 需要提醒的是,如果面对可能有多个空格分隔的元素,例如 11 12 13 14
,需要使用 split()
方法的默认形态,即:
l = input().split() # 请思考,此时的 l 是什么类型的?
关于此行为,在 Python 官方文档中是这样描述的:
如果
sep
未指定或为None
,则会应用另一种拆分算法:连续的空格会被视为单个分隔符,其结果将不包含开头或末尾的空字符串,如果字符串包含前缀或后缀空格的话. 因此,使用None
拆分空字符串或仅包含空格的字符串将返回[]
.
其中 sep
就是传入 split()
方法的参数. 关于更详细的内容,可以参考类型与值一节.
Q: 为什么 round()
函数调用完之后输出的结果会少末尾 0?
注意,round()
函数是将一个浮点数根据给定的精度转化成另一个浮点数,而浮点数输出时则会取末尾 0 尽可能少的形式. 因此,如果需要保证末尾 0 的个数,请采用格式化字符串的方法进行输出.
Q: round()
的规则是什么?
这里有一些非常容易混淆的地方. 在大部分情况下,round()
可能并不会按照我们预期的四舍五入的方式进行:
>>> round(0.5)
0
>>> round(1.5)
2
这里的原因是,round()
函数在处理的时候,是向最近的偶数进行舍入的.
而对于下面的例子则又完全不同:
>>> round(2.45, 1)
2.5
>>> round(2.55, 1)
2.5
>>> round(2.65, 1)
2.6
>>> round(2.75, 1)
2.8
这里的原因是浮点数的精度问题. 关于详细的解释,请阅读 Python 官方文档的浮点算术:争议和限制部分.
关于浮点数误差
一个极其经典的例子是:
>>> 0.1 + 0.2 == 0.3
False
>>> 0.1 + 0.2
0.30000000000000004
简单的理解方式是,例如我们可以输出这些数保留到小数点后 60 位的结果:
>>> '{:.60f}'.format(2.45)
'2.450000000000000177635683940025046467781066894531250000000000'
>>> '{:.60f}'.format(2.55)
'2.549999999999999822364316059974953532218933105468750000000000'
>>> '{:.60f}'.format(2.65)
'2.649999999999999911182158029987476766109466552734375000000000'
>>> '{:.60f}'.format(2.75)
'2.750000000000000000000000000000000000000000000000000000000000'
在这里只有 2.75
是可以被精确表示的,而其他三个数则是无法精确表示的. round()
函数得到这样的结果,实际上就是因为这个原因.