python中的函数增强神器functools模块

170 阅读4分钟

partial

用于创建一个偏函数,将默认参数包装一个可调用对象,返回结果也是可调用对象。 偏函数可以固定住原函数的部分参数,从而在调用时更简单。

举一个简单的例子:

def add(a, b, c, x=1, y=2, z=3):
    return sum([a, b, c, x, y, z])

print(add(1, 2, 3, x=1, y=2, z=3))
#输出
12

如果我们频繁调用此函数,并且固定传入某些参数,比如b=20, x=100,就可以使用partial来构造函数

from functools import partial

def add(a, b, c, x=1, y=2, z=3):
    print(a, b, c, x, y, z)
    return sum([a, b, c, x, y, z])

add_100 = partial(add, 20, x=100)
print(add_100(1, 2, y=2, z=3))
# 输出
20 1 2 100 2 3
128
在接口调用的使用也可以使用,比如传参数:ip地址或者是域名是固定的,只是后面的url不固定,此时就可以使用partial来构造接口调用函数

cmp_to_key

原来那种cmp函数的模式是每次直接给你两个元素,你返回它们的大小关系。由于排序过程中同一个元素会被拿去跟别的不同元素cmp很多次,可能会有性能问题新的key函数这种模式并不直接比较任意两个原始元素,而是通过key函数把那些元素转换成一个个新的可比较对象,也就是元素的key,然后用元素的key代替元素去参与比较。如果元素key之间的比较操作比原始元素之间的比较操作效率高的话,那么就能提高性能。key函数的直接功能就是传入一个元素,返回一个可比较对象。如果原始元素本来就是可比较对象,比如数字、字符串,那么不考虑性能优化可以直接sort(key=lambda e: e)

例子:

class Solution:
    def largestNumber(self, nums):
        """
        :type nums: List[int]
        :rtype: str
        """
        from functools import cmp_to_key
        temp = list(map(str, nums))
        print(temp)
        temp.sort(key=cmp_to_key(lambda x, y: int(x+y)-int(y+x)), reverse=True)
        print(temp)
        return ''.join(temp if temp[0]!='0' else '0')


y = Solution()
x = y.largestNumber([3, 30, 34, 5, 9])
print(x)

lru_cache

允许我们将一个函数的返回值快速地缓存或取消缓存。 该装饰器用于缓存函数的调用结果,对于需要多次调用的函数,而且每次调用参数都相同,则可以用该装饰器缓存调用结果,从而加快程序运行。该装饰器会将不同的调用结果缓存在内存中,因此需要注意内存占用问题。

from functools import lru_cache

@lru_cache()
def a(x):
    print(x)
    return x+1

print(a(3))

使用缓存记录后,第一次a(3)调用,计算了数据后会进行缓存,第二次a(3)调用,因为参数相同,所以直接返回缓存的数据,第三次a(4)调用,因为参数不同,需要重新计算

singledispatch

单分发器, Python3.4新增,用于实现泛型函数。 根据单一参数的类型来判断调用哪个函数。 例如

def connect(address):
    if isinstance(address, str):
        ip, port = address.split(':')
    elif isinstance(address, tuple):
        ip, port = address
	  else:
        print('地址格式不正确')

# 传入字符串
connect('123.45.32.18:8080')

# 传入元祖
connect(('123.45.32.18', 8080))

简单来说就是address可能是字符串,也可能是元组,那么我们就需要在函数内进行单独处理,如果这种类型很多呢?那就需要if...elif...elif...elif..esle...,写起来非常不美观,而且函数的可读性也会变差。 学过C++和Java的同学都知道函数重载,同样的函数名,同样的参数个数,不同的参数类型,实现多个函数,程序运行时将根据不同的参数类型自动调用对应的函数。python也提供了这样的重载方式

from functools import singledispatch

@singledispatch
def connect(address):
    print(f'传入参数类型为:{type(address)}, 不是有效的类型')

@connect.register
def connect_str(address: str):
    ip, port = address.split(':')
    print(f'参数为字符串,IP是{ip}, 端口是{port}')

@connect.register
def connect_tuple(address: tuple):
    ip, port = address
    print(f'参数为元组,IP是{ip}, 端口是{port}')

connect('123.45.32.18:8080')
# 输出
参数为字符串,IP是123.45.32.18, 端口是8080

connect(('123.45.32.18', '8080'))
# 输出
参数为元组,IP是123.45.32.18, 端口是8080