Skip to content

Latest commit

 

History

History
511 lines (359 loc) · 19 KB

04.basis.md

File metadata and controls

511 lines (359 loc) · 19 KB

4 基础:基本概念

只打印 hello world 肯定是不够的,对吗?你希望做得比这还要多——你想要输入一些内容,进行操作,然后得到输出。在 Python 中我们可以通过使用变量与常量来实现这一目标,在本章中我们还会学习其它的一些概念。

注释

注释 是任何存在于 # 号右侧的文字,其主要用作写给程序读者看的笔记。

举个例子:

print('hello world') #注意 print 是一个函数

或者:

# 注意 print 是一个函数
print('hello world')

你应该在程序中尽可能多地使用有注释:

  • 解释假设
  • 说明重要的决定
  • 解释重要的细节
  • 说明你想要解决的问题
  • 说明你想要在程序中克服的问题,等等。

代码会告诉你怎么做,注释会告诉你为何如此。

这样做对你的程序的读者来说非常有用,他们可以很容易地理解你的程序是做什么的。请记住,这个人可以是六个月后的你!

字面常量

字面常量(Literal Constants)是诸如 51.23 这样的数字,或者是 This is a string 这样的文本。

因为你用的就是它字面意义上的值或是内容。数字 2 总是表示它本身——它是一个 常量,因为它的值不能被改变。因此,所有的这些都被称作字面常量。

变量

如果只使用字面常量很快就会让人感到无聊——我们需要一些能够存储任何信息并且也能操纵它们的方式。这便是 变量(Variables) 登场的时刻。正如其名字所述那般,变量的值是可以变化的,也就是说,你可以用变量来存储任何东西。变量只是你的计算机内存中用以存储信息的一部分。与文字常量不同,你需要通过一些方式来访问这些变量,因此,你需要为它们命名。

命名需要遵守以下规则:

  • 第一个字符必须是字母表中的字母(大写 ASCII 字符或小写 ASCII 字符或 Unicode 字符)或下划线(_)。
  • 其它部分可以由字符(大写 ASCII 字符或小写 ASCII 字符或 Unicode 字符)、下划线(_)、数字(0~9)组成。
  • 名称区分大小写。例如,mynamemyName 并_不_等同。要注意到前者是小写字母 n 而后者是大写字母 N
  • 有效 的名称可以是 iname_2_3 ,而 2thingsthis is spaced outmy-name>a1b2_c3都是_无效_ 的名称。

数字

数字主要分为两种类型——整数(Integers)与浮点数(Floats)。

整数是 2这样的数,它只是一个整数。

浮点数(Floating Point Numbers,在英文中也会简写为 floats )的例子是 3.2352.3E-4。其中,E 表示 10 的次幂。在这里,52.3E-4 表示 52.3 * 10^-4

对有经验的程序员的提示

没有单独的长整数类型 long 。整数类型int可以指任何大小的整数。

字符串

一串字符串(String)是 字符(Characters)序列(Sequence)。而字符包括所有的数字、符号、大小写字母、空格、制表符甚至回车。

你会在几乎所有的 Python 程序中使用字符串,所以请特别留意下面这部分内容。

单引号

你可以使用单引号来指定字符串,例如 '将我这样框进来''Quote me on this'

所有引号内的空间,诸如空格与制表符,都将按原样保留。

双引号

被双引号包括的字符串和被单引号括起的字符串其工作机制完全相同。例如 "你的名字是?""What's your name?"

