Python 学习

因为在写算法时,很多算法用python实现,为了参考、学习、对比,觉得有必要学一下python,
简单的学一下,能看懂别人的代码就可以(其实是python的库太多了,觉得一定时间里看不完源代码)

(1) 简介

Python的哲学就是简单优雅,尽量写容易看明白的代码,尽量写少的代码。

(2) 安装

https://www.python.org/downloads/

(3) 第一个python程序

文件编码

.py文件默认使用UTF-8无BOM编码
即使是在windows下新建一个txt文件(gbk编码),把txt文件重命名成.py文件并且执行后,
文件会自动变成UTF-8编码(可能是在程序执行时把文件转换成UTF-8编码了吧)

输入输出

输入

用print加上字符串,就可以向屏幕上输出指定的文字。

print语句也可以跟上多个字符串,用逗号“,”隔开,或者“+”,注意两个符号的效果不一样

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
WKQ@WKQ-PC C:\WorkSpaces\python
> python
Python 2.7.13 (v2.7.13:a06454b1afa1, Dec 17 2016, 20:53:40) [MSC v.1500 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.

>>> print 'hello', 'world', ' test'
hello world test

>>> print 'hello','world', ' test'
hello world test

>>> print 300
300

>>> print 100 + 200
300

>>> print '100' + '200'
100200

>>> print 'hello' + 'world'
helloworld

输出

Python提供了一个raw_input,可以让用户输入字符串,并存放到一个变量里。

1
2
3
4
5
>>> name = raw_input()
123 + 1223 + '1234', 'hello'

>>> name
"123 + 1223 + '1234', 'hello'"

自己写一个程序,执行该程序时,提示用户自己输入名字,输出 hello + 用户自定义的名字

1
2
3
4
# hello_name.py

name = raw_input('please enter your name: ')
print 'hello,', name

1
2
3
4
WKQ@WKQ-PC C:\WorkSpaces\python
> python hello_name.py
tom
hello, tom

(4) Python基础

1
2
3
4
5
6
# print absolute value of an integer:
a = 100
if a >= 0:
print a
else:
print -a

以#开头的语句是注释,注释是给人看的,可以是任意内容,解释器会忽略掉注释。其他每一行都是一个语句,
当语句以冒号“:”结尾时,缩进的语句视为代码块。

缩进是为了让人方便阅读

Python程序是大小写敏感的,如果写错了大小写,程序会报错。

数据类型和变量

整数

Python可以处理任意大小的整数,当然包括负整数,在程序中的表示方法和数学上的写法一模一样,
例如:1,100,-8080,0,等等。

计算机由于使用二进制,所以,有时候用十六进制表示整数比较方便,十六进制用0x前缀和0-9,a-f表示,
例如:0xff00,0xa5b4c3d2,等等。

浮点数

浮点数也就是小数,之所以称为浮点数,是因为按照科学记数法表示时,一个浮点数的小数点位置是可变的,
比如,1.23x109和12.3x108是相等的。浮点数可以用数学写法,如1.23,3.14,-9.01,等等。
但是对于很大或很小的浮点数,就必须用科学计数法表示,把10用e替代,1.23x109就是1.23e9,
或者12.3e8,0.000012可以写成1.2e-5,等等。

整数和浮点数在计算机内部存储的方式是不同的,整数运算永远是精确的(除法难道也是精确的?是的!),
而浮点数运算则可能会有四舍五入的误差。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 整数除法永远是整数,即使除不尽。
>>> 10 / 3
3

# 精确的除法,只需把其中一个整数换成浮点数做除法就可以
>>> 10.0 / 3
3.3333333333333335

# Python还提供一个余数运算,可以得到两个整数相除的余数
>>> 10 % 3
1

>>> 1.0 * 10 / 3
3.3333333333333335

字符串

字符串是以’’或””括起来的任意文本,比如’abc’,”xyz”等等。
请注意,’’或””本身只是一种表示方式,不是字符串的一部分,因此,字符串’abc’只有a,b,c这3个字符。
如果’本身也是一个字符,那就可以用””括起来,比如”I’m OK”包含的字符是I,’,m,空格,O,K这6个字符。

如果字符串内部既包含’又包含”怎么办?可以用转义字符\来标识,比如:

1
'I\'m \"OK\"!'

表示的字符串内容是:

1
I'm "OK"!

转义字符\可以转义很多字符,比如\n表示换行,\t表示制表符,字符\本身也要转义,所以\表示的字符就是\,
可以在Python的交互式命令行用print打印字符串看看:

1
2
3
4
5
6
7
8
9
10
11
12
13
>>> print 'I\'m ok.'
I'm ok.

>>> print '1\n2'
1
2

>>> print '\\\n\\'
\
\

>>> print '\\\\n\\'
\\n\

如果字符串里面有很多字符都需要转义,就需要加很多\,
为了简化,Python还允许用r’’表示’’内部的字符串默认不转义,可以自己试试:

1
2
3
4
5
>>> print '\\\t\\'
\ \

>>> print r'\\\t\\'
\\\t\\

如果字符串内部有很多换行,用\n写在一行里不好阅读,
为了简化,Python允许用’’’…’’’的格式表示多行内容,可以自己试试:

1
2
3
4
5
6
7
8
9
10
11
12
>>> print '''line1...line2...line3'''
line1...line2...line3

>>> print '''
... lint1
... line2
... line3
... '''

lint1
line2
line3

布尔值

布尔值和布尔代数的表示完全一致,一个布尔值只有True、False两种值,
在Python中,可以直接用True、False表示布尔值(请注意大小写),也可以通过布尔运算计算出来:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
>>> True
True

>>> False
False

>>> true
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'true' is not defined

>>> false
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'false' is not defined

>>> 2 > 1
True

>>> 2 > 3
False

布尔值可以用and、or和not运算。
and运算是与运算,只有所有都为True,and运算结果才是True
or运算是或运算,只要其中有一个为True,or运算结果就是True
not运算是非运算,它是一个单目运算符,把True变成False,False变成True

空值

空值是Python里一个特殊的值,用None表示。None不能理解为0,因为0是有意义的,而None是一个特殊的空值。

此外,Python还提供了列表、字典等多种数据类型,还允许创建自定义数据类型。

变量

变量的概念基本上和初中代数的方程变量是一致的,只是在计算机程序中,变量不仅可以是数字,还可以是任意数据类型。

变量在程序中就是用一个变量名表示了,变量名必须是大小写英文、数字和_的组合,且不能用数字开头,比如:

1
2
3
4
5
a = 1  # 变量a是一个整数

t_007 = 'T007' # 变量t_007是一个字符串

Answer = True # 变量Answer是一个布尔值True

在Python中,等号=是赋值语句,可以把任意数据类型赋值给变量,同一个变量可以反复赋值,而且可以是不同类型的变量,例如:

1
2
3
4
5
6
a = 123  # a是整数
print a
a = 'ABC' # a变为字符串
print a
a = True # a变为布尔类型
print a

这种变量本身类型不固定的语言称之为动态语言,与之对应的是静态语言。
静态语言在定义变量时必须指定变量类型,如果赋值的时候类型不匹配,就会报错。
例如Java是静态语言,赋值语句如下(在Java里,// 表示注释)

1
2
int a = 123;  // a是整数类型变量
a = "ABC"; // 错误:不能把字符串赋给整型变量

和静态语言相比,动态语言更灵活,就是这个原因。

请不要把赋值语句的等号等同于数学的等号。比如下面的代码:

1
2
x = 10
x = x + 2

如果从数学上理解x = x + 2那无论如何是不成立的,
在程序中,赋值语句先计算右侧的表达式x + 2,得到结果12,再赋给变量x。
由于x之前的值是10,重新赋值后,x的值变成12。

最后,理解变量在计算机内存中的表示也非常重要。当我们写:

1
a = 'ABC'

时,Python解释器干了两件事情:

  1. 在内存中创建了一个’ABC’的字符串。
  2. 在内存中创建了一个名为a的变量,并把它指向’ABC’。
    也可以把一个变量a赋值给另一个变量b,这个操作实际上是把变量b指向变量a所指向的数据,例如下面的代码:

    1
    2
    3
    4
    a = 'ABC'
    b = a
    a = 'XYZ'
    print b

    最后一行打印出变量b的内容到底是’ABC’呢还是’XYZ’?
    如果从数学意义上理解,就会错误地得出b和a相同,也应该是’XYZ’,
    但实际上b的值是’ABC’,让我们一行一行地执行代码,就可以看到到底发生了什么事:
    变量_内存引用

常量

所谓常量就是不能变的变量,比如常用的数学常数π就是一个常量。
在Python中,通常用全部大写的变量名表示常量

1
PI = 3.14159265359

但事实上PI仍然是一个变量,Python根本没有任何机制保证PI不会被改变,
所以,用全部大写的变量名表示常量只是一个习惯上的用法,如果你一定要改变变量PI的值,也没人能拦住你。

字符串和编码

编码

我们已经讲过了,字符串也是一种数据类型,但是,字符串比较特殊的是还有一个编码问题。

因为计算机只能处理数字,如果要处理文本,就必须先把文本转换为数字才能处理。
最早的计算机在设计时采用8个比特(bit)作为一个字节(byte),
所以,一个字节能表示的最大的整数就是255(二进制11111111=十进制255),如果要表示更大的整数,就必须用更多的字节。
比如两个字节可以表示的最大整数是65535,4个字节可以表示的最大整数是4294967295。

由于计算机是美国人发明的,因此,最早只有127个字母被编码到计算机里,也就是大小写英文字母、数字和一些符号,
这个编码表被称为ASCII编码,比如大写字母A的编码是65,小写字母z的编码是122。

但是要处理中文显然一个字节是不够的,至少需要两个字节,而且还不能和ASCII编码冲突,
所以,中国制定了GB2312编码,用来把中文编进去。

你可以想得到的是,全世界有上百种语言,日本把日文编到Shift_JIS里,韩国把韩文编到Euc-kr里,
各国有各国的标准,就会不可避免地出现冲突,结果就是,在多语言混合的文本中,显示出来会有乱码。

因此,Unicode应运而生。Unicode把所有语言都统一到一套编码里,这样就不会再有乱码问题了。

Unicode标准也在不断发展,但最常用的是用两个字节表示一个字符(如果要用到非常偏僻的字符,就需要4个字节)。
现代操作系统和大多数编程语言都直接支持Unicode。

现在,捋一捋ASCII编码和Unicode编码的区别:ASCII编码是1个字节,而Unicode编码通常是2个字节。

字母A用ASCII编码是十进制的65,二进制的01000001;

字符0用ASCII编码是十进制的48,二进制的00110000,注意字符’0’和整数0是不同的;

汉字中已经超出了ASCII编码的范围,用Unicode编码是十进制的20013,二进制的01001110 00101101。

你可以猜测,如果把ASCII编码的A用Unicode编码,只需要在前面补0就可以,因此,A的Unicode编码是00000000 01000001。

新的问题又出现了:如果统一成Unicode编码,乱码问题从此消失了。但是,如果你写的文本基本上全部是英文的话,
用Unicode编码比ASCII编码需要多一倍的存储空间,在存储和传输上就十分不划算。

所以,本着节约的精神,又出现了把Unicode编码转化为“可变长编码”的UTF-8编码。
UTF-8编码把一个Unicode字符根据不同的数字大小编码成1-6个字节,
常用的英文字母被编码成1个字节,
汉字通常是3个字节,
只有很生僻的字符才会被编码成4-6个字节。
如果你要传输的文本包含大量英文字符,用UTF-8编码就能节省空间:

字符 ASCII Unicode UTF-8
A 01000001 00000000 01000001 01000001
中 x 01001110 00101101 11100100 10111000 10101101
从上面的表格还可以发现,UTF-8编码有一个额外的好处,就是ASCII编码实际上可以被看成是UTF-8编码的一部分,
所以,大量只支持ASCII编码的历史遗留软件可以在UTF-8编码下继续工作。

搞清楚了ASCII、Unicode和UTF-8的关系,我们就可以总结一下现在计算机系统通用的字符编码工作方式:

在计算机内存中,统一使用Unicode编码,当需要保存到硬盘或者需要传输的时候,就转换为UTF-8编码。

用记事本(推荐使用notepad++)编辑的时候,从文件读取的UTF-8字符被转换为Unicode字符到内存里,
编辑完成后,保存的时候再把Unicode转换为UTF-8保存到文件:
python_code

浏览网页的时候,服务器会把动态生成的Unicode内容转换为UTF-8再传输到浏览器:
python_code_web

所以你看到很多网页的源码上会有类似

1
<meta charset="UTF-8" />

的信息,表示该网页正是用的UTF-8编码。

Python的字符串

搞清楚了令人头疼的字符编码问题后,我们再来研究Python对Unicode的支持。

因为Python的诞生比Unicode标准发布的时间还要早,所以最早的Python只支持ASCII编码,
普通的字符串’ABC’在Python内部都是ASCII编码的。Python提供了ord()和chr()函数,可以把字母和对应的数字相互转换:

1
2
3
4
5
6
7
8
9
10
11
>>> ord('A')
65

>>> ord("A")
65

>>> ord('a')
97

>>> chr(65)
'A'

Python在后来添加了对Unicode的支持,以Unicode表示的字符串用u’…’表示,比如:

1
2
3
4
5
6
7
8
9
10
11
>>> print u'中文'
中文

>>> u'中'
u'\u4e2d'

>>> u'中文'
u'\u4e2d\u6587'

>>> print u'\u4e2d\u6587'
中文

写u’中’和u’\u4e2d’是一样的,\u后面是十六进制的Unicode码。因此,u’A’和u’\u0041’也是一样的。

两种字符串如何相互转换?字符串’xxx’虽然是ASCII编码,但也可以看成是UTF-8编码,而u’xxx’则只能是Unicode编码。

把u’xxx’转换为UTF-8编码的’xxx’用encode(‘utf-8’)方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
>>> u'ABC'.encode('utf-8')
'ABC'
>>> u'ABC'.encode('gbk')
'ABC'
>>> u'ABC'.encode('ansi')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
LookupError: unknown encoding: ansi
>>> u'ABC'.encode('ANSI')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
LookupError: unknown encoding: ANSI
>>> u'ABC'.encode('gb2312')
'ABC'
>>> u'ABC'.encode('utf-16')
'\xff\xfeA\x00B\x00C\x00'
>>> u'ABC'.encode('utf-32')
'\xff\xfe\x00\x00A\x00\x00\x00B\x00\x00\x00C\x00\x00\x00'
>>> u'ABC'.encode('utf-64')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
LookupError: unknown encoding: utf-64

>>> u'中文'.encode('utf-8')
'\xe4\xb8\xad\xe6\x96\x87'
>>> u'中文'.encode('gbk')
'\xd6\xd0\xce\xc4'
>>> u'中文'.encode('gb2312')
'\xd6\xd0\xce\xc4'
>>> u'中文'.encode('ansi')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
LookupError: unknown encoding: ansi
>>> u'中文'.encode('utf-16')
'\xff\xfe-N\x87e'
>>> u'中文'.encode('utf-32')
'\xff\xfe\x00\x00-N\x00\x00\x87e\x00\x00'
>>> u'中文'.encode('utf-64')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
LookupError: unknown encoding: utf-64

英文字符转换后表示的UTF-8的值和Unicode值相等(但占用的存储空间不同),
而中文字符转换后1个Unicode字符将变为3个UTF-8字符,你看到的\xe4就是其中一个字节,
因为它的值是228,没有对应的字母可以显示,所以以十六进制显示字节的数值。
len()函数可以返回字符串的长度:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
>>> len('ABC')
3
>>> len("ABC")
3
>>> len(u"ABC")
3
>>> len(U"ABC")
3
>>> len(U'ABC')
3
>>> len(u'ABC')
3

>>> len(u'中文')
2
>>> len(u'\u4e2d\u6587')
2
>>> len('\xe4\xb8\xad\xe6\x96\x87')
6

反过来,把UTF-8编码表示的字符串’xxx’转换为Unicode字符串u’xxx’用decode(‘utf-8’)方法:

1
2
3
4
5
6
>>> 'abc'.decode('utf-8')
u'abc'
>>> '\xe4\xb8\xad\xe6\x96\x87'.decode('utf-8')
u'\u4e2d\u6587'
>>> print '\xe4\xb8\xad\xe6\x96\x87'.decode('utf-8')
中文

由于Python源代码也是一个文本文件,所以,当你的源代码中包含中文的时候,在保存源代码时,
就需要务必指定保存为UTF-8编码。当Python解释器读取源代码时,为了让它按UTF-8编码读取,
我们通常在文件开头写上这两行:

1
2
#!/usr/bin/env python
# -*- coding: utf-8 -*-

第一行注释是为了告诉Linux/OS X系统,这是一个Python可执行程序,Windows系统会忽略这个注释;

第二行注释是为了告诉Python解释器,按照UTF-8编码读取源代码,否则,你在源代码中写的中文输出可能会有乱码。

如果你使用Notepad++进行编辑,除了要加上# -- coding: utf-8 --外,中文字符串必须是Unicode字符串:

格式化

最后一个常见的问题是如何输出格式化的字符串。
我们经常会输出类似’亲爱的xxx你好!你xx月的话费是xx,余额是xx’之类的字符串,而xxx的内容都是根据变量变化的,
所以,需要一种简便的格式化字符串的方式。

在Python中,采用的格式化方式和C语言是一致的,用%实现,举例如下:

1
2
3
4
5
6
7
8
9
>>> 'Hello, %s' % 'Tom'
'Hello, Tom'
>>> 'Hello, %s' % '123'
'Hello, 123'

>>> 'Hi, %s, you have $%d' % ('Tom', 10000)
'Hi, Tom, you have $10000'
>>> 'Hi, %s, you have $%d' % ('123', 123)
'Hi, 123, you have $123'

你可能猜到了,%运算符就是用来格式化字符串的。
在字符串内部,%s表示用字符串替换,%d表示用整数替换,有几个%?占位符,后面就跟几个变量或者值,顺序要对应好。
如果只有一个%?,括号可以省略。

常见的占位符有:
%d 整数
%f 浮点数
%s 字符串
%x 十六进制整数

其中,格式化整数和浮点数还可以指定是否补0和整数与小数的位数:

1
2
3
4
5
6
7
8
>>> '%2d-%02d' % (3, 1)
' 3-01'

>>> '%2d-%02d-%03d' % (3,2,1)
' 3-02-001'

>>> '%.2f' % 3.1415926
'3.14'

如果你不太确定应该用什么,%s永远起作用,它会把任何数据类型转换为字符串:

1
2
>>> 'Age: %s. Gender: %s' % (25, True)
'Age: 25. Gender: True'

对于Unicode字符串,用法完全一样,但最好确保替换的字符串也是Unicode字符串:

1
2
>>> u'Hi, %s' % u'Michael'
u'Hi, Michael'

有些时候,字符串里面的%是一个普通字符怎么办?这个时候就需要转义,用%%来表示一个%:

1
2
>>> 'growth rate: %d %%' % 7
'growth rate: 7 %'

使用list和tuple

list

Python内置的一种数据类型是列表:list。list是一种有序的集合,可以随时添加和删除其中的元素。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 比如,列出班里所有同学的名字,就可以用一个list表示:
>>> c = ['1', '2', '3']
>>> c
['1', '2', '3']

# 变量c就是一个list。用len()函数可以获得list元素的个数
>>> len(c)
3

# 用索引来访问list中每一个位置的元素,记得索引是从0开始的
>>> c[0]
'1'
>>> c[1]
'2'
>>> c[2]
'3'
>>> c[3]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
IndexError: list index out of range

如果要取最后一个元素,除了计算索引位置外,还可以用-1做索引,直接获取最后一个元素

1
2
3
4
5
6
7
8
9
10
>>> c[-1]
'3'

# 以此类推,可以获取倒数第2个、倒数第3个
>>> c[-2]
'2'
>>> c[-5]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
IndexError: list index out of range

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# list是一个可变的有序表,所以,可以往list中追加元素到末尾
>>> c.append('4')
>>> c
['1', '2', '3', '4']

# 也可以把元素插入到指定的位置,比如索引号为1的位置
>>> c.insert(1, 'tom')
>>> c
['1', 'tom', '2', '3', '4']

# 要删除list末尾的元素,用pop()方法
>>> c.pop
<built-in method pop of list object at 0x000000000276BF48>
>>> c.pop()
'4'
>>> c
['1', 'tom', '2', '3']

# 要删除指定位置的元素,用pop(i)方法,其中i是索引位置
>>> c.pop(1)
'tom'
>>> c
['1', '2', '3']

# 要把某个元素替换成别的元素,可以直接赋值给对应的索引位置
>>> c[1] = 'ke'
>>> c
['1', 'ke', '3']

条件判断和循环

使用dict和set

(5) 函数

调用函数

定义函数

函数的参数

递归函数

(6) 高级特性

切片

迭代

列表生成式

生成器

(7) 函数式编程

高阶函数

map/reduce

filter

sorted

返回函数

匿名函数

装饰器

偏函数

(8) 模块

使用模块

安装第三方模块

使用future

(9) 面向对象编程

(10) 面向对象高级编程

(11) 错误、调试和测试

(12) IO编程

(13) 进程和线程

(14) 正则表达式

(15) 常用内建模块

(16) 常用第三方模块

(17) 图形界面

(18) 网络编程

(19) 电子邮件

(20) 访问数据库

(21) Web开发

(22) 协程

实战

References

[1] python 2.7
[2] python 3.5
[3] Python基础编程1小时快速实战掌握
[4] 模块 logging — Python 的日志记录工具
[5] Python日志库logging总结-可能是目前为止将logging库总结的最好的一篇文章