python进阶

函数的参数传递机制和赋值机制一样,本质都是共用内存地址,称作引用传递

名字相当于标签

>>> 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这个关键字,我们可以把闭包变量改成可变类型数据进行修改,比如列表。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值