4.Python3标准库--算法

(一)functools:管理函数的工具

import functools

‘‘‘
functools模块提供了一些工具来管理或扩展和其他callable对象,从而不必完全重写
‘‘‘

  

1.修饰符

from functools import partial

‘‘‘
functools模块提供的主要工具就是partial类,可以用来包装一个有默认参数的callable对象。
得到的对象本身就是callable,可以把它看作是原来的参数。
‘‘‘

# 举个栗子
def foo(name, age, gender):
    print(name, age, gender)

p = partial(foo, "mashiro", 16)
p("female")  # mashiro 16 female
‘‘‘
可以看到p相当于是已经绑定了name和age的foo函数,name我们在传参的时候只需要传入一个gender就可以了
这个函数的源码实现比较复杂,但是如果以简单的装饰器的方式实现就很清晰了
‘‘‘

def my_partial(f, name, age):
    def inner(gender):
        return f(name, age, gender)
    return inner

p = my_partial(foo, "satori", 16)
p("female")  # satori 16 female
‘‘‘
可以看到,当我调用my_partial(foo, "satori", 16)的时候,返回了inner函数
此时的p相当于是inner,当我再调用p("female")的时候,等价于调用inner("female")
然后将两次传入的参数,按照顺序组合起来传递给foo函数,如果不限制参数的话就是:
def my_partial(f, *args1, **kwargs1):
    def inner(*args2, **kwargs2):
        from collections import ChainMap
        args = args1 + args2
        kwargs = dict(ChainMap(kwargs1, kwargs2))
        return f(*args, **kwargs)
    return inner

所以一定要和原函数的参数顺序保持一致,如果我传入p = my_partial("mashiro", 16),此时"mashiro"会传给name,16传给age
我再调用p(name="xxx")的话,肯定会报错的
‘‘‘

  

from functools import partial
import functools

‘‘‘
默认情况下,partial对象没有__name__属性的,如果没有这些属性,那么被修饰的函数会很难调试。
‘‘‘

def foo():
    # fucking
    pass

print(foo.__name__)  # foo
p = partial(foo)
try:
    print(p.__name__)
except AttributeError as e:
    print(e)  # ‘functools.partial‘ object has no attribute ‘__name__‘

# 那么如何添加呢?首先增加到包装器的属性在WRAPPER_ASSIGNMENTS中定义,另外WRAPPER_UPDATES列出了要修改的值
print("assign:", functools.WRAPPER_ASSIGNMENTS)  # assign: (‘__module__‘, ‘__name__‘, ‘__qualname__‘, ‘__doc__‘, ‘__annotations__‘)
print("update:", functools.WRAPPER_UPDATES)  # update: (‘__dict__‘,)

# 添加,表示从原函数将属性赋值或增加到partial对象
functools.update_wrapper(p, foo)
print(p.__name__)  # foo

  

from functools import partial

‘‘‘
可以把partial看成是一个简单的装饰器,装饰器不仅可以装饰函数,还可以装饰类,只要是callable对象,说白了只要是能加上()的都可以
这就是Python的魅力,非常的动态。比如列表进行extend, 其实不仅仅可以extend一个列表,还可以是元组,甚至是字典,只要是iterable对象都可以。
‘‘‘

class A:
    def __init__(self, name, age, gender):
        self.name = name
        self.age = age
        self.gender = gender

    def print_info(self):
        print(f"name: {self.name}, age: {self.age}, gender: {self.gender}")

p = partial(A, "mashiro", 16)
a = p("female")  # 这两步等价于 a = A("mashiro", 16, "female")
a.print_info()  # name: mashiro, age: 16, gender: female

  

from functools import partial, partialmethod

‘‘‘
partial返回一个可以直接使用的callable,partialmethod返回的callable则可以用做对象的非绑定方法
‘‘‘

# 举个例子
def standalone(self):
    print(f"self = {self}")

class A:
    method1 = partial(standalone)
    method2 = partialmethod(standalone)

a = A()
try:
    a.method1()
except TypeError as e:
    # 由于standalone需要一个参数self,我们这里没有传,因此报错
    print(e)  # standalone() missing 1 required positional argument: ‘self‘

# 但是我们调用method2呢?
a.method2()  # self = <__main__.A object at 0x0000000002964588>
‘‘‘
得到了一个A的实例对象。
所以,partial在哪里调用时没有区别的,必须手动显示地传递,该是几个就是几个。
但是在类中如果使用partialmethod定义的话,那么在使用实例(a)调用的话,会自动将实例传进去。
‘‘‘

  

