Python 系列学习之一:基础

前言

打算写一系列文章来记录自己学习 Python 3 的点滴;本篇将会重点介绍 python 的最基础部分知识,字符串、编码、队列等等;

本文为作者的原创作品,转载需注明出处;

数据类型和变量

字符串

特殊使用方式

Python 允许使用 '''...''' 的格式表示多行的内容;替代重复写多个\t换行符的方式

1
2
3
4
5
6
>>> print('''line1
... line2
... line3''')
line1
line2
line3

格式化输出

来看一个最简单的格式化输出,

1
2
>> print('hello %s' % 'world')
>> hello world

%s 是一个占位符,使用 ‘world’ 来进行填充,除了 %s 还有如下的占位符

1
2
3
4
5
6
7
8
9
10
11
12
13
14
%s    字符串 (采用str()的显示)
%r 字符串 (采用repr()的显示)
%c 单个字符
%b 二进制整数
%d 十进制整数
%i 十进制整数
%o 八进制整数
%x 十六进制整数
%e 指数 (基底写为e)
%E 指数 (基底写为E)
%f 浮点数
%F 浮点数,与上相同
%g 指数(e)或浮点数 (根据显示长度)
%G 指数(E)或浮点数 (根据显示长度)

所以,我们不仅可以对自己进行格式化输出,同样可以对数字进行格式化输出,比如,

1
2
>> print('hello %s, this is the year %d' % ('world', 2018))
>> hello world, this is the year 2018

布尔值

Python 布尔值的写法为 TrueFalse;注意的是,第一个字母大写;

空值

Python 的空值用None表示;该值相当于 Java 中的 Null

常量

Python 中约定大写的变量名为常量,比如

1
PI = 3.14159265359

不过实际上,它仍然是一个变量;

弱类型

Python 的变量是弱类型,也就是说变量的类型是在运行时刻才被决定的;比如,

1
2
3
4
a = 123 # a是整数
print(a)
a = 'ABC' # a变为字符串
print(a)

类型转换

1
2
3
4
5
>>> s = "100"
>>> s > 99
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: '>' not supported between instances of 'str' and 'int'

可以看到,python 在将一个 str 类型与 int 类型比对的时候,不会像赋值那样,自动的判断和赋值类型,这里是会报错的;所以,在比较之前,我们需要对其进行类型转换;

1
2
>>> int(s) > 99
True

通过int()方法进行类型转换即可;其它的类型转换如下所述

1
2
3
4
5
6
7
8
9
10
>>> float('12.34')
12.34
>>> str(1.23)
'1.23'
>>> str(100)
'100'
>>> bool(1)
True
>>> bool('')
False

list 和 tuple

list

python 的一种内置类型,表示一个有序列表,可以对其修改该,增加或者删除元素;

比如,我们有如下的列表结合,

1
2
3
>>> classmates = ['Michael', 'Bob', 'Tracy']
>>> classmates
['Michael', 'Bob', 'Tracy']

长度

1
2
>>> len(classmates)
3

访问元素

两种访问方式,

正序访问,

1
2
3
4
5
6
7
8
9
10
>>> classmates[0]
'Michael'
>>> classmates[1]
'Bob'
>>> classmates[2]
'Tracy'
>>> classmates[3]
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
>>> classmates[-2]
'Bob'
>>> classmates[-3]
'Michael'
>>> classmates[-4]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
IndexError: list index out of range

追加

通过调用append方法将元素追加到队列的末尾;

1
2
3
>>> classmates.append('Adam')
>>> classmates
['Michael', 'Bob', 'Tracy', 'Adam']

插入

通过insert方法插入到指定位置,

1
2
3
>>> classmates.insert(1, 'Jack')
>>> classmates
['Michael', 'Jack', 'Bob', 'Tracy', 'Adam']

删除并返回

通过pop()删除并返回队列末尾的元素

1
2
3
4
>>> classmates.pop()
'Adam'
>>> classmates
['Michael', 'Jack', 'Bob', 'Tracy']

通过pop(n)删除并返回指定坐标的元素

1
2
3
4
>>> classmates.pop(1)
'Jack'
>>> classmates
['Michael', 'Bob', 'Tracy']

替换

1
2
3
>>> classmates[1] = 'Sarah'
>>> classmates
['Michael', 'Sarah', 'Tracy']

多种数据类型

list 中的元素可以由多种其它数据类型组成;

