Python基础笔记(二)

函数

Python内置的常用函数还包括数据类型转换函数,比如int()函数可以把其他数据类型转换为整数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
>>> int('123')
123
>>> int(12.34)
12
>>> float('12.34')
12.34
>>> str(1.23)
'1.23'
>>> str(100)
'100'
>>> bool(1)
True
>>> bool('')
False

定义函数要使用def语句。定义一个什么事都不做的空函数用pass语句。函数可以同时返回多个值,但其实就是一个tuple。

定义默认参数必须指向不变对象。

为什么要设计str、None这样的不变对象呢?因为不变对象一旦创建,对象内部的数据就不能修改,这样就减少了由于修改数据导致的错误。此外,由于对象不变,多任务环境下同时读取对象不需要加锁,同时读一点问题都没有。我们在编写程序时,如果可以设计一个不变对象,那就尽量设计成不变对象。

定义可变参数和定义一个list或tuple参数相比,仅仅在参数前面加了一个\*号。这些可变参数在函数调用时自动组装为一个tuple。Python允许在list或tuple前面加一个*号,把list或tuple的元素变成可变参数传进去:

1
2
3
>>> nums = [1, 2, 3]
>>> calc(*nums)
14

关键字参数允许你传入0个或任意个含参数名的参数,这些关键字参数在函数内部自动组装为一个dict:

1
2
3
4
5
6
7
8
9
10
def person(name, age, **kw):
print('name:', name, 'age:', age, 'other:', kw)

>>> person('Michael', 30)
name: Michael age: 30 other: {}

>>> person('Bob', 35, city='Beijing')
name: Bob age: 35 other: {'city': 'Beijing'}
>>> person('Adam', 45, gender='M', job='Engineer')
name: Adam age: 45 other: {'gender': 'M', 'job': 'Engineer'}

使用递归函数的优点是逻辑简单清晰,缺点是过深的调用会导致栈溢出。针对尾递归优化的语言可以通过尾递归防止栈溢出。尾递归事实上和循环是等价的,没有循环语句的编程语言只能通过尾递归实现循环。Python标准的解释器没有针对尾递归做优化,任何递归函数都存在栈溢出的问题。

如果一个函数中所有递归形式的调用都出现在函数的末尾,我们称这个递归函数是尾递归的。当递归调用是整个函数体中最后执行的语句且它的返回值不属于表达式的一部分时,这个递归调用就是尾递归。

1
2
3
4
5
6
7
def fact(n):
return fact_iter(n, 1)

def fact_iter(num, product):
if num == 1:
return product
return fact_iter(num - 1, num * product)

在很多编程语言中,针对字符串提供了很多各种截取函数(例如,substring),其实目的就是对字符串切片。Python没有针对字符串的截取函数,只需要切片一个操作就可以完成,非常简单。

1
2
3
4
5
6
7
>>> (0, 1, 2, 3, 4, 5)[:3]
(0, 1, 2)

>>> 'ABCDEFG'[:3]
'ABC'
>>> 'ABCDEFG'[::2]
'ACEG'

列表生成式

列表生成式可以用来创建list的生成式。生成式可以表示为:

1
2
3
4
5
6
7
8
9
10
11
[要生成的元素(如:x * x) for循环  for循环或if语句]

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

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

>>> d = {'x': 'A', 'y': 'B', 'z': 'C' }
>>> [k + '=' + v for k, v in d.items()]
['y=B', 'x=A', 'z=C']

生成器

一边循环一边计算的机制称为生成器(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关键字。

1
2
3
4
5
6
7
def odd():
print('step 1')
yield 1
print('step 2')
yield(3)
print('step 3')
yield(5)

迭代器

凡是可作用于for循环的对象都是Iterable类型;

凡是可作用于next()函数的对象都是Iterator类型,它们表示一个惰性计算的序列;集合数据类型如list、dict、str等是Iterable但不是Iterator,不过可以通过iter()函数获得一个Iterator对象。

高阶函数map

map()函数接收两个参数,一个是函数,一个是Iterable这是一个可变参数,map将传入的函数依次作用到序列的每个元素,并把结果作为新的Iterator返回。

1
2
3
4
5
6
>>> def f(x):
... return x * x
...
>>> r = map(f, [1, 2, 3, 4, 5, 6, 7, 8, 9])
>>> list(r)
[1, 4, 9, 16, 25, 36, 49, 64, 81]

高阶函数reduce

reduce把一个函数作用在一个序列[x1, x2, x3, ...]上,这个函数必须接收两个参数,reduce把结果继续和序列的下一个元素做累积计算,其效果就是:

1
2
3
4
5
6
7
8
reduce(f, [x1, x2, x3, x4]) = f(f(f(x1, x2), x3), x4)

>>> from functools import reduce
>>> def fn(x, y):
... return x * 10 + y
...
>>> reduce(fn, [1, 3, 5, 7, 9])
13579

高阶函数filter

filter()函数用于过滤序列。和map()类似,filter()也接收一个函数和一个序列。和map()不同的是,filter()把传入的函数依次作用于每个元素,然后根据返回值是True还是False决定保留还是丢弃该元素。

1
2
3
4
5
def is_odd(n):
return n % 2 == 1

list(filter(is_odd, [1, 2, 4, 5, 6, 9, 10, 15]))
# 结果: [1, 5, 9, 15]

filter()这个高阶函数,关键在于正确实现一个“筛选”函数。filter()函数返回的是一个Iterator,也就是一个惰性序列,所以要强迫filter()完成计算结果,需要用list()函数获得所有结果并返回list。

高阶函数排序算法sorted函数

1
2
3
4
5
sorted(iterable, /, *, key=None, reverse=False)
Return a new list containing all items from the iterable in ascending order.

A custom key function can be supplied to customize the sort order, and the
reverse flag can be set to request the result in descending order.

key指定的函数将作用于list的每一个元素上,并根据key函数返回的结果进行排序。key接收的是函数名。

1
2
3
4
5
6
L = [('Bob', 75), ('Adam', 92), ('Bart', 66), ('Lisa', 88)]
Task 1:
L1 = sorted(L,key = lambda x :x[0])

Task 2:
L2 = sorted(L,key = lambda x : x[1])

闭包

返回闭包时牢记一点:返回函数不要引用任何循环变量,或者后续会发生变化的变量。

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
42
利用闭包返回一个计数器函数,每次调用它返回递增整数:
def cCounter():
fs = [0]
def counter():
fs[0] = fs[0] + 1
return fs[0]
return counter

>>> f = cCounter()
>>> f()
1
>>> f()
2

def sCounter():
i = 0
def counter():
nonlocal i
i = i + 1
return i
return counter

>>> f = sCounter()
>>> f()
1
>>> f()
2

def oCounter():
global n
n = 0
def counter():
global n
n = n + 1
return n
return counter

>>> f = oCounter()
>>> f()
1
>>> f()
2

作用域

正常的函数和变量名是公开的(public),可以被直接引用,比如:abc,x123,PI等;

类似_xxx__xxx这样的函数或变量就是非公开的(private),不应该被直接引用,比如_abc__abc等。

模块

在Python中,一个.py文件就称之为一个模块(Module)。

请注意,每一个包目录下面都会有一个__init__.py的文件,这个文件是必须存在的,否则,Python就把这个目录当成普通目录,而不是一个包。__init__.py可以是空文件,也可以有Python代码,因为__init__.py本身就是一个模块。

学习资料

菜鸟教程Python3

廖雪峰的官方网站Python3