Python

1 数据类型与基本数据结构

1.0 理解切片

1
2
3
4
5
 +---+---+---+---+---+---+
| P | y | t | h | o | n |
+---+---+---+---+---+---+
0 1 2 3 4 5 6
-6 -5 -4 -3 -2 -1

1.1 工厂函数

  • type()
  • int()
  • float()
  • str()
  • list()——生成列表
  • tuple()——生成元组
  • dict()——生成字典
  • bool()
  • set()——生成可变集合

1.2 列表

keywords function
list.append(x) 在列表的末尾添加一个元素。相当于 a[len(a):] = [x]
list.extend(iterable) 使用可迭代对象中的所有元素来扩展列表。相当于 a[len(a):] = iterable
list.len() 返回当前list的长度
list.insert(i, x) 在给定的位置插入一个元素。第一个参数是要插入的元素的索引,所以 a.insert(0, x) 插入列表头部, a.insert(len(a), x) 等同于 a.append(x)
list.remove(x) 移除列表中第一个值为 x 的元素。如果没有这样的元素,则抛出 ValueError 异常
list.pop([i]) 删除列表中给定位置的元素并返回它。如果没有给定位置,a.pop() 将会删除并返回列表中的最后一个元素。[ ]的意思是可选参数。
list.clear() 移除列表中的所有元素。等价于del a[:]
list.index(x[,start[,end]]) 返回列表中第一个值为 x 的元素的从零开始的索引。如果没有这样的元素将会抛出 ValueError 异常
可选参数 startend 是切片符号,用于将搜索限制为列表的特定子序列。返回的索引是相对于整个序列的开始计算的,而不是 start 参数
list.sort(key=None, reverse=False) 对列表中的元素进行排序(参数可用于自定义排序,解释请参见 sorted()
list.reverse() 翻转列表的元素
list.copy() 返回列表的一个浅拷贝,等价于 a[:]
list.count(x) 返回元素 x 在列表中出现的次数
[x*x for x in list] list comprehensions

1.3 字典

keywords function
dict.items()
dict.keys()
dict.values()
{i: i*i for i in range(20, 31) if i%2 != 0} dictionary comprehensions

1.4 字符串

1.4.1 基本概念

如果你不希望前置了 \ 的字符转义成特殊字符,可以使用 原始字符串 方式,在引号前添加 r 即可:

1
2
3
4
5
>>> print('C:\some\name')  # here \n means newline!
C:\some
ame
>>> print(r'C:\some\name') # note the r before the quote
C:\some\name

字符串字面值可以跨行连续输入。一种方式是用三重引号:"""..."""'''...'''。字符串中的回车换行会自动包含到字符串中,如果不想包含,在行尾添加一个 \ 即可。如下例:

1
2
3
4
5
print("""\
Usage: thingy [OPTIONS]
-h Display this usage message
-H hostname Hostname to connect to
""")

将产生如下输出(注意最开始的换行没有包括进来):

1
2
3
Usage: thingy [OPTIONS]
-h Display this usage message
-H hostname Hostname to connect to

字符串可以用 + 进行连接(粘到一起),也可以用 * 进行重复:

1
2
3
>>> # 3 times 'un', followed by 'ium'
>>> 3 * 'un' + 'ium'
'unununium'

相邻的两个或多个 字符串字面值 (引号引起来的字符)将会自动连接到一起.

1
2
>>> 'Py' 'thon'
'Python'

把很长的字符串拆开分别输入的时候尤其有用:

1
2
3
4
>>> text = ('Put several strings within parentheses '
... 'to have them joined together.')
>>> text
'Put several strings within parentheses to have them joined together.'

如果你想连接变量,或者连接变量和字面值,可以用 + 号:

1
2
>>> prefix + 'thon'
'Python'

字符串是可以被 索引 (下标访问)的,第一个字符索引是 0。单个字符并没有特殊的类型,只是一个长度为一的字符串:

1
2
3
4
5
>>> word = 'Python'
>>> word[0] # character in position 0
'P'
>>> word[5] # character in position 5
'n'

1.4.2 常用方法

keywords function
str.strip() 删除开头和结尾的空白字符
str.lower(), str.upper() 返回小写,大写的字符串
str1.replace(str2) str2替换str1
str.split(",") 以分隔符为界将字符串拆成子串
in, not in 检查是否在字符串内
+ 串联字符串
str.format() 格式化组合字符串,很好地替代了printf()
>>>"The sum of 1 + 2 is {0}".format(1+2)
'The sum of 1 + 2 is 3'

2 函数

2.1 一个简单的Fibonacci数列函数

1
2
3
4
5
6
7
8
9
10
11
12
>>> def fib2(n):  # return Fibonacci series up to n
... """Return a list containing the Fibonacci series up to n."""
... result = []
... a, b = 0, 1
... while a < n:
... result.append(a) # see below
... a, b = b, a+b
... return result
...
>>> f100 = fib2(100) # call it
>>> f100 # write the result
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]