from functools import wraps

‘‘‘
我们在知道在使用装饰器装饰完函数的时候,属性会变。比如:
‘‘‘

def deco(func):
    def inner(*args, **kwargs):
        return func(*args, **kwargs)
    return inner

@deco
def foo():
    pass

# 函数从下到下执行,加上@deco等价于,foo = deco(foo) = inner,也就是说此时的foo不再是foo了,已经是inner了
print(foo.__name__)  # inner
# 那么如何在装饰的时候,还保证原来函数的信息呢

def deco(func):
    @wraps(func)  # 只需要加上这一个装饰器即可,会自动对所修饰的函数应用update_wrapper
    def inner(*args, **kwargs):
        return func(*args, **kwargs)
    return inner

@deco
def bar():
    pass

# 可以看到原来函数的信息并没有改变
print(bar.__name__)  # bar

  

2.比较

import functools

‘‘‘
在Python2中,类可以一个__cmp__()方法,它会根据这个对象小于、等于、或者大于所比较的元素而分别返回-1、0、1.
Python2.1中引入了富比较(rich comparision)的方法。
如:__lt__(),__gt__(),__le__(),__eq__(),__ne__(),__gt__()和__ge__(),可以完成一个比较操作并返回一个bool值。
Python3已经废弃了__cmp__()方法。
另外,functools提供了一些工具,从而能更容易地编写符合新要求的类,即符合Python3中新的比较需求。
‘‘‘

@functools.total_ordering
class A:
    def __init__(self, val):
        self.val = val

    def __eq__(self, other):
        return self.val == other.val

    def __gt__(self, other):
        return self.val > other.val

a1 = A(1)
a2 = A(2)
print(a1 < a2)

‘‘‘
这个类必须提供__eq__()和另外一个富比较方法的实现,这个修饰符会自动增加其余的方法。
‘‘‘

  

import functools

‘‘‘
由于Python3废弃了老式的比较函数,sort()之类的函数中也不再支持cmp参数。
对于使用了比较函数的较老的程序,可以使用cmp_to_key()将比较函数转换为一个比对键的函数,这个键用于确定元素在最终序列中的位置
‘‘‘

def compare_obj(a, b):
    if a < b:
        return -1
    elif a > b:
        return 1
    else:
        return 0

l = [1, 5, 2, 11, 2, 44, 54, 5, 1]

print(sorted(l, key=functools.cmp_to_key(compare_obj)))  # [1, 1, 2, 2, 5, 5, 11, 44, 54]

  

3.缓存

import functools

‘‘‘
lru_cache()修饰符将一个函数包装在一个"最近最少使用的"缓存中。函数的参数用来建立一个散列键,然后映射到这个结果。
后续调用如果有相同的参数,就会从这个缓存中获取值而不会再次调用这个函数。
这个修饰符还会为函数增加方法来检查缓存的状态(cache_info)和清空缓存(cache_clear)
‘‘‘

@functools.lru_cache()  # 里面可以执行参数maxsize,默认是128
def foo(a, b):
    print(f"foo({a} * {b})")
    return a * b

print("第一次调用")
for i in range(2):
    for j in range(2):
        foo(i, j)
print(foo.cache_info())

print("\n第二次调用")
for i in range(3):
    for j in range(3):
        foo(i, j)
print(foo.cache_info())

print("清除缓存")
foo.cache_clear()
print(foo.cache_info())

print("\n第三次调用")
for i in range(2):
    for j in range(2):
        foo(i, j)
print(foo.cache_info())
‘‘‘
第一次调用
foo(0 * 0)
foo(0 * 1)
foo(1 * 0)
foo(1 * 1)
CacheInfo(hits=0, misses=4, maxsize=128, currsize=4)

第二次调用
foo(0 * 2)
foo(1 * 2)
foo(2 * 0)
foo(2 * 1)
foo(2 * 2)
CacheInfo(hits=4, misses=9, maxsize=128, currsize=9)
清除缓存
CacheInfo(hits=0, misses=0, maxsize=128, currsize=0)

第三次调用
foo(0 * 0)
foo(0 * 1)
foo(1 * 0)
foo(1 * 1)
CacheInfo(hits=0, misses=4, maxsize=128, currsize=4)
‘‘‘
# 我们观察一下第二次调用,3 * 3应该是9次,为什么只有5次,因为第一次调用有4次执行过了,放到缓存里,因此不需要执行了

  