1
>>> L = ['Apple', 123, True]

同时,list 中的某个元素还可以是一个 list,也就是说,可以嵌套另外一个 list;

1
2
3
>>> s = ['python', 'java', ['asp', 'php'], 'scheme']
>>> len(s)
4

排序

可以直接调用 list 的内置方法,对其进行排序;

1
2
3
4
>>> s = ['python', 'java', 'php', 'scheme']
>>> s.sort()
>>> s
['java', 'php', 'python', 'scheme']

tuple

也是一种有序列表,叫做元组;与 list 非常的类似,但,tuple 一旦初始化以后,就不允许修改该了;

1
2
3
>>> classmates = ('Michael', 'Bob', 'Tracy')
>>> classmates
('Michael', 'Bob', 'Tracy')

这样的话,也就是说 classmates 这个 tuple 元组就不再允许被修改该了,也就是说,没有对应的增、删、改等方法了,也就是没有了对应的 insert、pop 等方法了;注意,元组的定义,使用的是双括号,而 list 使用的是方括号

要注意的是,如果你要定义只有一个元素的 tuple,

1
2
3
>>> t = (1)
>>> t
1

可见,这里输出的并不是一个 tuple,而是一个数字 1;因为这样写会引起歧义,这里会被解释器识别为数学公式中的括号,而这里,python 将会把它识别为数学公式中的括号,而直接忽略掉了,所以输出的就是一个数字;所以,python 规定,如果只有一个元素的 tuple,定义的时候,为了避免歧义,必须加上一个,的后缀符号,

1
2
3
>>> t = (1,)
>>> t
(1,)

tuple 中可以包含 list 元素,且该 list 仍然是可变的

1
2
3
4
5
>>> t = ('a', 'b', ['A', 'B'])
>>> t[2][0] = 'X'
>>> t[2][1] = 'Y'
>>> t
('a', 'b', ['X', 'Y'])

当然原因也非常的简单,就是因为 list 对象本身的特性所决定的;

条件判断

通过几个例子来看看 IF ELSE 在 python 中的写法,

IF…ELSE

1
2
3
4
5
6
7
age = 3
if age >= 18:
print('your age is', age)
print('adult')
else:
print('your age is', age)
print('teenager')

IF…ELSEIF

1
2
3
4
5
6
7
age = 3
if age >= 18:
print('adult')
elif age >= 6:
print('teenager')
else:
print('kid')

要特别注意上面的写法,每一次条件语句后面都会跟一个:

IF X

1
2
if x:
print('True')

只要x是非零数值、非空字符串、非空 list 等,就判断为True,否则为False

再来看一下如何在 if 判断语句中表示 and 和 or 的逻辑,

1
2
3
4
age = 3
if age >= 18 and age < 100:
print('your age is', age)
print('adult')

for 语句表达式

