1. functools.partial
# functools.partial就是帮助我们创建一个偏函数的,不需要我们自己定义int2(),可以直接使用下面的代码创建一个新的函数int2:
import functools
int3 = functools.partial(int, base = 3)
print(int3('102102'))
'''
functools.partial的作用就是,把一个函数的某些参数给固定住(也就是设置默认值),返回一个新的函数,调用这个新函数会更简单。
'''
kw = {'base':5}
print(int('1021234', **kw))
2. defaultdict
说明
class collections.defaultdict([default_factory[, ...]])
defaultdict,顾名思义是默认字典,它返回一个类似 dict 的新对象。defaultdict 作为内置 dict 类的子类,重载了一个新方法并添加了一个可写的实例变量,而其余功能则与 dict 类相同。
第一个参数 default_factory 是 工厂函数 (factory function),默认为 None,可选 list、tuple、set 、str 、int 等 用于创建各种类型对象的内建函数 (工厂),其作用在于:当用 defaultdict 创建的默认字典的 key 不存在时,将返工厂函数 default_factory 的默认值。例如,list 对应 [ ]、tuple 对应 ()。而其他参数用法则同 dict,包括关键词参数。
通常,我们创建 dict 时,若索引了不存在的 key,则会导致 KeyError:
>>> dict0 = {}
>>> dict0[0]
Traceback (most recent call last):
File "<pyshell#2>", line 1, in <module>
dict0[0]
KeyError: 0
为避免这样的问题,可以对默认字典 defaultdict 馈入指定“工厂” 作为默认工厂,当访问不存在的 key 时,会调用默认工厂自动创建并返回一个默认 value,而非 KeyError,
例如:
>>> from collections import defaultdict
## dict 初始化之初, key=0 原本是不存在的,
## 但在 defaultdict 的作用下, 根据工厂函数构建了相应的 默认 value
# -------------------------------------------------------------
>>> dict1 = defaultdict(list)
>>> dict1
defaultdict(<class 'list'>, {})
>>> dict1[0]
[]
>>> dict1
defaultdict(<class 'list'>, {0: []})
# -------------------------------------------------------------
>>> dict2 = defaultdict(tuple)
>>> dict2
defaultdict(<class 'tuple'>, {})
>>> dict2[0]
()
>>> dict2
defaultdict(<class 'tuple'>, {0: ()})
# -------------------------------------------------------------
>>> dict3 = defaultdict(set)
>>> dict3
defaultdict(<class 'set'>, {})
>>> dict3[0] # 注意, 为区别 dict 的 {}, set 创建的是 set()
set()
>>> dict3
defaultdict(<class 'set'>, {0: set()})
# -------------------------------------------------------------
>>> dict4 = defaultdict(str)
>>> dict4
defaultdict(<class 'str'>, {})
>>> dict4[0]
''
>>> dict4
defaultdict(<class 'str'>, {0: ''})
# -------------------------------------------------------------
>>> dict5 = defaultdict(int)
>>> dict5
defaultdict(<class 'int'>, {})
>>> dict5[0]
0
>>> dict5
defaultdict(<class 'int'>, {0: 0})
# -------------------------------------------------------------
>>> dict6 = defaultdict(float)
>>> dict6
defaultdict(<class 'float'>, {})
>>> dict6[0]
0.0
>>> dict6
defaultdict(<class 'float'>, {0: 0.0})
注意,上述例子的实参是 list、tuple、set、str、int、float,它们均为类对象,而非新列表 list()、新元组 tuple() 等等。
当然,形参工厂函数 default_factory 可接受的工厂实参远不止这些。
但是,若实例化 defaultdict 对象时不指定 default_factory 参数,则默认为 None,使之与 dict 基本没有区别,访问不存在的 key 时同样会抛出 KeyError,而不会创建默认 value (因为没有默认工厂可用!):
>>> dict00 = defaultdict()
>>> dict00
defaultdict(None, {})
>>> dict00[0]
3 . 迭代器
3.1、迭代器
介绍前先来两个问题:
什么是迭代器和可迭代对象 ?
- 迭代器:同时实现了__iter__方法和__next__方法的对象
- 可迭代对象:只实现了__iter__方法的对象
- 关系: 迭代器一定是可迭代对象,反之不成立,可迭代对象的__iter__方法必须返回一个迭代器
迭代器可以迭代的原因 ?
使用next函数可以返回迭代器下一个值,所谓迭代就是不停的调用next函数直到引发StopIteration异常,next函数内部调用迭代器的__next__方法,具体返回哪个值是由__next__来决定的
1 迭代器概述
迭代器:如果它的类有next方法和iter方法返回自己本身,对于string、list、dict、tuple等这类容器对象,使用for循环遍历是很方便的。在后台for语句对容器对象调用iter()函数,iter()是python的内置函数。__iter__方法需要返回对象本身,即self,而__next__方法需要返回下一个数据,如果没有数据了,则会抛出一个StopIteration异常。
1)可迭代对象包含迭代器
2)如果一个对象拥有__iter_方法,其是可迭代对象;如果一个对象拥有__next__方法,其就是迭代器
3)定义可迭代对象,必须实现__iter__方法;定义迭代器,必须实现__iter__方法和__next__方法
可以直接作用于for循环的数据类型有以下几种:
- 一类是集合数据类型,如list,tuple,dict,set,str等
- 一类是generator ,包括生成器和带yeild的generator function
- 这些可以 直接作用于for循环的对象统称为可迭代对象:Iterable
- 可以被next()函数调用并不断返回下一个值的对象称为迭代器:Iterator
可迭代对象包括如下:
-- 迭代器 ---> 生成器
-- 序列(包括 列表 字符串 元组)
-- 字典
4. 装饰器
装饰器:本质是函数(装饰其他函数)就是为其他函数添加附加功能。可以在其他函数不做任何代码变动的前提下增加额外的功能。 装饰器的返回值也是一个函数的对象。
使用场景:经常用于有切面需求的场景。 比如:插入日志、性能测试、事务处理、缓存、权限的校验等场景 。有了装饰器就可以抽离出大量的与函数功能本身无关的雷同代码并发并继续使用。
原则:
- 不能修改被装饰的函数的源代码
- 不能修改被装饰的函数的调用方式
实现装饰器知识储备:
- 函数即“”变量“”
- 高阶函数+嵌套函数 = 装饰器
4.1 闭包
内部函数对外部函数作用域里变量的引用(非全局变量),则称内部函数为闭包。闭包三要素:
- 嵌套函数
- 变量的引用
- 返回内部函数
闭包有什么好处呢?
闭包可以避免全局变量的使用以及提供某种形式数据的隐藏。当函数中的变量和函数较少且其中某个功能常用时,使用闭包来封装。当变量和函数更加复杂时,则使用类来实现。
举例
def fun(a,b): #其中a,b是固定的
def fun_in(x):
return a*x+b
return fun_in
f = fun(2,4)
print(f(1)) #每次输入不同的x值即可求出对应的y值
print(f(2))
#输出的结果:
6
8
上面的函数中,利用闭包来求一元一次方程的值,直接输入x的值即可求出对应的y的值。因为这利用了闭包可以记住外部函数的参数的特性。
正式讲装饰器之前,我们要先插入闭包这一个概念。
闭包 : 函数内的函数以及其自由变量形成闭包。也即闭包时一个保留定义函数时存在的自由变量的绑定的函数,这样在调用函数时,绑定的自由变量依旧可用!
闭包有什么好处呢?
闭包可以避免全局变量的使用以及提供某种形式数据的隐藏。当函数中的变量和函数较少且其中某个功能常用时,使用闭包来封装。当变量和函数更加复杂时,则使用类来实现。
又来一个栗子吧!
# 计算移动平均值的函数
def make_averager():
series = []
def averager(new_value):
series.append(new_value)
total = sum(series)
return total/len(series)
return averager
// 在这里边 series = [] 到第七行 return total/len(series)为闭包,变量series为averager()函数中的自由变量!
来我们进入正文:
让我们一步一步的来深入的(一步一步引导面试官走进来):
1).装饰器的基础知识
我们假设又一个decorate的装饰器
@decorate
def target():
print('running target()!')
// 其实这个代码相当与以下的效果
target = decorate(target)
装饰器有两大特性:
- 第一大特性是, 能把被装饰的函数替换成其他函数。
- 第二大特征是,装饰器在加载模块时立即执行(一定要记得,面试的时候可能会被问到)
4.2 装饰器(decorator)
装饰器其实就是一个闭包,把一个函数当作参数然后返回一个替代版函数。
装饰器有2个特性:
- 可以把被装饰的函数替换成其他函数
- 可以在加载模块时候立即执行【重要,常出现面试中】
有四个例子:
1)装饰器对无参数函数进行装饰:
#装饰器本质就是一个闭包,
#将函数当成参数传进去
def deco(fun):
def inner():
print("加入操作1")
print("加入操作2")
fun()
return inner
#@deco是一个“语法糖”
@deco
def test():
print("test函数操作")
#这个test已经被装饰了,不是原来的test
test()
#结果:
加入操作1
加入操作2
test函数操作
2)装饰器对有参数函数进行装饰
#定义一个装饰器
def deco(func):
def wrapper(a,b): #内部函数的参数必须和被装饰的函数保持一致
print("添加的功能")
func(a,b)
return wrapper
@deco
#有参数的函数
def sum(a,b):
print(a+b)
sum(1,2)
#结果
添加的功能
3
3)装饰器对不定长参数函数进行装饰
from time import ctime,sleep
#定义一个装饰器,装饰不定长参数函数
def deco(func):
def wrapper(*args,**kwargs):
print("%s called at the %s"%(func.__name__,ctime()))
func(*args,**kwargs)
return wrapper
@deco
def test1(a,b,c):
print(a+b+c)
@deco
def test2(a,b):
print(a+b)
test1(1,2,3)
sleep(1)
sleep(1)
test2(1,2)
#结果
test1 called at the Sat Aug 27 23:23:38 2022
6
test2 called at the Sat Aug 27 23:23:40 2022
3
4)装饰器对有返回值的函数进行装饰(万能)
from time import ctime,sleep
#定义一个装饰器,装饰不定长参数函数
def deco(func):
def wrapper(*args,**kwargs):
print("%s called at the %s"%(func.__name__,ctime()))
return func(*args,**kwargs)
return wrapper
#上面的装饰器是一个万能的装饰器,因为它可以装饰任意一种函数
#包括有无参数函数和有无返回值函数
@deco
def test():
return "---ha--ha---"
t = test()
print(t)
#结果
test called at the Sat Aug 27 23:25:20 2022
---ha--ha---
3 基于类实现的装饰器
基于类装饰器的实现,必须实现**_call_和_init_**两个内置的函数。
- _init_:接受被装饰函数
- _call_:实现装饰逻辑
下面以一个日志打印为例
class logger(object):
def __init__(self, func):
self.func = func
def __call__(self, *args, **kwargs):
print("[INFO]: the function {func}() is running...".format(func=self.func.__name__))
return self.func(*args, **kwargs)
@logger
def say(something):
print("say {}!".format(something))
say("hello,yifan")
#以下是结果
[INFO]: the function say() is running...
say hello,yifan!
带参数的类装饰器
【注意】带参数和不带参数的类装饰器有很大的不同。
- _init_: 不再接受被装饰函数,而是接受传入参数。
- _call_:接受被装饰函数,实现装饰逻辑
class logger(object):
def __init__(self, level='INFO'):
self.level = level
def __call__(self, func):
def wrapper(*args, **kwargs):
#print("[{level}]: the function {func}() is running...".format(level=self.level,func=self.func.__name__))
print("[{level}]: the function {func}() is running...".format(level=self.level, func=func.__name__))
func(*args, **kwargs)
return wrapper
@logger(level='WARNING')
def say(something):
print("say {}!".format(something))
say("hello,yifan")
#以下是结果
[WARNING]: the function say() is running...
say hello,yifan!
总结:
装饰器 的作用是 提高代码复用性,如果有大量代码是动作重复,则可以考虑装饰器
生成器 的作用是 减少内存消耗,提高性能
迭代器 的作用主要是应用在计算量非常大的情况下,有可以会占用非常大的内存,从而导致内存爆了,所以这个时候就需要迭代器的存在了