4.reduce

import functools

‘‘‘
reduce这个函数无需介绍,在Python2中是内置的,但是在Python3中被移到functools下面
‘‘‘
l = range(100)
print(functools.reduce(lambda x, y: x+y, l))  # 4950
print(functools.reduce(lambda x, y: x+y, l, 10))  # 4960
print(functools.reduce(lambda x, y: x+y, l, 100))  # 5050

l = [1, 2, 3, 4, 5]
print(functools.reduce(lambda x, y: x*y, l))  # 120

  

5.泛型函数

import functools

‘‘‘
在类似Python的动态类型语言中,通常需要基于参数的类型完成稍有不同的操作,特别是在处理元素列表与单个元素的差别时。
直接检查参数的类型固然很简单,但是有些情况下,行为差异可能被隔离到单个的函数中。
对于这些情况,functools提供了singledispatch修饰符来注册一组泛型函数,可以根据函数第一个参数的类型自动切换
‘‘‘

@functools.singledispatch
def myfunc(arg):
    print(f"default myfunc {arg}")

@myfunc.register(int)
def myfunc1(arg):
    print(f"myfunc1 {arg}")

@myfunc.register(list)
def myfunc2(arg):
    print(f"myfunc2 {arg}")

myfunc("string")  # default myfunc string
myfunc(123)  # myfunc1 123
myfunc(["1", "2"])  # myfunc2 [‘1‘, ‘2‘]
‘‘‘
可以看到使用signledispatch包装的是默认实现,在未指定其他类型特定函数的时候就用这个默认实现。
然后使用包装的函数这里是myfunc,通过register(数据类型)进行注册,根据所传参数的类型,从而执行对应的函数
‘‘‘

  

(二)itertools:迭代器函数

1.合并和分解迭代器

import itertools

‘‘‘
chain函数可以接收多个可迭代对象(或者迭代器)作为参数,最后返回一个迭代器。
它会生成所有输入迭代器的内容,就好像这些内容来自一个迭代器一样。
类似于collections下的ChainMap,可以合并多个字典。chain可以合并多个可迭代对象
‘‘‘

c = itertools.chain([1, 2, 3], "abc", {"k1": "v1", "k2": "v2"})
print(c)  # <itertools.chain object at 0x00000000029745F8>
for i in c:
    print(i, end=" ")  # 1 2 3 a b c k1 k2

print()
# 还可以使用chain.from_iterable,参数接收多个可迭代对象组成的一个可迭代对象
c = itertools.chain.from_iterable([[1, 2, 3], "abc", {"k1": "v1", "k2": "v2"}])
for i in c:
    print(i, end=" ")  # 1 2 3 a b c k1 k2

# 函数zip则是把多个迭代器对象组合到一个元组中
name = ["古明地觉", "椎名真白", "雪之下雪乃"]
where = ["东方地灵殿", "樱花张的宠物女孩", "春物"]
z = zip(name, where)
print("\n", z)  # <zip object at 0x0000000001DC03C8>
print(list(z))  # [(‘古明地觉‘, ‘东方地灵殿‘), (‘椎名真白‘, ‘樱花张的宠物女孩‘), (‘雪之下雪乃‘, ‘春物‘)]

# zip英文意思是拉链,很形象,就是把对应元素给组合起来
# 但如果两者长度不一致怎么办?
name = ["古明地觉", "椎名真白", "雪之下雪乃", "xxx"]
where = ["东方地灵殿", "樱花张的宠物女孩", "春物"]
print(list(zip(name, where)))  # [(‘古明地觉‘, ‘东方地灵殿‘), (‘椎名真白‘, ‘樱花张的宠物女孩‘), (‘雪之下雪乃‘, ‘春物‘)]
# 可以看到,不一致的时候,当一方结束之后就停止匹配。

# 如果想匹配长的,那么可以使用zip_longest,这个函数不像zip一样是内置的,它在itertools下面
print(list(itertools.zip_longest(name, where)))  # [(‘古明地觉‘, ‘东方地灵殿‘), (‘椎名真白‘, ‘樱花张的宠物女孩‘), (‘雪之下雪乃‘, ‘春物‘), (‘xxx‘, None)]
# 可以看到没有的默认赋值为None了,当然我们也可以指定填充字符
print(list(itertools.zip_longest(name, where, fillvalue="你输入的是啥啊")))
# [(‘古明地觉‘, ‘东方地灵殿‘), (‘椎名真白‘, ‘樱花张的宠物女孩‘), (‘雪之下雪乃‘, ‘春物‘), (‘xxx‘, ‘你输入的是啥啊‘)]

