函数中的可变参数列表

当函数调用可变个数的参数时,这些可变参数被包装进一个元组,在可变参数前,可以有0到多个普通的参数,多个参数用*作为前缀.

1
2
3
4
5
6
7
8
9
10
def write_multiple_items(file, separator, *args):
file.write(separator.join(args))

def concat(*args, sep="/"):
return sep.join(args)

concat("earth", "mars", "venus")
'earth/mars/venus'
concat("earth", "mars", "venus", sep=".")
'earth.mars.venus'

相反的情况

要传递的参数已经是一个列表,但是调用的函数却接手分开一个个的参数值,这时候就要把已有的列表拆开,,可以在调用函数时加一个*操作符来自动把参数列表拆开

1
2
3
4
5
list(range(3, 6))
[3, 4, 5]
args = [3,6]
list(range(*args)) # 和第一行的效果相同
[3,4,5]

以同样的方式,可以用**操作符拆关键字参数为字典

1
2
3
4
5
6
7
8
 def parrot(voltage, state='a stiff', action='voom'):
print("-- This parrot wouldn't", action, end=' ')
print("if you put", voltage, "volts through it.", end=' ')
print("E's", state, "!")

d = {"voltage": "four million", "state": "bleedin' demised", "action": "VOOM"}
parrot(**d)
--This parrot wouldn't VOOM if you put four million volts through it. E's bleedin' demised !

迭代器

大部分容器对象都可以用for循环

1
2
3
4
5
6
7
8
9
10
for element in [1, 2, 3]:
print(element)
for element in (1, 2, 3):
print(element)
for key in {'one':1, 'two':2}:
print(key)
for char in "123":
print(char)
for line in open("myfile.txt"):
print(line, end='')

这种形式的访问清晰、简洁、方便。迭代器的用法在 Python 中普遍而且统一。在后台, for语句在容器对象中调用 iter() 。该函数返回一个定义了 __next__()方法的迭代器对象,它在容器中逐一访问元素。没有后续的元素时, __next__() 抛出一个 StopIteration 异常通知 for语句循环结束。你可以是用内建的 next()函数调用 __next__() 方法;以下是其工作原理的示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
s = 'abc'
it = iter(s)
it
<iterator object at 0x00A1DB50>
next(it)
'a'
next(it)
'b'
next(it)
'c'
next(it)
Traceback (most recent call last):
File "<stdin>", line 1, in ?
next(it)
StopIteration

了解了迭代器协议的后台机制,就可以给自己的类添加迭代器行为,定义一个__iter__()方法,使其返回一个带有__next__()方法的对象,如果这个类已经定义了__next__(),那么__iter__()只需要返回self.

1
2
3
4
5
6
7
8
9
10
11
12
13
class Reverse:
"""Iterator for looping over a sequence backwards."""
def __init__(self, data):
self.data = data
self.index = len(data)
def __iter__(self):
return self
# __next__()自定义迭代方法.
def __next__(self):
if self.index == 0:
raise StopIteration
self.index = self.index - 1
return self.data[self.index]

测试上面的类

1
2
3
4
5
6
7
8
9
10
rev = Reverse('spam')
iter(rev)
<__main__.Reverse object at 0x00A1DB50>
for char in rev:
print(char)

m
a
p
s

生成器

在Python中,这种一边循环一边计算的机制,称为生成器:generator。

创建一个generator,有很多种方法。第一种方法很简单,只要把一个列表生成式的[]改成(),就创建了一个generator.

第二种:如果一个函数定义中包含yield关键字,那么这个函数就不再是一个普通函数,而是一个generator.

在执行过程中,遇到yield就中断,下次又继续执行。

生成器Generator是创建迭代器简单而强大的工具,需要返回数据的时候使用yield语句,每次next()被调用时,生成器回复它脱离的位置.

1
2
3
def reverse(data):
for index in range(len(data)-1, -1, -1):
yield data[index]
1
2
3
4
5
6
for char in reverse('golf'):
print(char)
f
l
o
g

前一节中描述了基于类的迭代器,它能作的每一件事生成器也能作到。因为自动创建了 iter()next() 方法,生成器显得如此简洁。

*args 和 ** kw

补充 *args 代表多个参数,但是没有key值,**kw代表有多个参数的key值.

*args
1
2
3
4
5
def fun(f1, *args):  
print "arg:", f1
for value in args:
print "another arg:", value
fun(1, "two", 3) # *args可以当作可容纳多个变量组成的list
1
2
3
arg: 1  
another arg: two
another arg: 3
**kw
1
2
3
4
5
6
def fun(*args,**kwargs):
for a1 in args:
print(a1)
for k1 in kwargs:
print(k1,kwargs[k1])
fun(3, 4, 5,d=1,ff=2,v=34)
1
2
3
4
5
6
3
4
5
ff 2
d 1
v 34

装饰器

由于函数也是一个对象,而且函数对象可以被赋值给变量,所以,通过变量也能调用该函数。

1
2
3
4
5
def now():
print("2018")
>>> f = now
>>>f()
2018

函数对象有一个__name__属性,可以拿到函数的名字

假设我们要增强now()函数的功能,比如,在函数调用前后自动打印日志,但又不希望修改now()函数的定义,这种在代码运行期间动态增加功能的方式,称之为“装饰器”(Decorator)。

本质上,decorator就是一个返回函数的高阶函数。所以,我们要定义一个能打印日志的decorator,可以定义如下.

1
2
3
4
5
6
7
8
9
10
11
12
13
def use_logging(func):

def wrapper(*args, **kw):
print('call %s():' % func.__name__)
func(*args, **kw)
print("after")
return
return wrapper
def bar():
print('i am bar')

bar = use_logging(bar)
bar()

函数use_logging就是装饰器,wrapper是组装的最终函数

1
2
3
call bar():
i am bar
after

@符号是装饰器的语法糖,在定义函数的时候使用,避免再一次赋值操作

1
2
3
4
5
6
7
8
9
10
11
12
13
def use_logging(func):
def wrapper(*args, **kw):
print('call %s():' % func.__name__)
func(*args, **kw)
print("after")
return
return wrapper
@use_logging
def foo():
print('i am foo')
@use_logging
def bar():
print('i am bar')
带参数的装饰器

装饰器语法允许在调用时,提供其他参数,可以在装饰器的外面再封装一层

1
2
3
4
5
6
7
8
9
10
def gaoceng_fengzhuang(参数)  
def use_logging(func):
def wrapper(*args, **kw):
print('call %s():' % func.__name__)
#里面写参数的处理
func(*args, **kw)
print("after")
return
return wrapper
return use_logging

类装饰器

使用类装饰器还可以依靠内部的__call__方法,当使用@形式将装饰器附加到函数上时,就会调用此方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Foo(Object):
def __init__(self,func):
self.func =func
# 装饰器方法
def __call__(self):
print('before')
self.func()
print('after')

@Foo
def bar():
print('bar')
bar()
# 输出 before /bar /after