2.2 默认值参数

最有用的形式是对一个或多个参数指定一个默认值。这样创建的函数,可以用比定义时允许的更少的参数调用,比如:

1
2
3
4
5
6
7
8
9
10
11
def ask_ok(prompt, retries=4, reminder='Please try again!'):
while True:
ok = input(prompt)
if ok in ('y', 'ye', 'yes'):
return True
if ok in ('n', 'no', 'nop', 'nope'):
return False
retries = retries - 1
if retries < 0:
raise ValueError('invalid user response')
print(reminder)

这个函数可以通过几种方式调用:

  • 只给出必需的参数:ask_ok('Do you really want to quit?')
  • 给出一个可选的参数:ask_ok('OK to overwrite the file?', 2)
  • 或者给出所有的参数:ask_ok('OK to overwrite the file?', 2, 'Come on, only yes or no!')

这个示例还介绍了 in 关键字。它可以测试一个序列是否包含某个值。

默认值是在 定义过程 中在函数定义处计算的,所以

1
2
3
4
5
6
7
i = 5

def f(arg=i):
print(arg)

i = 6
f()

会打印 5

重要警告: 默认值只会执行一次。这条规则在默认值为可变对象(列表、字典以及大多数类实例)时很重要。比如,下面的函数会存储在后续调用中传递给它的参数:

1
2
3
4
5
6
7
def f(a, L=[]):
L.append(a)
return L

print(f(1))
print(f(2))
print(f(3))

这将打印出

1
2
3
[1]
[1, 2]
[1, 2, 3]

如果你不想要在后续调用之间共享默认值,你可以这样写这个函数:

1
2
3
4
5
def f(a, L=None):
if L is None:
L = []
L.append(a)
return L

2.3 关键字参数

也可以使用形如 kwarg=value关键字参数 来调用函数。例如下面的函数:

1
2
3
4
5
def parrot(voltage, state='a stiff', action='voom', type='Norwegian Blue'):
print("-- This parrot wouldn't", action, end=' ')
print("if you put", voltage, "volts through it.")
print("-- Lovely plumage, the", type)
print("-- It's", state, "!")

接受一个必需的参数(voltage)和三个可选的参数(state, action,和 type)。这个函数可以通过下面的任何一种方式调用:

1
2
3
4
5
6
parrot(1000)                                          # 1 positional argument
parrot(voltage=1000) # 1 keyword argument
parrot(voltage=1000000, action='VOOOOOM') # 2 keyword arguments
parrot(action='VOOOOOM', voltage=1000000) # 2 keyword arguments
parrot('a million', 'bereft of life', 'jump') # 3 positional arguments
parrot('a thousand', state='pushing up the daisies') # 1 positional, 1 keyword

但下面的函数调用都是无效的:

1
2
3
4
parrot()                     # required argument missing
parrot(voltage=5.0, 'dead') # non-keyword argument after a keyword argument
parrot(110, voltage=220) # duplicate value for the same argument
parrot(actor='John Cleese') # unknown keyword argument

3 语法糖||特性

3.1 lambda表达式

可以用 lambda 关键字来创建一个小的匿名函数。这个函数返回两个参数的和: lambda a, b: a+b 。Lambda函数可以在需要函数对象的任何地方使用。它们在语法上限于单个表达式。从语义上来说,它们只是正常函数定义的语法糖。与嵌套函数定义一样,lambda函数可以引用所包含域的变量:

1
2
3
4
5
6
7
8
>>> def make_incrementor(n):
... return lambda x: x + n
...
>>> f = make_incrementor(42)
>>> f(0)
42
>>> f(1)
43

上面的例子使用一个lambda表达式来返回一个函数。另一个用法是传递一个小函数作为参数:

1
2
3
4
>>> pairs = [(1, 'one'), (2, 'two'), (3, 'three'), (4, 'four')]
>>> pairs.sort(key=lambda pair: pair[1])
>>> pairs
[(4, 'four'), (1, 'one'), (3, 'three'), (2, 'two')]