# isslice返回一个迭代器,按照索引从迭代器返回所选择的元素
num = range(20)
# 从index=5的地方选到index=10(不包含)的地方
s = itertools.islice(num, 5, 10)
print(list(s))  # [5, 6, 7, 8, 9]
#  从开头选到index=5的地方
s = itertools.islice(num, 5)
print(list(s))  # [0, 1, 2, 3, 4]
# 从index=5的地方选择到index=15的地方,步长为3
s = itertools.islice(num, 5, 15, 3)
print(list(s))  # [5, 8, 11, 14]
‘‘‘
所以除了迭代器之外,
如果只传一个参数,比如5,表示从index=0选到index=5(不包含)的地方
如果传两个参数,比如5和10,表示从index=5选到index=10(不包含)的地方
如果传三个参数,比如5和10和2,表示从index=5选到index=10(不包含)的地方,步长为2
‘‘‘
# 那么支不支持负数索引呢?答案是不支持的,因为不知道迭代器有多长,除非全部读取,可是那样的话干嘛不直接转化为列表之后再用[:]这种形式呢?
# 之所以使用isslice这种形式,就是为了在不全部读取的情况下,也能选择出我们想要的部分,所以这种方式只支持从前往后,不能从后往前读。

# tee()函数根据一个原输入迭代器返回多个独立、和原迭代器一模一样的迭代器(默认为两个)
r = [1, 2, 3, 4, 5]
i1, i2 = itertools.tee(r)
print(list(i1))  # [1, 2, 3, 4, 5]
print(list(i2))  # [1, 2, 3, 4, 5]

  

2.转换输入

import itertools

‘‘‘
内置的map()函数返回一个迭代器,它对输入迭代器中的值调用一个函数并返回结果。
输入迭代中的元素全部被消费时,map()函数就会停止
‘‘‘
l = [1, 2, 3]
map_l = map(lambda x: str(x)+"a", l)
print(list(map_l))  # [‘1a‘, ‘2a‘, ‘3a‘]

l1 = [(0, 5), (1, 6), (2, 7)]
‘‘‘
注意map里面的函数只能有一个参数,因此不可以写成以下格式
map_l1 = map(lambda x, y: x*y, l1)
但是可以这样
‘‘‘
map_l1 = map(lambda x: x[0]*x[1], l1)
print(list(map_l1))  # [0, 6, 14]

# 但是itertools下的startmap()是支持的
l2 = [(1, 2, 3), (4, 5, 6), (7, 8, 9)]
# 注意里面的函数的参数的参数个数是由我们后面传入对象决定的,这里每个元组显然有三个元素,所以需要三个参数
map_l1 = itertools.starmap(lambda x, y, z: f"{x} + {y} + {z} = {x+y+z}", l2)
print(list(map_l1))  # [‘1 + 2 + 3 = 6‘, ‘4 + 5 + 6 = 15‘, ‘7 + 8 + 9 = 24‘]
# map的话只能通过lambda x: x[0], x[1], x[2]这样的形式
# starmap只能对类似于[(), (), ()]这种值进行处理,比如[1, 2, 3]使用starmap是会报错的,但是[(1, ), (2, ), (3, )]不会报错

  

3.生成新值

import itertools

‘‘‘
count(start=0, step=1)函数返回一个迭代器,该迭代器能够无限地生成连续的整数。
接收两个参数:起始(默认为0)和步长(默认为1)
等价于:
def count(firstval=0, step=1):
    x = firstval
    while 1:
        yield x
        x += step
‘‘‘

‘‘‘
cycle(iterable)返回一个迭代器,会无限重复里面的内容,直到内存耗尽
‘‘‘
c2 = itertools.cycle("abc")
print(list(itertools.islice(c2, 0, 10)))  # [‘a‘, ‘b‘, ‘c‘, ‘a‘, ‘b‘, ‘c‘, ‘a‘, ‘b‘, ‘c‘, ‘a‘]

‘‘‘
repeat(obj, times=None),无限重复obj,除非指定times。
‘‘‘
print(list(itertools.repeat("abc", 3)))  # [‘abc‘, ‘abc‘, ‘abc‘]

  

4.过滤

原文地址:https://www.cnblogs.com/traditional/p/10478534.html

时间: 2024-10-14 02:49:46

