Python3进阶:高级特性一

Python

接下来的文章学习Python的高级内容,这部分和之前相比有一定的难度,需要认真学习。加油吧,少年!

两个魔法变量

在Python中有两个变量参数容易困扰我们。它们是*args**kwargs。接下来我们看看究竟是什么意思!

*args

*args**kwargs主要用于函数定义。可以将不定数量的参数传递给函数。
*args是用来发送一个非键值对的可变数量的参数列表给函数。

举个🌰:

1
2
3
4
5
6
7
def test_var_args(f_arg, *argv):
print('first normal arg: ', f_arg)
for arg in argv:
print('anothor arg throgh * argv:', arg)

test_var_args('yasoob', 'python', 'eggs', 'test')

输出结果:

1
2
3
4
first normal arg:  yasoob
anothor arg throgh * argv: python
anothor arg throgh * argv: eggs
anothor arg throgh * argv: test

输出结果看出: f_arg只对应一项。

**kwargs

**kwargs允许将不定长度的键值对,作为参数传递。

🌰:

1
2
3
4
5
6
def greet_me(**kwargs):
for key, value in kwargs.items():
print("{0} == {1}".format(key, value))

greet_me(name = "yasoob", age = "12")

输出结果:

1
2
name == yasoob
age == 12

*args**kwargs使用

使用它们调用函数,🌰:

1
2
3
4
5
6
7
8
9
10
11
12
13
def test_args_kwars(arg1, arg2, arg3):
print('arg1', arg1)
print('arg2', arg2)
print('arg3', arg3)

# 使用*args
args = ('two', 3, 5)
test_args_kwars(*args)

# 使用**kwargs
kwargs = {'arg3': 3, 'arg2': 'two', 'arg1': 5}
test_args_kwars(**kwargs)

标准参数与*args**kwargs在使用时的顺序。

some_func(frags, *args, **kwargs)

调试(Debug)

调试在处理代码bug非常重要。

从命令行运行

1
python3 -m pdb xxx.py

触发调试功能,第一行指令处停止。

从脚本内部运行

设置断点,使用pdb.set_trace()方法实现。

1
2
3
4
5
6
7
import pdb

def make_bread():
pdb.set_trace()
return "I dont have time"

print(make_bread())

命令列表

  • c: 继续执行
  • w: 显示当前正在执行的代码行的上下文信息
  • a: 打印当前函数的参数列表
  • s: 执行当前代码行,并停在第一个能停的地方(相当于单步进入)
  • n: 继续执行到当前函数的下一行,或者当前行直接返回(单步跳过)

ns 区别: s单步进入会进入当前行调用函数内部并停在里面,而单步跳过会全速执行完当前行调用的函数,并停在当前函数的下一行。

其他内容请参考官方文档

生成器

前面在学习类时,简单学习了生成器。接下我们将深入学习生成器相关知识。学习生成器之前,我们需要了解迭代器和相关内容。

迭代器是让程序可以迭代遍历一个容器的对象。分为三个部分:

  • 可迭代对象(iterable)
  • 迭代器(iterator)
  • 迭代(iteration)

可迭代对象(iterable)

Python中任意对象,只要它定义了可以返回一个迭代器的__iter__方法,或者定义了可以支持下索引的__getitem__方法,那么他就是一个可迭代对象。简单来说,就是提供了迭代器的对象。

迭代器(iterator)

对象只要实现__next__方法,它就是一个迭代器。

迭代(iteration)

例如,循环遍历就是迭代。

生成器

生成器也是一种迭代器,但是只能对其迭代一次。因为它们并没有把所有的值存在内存中,而是运行时生成值。通过遍历来使用它。另外,它们并不返回值,而是yield一个值。

举个🌰

1
2
3
4
5
6
7
8
def generator_function():
for i in range(10):
yield i

for item in generator_function():
print(item)

# 输出 0 - 9

上面的只是例子。真正的运行场景是:不想在同一时间创建大量结果集分配到内存中,减少资源消耗。在Python3中生成器应用很广广泛。

高级函数

Map, Fiter和Reduce三个函数为函数式编程提供便利。

Map

Map将一个函数映射到一个输入列表的所有元素上。

格式:

1
map(函数, 列表)

将列表中元素一个个传递给函数,并收集输出。在Python3中返回的是迭代器。
通常,使用匿名函数来配合map

例如:

1
squared = list(map(lambda x: x ** 2, range(1, 10)))

Filter

顾名思义,Filter过滤列表中的元素,并且返回一个所有符合要求的元素组成的列表。符合要求就是函数映射到该元素时返回值为True。

1
2
3
4
5
6
number_list = range(-5, 5)
less_than_zero = filter(lambda x : x < 0, number_list)
print(list(less_than_zero))

# Output: [-5, -4, -3, -2, -1]

Reduce

对一个列表进行一些计算并返回结果时, Reduce是非常有用的函数。