三引号 {#triple-quotes}

你可以通过使用三个引号——"""''' 来指定多行字符串。你可以在三引号之间自由地使用单引号与双引号。来看看这个例子:

'''这是一段多行字符串。这是它的第一行。
This is the second line.
"What's your name?," I asked.
He said "Bond, James Bond."
'''

字符串是不可变的

这意味着一旦你创造了一串字符串,你就不能再改变它。尽管这看起来像是一件坏事,但实际上并非如此。我们将会在稍后展现的多个程序中看到为何这一点不是一个限制。

针对 C/C++ 程序员的提示

Python 中没有单独的 char 数据类型。它并非切实必要,并且我相信你不会想念它的。

针对 Perl/PHP 程序员的提示

记住单引号括起的字符串和双引号括起的字符串是一样的——它们不存在任何区别。

格式化方法

有时候我们会想要从其他信息中构建字符串。这正是 format() 方法大有用武之地的地方。

将以下内容保存为文件 str_format.py

age = 20
name = 'Swaroop'

print('{0} was {1} years old when he wrote this book'.format(name, age))
print('Why is {0} playing with that python?'.format(name))

输出:

$ python str_format.py
Swaroop was 20 years old when he wrote this book
Why is Swaroop playing with that python?

它是如何工作的

一个字符串可以使用某些特定的格式(Specification),随后,format 方法将被调用,使用这一方法中与之相应的参数替换这些格式。

在这里要注意我们第一次应用这一方法的地方,此处 {0} 对应的是变量 name,它是该格式化方法中的第一个参数。与之类似,第二个格式 {1} 对应的是变量 age,它是格式化方法中的第二个参数。请注意,Python 从 0 开始计数,这意味着索引中的第一位是 0,第二位是 1,以此类推。

我们可以通过联立字符串来达到相同的效果:

name + 'is' +str(age) + 'years old'

但这样实现是很丑陋的,而且也容易出错。其次,转换至字符串的工作将由 format 方法自动完成,而不是如这般需要明确转换至字符串。再次,当使用 format 方法时,我们可以直接改动文字而不必与变量打交道,反之亦然。

同时还应注意数字只是一个可选选项,所以你同样可以写成:

age = 20
name = 'Swaroop'

print('{} was {} years old when he wrote this book'.format(name, age))
print('Why is {} playing with that python?'.format(name))

这样做同样能得到与前面的程序一样的输出结果。

Python 中 format 方法所做的事情便是将每个参数值替换至格式所在的位置。这之中可以有更详细的格式,例如:

# 对于浮点数 '0.333' 保留小数点(.)后三位
print('{0:.3f}'.format(1.0/3))
# 使用下划线填充文本,并保持文字处于中间位置
# 使用 (^) 定义 '___hello___'字符串长度为 11
print('{0:_^11}'.format('hello'))
# 基于关键词输出 'Swaroop wrote A Byte of Python'  
print('{name} wrote {book}'.format(name='Swaroop', book='A Byte of Python'))

输出:

0.333
___hello___
Swaroop wrote A Byte of Python

由于我们正在讨论格式问题,就要注意 print 总是会以一个不可见的“新一行”字符(\n)结尾,因此重复调用 print将会在相互独立的一行中分别打印。为防止打印过程中出现这一换行符,你可以通过 end 指定其应以空白结尾:

print('a', end='')
print('b', end='')

输出结果如下:

ab

或者你通过 end 指定以空格结尾:

print('a', end=' ')
print('b', end=' ')
print('c')

输出结果如下:

a b c

转义序列

想象一下,如果你希望生成一串包含单引号(')的字符串,你应该如何指定这串字符串?例如,你想要的字符串是 "What's your name?"。你不能指定 'What's your name?',因为这会使 Python 对于何处是字符串的开始、何处又是结束而感到困惑。所以,你必须指定这个单引号不代表这串字符串的结尾。这可以通过 转义序列(Escape Sequence) 来实现。你通过 \ 来指定单引号:要注意它可是反斜杠。现在,你可以将字符串指定为 'What\'s your name?'

另一种指定这一特别的字符串的方式是这样的: "What's your name?" ,如这个例子般使用双引号。类似地, 你必须在使用双引号括起的字符串中对字符串内的双引号使用转义序列。同样,你必须使用转义序列 \\ 来指定反斜杠本身。

如果你想指定一串双行字符串该怎么办?一种方式即使用如前所述的三引号字符串,或者你可以使用一个表示新一行的转义序列——\n 来表示新一行的开始。下面是一个例子:

'This is the first line\nThis is the second line'

另一个你应该知道的大有用处的转义序列是制表符:\t 。实际上还有很多的转义序列,但我必须只在此展示最重要的一些。

还有一件需要的事情,在一个字符串中,一个放置在末尾的反斜杠表示字符串将在下一行继续,但不会添加新的一行。来看看例子:

"This is the first sentence. \
This is the second sentence."

相当于

"This is the first sentence. This is the second sentence."

原始字符串

如果你需要指定一些未经过特殊处理的字符串,比如转义序列,那么你需要在字符串前增加 rR 来指定一个 原始(Raw) 字符串。下面是一个例子:

r"Newlines are indicated by \n"

针对正则表达式用户的提示

在处理正则表达式时应全程使用原始字符串。否则,将会有大量 Backwhacking 需要处理。举例说明的话,反向引用可以通过 '\\1'r'\1' 来实现。

字符串方法

我们可以通过使用字符串方法来对字符串进行操作。

  • .strip(): 去除首尾空白字符
  • .split(): 分割字符串(默认为空格)
  • .replace(): 替换字符
  • .find(): 查找字符
  • .count(): 字符计数
  • .upper()/.lower(): 转大/小写
  • .ljust()/rjust()/zfill(): 指定宽度
  • .isalpha()/isdigit()/.isalnum()

现在我们通过几个例子来学习一下:

join & split

>>>"+".join(['a','b','c'])
a+b+c
>>>"a+b+c".split("+")
['a','b','c']
>>>"I'm fine".split()
["I'm","fine"]
>>>"I'm fine".split("'")
["I","m fine"]

replace & find

a="abcdacd"
a.replace('a' ,'b')
a.replace('a' ,'b').replace('b' ,'c')
a.find ('a')
a.rfind('a')
a.count('a')  

isalnum

a='hello'
help(a.isalnum)
print(a.isalnum())
print("1234".isalnum())
print("123abc".isalnum())
print("1.23".isalnum())
print("123.abc".isalnum())

数据类型

变量可以将各种形式的值保存为不同的_数据类型(Data Type)_。基本的类型是我们已经讨论过的数字与字符串。在后面的章节中,我们会了解如何通过 类(Classes) 类创建我们自己的类型。

这里我们再来看一些常用的数据类型:

  • 布尔类型:True/False
  • 数字:1234,3.14159,3+4j
  • 字符串:'hello', "I'm", """a\nb ""
  • 列表(list):[1,[2,'three'],4]
  • 字典(dict):{'name':"zhang","age":18}
  • 元组(Tuple):(1,'spam',4,'K')
  • 集合(set):set('abca'),{'a','b','c'}

当我们面对如此之多的数据类型时,我们往往需要对一些数据的原有类型进行转换,这就涉及到了类型转换的问题,下面给出一些常见的转换方法:

  • 布尔类型(True/False):bool
  • 数字:整型:int(), round(); 浮点型:float()
  • 字符串:str(), 不可变
  • 列表:list(),任意数据类型组成的有序集合
  • 元组:tuple(),不可变的有序序列
  • 集合:set(),无序不重复元素集
  • 字典:dict(),描述映射关系的无序集合

布尔类型

布尔类型常用于逻辑判断。 函数: bool(); 运算符: not,or,and; 测试: in,is。 我们现在通过下面2段例子学习其用法:

>>> bool('')
False
>>> bool('hello')
True
>>> a = None
>>> b = 1
>>> bool(a)
False
>>> bool(b)
True
>>> not 1==3        
True
>>> 1>2 or 2>1      
True
>>> (1>2) + (2>1)  
1                   
>>> (1>2) and (2>1)  
False
>>> (1>2) * (2>1)    
0

对象

需要记住的是,Python 将程序中的任何内容统称为 对象(Object)。这是一般意义上的说法。我们以“某某对象(object)”相称,而非“某某东西(something)”。

针对面向对象编程语言用户的提示:

Python 是强(Strongly)面向对象的,因为所有的一切都是对象, 包括数字、字符串与函数。

接下来我们将看见如何使用变量与字面常量。你需要保存以下案例并试图运行程序。

如何编写 Python 程序

从今以后,保存和运行 Python 程序的标准步骤如下:

对于 PyCharm 用户

  1. 打开 PyCharm
  2. 以给定的文件名创建新文件。
  3. 输入案例中给出的代码。
  4. 右键并运行当前文件。

注意:每当你需要提供 命令行参数(Command Line Arguments)时,点击 Run -> Edit Configurations 并在 Script parameters: 部分输入相应参数,并点击 OK 按钮:

对于其他编辑器用户

  1. 打开你选择的编辑器。
  2. 输入案例中给出的代码。
  3. 以给定的文件名将其保存成文件。
  4. 在解释器中通过命令 python program.py 来运行程序。

案例:使用变量与字面常量

输入并运行以下程序:

# 文件名:var.py
i = 5
print(i)
i = i + 1
print(i)

s = '''This is a multi-line string.
This is the second line.'''
print(s)

输出:

5
6
This is a multi-line string.
This is the second line.

它是如何工作的

下面是这一程序的工作原理。首先,我们使用赋值运算符(=)将字面常量数值 5 赋值给变量 i。这一行被称之为声明语句(Statement)因为其工作正是声明一些在这一情况下应当完成的事情:我们将变量名 i 与值 5 相连接。然后,我们通过 print 语句来打印变量 i 所声明的内容,这并不奇怪,只是将变量的值打印到屏幕上。

接着,我们将 1 加到 i 变量所存储的值中,并将得出的结果重新存储进这一变量。然后我们将这一变量打印出来,并期望得到的值应为 6

类似地,我们将字面文本赋值给变量 s,并将其打印出来。

针对静态编程语言程序员的提示

变量只需被赋予某一值。不需要声明或定义数据类型。

逻辑行与物理行

所谓物理行(Physical Line)是你在编写程序时 你所看到 的内容。所谓逻辑行(Logical Line)是 Python 所看到 的单个语句。Python 会假定每一 物理行 会对应一个 逻辑行

有关逻辑行的一个例子是诸如 print('hello world') 这样一句语句——如果其本身是一行(正如你在编辑器里所看到的那样),那么它也对应着一行物理行。

Python 之中暗含这样一种期望:Python 鼓励每一行使用一句独立语句从而使得代码更加可读。

如果你希望在一行物理行中指定多行逻辑行,那么你必须通过使用分号(;)来明确表明逻辑行或语句的结束。下面是一个例子:

i = 5
print(i)

实际上等同于

i = 5;
print(i);

同样可以看作

i = 5; print(i);

也与这一写法相同

i = 5; print(i)

然而,我_强烈建议_你对于每一行物理行最多只写入一行逻辑行。这个观点就是说你不应该使用分号。实际上,我_从未_在 Python 程序中使用、甚至是见过一个分号。

在一类情况下这一方法会颇为有用:如果你有一行非常长的代码,你可以通过使用反斜杠将其拆分成多个物理行。这被称作_显式行连接(Explicit Line Joining)_:

s = 'This is a string. \
This continues the string.'
print(s)

输出:

This is a string. This continues the string.

类似地,

i = \
5

等同于

i = 5

在某些情况下,会存在一个隐含的假设,允许你不使用反斜杠。这一情况即逻辑行以括号开始,它可以是方括号或花括号,但不能是右括号。这被称作 隐式行连接(Implicit Line Joining)。你可以在后面当我们讨论列表(List)的章节时了解这一点。

缩进

空白区在 Python 中十分重要。实际上,空白区在各行的开头非常重要。这被称作 缩进(Indentation)。在逻辑行的开头留下空白区(使用空格或制表符)用以确定各逻辑行的缩进级别,而后者又可用于确定语句的分组。

这意味着放置在一起的语句_必须_拥有相同的缩进。每一组这样的语句被称为 块(block)。我们将会在后文章节的案例中了解块这一概念是多么重要。

有一件事你需要记住:错误的缩进可能会导致错误。下面是一个例子:

i = 5
# 下面将发生错误,注意行首有一个空格
 print('Value is', i)
print('I repeat, the value is', i)

当你运行这一程序时,你将得到如下错误:

  File "whitespace.py", line 3
    print('Value is', i)
    ^
IndentationError: unexpected indent
# 缩进错误:意外缩进

你会注意到第二行开头有一个空格。Python 指出的错误信息告诉我们程序的语法是无效的,意即,程序没有被正确地写入。这一信息对你的意义是 你不能任意开始一个新的语句块(当然,除非你一直在使用默认的主代码块)。你可以使用新块的情况将会在后面诸如控制流等章节加以介绍。

如何缩进

使用四个空格来缩进。这是来自 Python 语言官方的建议。好的编辑器会自动为你完成这一工作。请确保你在缩进中使用数量一致的空格,否则你的程序将不会运行,或引发不期望的行为。

针对静态编程语言程序员的提示

Python 将始终对块使用缩进,并且绝不会使用大括号。你可以通过运行 from __future__ import braces 来了解更多信息。

总结

现在我们已经了解了诸多本质性的细节,我们可以前去了解控制流语句等更多更加有趣的东西。记得一定要充分理解你在本章所阅读的内容。