4.Python3标准库--算法的相关文章

利用标准库算法求解排列组合

以前求序列的排列时,最常用的方法就是递归回溯,现在发现其实像这样有特定算法的重复性工作是可以在STL标准库中找到答案的. 在STL的变序性算法中,有两个用于排列元素的算法分别如下: bool next_permutation(Iterator beg,Iterator end) bool prev_permutation(Iterator beg,Iterator end) 这两个算法的功能也很简单,next_permutation()会改变区间(beg,end)内的元素次序,使它们符合"下一个

STL标准库-算法-常用算法

摘要: 摘要: 摘要: 技术在于交流.沟通,本文为博主原创文章转载请注明出处并保持作品的完整性 介绍11种STL标准库的算法,从这11种算法中总结一下算法的基本使用 1.accumulate() 累加 2.for_each() for一段区间 做你指定的行为 3.replace(), replace_if(), replace_copy() 替换函数 4.count(), count_if() 计数 5.find() 查找 6.sort() 排序 7.binary_search()查看元素是否在

Python3标准库

文本 1. string:通用字符串操作 2. re:正则表达式操作 3. difflib:差异计算工具 4. textwrap:文本填充 5. unicodedata:Unicode字符数据库 6. stringprep:互联网字符串准备工具 7. readline:GNU按行读取接口 8. rlcompleter:GNU按行读取的实现函数 二进制数据 9. struct:将字节解析为打包的二进制数据 10. codecs:注册表与基类的编解码器 数据类型 11. datetime:基于日期与

Python3标准库:hashlib密码散列

1. hashlib密码散列 hashlib模块定义了一个API来访问不同的密码散列算法.要使用一个特定的散列算法,可以用适当的构造器函数或new()来创建一个散列对象.不论使用哪个具体的算法,这些对象都使用相同的API. 1.1 散列算法 由于hashlib有OpenSSL提供“底层支持”,所以OpenSSL库提供的所有算法都可用,包括: md5 sha1 sha224 sha256 sha384 sha512 有些算法在所有平台上都可用,而有些则依赖于底层库.这两种算法分别由algorith

Python3标准库:bisect维护有序列表

1. bisect维护有序列表 bisect模块实现了一个算法来向列表中插入元素,同时仍保持列表有序. 1.1 有序插入 下面给出一个简单的例子,这里使用insort()按有序顺序向一个列表中插入元素. import bisect # A series of random numbers values = [14, 85, 77, 26, 50, 45, 66, 79, 10, 3, 84, 77, 1] print('New Pos Contents') print('--- --- ----

Python3标准库:weakref对象的非永久引用

1. weakref对象的非永久引用 weakref模块支持对象的弱引用.正常的引用会增加对象的引用数,并避免它被垃圾回收.但结果并不总是如期望中的那样,比如有时可能会出现一个循环引用,或者有时需要内存时可能要删除对象的缓存.弱引用(weak reference)是一个不能避免对象被自动清理的对象句柄. 1.1 引用 对象的弱引用要通过ref类来管理.要获取原对象,可以调用引用对象. import weakref class ExpensiveObject: def __del__(self):

Python3标准库:random伪随机数生成器

1. random伪随机数生成器 random模块基于Mersenne Twister算法提供了一个快速伪随机数生成器.原来开发这个生成器是为了向蒙特卡洛模拟生成输入,Mersenne Twister算法会生成大周期近均匀分布的数,因此适用于大量不同类型的应用. 1.1 生成随机数 random()函数从所生成的序列返回下一个随机的浮点值.返回的所有值都落在0<=n<1.0区间内. import random for i in range(5): print('%04.3f' % random

Python3标准库:urllib.parse分解URL

1. urllib.parse分解URL urllib.parse模块提供了一些函数,可以管理URL及其组成部分,这包括将URL分解为组成部分以及由组成部分构成URL. 1.1 解析 urlparse()函数的返回值是一个ParseResult对象,其相当于一个包含6个元素的tuple. from urllib.parse import urlparse url = 'http://netloc/path;param?query=arg#frag' parsed = urlparse(url)

将函数对象用于标准库算法

1 #include<iostream> 2 #include<vector> 3 4 bool GT6(const int &s){ 5 return s>=6; 6 } 7 int main(){ 8 int a[]={0,1,2,3,4,5,6,7,8,9}; 9 std::vector<int>vec(a,a+10); 10 std::cout<<count_if(vec.begin(),vec.end(),GT6)<<st