函数的参数传递机制和赋值机制一样,本质都是共用内存地址,称作引用传递
名字相当于标签
>>> def f(x):
x=[4,5,6]
return x
>>> b=[1,2,3]
>>> f(b)
[4, 5, 6]
>>> b
[1, 2, 3]
在使用默认参数时,我们应当尽量减少使用可变的容器类型,如列表、字典等
>>> def f(x=[]):
x.append(1)
return x
>>> f()
[1]
>>> f()
[1, 1]
直觉上,我们认为它应该每次都返回列表[1],实际上并不是,.append()改变了这个列表
高阶函数:
函数的对象性:
函数的对象性意味着我们可以对函数进行以下操作:1.将函数作为参数传给另一个函数 2.将函数作为字典的值 3.将函数作为另一个函数的返回值
>>> def square(x):
return x**2
>>> def cube(x):
return x**3
>>> d={'power2':square,'power3':cube}
>>> d['power3'](3)
27
map(f,sq)函数:将函数f作用在序列sq的每个元素上
以函数为返回值的函数:
>>> def power_func(num):
def func(x):
return x**num
return func
>>> square2=power_func(2)
>>> square2(4)
16
固定部分参数的函数:
ptyhon的functools模块提供了一个叫partial()函数,它接受一个函数作为参数,并返回一个将这个函数的一部分参数固定的新函数
>> from functools import partial
>>> def power(x,num):
return x**num
>>> square3=partial(power,num=2)
>>> square3(4)
16
map(f,sq)等价于[f(x) for x in sq]
filter(f,sq)等价于[x for x in sq if f(x)],使用filter时最好转换成列表
reduce(f,sq)函数:该函数接受的是一个支持二元操作的函数,对序列sq中的元素累加计算,返回单一的结果,此外,还可提供初始值
>>> from functools import reduce
>>> reduce(add,[1,2,3,4,5],10)
25
Lambda表达式:
>>> a=filter(lambda x:x%2==0,range(5))
>>> list(a)
[0, 2, 4]
global关键字:在函数中给外部变量赋值,外面的x不会变化,这时可以用global声明一下外部变量并在函数里给该变量赋值,此时外部变量就会改变
利用缓存和递归写的斐波那契数列:
>>> def fib3(n,cache={0:1,1:1}):
try:
return cache[n]
except KeyError:
cache[n]=fib3(n-1)+fib3(n-2)
return cache[n]
>>> list(map(fib3,range(10)))
[1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
迭代器和生成器:
python中的容器类型通常包含一个迭代器帮助它们支持for循环的操作,这些容器类型都需要实现一个.__iter__()方法返回相应的迭代器
迭代器对象支持.next()方法,该方法返回容器中被迭代到的下一个元素,迭代到最后会抛出StopIteration异常
在print()的参数后面加上逗号可以不自动输出回车
实现了.__iter__()和.__next__()两种方法的自定义类型都可以称为一个迭代器
>>> class Reverse(object):
def __init__(self,x):
self.seq=x
self.idx=len(x)
def __iter__(self):
return self
def __next__(self):
self.idx-=1
if self.idx>=0:
return self.seq[self.idx]
else:
raise StopIteration
>>> for i in Reverse(range(10)):
print(i,)
9
8
7
6
5
4
3
2
1
0
构造迭代器不一定需要容器对象
为了方便多次循环,对于列表,元组,字典等容器类型,每次调用.__iter__()方法时会返回一个新的迭代器,而对于文件对象来说,每次回返回同一个迭代器(会记录上一次使用时的文件指针的位置)
生成器:
>>> def reverse(data):
for index in range(len(data)-1,-1,-1):
yield data[index]
>>> for c in reverse('abcde'):
print(c,end='')
edcba
基于for循环的列表推导式中的 内容是基于生成器实现的
推导式生成元组需要显示地调用tuple()函数
使用生成器或者迭代器,不需要一次性保存序列的所有值,而只在需要的时候计算序列的下一个值,从而减少程序使用的内存空间
装饰器:
以下代码实现调用函数时,若不传入参数,则返回函数本身,若传入参数,则打印调用提示,并返回结果,像loud_info(f)这种为函数添加新特性的函数称为装饰器
>>> def loud_info(f):
def g(*args,**kwargs):
print('calling function',f.__name__)
return f(*args,**kwargs)
return g
>>> def add(x,y):
return x+y
>>> loud_info(add)
<function loud_info.<locals>.g at 0x00000201CDE57E18>
>>> loud_info(add)(1,2)
calling function add
3
python提供"@"简化装饰器的使用
>>> @loud_info
def add(x,y):
return x+y
>>> add(1,2)
calling function add
3
装饰器的原理:装饰器的本质是一个接受函数参数的函数
@操作符必须一行一个
@A
@B
@C
def f():
#相当于f=A(B(C(f)))
assert关键字用来检测之后的表达式是否为真,如果表达式值为假,抛出异常,中断程序
isinstance(a,b)函数用来检测a是否为b的一个实例
装饰器工厂:
@A
@B
@C(args)
def f():
相当于D=C(args)
f=A(B(D(f)))
>>> def plus_n(n):
def plus_dec(f):
def new_func(x):
return f(x)+n
return new_func
return plus_dec
>>> def times_n(n):
def times_dec(f):
def new_func(x):
return f(x)*n
return new_func
return times_dec
>>> @plus_n(1)
@times_n(2)
def foo(x):
return x
>>> foo(13)
27
上下文管理器是一个实现了.__enter__()方法和.__exit__()方法的对象
.exit()方法需要接受3个额外参数(加上self是四个)
>>> class TestManager():
def __enter__(self):
print('Entering')
def __exit__(self,exc_type,exc_value,traceback):
print('Exiting')
>>> with TestManager():
print('Hello!')
Entering
Hello!
Exiting
如果在执行过程中抛出异常,.__exit__()方法会被优先执行,然后抛出异常
as关键字只是将上下文管理器.__enter__()方法的返回值付给了f,而文件对象的.__enter__()方法的返回值刚好是它本身
>>> f=open('tmp.txt','w')
>>> f.__enter__() is f
True
>>> class TestManager():
def __enter__(self):
print('Entering')
return 'My value!'
def __exit__(self,exc_type,exc_value,traceback):
print('Exiting')
>>> with TestManager() as value:
print(value)
Entering
My value!
Exiting
以上代码中的return ‘My value!’改成return self 返回这个上下文管理器本身是一种常用的设计
.__exit__()与异常处理:
>>> class TestManager():
def __enter__(self):
print('Entering')
def __exit__(self,exc_type,exc_value,traceback):
print('Exiting')
print('Arg:',exc_type)
print('Arg:',exc_value)
print('Arg:',traceback)
return True
>>> with TestManager():
a=1/0
Entering
Exiting
Arg: <class 'ZeroDivisionError'>
Arg: division by zero
Arg: <traceback object at 0x000001EA6BD5D988>
如果不想后面再抛出系统提示的异常,在__exit__()结尾处返回True
contextlib模块:
contextmanager装饰器:
contextmanager作为一个装饰器,对所修饰的函数是有固定的要求的:该函数必须是一个生成器,且yhield只被执行一次,在该函数中yield之前的部分可以看成是.__enter__()方法的部分,yield返回的值可以看成是.__enter__()方法返回的值,yield之后的部分可以看成是.__exit__()方法的部分,函数test_manager()返回了一个上下文管理器
>>> import contextlib
>>> @contextlib.contextmanager
def test_manager():
print('Entering')
yield 'Some value'
print('Exiting')
>>> with test_manager() as value:
print(value)
print('Hello World!')
Entering
Some value
Hello World!
Exiting
使用contextmanager构造上下文管理器时,如果抛出异常,那么这个异常会在yield的地方被重新抛出,使用try对yield进行处理,并将yield后面的部分放入finally
closing函数:可以确保对象正常关闭
>>> import urllib.request
>>> from contextlib import closing
>>> with closing(urllib.request.urlopen('http://www.python.org')) as url:
line=url.readline()
print(line)
b'<!doctype html>\n'
变量作用域:
函数局部作用域 闭包作用域 全局作用域 内置作用域
如果有重名的变量,局部优先于闭包,闭包优先于全局,以此类推
在函数内部对全局变量进行赋值并不会影响全局变量原来的值,若想在函数中对全局变量重新赋值,可以使用关键字global
>>> def outer():
a=1
def inner():
return a
return inner
>>> f=outer()
>>> f()
1
对于inner()函数,它调用的变量a,既不在函数inner()的函数局部作用域中,也不在全局作用域中,而是于两者之间的作用域,人们把这个作用域起名叫闭包作用域
闭包:在一个外函数中定义了一个内函数,内函数里运用了外函数的临时变量,并且外函数的返回值是内函数的引用。这样就构成了一个闭包。
在闭包内函数也是类似的情况。在内函数中想修改闭包变量(外函数绑定给内函数的局部变量)的时候:
1.在python3中,可以用nonlocal 关键字声明 一个变量, 表示这个变量不是局部变量空间的变量,需要向上一层变量空间找这个变量。
2.在python2中,没有nonlocal这个关键字,我们可以把闭包变量改成可变类型数据进行修改,比如列表。