跟我一起学Python(三)
函数
一、Python内置函数
abs()
求绝对值的函数
abs(-100)#100
调用函数的时候,如果传入的参数数量不对,会报TypeError的错误;
参数类型不能被函数所接受,也会报TypeError的错误
max()
max(2, 3, 1, -5) #3
sum()
sum(iterable[, start])
start -- 指定相加的参数,如果没有设置这个值,默认为0。
print(sum([1,2,3,4,5],2)) #17 元组计算总和后再加 2
print(sum((1,2,3,4,5))) #15
upper()
把所有字符中的小写字母转换成大写字母
str = "www.runoob.com"
print(str.upper())
lower()
把所有字符中的大写字母转换成小写字母
list()
方法用于将元组转换为列表,list( tup );也可以将Iterator的所有结果转化为list
数据类型转换
int(12.34)
12
>>> float('12.34')
12.34
>>> str(1.23)
'1.23'
>>> str(100)
'100'
>>> bool(1)
True
>>> bool('')
False
二、自定义函数
def my_abs(x):
if x >= 0:
return x
else:
return -x
如果没有return语句,函数执行完毕后也会返回结果,只是结果为None。
调用自定义函数时,如果参数个数不对,Python解释器会自动检查出来,并抛出TypeError:但是如果参数类型不对,则Python解释器就无法帮我们检查
def my_abs(x):
if not isinstance(x, (int, float)):
raise TypeError('bad operand type')
if x >= 0:
return x
else:
return -x
返回多个值?
可以
import math
def move(x, y, step, angle=0):
nx = x + step * math.cos(angle)
ny = y - step * math.sin(angle)
return nx, ny
x, y = move(100, 100, 60, math.pi / 6)
print(x, y)
原来返回值是一个tuple!但是,在语法上,返回一个tuple可以省略括号,而多个变量可以同时接收一个tuple,按位置赋给对应的值
1.参数
Python的函数定义非常简单,但灵活度却非常大。除了正常定义的必选参数外,还可以使用默认参数、可变参数和关键字参数,使得函数定义出来的接口,不但能处理复杂的参数,还可以简化调用者的代码。
s = 1
while n > 0:
n = n - 1
s = s * x
return s
默认参数
def add_end(L=[]):
L.append('END')
return L
add_end() #['END']
add_end() #['END', 'END']
默认参数必须指向不变对象!(str、None不变对象)
def add_end(L=None):
if L is None:
L = []
L.append('END')
return L
可变参数
定义可变参数和定义一个list或tuple参数相比,仅仅在参数前面加了一个*号。
def calc(*numbers):
sum = 0
for n in numbers:
sum = sum + n * n
return sum
>>> calc(1, 2)
5
>>> calc()
0
>>> nums = [1, 2, 3]
>>> calc(*nums)
14
如果已经有一个list或者tuple,Python允许你在list或tuple前面加一个*号,把list或tuple的元素变成可变参数传进去:
关键字参数
def person(name, age, **kw):
print('name:', name, 'age:', age, 'other:', kw)
>>> person('Bob', 35, city='Beijing')
name: Bob age: 35 other: {'city': 'Beijing'}
>>> extra = {'city': 'Beijing', 'job': 'Engineer'}
>>> person('Jack', 24, **extra)
name: Jack age: 24 other: {'city': 'Beijing', 'job': 'Engineer'}
** extra表示把extra这个dict的所有key-value用关键字参数传入到函数的**kw参数,kw将获得一个dict,注意kw获得的dict是extra的一份拷贝,对kw的改动不会影响到函数外的extra。
命名关键字参数
如果要限制关键字参数的名字,就可以用命名关键字参数,例如,只接收city和job作为关键字参数
def person(name, age, *, city, job):
print(name, age, city, job)
和关键字参数kw不同,命名关键字参数需要一个特殊分隔符,后面的参数被视为命名关键字参数。
>>> person('Jack', 24, city='Beijing', job='Engineer')
Jack 24 Beijing Engineer
如果函数定义中已经有了一个可变参数,后面跟着的命名关键字参数就不再需要一个特殊分隔符*了:
def person(name, age, *args, city, job):
print(name, age, args, city, job)
命名关键字参数必须传入参数名,也可以有默认值
def person(name, age, *, city='Beijing', job):
print(name, age, city, job)
参数组合
参数定义的顺序必须是:必选参数、默认参数、可变参数、命名关键字参数和关键字参数。
def f1(a, b, c=0, *args, **kw):
print('a =', a, 'b =', b, 'c =', c, 'args =', args, 'kw =', kw)
>>> f1(1, 2)
a = 1 b = 2 c = 0 args = () kw = {}
>>> f1(1, 2, c=3)
a = 1 b = 2 c = 3 args = () kw = {}
>>> f1(1, 2, 3, 'a', 'b')
a = 1 b = 2 c = 3 args = ('a', 'b') kw = {}
>>> f1(1, 2, 3, 'a', 'b', x=99)
a = 1 b = 2 c = 3 args = ('a', 'b') kw = {'x': 99}
>>> f2(1, 2, d=99, ext=None)
a = 1 b = 2 c = 0 d = 99 kw = {'ext': None}
>>> args = (1, 2, 3, 4)
>>> kw = {'d': 99, 'x': '#'}
>>> f1(*args, **kw)
a = 1 b = 2 c = 3 args = (4,) kw = {'d': 99, 'x': '#'}
对于任意函数,都可以通过类似func(*args, **kw)的形式调用它,无论它的参数是如何定义的。
2.空函数
如果想定义一个什么事也不做的空函数,可以用pass语句:
def nop():
pass
3.递归函数
def fact(n):
if n==1:
return 1
return n * fact(n - 1)
使用递归函数需要注意防止栈溢出。在计算机中,函数调用是通过栈(stack)这种数据结构实现的,每当进入一个函数调用,栈就会加一层栈帧,每当函数返回,栈就会减一层栈帧。由于栈的大小不是无限的,所以,递归调用的次数过多,会导致栈溢出.
尾递归是指,在函数返回的时候,调用自身本身,并且,return语句不能包含表达式。这样,编译器或者解释器就可以把尾递归做优化,使递归本身无论调用多少次,都只占用一个栈帧,不会出现栈溢出的情况。
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)
Python标准的解释器没有针对尾递归做优化,任何递归函数都存在栈溢出的问题。
4.匿名函数
map()函数为例,计算f(x)=x2时,除了定义一个f(x)的函数外,还可以直接传入匿名函数:
map() 函数会根据提供的函数对指定序列做映射,从而形成新的列表。它的返回值是:<map object at 0x000001F0FABA7898>,然后由list()方法即将其转换为真正的list。
>>> list(map(lambda x: x * x, [1, 2, 3, 4, 5, 6, 7, 8, 9]))
[1, 4, 9, 16, 25, 36, 49, 64, 81]
lambda表示匿名函数,冒号前面的x表示函数参数。
只能有一个表达式,不用写return,返回值就是该表达式的结果。
问题:请用匿名函数改造下面的代码:
def is_odd(n):
return n % 2 == 1
L = list(filter(is_odd, range(1, 20)))
filter() 函数用于过滤序列,过滤掉不符合条件的元素,返回由符合条件元素组成的新列表。
L = list(filter(lambda x: x % 2 == 1, range(1, 20)))
三、高阶函数
定义:如果一个函数可以接收另一个函数作为参数或者返回值为另一个函数,那么这种函数就称之为高阶函数。
(一)Python内置高阶函数
1.map()
map()
函数接收两个参数,一个是函数,一个是Iterable
>>> 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]
#map的其他的用处:把这个list所有数字转为字符串:
>>> list(map(str, [1, 2, 3, 4, 5, 6, 7, 8, 9]))
['1', '2', '3', '4', '5', '6', '7', '8', '9']
2.reduce()
reduce
把一个函数作用在一个序列[x1, x2, x3, ...]上,这个函数必须接收两个参数,reduce把结果继续和序列的下一个元素做累积计算
>>> from functools import reduce
>>> def add(x, y):
... return x + y
...
>>> reduce(add, [1, 3, 5, 7, 9])
25
问题:利用map()函数,把用户输入的不规范的英文名字,变为首字母大写,其他小写的规范名字。输入:['adam', 'LISA', 'barT'],输出:['Adam', 'Lisa', 'Bart']:
def normalize(names):
name = ''
for index in range(len(names)):
if index == 0:
name = name + names[index].upper()
else:
name = name + names[index].lower()
return name
L1= ['adam', 'LISA', 'barT']
#L2 = [normallize(item) for item in L1]
L2 = list(map(normalize, L1))
print(L2)
3.filter()
filter()
也接收一个函数和一个序列(一个可迭代对象iterable
)
与map()
不同的是,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]
用filter求素数(埃氏筛法)
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
4.sorted()
sorted()
函数就可以对list进行排序:
>>> sorted([36, 5, -12, 9, -21])
[-21, -12, 5, 9, 36]
如果是字符串,则是按照ASCII的大小比较的
它还可以接收一个key函数来实现自定义的排序,例如按绝对值大小排序:
>>> sorted([36, 5, -12, 9, -21], key=abs)
[5, 9, -12, -21, 36]
我们给sorted传入key函数,即可实现忽略大小写的排序:
>>> sorted(['bob', 'about', 'Zoo', 'Credit'], key=str.lower)
['about', 'bob', 'Credit', 'Zoo']
要进行反向排序,不必改动key函数,可以传入第三个参数reverse=True:
>>> sorted(['bob', 'about', 'Zoo', 'Credit'], key=str.lower, reverse=True)
['Zoo', 'Credit', 'bob', 'about']
问题:假设我们用一组tuple表示学生名字和成绩:L = [('Bob', 75), ('Adam', 92), ('Bart', 66), ('Lisa', 88)]
请用sorted()对上述列表分别按名字排序:
L = [('Bob', 75), ('Adam', 92), ('Bart', 66), ('Lisa', 88)]
def sortStr(tup):
return tup[0]
L1 = sorted(L,key=sortStr)
print(L1)
(二)自定义函数
嵌套函数
小范围可以使用大范围的,但不能修改 ;
如下可以正常输出 3
n = 3
def method():
print(n)
method()
但是若将代码修改为
n = 3
def method():
n = n +2
print(n)
method()
此时就会会报错:local variable 'n' referenced before assignment
如果想要修改全局的:使用global
关键字,如下即可正常使用 —— 尽量避免
n = 3
def func():
global n
n = n+1
print(n)
method()
注意global还有一个缺陷,如下:将n定义在父函数method2()里,则
global
也不起作用了
def method2():
n = 3
def method():
global n
n = n +2
print(n)
method()
method2()
这时后nonlocal
关键字就起了作用,将global
换做nonlocal
即可
闭包
def lazy_sum(*args):
def sum():
ax = 0
for n in args:
ax = ax + n
return ax
return sum
这实际上是一个闭包
什么是闭包
?
当一个函数返回了另一个函数,且其内部的局部变量还被新函数引用,那么这个函数就被称为闭包
返回闭包时牢记一点:返回函数不要引用任何循环变量,或者后续会发生变化的变量。
例如:
def count():
fs = []
for i in range(1, 4):
def f():
return i*i
fs.append(f)
return fs
f1, f2, f3 = count()
>>> f1()
9
>>> f2()
9
>>> f3()
9
如何修改,才可以得到你想要的结果?
def count():
def f(j):
def g():
return j*j
return g
fs = []
for i in range(1, 4):
fs.append(f(i)) # f(i)立刻被执行,因此i的当前值被传入f()
return fs
JS
function a(arr){
var count = [];
for(var i=0;i<arguments.length;i++){
count[i] = (function(i){
return function(){
return i*i;
}
})(i);
console.log( count[i])
}
return count;
}
问题:利用闭包返回一个计数器函数,每次调用它返回递增整数:
def createCounter():
a = 0
def counter():
nonlocal a
a = a+1
return a
return counter
count = createCounter()
print(count(),count(),count())
装饰器
>>> def now():
... print('2015-3-25')
>>> f = now
>>> f()
>>> now.__name__
'now'
>>> f.__name__
'now'
假设我们要增强now()函数的功能,比如,在函数调用前后自动打印日志,但又不希望修改now()函数的定义,这种在代码运行期间动态增加功能的方式,称之为“装饰器”(Decorator)。
先来认识一下@修饰符
‘@’符号用作函数修饰符是python2.4新增加的功能
修饰符必须出现在函数定义前一行,不允许和函数定义在同一行。
一个修饰符就是一个函数,它将被修饰的函数作为参数,并返回修饰后的同名函数或其他可调用的东西
如1:
import time
def t(func):
print(time.ctime())
return func()
@t # 从这里可以看出@time 等价于 time(xxx()),但是这种写法你得考虑python代码的执行顺序
def xxx():
print('Hello world!')
#输出
$ python demo.py
Thu Nov 1 14:10:02 2018
Hello world!
@t
def xxx():
等价于 xxx = t(xxx)
如2:
def log(func):
def wrapper(*args, **kw):
print('call %s():' % func.__name__)
return func(*args, **kw)
return wrapper
@log
def now():
print('2015-3-25')
print(now())
>>> now()
call now():
2015-3-25
如果decorator本身需要传入参数,那就需要编写一个返回decorator的高阶函数,写出来会更复杂。比如,要自定义log的文本:
import functools
def log(text):
@functools.wraps(func) #此处用途为 wrapper.__name__ = func.__name__
def decorator(func):
def wrapper(*args, **kw):
print('%s %s():' % (text, func.__name__))
return func(*args, **kw)
return wrapper
return decorator
@log('execute')
def now():
print('2015-3-25')
>>> now()
execute now():
2015-3-25
@log('execute')
def now():
等价于 now = log('execute')(now)
问题:请设计一个decorator,它可作用于任何函数上,并打印该函数的执行时间:
偏函数
Python的functools
模块提供了很多有用的功能,其中一个就是偏函数(Partial function)
int('1000000') # 结果按十进制转成 1000000
def int2(x, base=2):
return int(x, base)
int2('1000000') # 结果按二进制转成 64
偏函数就是类似帮我们简化int2() 函数的方法, functools.partial
,简化后为
import functools
int2 = functools.partial(int, base=2)
int2('1000000') # 64
简单总结functools.partial的作用就是,把一个函数的某些参数给固定住(也就是设置默认值),返回一个新的函数,调用这个新函数会更简单。