引言

Python真的是一门很优雅且富有哲理的语言。我这里便以python之禅 中的几句作为这篇文章的开始:

Beautiful is better than ugly.

Simple is better than complex.

Complex is better than complicated.

Readability counts.

Now is better than never.


解释型语言

python和C、C++等编译型语言不同,而和matlab类似:它可以在解释器中一行一行交互式地运行,也可以编写成脚本文件来运行——是一门解释型语言

解释型语言的优点是简单,方便,可以化繁就简;缺点是相比编译型语言,运行速度比较慢 ,而且运行时报错 ,不利于提前发现BUG

一般的开发行业,如游戏行业等,基本上不会用python。但是由于python的应用面广,编程效率高,在不是那么注重程序运行速度的领域,如数据分析、机器学习等领域则备受青睐。

说现实点,学校实验室收人基本上都要求会python(大嘘。


简单数据类型

python是一种动态类型语言 ,或者说它可以自动推导出变量的类型,而不需要我们再专门去声明,就和C++11中的auto 一样。

字符串

python和C语言不同,它支持字符串类型。而且python的字符串类型和C++的string类型很相似,都内置了诸多方法以方便对字符串的操作。

字符串的表示

python中的字符串可以用单引号或双引号括出,所以用法可以很灵活,按照不同情况可以选择不同引号

如果一个字符串中出现了单引号,那么就选用双引号括出;如果字符串中出现了双引号,那么就选用单引号括出。但是单双引号一定要匹配,不能混用!

1
2
"I'm Sato." # 情况一
'I told him,"Hei,hurry up!"' # 情况二

字符串的操作

前面说了python中字符串支持很多操作,我这里选择几个比较常用的来讲。

大小写转换

先定义一个字符串,然后使用字符串变量.函数 即可调用对应的方法。示例程序如下:

1
2
3
4
5
company='yuzusoft Sour'
print(company.title()) # 操作一
print(company.upper()) # 操作二
print(company.lower()) # 操作三
print(company) #检查是否改变
1
2
3
4
Yuzusoft Sour # 操作一
YUZUSOFT SOUR # 操作二
yuzusoft sour # 操作三
yuzusoft Sour # 检查
通过这段示例程序可以看出:

title()可将字符串中的每个单词首字母大写;upper()可将字符串的所有字符大写;lower()则将所有字符小写。但是这个字符串最终没有改变,用C语言来理解就是通过返回值来传递结果,而不改变其本身。

合并字符串

python中可以使用+来将字符串连接起来,在C++中相当于对+进行了重载。

1
print("Hello"+",python"+'!')

要注意连接的每一项都是字符串!

1
2
3
age=18
# print("I'm "+age+' years old.') 报错,因为age不是字符串
print("I'm "+str(age)+' years old.') # 强制类型转换

更多字符串的操作请前往编辑器自行查看:

数字

python中的数字只分为整数和浮点数 两种

数字的运算支持+-*/运算,而且python会尽可能精确地表示出结果:

  1. 结果小数位数有限且比较少,则只表示到最小位数,不显示多余的0:7/2=3.5
  2. 无限不循环小数,尽可能显示多的小数位数:1/3=0.3333333333333333
  3. 很大的数进行运算,python将自动采用高精度算法,所以结果不会溢出:
1
2
>>> 8301830182912908982*27189371872987981
225721548069614912986831433032945342

由于python计算精确且可以在解释器中交互运行,所以可以很好地拿来做数值计算(计算器)。

而这个计算拿到C++中就是这样:

所以,让我们高呼:Python,YES! (bushi,逐渐发出了C语言


列表及其操作

python中的列表是一个很神奇的数据类型。它可以作为C语言中的数组、结构体,C++中的vector,也可以作为数据结构中的栈、队列……用好了就会觉得list真的太有用了!

列表的定义及访问

列表有一系列按特定顺序排列的元素组成,而且列表中可以包含不同元素。因为列表元素有顺序,所以它像C语言的数组;因为元素可以是不同类型,所以又像结构体。

1
2
3
info=['Sato',18,'male'] # 这是合法的
print(info[1]) # 打印18
print(info[-1]) # 打印最后一个元素

列表用方括号[] 来表示,并且用逗号依次分隔其中的元素。访问的时候通过下标来访问即可。更方便的是可以通过下标-1直接访问到最后一个元素

添加和删除元素

添加元素

python中的列表可以动态增长 ,所以又像C++中的vector。

  1. 使用append列表末尾添加元素
  2. 使用insert列表任意位置添加元素
1
2
3
4
5
games=['Senren*Banka','RIDDLE JOKER']
games.append['Cafe Stella']
print(games)
games.insert(1,'Sabbat of the Witch')
print(games)

输出如下:

1
2
['Senren*Banka', 'RIDDLE JOKER', 'Cafe Stella']
['Senren*Banka', 'Sabbat of the Witch', 'RIDDLE JOKER', 'Cafe Stella']

删除元素

和添加元素一样,删除列表元素也有很多方式:

  1. 使用del 语句删除指定下标的元素
  2. 使用pop 弹出列表最后一个元素
  3. 使用pop 弹出列表任意位置的元素
  4. 使用remove 根据值删除元素
1
2
3
4
5
6
7
games=['Senren', 'Sabbat', 'JOKER', 'Cafe','Stella']
# 使用下面三种方法都可以删除最后一个元素
del games[-1]
games.pop()
games.pop(-1)
# 根据值删除元素
games.remove('Senren')

因为列表是一种顺序存储的结构,所以前三种按地址查找的时间复杂度为O(1),是很快的;而第四种按值查找的时间复杂度为则O(n)。

经测试,当列表中有重复的元素时,使用remove方法删除该元素只会删除前面的那一个元素:

1
2
3
4
games=['Senren', 'Sabbat','Senren']
games.remove('Senren')
print(games)
# 打印['Sabbat', 'Senren']

列表切片

使用列表的一部分

如果我们只需要取出列表的一部分,那么就可以使用切片 的方法。通过指定列表下标的范围,即可获得对应下标范围的那一部分列表:

1
2
3
4
5
games=['Senren', 'Sabbat','Senren']
print(games[0:2]) # 1
print(games[1:3]) # 2
# 1 打印['Senren', 'Sabbat']
# 2 打印['Sabbat', 'Senren']

通过这段程序我们可以观察出来,这个下标范围相当于一个左闭右开区间切片的列表范围从左边的下标开始取到右边的下标-1。(实际上,计算机中对区间范围的约定很多也是左闭右开)

对于列表切片,如果省略左边的下标,那么就会从第一个元素开始访问;如果省略右边的下标,那么就会一直访问到最后一个元素;如果两个都省略,那么就会访问整个列表:

1
2
3
4
5
6
7
games=['Senren', 'Sabbat','Senren']
print(games[:2]) # 1
print(games[1:]) # 2
print(games[:]) # 3
# 1 打印['Senren', 'Sabbat']
# 2 打印['Sabbat', 'Senren']
# 3 打印['Senren', 'Sabbat', 'Senren']

值得注意的是,列表切片并不改变列表本身,相当于是通过返回值进行传参。

复制列表

如果想要获得一个和已有列表完全一样,但又相互独立的另一个列表,你打算怎么做呢?

这时我们一般会想到赋值运算符 。毕竟python对字符串的+都进行了重载,再重载一下=也不过分吧?

1
2
3
4
5
games=['Senren', 'Sabbat','Senren']
playlist=games
playlist.pop()
print(games)
# 打印['Senren', 'Sabbat']

可以看到,修改另一个列表时,原本的列表也改变了。这是因为两个列表并不独立,通过赋值操作赋值的是列表的地址,这样另一个列表就相当于原本列表的引用

要想根据已有列表创建一个与之独立的列表,就需要用到上面的列表切片

1
2
3
4
5
games=['Senren', 'Sabbat','Senren']
playlist=games[:]
playlist.pop()
print(games)
# 打印['Senren', 'Sabbat', 'Senren']

元组

列表的元素是可变的,要使元素不可变就要用元组

元组使用圆括号() 进行标识。除了元素不能改变外,其他操作和列表相同,所以不多赘述。一旦使用改变元组本身的操作,python就会报错说:

1
2
AttributeError: 'tuple' object has no attribute 'pop'
# 其他同理

字典

字典,顾名思义,其中的元素就是索引(key)值(value) 。python中的字典和C++中的map很相似,可以将元素哈希化后进行存储,很有实用性

字典的定义和使用

字典用放在花括号{} 中的一系列键-值对表示,通过键可以访问到相应的值,字典中存放元素的顺序无关紧要。

1
2
3
settings={'model':'easy','speed':'fast'}
print(settings['speed'])
# 打印fast

现在我们把三种括号都用完了:[]对应列表、()对应元组、{}对应字典。

字典的操作

字典的操作并没有列表那么多,涉及到的操作无非就是添加、修改和删除元素,示例见下:

1
2
3
4
5
6
7
settings={'model':'easy','speed':'fast'}
# 添加元素
settings['voice']='open'
# 修改元素
settings['model']='peace'
# 删除元素
del settings['speed']

可以看出,添加元素和修改元素的方式相同,区别就是键在字典中是否已经存在了:如果存在了就是修改。字典允许值重复,但键是不能重复的

1
2
3
settings={'model':'easy','speed':'peace','model':'peace'}
print(settings)
# 打印{'model': 'peace', 'speed': 'peace'}

上面的程序中键重复了,所以后面的就覆盖掉了前面的——这就和修改的原理一样了。


循环与判断

python的循环逻辑和C语言大致相同,区别就是写法上有一些不同。所以这一部分也会过得很快。

python在写法上更加注重缩进,用缩进来表示一个代码块。比如说for循环的循环体就要缩进一个tab四个空格 ,循环体结束就取消缩进。注意不要漏写冒号!

使用for循环遍历

python中的for循环比C语言更加精简,更像C++11中基于范围的for循环

遍历列表

1
2
3
settings=['model','speed','screen']
for setting in settings:
print(setting)

for循环通过这样的方式每次从列表中按顺序取一个元素 ,直到取完所有元素。

通过range()的方式可以快速定义一个区间,然后可以使用for循环遍历:

1
2
3
settings=['model','speed','screen']
for num in range(0,3):
print(settings[num])

这样可以获得和之前程序一样的结果。

还可以给range函数定义步长:range(0,5,2)就是从0~4,步长为2的一系列数据:0,2,4。

遍历字典

字典的每一项都是一个键值对,所以遍历相对列表要复杂一些。

遍历字典的键

使用keys() 方法可以将字典的键以列表的形式返回,再用for循环遍历这列表即可。

1
2
3
4
settings={'model':'easy','speed':'fast'}
for key in settings.keys():
print(key)
# 打印model \n speed

实际上,我们对字典进行遍历,python默认我们遍历的是字典的键

1
2
3
4
settings={'model':'easy','speed':'fast'}
for key in settings:
print(key)
# 输出和上面的程序一样

遍历字典的值

同理,使用values() 方法可以将字典的值以列表的形式返回,再用for循环遍历这列表即可。

1
2
3
4
settings={'model':'easy','speed':'fast'}
for value in settings.values():
print(value)
# 打印easy \n fast

遍历字典的键值

使用items() 方法可以将字典的键值对作为元组以列表的形式返回:

1
2
3
settings={'model':'easy','speed':'fast'}
print(settings.items())
# 打印[('model', 'easy'), ('speed', 'fast')]

可以在for循环中使用两个参数来分别接收键和值:

1
2
3
4
settings={'model':'easy','speed':'fast'}
for k,v in settings.items():
print(k+' '+v)
# 打印model easy \n speed fast

while循环

python中while循环真的和C语言差不多了,后面都是一个逻辑表达式True则继续,False则退出。

1
2
3
4
msg='Hello!'
while msg!='quit':
print(msg)
msg=input('Please enter a word:')

这里还涉及到两个知识点:

  1. python中表示正误的为TrueFalse,相当于C++中的bool类型,但开头字母都要大写
  2. python中使用input函数来接受输入。括号中的字符串为输入提示

和C语言一样,python还可以通过breakcontinue 关键字进行循环控制,因为用法一样,所以就不介绍了。

if语句

if语句也是后面加一个条件表达式和冒号即可,和C语言类似。下面讲一些和C有区别的用法。

多个条件

C语言中可以使用&&||对多个逻辑表达式进行运算,python则使用的是andor

1
2
3
info={'age':18,'sex':'male'}
if info['age']>=18 and info['sex']=='male':
print('Welcome,master!')# 不对劲

if-else结构

C语言用的是else if,而python使用的是elifelse都是一样的。

1
2
3
4
5
6
7
info={'age':18,'sex':'male'}
if info['age']>=18 and info['sex']=='male':
print('Welcome,master!')# 不对劲
elif info['age']>=18 and info['sex']=='female':
print('Miss, come this way, please')
else:
print('Welcome to the amusement park!')

判断列表元素

使用innot in可以判断元素是否在列表中:

1
2
3
4
5
games=['Senren', 'Sabbat', 'Senren']
if 'Senren' in games:
print('决定了,今天就玩千恋万花!')
if 'Cafe' not in games:
games.append('Cafe')

函数

一个C程序可以看成由函数组成,程序从主函数作为入口开始运行,主函数可以调用其他函数,其他函数之间也可以相互调用。函数之外的全局区则可以定义一些宏和全局变量等。

而python是一种脚本语言,不存在什么主函数,你写一句程序便运行一句。此时,函数则可看成一个语句块。但是,python的函数调用也并非直接替换那么简单。比如,你在函数外面定义的一个变量,如果不传递给函数的话,函数是访问不到的。下面我们将细讲python中的函数。

函数的定义和使用

1
2
3
def greet(name):
print('Hello,'+name.title()+'!')
greet('sato')

上面就是定义和使用函数的基本姿势。def代表这是一个函数,紧跟函数名的括号里面的是形参列表 。通过冒号引出这个函数,缩进的部分就是函数体。此外函数调用必须在函数定义之后

函数传参

python函数的传递实参的方式和C语言可以有一些不同,可以使用位置实参 ,也可以使用关键字实参

位置实参

调用函数时,需要将每个实参都关联到相应的形参基于实参顺序传递参数 的关联方式就是位置实参。C语言中传参的方式就可以看成使用位置实参

1
2
3
def greet(msg,name):
print(msg.title()+','+name.title()+'!')
greet('goodby','sato')

上面程序将'goodby'传递给msg,'sato'传递给name,是按照实参顺序传递的。

关键字实参

关键字实参是传递给函数的形参名-实参对 。通过在实参列表中指定形参名,从而将实参与相应形参关联起来。这样我们就无需考虑函数中实参的调用顺序了

1
2
3
def greet(msg,name):
print(msg.title()+','+name.title()+'!')
greet(name='sato',msg='hello')

形参默认值

和C++的缺省参数一样,我们可以给函数形参一个默认值当我们没有给这个形参传递值时,该形参将采用预先设定的默认值

1
2
3
def greet(msg='hello',name='Li Hua'):
print(msg.title()+','+name.title()+'!')
greet(name='sato')

由于python可以使用关键字实参,不要求实参列表的顺序和形参列表相同,所以默认形参可以放在形参列表的任意位置,而不必像C++一样必须把默认形参放在形参列表的后面。

使用关键字实参怎么知道形参名有哪些?

当使用自己的函数时,形参名自己当然清楚。但是调用其他人的函数时,我们怎么知道这个函数的形参名有哪些呢?

其实很简单的说,自己去翻一下那个文件不就知道了吗。当然,这一步一般编辑器就帮我们做了,我们只需要看编辑器给出的提示即可。

函数返回值

在上面的函数提示中,我们看到了这样的提示:

1
(function) greet: (msg: str = 'hello', name: str = 'Li Hua') -> None

前面的还好理解,后面的->None是什么意思呢?其实,这里的None指的就是函数的返回值。

和C语言一样,函数通过return关键字来传递返回值。所以我们用函数返回值来改写一下之前的程序:

1
2
3
def greet(msg='hello',name='Li Hua'):
return(msg.title()+','+name.title()+'!')
print(greet(name='sato'))

禁止修改函数列表

前面我们讲过,将一个列表用=复制给另一个变量,相当于复制列表的地址,最后该变量相当于是原来列表的引用

同理,如果我们直接将列表作为实参传递给函数,那么对应的形参相当于也是该列表的引用 ,函数中对列表的修改将直接改变列表本身。

所以,和之前一样,我们使用列表切片来复制列表的值。

1
2
3
4
5
6
def greet(info):
info[0]='goodby'
print(info)
info=['hello','sato']
greet(info[:])
print(info)
1
2
['goodby', 'sato']
['hello', 'sato']

但是如果函数中不涉及到修改列表值的问题的话,我们直接传递列表即可。特别是列表元素很多的时候,列表的拷贝会花很多时间。

多文件编程

在C语言中,我们多文件编程的方式是把函数声明放在头文件中,函数实现放在c或cpp文件中。分别编译后再将生成的.obj文件链接起来即可。

使用python进行多文件编程更为简单,因为它没有函数声明之类复杂的东西。我们把函数写在一个文件中,这个文件就称为模块 。当需要这函数的时候,使用import 引入这个函数即可。

导入整个模块

使用import 模块名 即可将整个模块导入:

注意这种导入方式还存在一个作用域 的问题,即调用的函数前面需要加上模块的名字

导入指定函数

我们可以只从那个模块中导入指定的一个或几个函数,这时就不需要作用域了

如果我们想要导入整个模块的所有函数,而不想加上作用域(就是懒),那么就可以使用*这个符号。

但是要求就是现在文件中已有的函数不能与导入的函数同名

为模块指定别名

使用as 可以为模块指定别名,这在一定程度上可以减少输入的字数(笑


现在python还差 的内容没有讲,这部分我打算放在《python和C++面向对象》的另外一篇文章来讲。

此外,我打算之后还学习python的编码规范,还会在另一篇文章里介绍怎样写出格式优美的python代码