函数的参数
位置参数
按照对应的位置引用参数;
默认参数
def power(x, n=2):
s = 1
while n > 0:
n = n - 1
s = s * x
return s
#调用
>>> power(5)
25
>>> power(5, 2)
25
例子2:
def enroll(name, gender, age=6, city='Beijing'):
print('name:', name)
print('gender:', gender)
print('age:', age)
print('city:', city)
不按顺序提供部分默认参数时,需要把参数名写上。比如调用enroll('Adam', 'M', city='Tianjin')
,意思是,city参数用传进去的值,其他默认参数继续使用默认值。
默认参数的坑:
定义一个函数,传入一个list
,添加一个END再返回:
def add_end(L=[]):
L.append('END')
return L
#调用
>>> add_end()
['END']
#但是,再次调用add_end()时,结果就不对了:
>>> add_end()
['END', 'END']
>>> add_end()
['END', 'END', 'END']
应该用None这个不变对象来实现:
def add_end(L=None):
if L is None:
L = []
L.append('END')
return L
可变参数
给定一组数字a,b,c……,请计算a2 + b2 + c2 + ……
方法一: 对于提前组装好的list或tuple:
def calc(numbers):
sum = 0
for n in numbers:
sum = sum + n * n
return sum
#调用
>>> calc([1, 2, 3])
14
>>> calc((1, 3, 5, 7))
84
方法二:利用可变参数:
def calc(*numbers):
sum = 0
for n in numbers:
sum = sum + n * n
return sum
#直接数字
>>> calc(1, 2)
5
>>> calc()
0
#引用list或tuple:
>>> nums = [1, 2, 3]
>>> calc(*nums)
14
关键字参数
关键字参数允许你传入0个或任意个含参数名的参数,这些关键字参数在函数内部自动组装为一个dict
。
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'}
#调用dict
>>> extra = {'city': 'Beijing', 'job': 'Engineer'}
>>> person('Jack', 24, **extra)
name: Jack age: 24 other: {'city': 'Beijing', 'job': 'Engineer'}
命名关键字参数
只接收city
和job
作为关键字参数, 这种方式定义的函数如下:
用到特殊分隔符*
,*
后面的参数被视为命名关键字参数;
def person(name, age, *, city, job):
print(name, age, city, job)
#调用
>>> person('Jack', 24, city='Beijing', job='Engineer')
Jack 24 Beijing Engineer
#已经有了一个可变参数
def person(name, age, *args, city, job):
print(name, age, args, city, job)
参数组合
参数定义的顺序必须是:必选参数、默认参数、可变参数、命名关键字参数和关键字参数。
递归函数
计算阶乘n! = 1 x 2 x 3 x ... x n
def fact(n):
if n==1:
return 1
return n * fact(n - 1)
递归调用栈溢出的方法是通过尾递归优化:
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)
#调用:
fact_iter(5, 1)
===> fact_iter(5, 1)
===> fact_iter(4, 5)
===> fact_iter(3, 20)
===> fact_iter(2, 60)
===> fact_iter(1, 120)
===> 120
汉诺塔的移动可以用递归函数非常简单地实现。
# -*- coding: utf-8 -*-
def move(n, a, b, c):
if n == 1:
print(a, '-->', c)
else:
move(n - 1, a, c, b)
print(a, '-->', c)
move(n - 1, b, a, c)
#调用
move(3, 'A', 'B', 'C')
高级特性
切片
>>> L = ['Michael', 'Sarah', 'Tracy', 'Bob', 'Jack']
>>> L[0:3]
['Michael', 'Sarah', 'Tracy']
L[0:3]
表示,从索引0
开始取,直到索引3
为止,但不包括索引3
.
第一个索引是0
,可以省略:
>>> L[:3]
['Michael', 'Sarah', 'Tracy']
倒数切片:
>>> L[-2:]
['Bob', 'Jack']
>>> L[-2:-1]
['Bob']
>>> L[:10:2]
[0, 2, 4, 6, 8]
迭代
dict
迭代的是key
。如果要迭代value
,可以用for value in d.values()
,如果要同时迭代key
和value
,可以用for k, v in d.items()
.
判断是否可以迭代:
>>> from collections.abc import Iterable
>>> isinstance('abc', Iterable) # str是否可迭代
True
>>> isinstance([1,2,3], Iterable) # list是否可迭代
True
>>> isinstance(123, Iterable) # 整数是否可迭代
False
列表生成式
把要生成的元素x * x
放到前面,后面跟for
循环,就可以把list
创建出来;
>>> [x * x for x in range(1, 11)]
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
后面还可以接过滤条件:
>>> [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']
列出当前目录下的所有文件和目录名:
>>> 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']
使用两个变量来生成list:
>>> d = {'x': 'A', 'y': 'B', 'z': 'C' }
>>> [k + '=' + v for k, v in d.items()]
['y=B', 'x=A', 'z=C']
if...else...
for
后面的if
是一个筛选条件,不能带else
>>> [x for x in range(1, 11) if x % 2 == 0 else 0]
File "<stdin>", line 1
[x for x in range(1, 11) if x % 2 == 0 else 0]
^
SyntaxError: invalid syntax
for
前面的部分是一个表达式,它必须根据x
计算出一个结果,必须加上else:
>>> [x if x % 2 == 0 else -x for x in range(1, 11)]
[-1, 2, -3, 4, -5, 6, -7, 8, -9, 10]
生成器
1. 将列表生成式的[]
变为()
即是生成器:
>>> 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>
可以next()
不断生成下一个对象,一般用for...in...
迭代产生:
>>> next(g)
0
>>> next(g)
1
>>> next(g)
4
>>> next(g)
9
>>> g = (x * x for x in range(10))
>>> for n in g:
... print(n)
...
0
1
4
9
.
.
81
2. 函数中加yield
即为generator
函数:
斐波拉契数列生成器:
def fib(max):
n, a, b = 0, 0, 1
while n < max:
yield b
a, b = b, a + b
n = n + 1
return 'done'
>>> f = fib(6)
>>> f
<generator object fib at 0x104feaaa0>
调用generator函数会创建一个generator对象,多次调用generator函数会创建多个相互独立的generator。
杨辉三角:
def triangles():
last = [1]
while True:
yield last
next = [last[n]+last[n-1] for n in range(1,len(last))] #中间元素
next = [1]+next+[1] #首尾加1
last = next
迭代器
可以直接作用于for
循环的数据类型有以下几种:
一类是集合数据类型,如list
、tuple
、dict
、set
、str
等;
一类是generator
,包括生成器
和带yield
的generator function
。
这些可以直接作用于for
循环的对象统称为可迭代对象:Iterable
。
使用isinstance()
判断一个对象是否是Iterable
对象:
>>> from collections.abc import Iterable
>>> isinstance([], Iterable)
True
>>> isinstance({}, Iterable)
True
>>> isinstance('abc', Iterable)
True
>>> isinstance((x for x in range(10)), Iterable)
True
>>> isinstance(100, Iterable)
False
可以被next()
函数调用并不断返回下一个值的对象称为迭代器:Iterator
。
可以使用isinstance()
判断一个对象是否是Iterator
对象:
>>> from collections.abc import Iterator
>>> isinstance((x for x in range(10)), Iterator)
True
>>> isinstance([], Iterator)
False
>>> isinstance({}, Iterator)
False
>>> isinstance('abc', Iterator)
False
不能提前知道序列的长度,只能通过next()
函数实现按需计算下一个数据,所以Iterator
的计算是惰性的,只有在需要返回下一个数据时它才会计算。
Iterator
甚至可以表示一个无限大的数据流,例如全体自然数。
函数式编程
高阶函数
把函数作为参数传入,这样的函数称为高阶函数,函数式编程就是指这种高度抽象的编程范式。
简单的示例:
def add(x, y, f):
return f(x) + f(y)
map/reduce
>>> 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(f, [x1, x2, x3, x4]) = f(f(f(x1, x2), x3), x4)
利用map和reduce编写一个str2float函数:
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from functools import reduce
def str2float(s):
DIGITS = {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9}
index = s.find('.')
def char2num(s):
return DIGITS[s]
to_int = reduce(lambda x, y: x * 10 + y, map(char2num, s.replace('.','')))
return to_int * (10 ** (index - len(s) + 1)) # s含小数点,所以index-len(s)会多减去一位数,需要加1补足
print(str2float('111.11'))
filter
str.strip( '0' );
# 去除首尾字符 0
str2.strip();
#去除首尾空格
filter()
把传入的函数依次作用于每个元素,然后根据返回值是True:
保留;还是False:
丢弃该元素。
保留奇数:
def is_odd(n):
return n % 2 == 1
list(filter(is_odd, [1, 2, 4, 5, 6, 9, 10, 15]))
# 结果: [1, 5, 9, 15]
求素数:利用埃氏筛法
构造一个从3开始的奇数序列:
def _odd_iter():
n = 1
while True:
n = n + 2
yield n
定义一个筛选函数:
def _not_divisible(n):
return lambda x: x % n > 0
定义一个生成器,不断返回下一个素数:
def primes():
yield 2
it = _odd_iter() # 初始序列
while True:
n = next(it) # 返回序列的第一个数
yield n
it = filter(_not_divisible(n), it) # 构造新序列
设置一个退出循环的条件:
# 打印1000以内的素数:
for n in primes():
if n < 1000:
print(n)
else:
break
利用filter() 筛选出回数:
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
def is_palindrome(n):
a=str(n)
b=a[::-1]
if a==b:
return n
# 测试:
output = filter(is_palindrome, range(1, 1000))
print('1~1000:', list(output))
sorted
>>> sorted([36, 5, -12, 9, -21], key=abs)
[5, 9, -12, -21, 36]
key
选择的函数是对list
的每项作用, 而不是作用在整个list
要进行反向排序,不必改动key函数,可以传入第三个参数reverse=True
:
>>> sorted(['bob', 'about', 'Zoo', 'Credit'], key=str.lower, reverse=True)
['Zoo', 'Credit', 'bob', 'about']