例如:计算乘积

1
2
3
4
5
from functools import reduce
product = reduce((lambda x, y: x * y), [1, 2, 3, 4])
print(product)

# output: 24

装饰器

装饰器(Decorators)是Python的一个重要部分。他们是修改其他函数的功能的函数。

学习装饰器之前,你应该知道一切皆对象函数定义函数函数返回函数将函数作为参数传递。如果对上面内容不了解,那请补一下面向对象吧。

第一个装饰器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
def a_new_decorator(a_func):
def wrapTheFunction():

print("before")
a_func() # 注意有括号,别问我怎么知道的
print("after")

return wrapTheFunction

def a_function_requiring_decoration():
print("dslafalsfdsa")

a_function_requiring_decoration()
# Output: dslafalsfdsa

a_function_requiring_decoration = a_new_decorator(a_function_requiring_decoration)

# 获得返回函数
a_function_requiring_decoration()
# Output:before
# dslafalsfdsa
# after

装饰器作用:封装了一个函数,用来修改函数行为

上面的简短写法:

1
2
3
4
5
6
7
8
@a_new_decorator
def a_function_requiring_decoration():
print("dslafalsfdsa")
a_function_requiring_decoration()

# Output:before
# dslafalsfdsa
# after

上面的@a_new_decorator就相当于a_function_requiring_decoration = a_new_decorator(a_function_requiring_decoration)

👆是装饰器的基本工作原理。但是这样写存在一个问题。

请看:

1
2
3
print(a_function_requiring_decoration.__name__)
# Output : wrapTheFunction

我们希望的结果是,Output应该输出:a_function_requiring_decoration。Python提供了一个函数解决这个问题,就是functools.wraps

修改方法:

1
2
3
4
5
6
7
8
def a_new_decorator(a_func):
@wraps(a_func)
def wrapTheFunction():
print("before")
a_func() # 注意有括号,别问我怎么知道的
print("after")

return wrapTheFunction

wrapTheFunction之前添加@wraps()。注意需要引包from functools import wraps

好了现在学会装饰器了,接下来练习一下吧!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
from functools import wraps
def decorator_name(f):
@wraps(f)
def decorated(*args, **kwargs):
if not can_run:
return "Function will not run"
return f(*args, **kwargs)
return decorated

@decorator_name
def func():
return "Function is running"

can_run = True
print(func())

can_run = False
print(func())


# Output:
# Function is running
# Function will not run

@wraps接受一个函数来进行装饰,并加入了复制函数名称、注释文档、参数列表等等的功能。这可以让我们在装饰器里面访问在装饰之前的函数的属性

使用场景

授权检验

装饰能有助于检查某个人是否被授权使用一个web应用的断点。它们被大量使用在Flask和Django框架中。

日志

日志是装饰器运行的另一个亮点。

带参数的装饰

@wraps可以接收一个参数,就像普通的函数一样。我们也可以实现。

例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
from functools import wraps

# 定义一个函数,返回装饰器
def logit(logfile = 'out.log'):
# 定义一个装饰器
def logging_decorator(func):
# 使用wraps重新设置“__name__”
@wraps(func)
def wraped_function(*args, **kwargs):
log_string = func.__name__ + " was called"
print(log_string)
with open(logfile, 'a') as opened_file:
opened_file.write(log_string + '\n')
return func(*args, **kwargs)
return wraped_function
return logging_decorator

@logit()
def myfunc1():
pass
myfunc1()
# output : myfunc1 was called

# 设置文件名称
@logit(logfile = 'func2.log')
def myfunc2():
pass

myfunc2()
# output : myfunc2 was called

装饰器类

上面只是将装饰器写成函数的形式,接下来,以类的形式实现装饰器。

下面还是以logit为例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
from functools import wraps

class logit(object):
def __init__(self, logfile = 'classout.log'):
self.logfile = logfile
def __call__(self, func):
@wraps(func)
def wrapped_function(*args, **kwargs):

log_string = func.__name__ + " was called"
print(log_string)
# 打开并写入日志
with open(self.logfile, 'a') as opened_file:
# 将日志保存到指定文件
opened_file.write(log_string + '\n')
# 发送一个通知
self.notify()
return func(*args, **kwargs)
return wrapped_function
def notify(self):
# 只打印日志,不做别的
pass

调用方法和函数形式相同。

因为是装饰器类,所以具有类的特性:

1
2
3
4
5
6
7
class email_logit(logit):
def __init__(self, email="jesus_lqq@163.com", *args, **kwargs):
self.email = email
super(logit, self).__init__(*args, *kwargs)
def notify(self):
# 发送一封Email到self.email
pass

@email_logit@logit产生同样的效果,只是前者会发送email。

小结

以上内容学习了*args**kwargs作用和区别,高阶函数,Debug调试,生成器和装饰器的原理和应用。学会不是目的,应用才是王道。

参考