3.2 列表生成式 list comprehensions

  • 列表生成式即List Comprehensions,是Python内置的非常简单却强大的可以用来创建list的生成式。

    举个例子,要生成list [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]可以用list(range(1, 11))

    1
    2
    >>> list(range(1, 11))
    [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
  • 但如果要生成[1x1, 2x2, 3x3, ..., 10x10]怎么做?

    1
    2
    >>> [x * x for x in range(1, 11)]
    [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
  • 写列表生成式时,把要生成的元素x * x放到前面,后面跟for循环,就可以把list创建出来,十分有用,多写几次,很快就可以熟悉这种语法。

    for循环后面还可以加上if判断,这样我们就可以筛选出仅偶数的平方:

    1
    2
    >>> [x * x for x in range(1, 11) if x % 2 == 0]
    [4, 16, 36, 64, 100]

    还可以使用两层循环,可以生成全排列:

    1
    2
    >>> [m + n for m in 'ABC' for n in 'XYZ']
    ['AX', 'AY', 'AZ', 'BX', 'BY', 'BZ', 'CX', 'CY', 'CZ']

    三层和三层以上的循环就很少用到了。

    运用列表生成式,可以写出非常简洁的代码。例如,列出当前目录下的所有文件和目录名,可以通过一行代码实现:

    1
    2
    3
    >>> import os # 导入os模块,模块的概念后面讲到
    >>> [d for d in os.listdir('.')] # os.listdir可以列出文件和目录
    ['.emacs.d', '.ssh', '.Trash', 'Adlm', 'Applications', 'Desktop', 'Documents', 'Downloads', 'Library', 'Movies', 'Music', 'Pictures', 'Public', 'VirtualBox VMs', 'Workspace', 'XCode']

3.1 生成器 generator

1. 生成器定义

在Python中,一边循环一边计算的机制,称为生成器:generator。

2. 为什么要有生成器

列表所有数据都在内存中,如果有海量数据的话将会非常耗内存。

如:仅仅需要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了。

如果列表元素按照某种算法推算出来,那我们就可以在循环的过程中不断推算出后续的元素,这样就不必创建完整的list,从而节省大量的空间。

简单一句话:我又想要得到庞大的数据,又想让它占用空间少,那就用生成器!

3. 如何创建生成器

第一种方法很简单,只要把一个列表生成式的[]改成(),就创建了一个generator:

1
2
3
4
5
6
>>> L = [x * x for x in range(10)]
>>> L
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
>>> g = (x * x for x in range(10))
>>> g
<generator object <genexpr> at 0x1022ef630>

方法二, 如果一个函数中包含yield关键字,那么这个函数就不再是一个普通函数,而是一个generator。调用函数就是创建了一个生成器(generator)对象。

4. 生成器的工作原理

(1)生成器(generator)能够迭代的关键是它有一个next()方法,

  工作原理就是通过重复调用next()方法,直到捕获一个异常。

(2)带有 yield 的函数不再是一个普通函数,而是一个生成器generator。

  可用next()调用生成器对象来取值。next 两种方式 t.next() | next(t)。

  可用for 循环获取返回值(每执行一次,取生成器里面一个值)

  (基本上不会用next()来获取下一个返回值,而是直接使用for循环来迭代)。

(3)yield相当于 return 返回一个值,并且记住这个返回的位置,下次迭代时,代码从yield的下一条语句开始执行。

(4).send() 和next()一样,都能让生成器继续往下走一步(下次遇到yield停),但send()能传一个值,这个值作为yield表达式整体的结果

  ——换句话说,就是send可以强行修改上一个yield表达式值。比如函数中有一个yield赋值,a = yield 5,第一次迭代到这里会返回5,a还没有赋值。第二次迭代时,使用.send(10),那么,就是强行修改yield 5表达式的值为10,本来是5的,那么a=10

感受下yield返回值的过程(关注点:每次停在哪,下次又开始在哪)及send()传参的通讯过程,

思考None是如何产生的(第一次取值:yield 返回了 i 值 0,停在yield i,temp没赋到值。第二次取值,开始在print,temp没被赋值,故打印None,i加1,继续while判断,yield 返回了 i 值 1,停在yield i):

img

总结:

什么是生成器?

生成器仅仅保存了一套生成数值的算法,并且没有让这个算法现在就开始执行,而是我什么时候调它,它什么时候开始计算一个新的值,并给你返回。

补充

  • *<generator>可以将一个generator变为元组处理

4 IO流

n 文件操作

keywords function
open()
read()
readline()
readlines()
write()
close()