网上已经有很多 for 的基本用法,比如 for for,for else 等等,基本用法可以参考官方的教程 https://wiki.python.org/moin/ForLoop 如果笔者在这里还是照本宣科,将一些 for 语句的基本用法,就没有什么意思了,我们来看看教科书上不曾写到的一些进阶的用法,for 语句可以用来构造 Dict、Set、List 实例,下面笔者一一来举例

  1. for 语句用来构造 Set 实例
    比如,我们可以使用 for 语句来直接构造 1 到 10 的一个 Set

    1
    2
    3
    >>> set = {n for n in range(1,11)}
    >>> set
    {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}

    注意,表达式的写法 n for n in range(1,11),其中第一个 n 表示当前循环的返回值,这样循环 10 次便构造了包含 10 个数字的 Set 实例;

  2. for 语句用来构造 List 实例
    同样,我们可以使用上面的方式来构造一个 List 实例,

    1
    2
    3
    >>> list = [n for n in range(1,11)]
    >>> list
    [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
  3. for 语句用来构造 Dict 实例
    这是最有趣也是最有用的一种方式了,看一个例子,

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    >>> results = {pow(2, n): n for n in range(1,11)}
    >>> results
    {
    2: 1,
    4: 2,
    8: 3,
    16: 4,
    32: 5,
    64: 6,
    128: 7,
    256: 8,
    512: 9,
    1024: 10
    }

    这里要注意 Dict 中左边表达式 pow(2, n) 中的 n 既是 for 循环中 n 的当前值;从输出结果中可以看到,我们自动的将 pow 的结果和输入值构建了一个 Dict 对象,这样,我们便快速的构建了一种键值对的数据模型;有意思吗?我的数学能力很好,1024 我可以直接推导出是 10,建立这种映射关系有何用?试想,如果我们计算的是 pow(111,n) 呢?或者是对某个质数计算 pow 呢?使用 for 循环构建 Dict 实例的方式可以让我们快速的构建出这种 Dict 数据关系,仅需一行代码;

读者可能会问,这样做到底有多大意思?笔者想说的是,这样做很有意思,可以极大的简化我们的代码,10 行代码可以并做一行,看下面这例子,

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
import concurrent.futures
import urllib.request

URLS = ['http://www.foxnews.com/',
'http://www.cnn.com/',
'http://europe.wsj.com/',
'http://www.bbc.co.uk/',
'http://some-made-up-domain.com/']

# Retrieve a single page and report the URL and contents
def load_url(url, timeout):
with urllib.request.urlopen(url, timeout=timeout) as conn:
return conn.read()

# We can use a with statement to ensure threads are cleaned up promptly
with concurrent.futures.ThreadPoolExecutor(max_workers=5) as executor:
# Start the load operations and mark each future with its URL
future_to_url = {executor.submit(load_url, url, 60): url for url in URLS}
for future in concurrent.futures.as_completed(future_to_url):
url = future_to_url[future]
try:
data = future.result()
except Exception as exc:
print('%r generated an exception: %s' % (url, exc))
else:
print('%r page is %d bytes' % (url, len(data)))

这是一个 Thread Pool 的例子,通过多线程的方式同时下载 URLS 中的链接,看代码第 18 行,

1
future_to_url = {executor.submit(load_url, url, 60): url for url in URLS}

仅通过一行代码,我便建立了不同的 future 对象与相关 URL 的键值模型的的 Dict 对象;

dict 和 set

dict

Python 的内置的字典;全称是 Dictionary,在其它的语言中称为 map,使用键值对(Key-Value)进行存储,因为是通过 Hash(Key) 的方式来存取 Value 值,所以它的执行效率非常的块,典型的以空间换时间的方式;

创建

创建一个字典的方式有好几种方式,下面以创建一个学生的成绩字典的例子来分别进行演示

第一种方式,

1
2
3
>>> d = {'Michael': 95, 'Bob': 75, 'Tracy': 85}
>>> d['Michael']
95

第二种方式,

1
2
3
>>> d = dict( Michael = 95, Bob = 75, Tracy = 85)
>>> d['Michael']
95

第三种方式,

1
2
3
>>> d = dict({'Michael': 95, 'Bob': 75, 'Tracy': 85})
>>> d['Michael']
95

第四种方式,

1
2
3
>>> d = dict( [ ['Michael', 95], ['Bob', 75], ['Tracy', 85] ] )
>>> d['Michael']
95

追加

  1. 方式一

    1
    2
    3
    >>> d['Adam'] = 67
    >>> d['Adam']
    67

    这样,我们就新增了一个字典键值对 'Admin':67

    多次对同一个键进行追加,会覆盖之前的 Value;

    1
    2
    3
    4
    5
    6
    >>> d['Jack'] = 90
    >>> d['Jack']
    90
    >>> d['Jack'] = 88
    >>> d['Jack']
    88
  2. 方式二、通过 update() 方法,
    新增

    1
    2
    3
    4
    >>> x = {1:2}
    >>> x.update({3:4})
    >>> print(x)
    {1: 2, 3: 4}

    注意,通过 update 方法给一个空的 dict 对象新增值也是可以的,

    1
    2
    3
    4
    >>> x = {}
    >>> x.update({1:2})
    >>> print(x)
    {1: 2}

    更新

    1
    2
    3
    >>> x.update({3:6})
    >>> print(x)
    {1: 2, 3: 6}

取值

1
2
>>> d['Jack']
88

如果取到一个不存在的值,将会报错,

1
2
3
4
>>> d['Thomas']
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
KeyError: 'Thomas'

所以,为了避免这样的错误,在取值之前可以做一些判断,可以通过in来判断其键是否存在;

1
2
>>> 'Thomas' in d
False

另外,可以通过get方法来进行取值,这样如果该值不存在,将会返回None,或者指定一个自己期望的值;

1
2
3
>>> d.get('Thomas')
>>> d.get('Thomas', -1)
-1

第一个返回的是None,Python 默认不显示,第二个返回的就是自己指定的值 -1

删除

通过pop(key)方法,将对应的 key 和 value 一并从 dict 中删除;

1
2
3
4
>>> d.pop('Bob')
75
>>> d
{'Michael': 95, 'Tracy': 85}

key 必须是不可变的

因为 Dict 是根据 hash(Key) 来存取 value 的,所以有一个非常重要的前提就是,Key 是不可变的元素;Python 中,字符串、数字、Tuple 对象都是不可变的,因此非常适合作为 Dict 的 Key,而 List 是可变元素,因此,在 Python 中是强制规定其不能作为 Key 的,若作为 key,编译的时候就会出错;

1
2
3
4
5
>>> key = [1, 2, 3]
>>> d[key] = 'a list'
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'list'

set

set 类似于其实也是一个 Dict,唯一区别是,set 只存储 Keys,不存储 Values;既然是一个 Dict,那么就要求所存储的 Keys 是不可变元素,这一点可以从创建小节中得到论证;

创建

在创建 set 集合的时候,要求传入一个 list 集合作为参数;

1
2
3
>>> s = set([1, 2, 3])
>>> s
{1, 2, 3}

要求作为参数的 list 不能有可变元素,比如

1
2
3
4
>>> s = set([1, [2, 3], 4])
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'list'

可见,list 中的第二个元素 [2, 3] 是一个 list,是一个可变元素,所以在创建的时候,报错;这也就间接的证明了,set 实际上是一个 dict;

重复元素将会被自动的去重;

1
2
3
>>> s = set([1, 1, 2, 2, 3, 3])
>>> s
{1, 2, 3}

追加

通过方法add(key)的方式追加元素到 set 中;

1
2
3
4
5
6
>>> s.add(4)
>>> s
{1, 2, 3, 4}
>>> s.add(4)
>>> s
{1, 2, 3, 4}

可以看到,重复元素将会被自动过滤;

删除

通过 remove(key) 的方法进行删除;

1
2
3
>>> s.remove(4)
>>> s
{1, 2, 3}

排序

在创建和添加 set 的时候,会自动的根据 key 值进行排序;

1
2
3
4
5
6
>>> s = set([ 8, 3, 2, 1, 9, 3, 4 ] )
>>> s
{1, 2, 3, 4, 8, 9}
>>> s.add(5)
>>> s
{1, 2, 3, 4, 5, 8, 9}

可以看到,无论是在新建还是追加的过程中,set 集合都会自动的将 keys 进行排序;

交集和并集

我们有两个 set 集合,

1
2
>>> s1 = set([1, 2, 3])
>>> s2 = set([2, 3, 4])

交集&

1
2
>>> s1 & s2
{2, 3}

并集|

1
2
>>> s1 | s2
{1, 2, 3, 4}

进制转换

有时候,在处理底层字符编码的时候,我们经常需要在二进制、八进制、十进制和十六进制之间进行转换;

转换为十进制

如何将其它进制的数据转换为十进制;通过方法int(value, format),参数 value 是其它进制的数据,参数 format 表示当前数字的进制;

比如,我们要将十六进制转换为十进制,

1
2
>>> int('e4b8', 16)
58552

将二进制转换为十进制,

1
2
>>> int('11010011111', 2)
1695

转换为八进制

通过 oct(value) 方法将 value 转换为对应的八进制;注意,这里有一个限制,就是只能转换十进制的数据,也就是说,输入的 value 必须是十进制,而且也只有这样一个方法可用,oct(value, format)不存在的;

1
2
>>> oct(58552)
'0o162270'

转换为十六进制

通过 hex(value) 方法将 value 转换为对应的十六进制,同转换为八进制,这里的参数 value 也必须是十进制数据;

1
2
>>> hex(58552)
'0xe4b8'

十进制转换为二进制

通过bin(value)方法将 value 转换为对应的二进制,同转换为八进制,这里的参数 value 也必须是十进制数据;

1
2
>>> bin(1695)
'11010011111'

Reference

upgrade from python 2 to 3: https://www.macobserver.com/tmo/article/how-to-upgrade-your-mac-to-python-3
python 3 tutorial: https://docs.python.org/3/tutorial/index.html
python 3: http://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000
dive into python3: http://www.diveintopython3.net/
python yield: https://www.ibm.com/developerworks/cn/opensource/os-cn-python-yield/index.html
http://www.liaoxuefeng.com/