Week_five_assignment

base64

解法一

暴力求解法:1、字符串每3步一切,得到目标字符串target,将其放入列表
2、遍历列表,按个将target转为字节序列
2、正常情况:字节序列前补零得到24个字节序列,6步一切得到4个子节序列b_character
3、尾部特殊情况:字节序列target不足3个字符,字节序列前补零,每缺一个字符,后面补8个零,最多补16个零,然后6补一切,得到字节序列b_character
4、循环处理b_character,正常4次循环;尾部特殊情况,target长度为2,切3次,避开补的零;target长度为2,切2次,避开补的零。
5、’=‘号追加,target=2,补一个’=’,target=1,补两个’=‘
缺点:
多次遍历、多个列表和字符串,效率极低

import string
base64 = string.ascii_uppercase + string.ascii_lowercase + string.digits + '+' + '/'   
# 得到base64字符串

word = '''Man is distinguished, not only by his reason, but by this singular passion 
from other animals, which is a lust of the mind, that by a perseverance of delight in 
the continued and indefatigable generation of knowledge, exceeds the short vehemence 
of any carnal pleasure'''

start = 0
step = 3
stop = start + step
target_lst = []

while True:
    target = word[start:stop]  # 每三位切割字符串
    if len(target) == 0:  # 如果没切到,直接终止
        break
    target_lst.append(target)  # 将得到的字符串依次放入列表,
    start += step    
    stop = start + step
    if len(target) < 3:   # 如果切割的字符串长度小于3,得到全部字符串,终止循环
        break
    
# print(target_lst,'1111')
str_target = ''   # 连结整个base64解码后的字符串

for i in target_lst: # 遍历列表,按个对字符串进行处理
    int_target = int.from_bytes(i.encode(),'big') # 得到字符串的十进制
    length = len(i)   # 取字符串长度,判断长度,小于三进行特殊处理
    
#     print(int_target,"22222")

    if length == 3:  # 字符串长度等于3,前补零,得到目标二进制
        b_character = '{:024b}'.format(int_target)
        # print(b_character,"33333") 

    elif length == 2: # 字符串长度等于2,前后补零,得到目标二进制
        b_character = '{:016b}'.format(int_target) + 8 * '0'

    elif length == 1:  # 同上
        b_character = '{:08b}'.format(int_target) + 16 * '0'
        
    start = 0
    step = 6
    stop = start + step
    
    # print(length,'4444')
    for _ in range(length+1):  # 计数,24为转为4个6位,如果length<3,只能切2次或者3次
    						   # 正好避免切补零的部分
        index = int(b_character[start:stop],2)  # int函数,将二进制字符串转为十进制
        str_target += base64[index]  # base64中,索引查找求得的十进制,即为base解码
        start += step
        stop = start + step
    else:   # 最后判断,如果目标字符串的长度小于3,补’=‘
        if length < 3:    
            while length:  # 长度2,补1个’=‘,长度1,补2个'='
                str_target += '='    # 目标字符串补'='
                length += 1

                if length == 3:   length等于3,循环终止
                    break

print(str_target)

解法二

1、判断字符串能否被3整除,整除不处理,未整除,余数为1,补2个null,余数为2,补1个null,因为null在内存中是0
2、null不可见字符,使用十六进制的字节表示方式’\x00’
3、3步一切,放入目标字典(优化点,步需要放入字典,而是取一个字符串,处理一次
4、位与63(二进制111111),技巧:n位数字位与n位的二进制1,得到的还是原数字。位与0,得0.
5、将目标字节序列右移6位,得到下一次的目标字节序列
6、从后往前且,先切的往后方
7、用切片将补的位置全部换成’='号(优化点,碰见特殊字符串单独处理,最后再连在一起
优点:
1、直接补齐字符串,不用特殊补零
2、位与、右移,不用转二进制,提高切割效率
缺点:
1、将切好的字符串放入列表,遍历处理
2、字符串拼好之后,特殊情况:切片生成新的字符串
3、3个大容器,内存开辟空间过大,效率低

import string
base64 = string.ascii_uppercase + string.ascii_lowercase + string.digits + '+' + '/'

word = '''Man is distinguished, not only by his reason, but by this singular passion 
from other animals, which is a lust of the mind, that by a perseverance of delight in 
the continued and indefatigable generation of knowledge, exceeds the short vehemence 
of any carnal pleasure'''

target_lst = []  # 存放目标字符串
length = len(word)  

div,mod = divmod(length,3)  # div为循环次数,mod用来判断字符串不足3个,做特殊处理
# print(div,mod)

for i in range(div):  # 通过步长,得到目标字符串,并且追加到目标列表中
    target_lst.append(word[3 * i:3 * (i + 1)]) # 此处可优化,不需要放入列表,只需要每三位转一下,可以提高运行效率
    
if mod != 0:  # 特殊情况,mod=1,字符串一个,需要补两个null,mod=2,需要补一个null
    target_lst.append(word[-mod:] + (3 - mod) * '\x00') # null不可见,'\x00'十六进制得到
    
# print(target_lst)

word_base64 = '' # 用于连结解码后的字符串

for val in target_lst: # 遍历目标列表,按个处理
    code = val.encode()  
    num = int.from_bytes(code,'big')  # 得到字符串的十进制表达
    word = ''  # 连结每次解码后的字符
    
    while num:  # num不为零进入,为零退出       
        index = num & 63   # 核心:63的二进制为111111,位与得到后六位(技巧:与1位与得到原数)
        word = base64[index] + word  # 从后向前切,切到的值向后追加
        num = num >> 6 # 向右位移6位,得到下一次目标字节序列,依次循环

    word_base64 += word  # 得到目标解码字符串

if mod != 0:  # 尾部特殊情况处理
    miss = 3 - mod    # 补一个null,0~-1 + ’=‘,补两个null,0~-2 + 2个"="
    word_base64 = word_base64[:-miss] + miss * '=' # 此处可优化,再次生成一个字符串,占用过多内存
#     lst = list(word_base64)
#     lst[-miss:] = miss *'='
    
#     word_base64 = ''.join(lst)

print(word_base64) 

cache过期清除

解法一

1、模仿源代码:make_key函数专门用来生成key,连接args和kwargs,以及里面元素的数据类型
2、得到被装饰函数的运行时间,使用二元组保存在value中,value[0]为值,value[1]为时间对象
3、如果delta时间超过10,列表中追加k。之后遍历列表,删除cache中的key
3、设定哨兵sentiel,get字典key,返回值不为哨兵,直接返回value。返回值为哨兵,说明cache中无此kv对,返回被装饰函数的返回值,得到时间对象,封装成二元组作为value,创建kv对
4、最后返回value[0]
5、设置哨兵sentinel,解决函数返回值为0,cache_get的值为0无法进入条件的问题。
缺点:
1、时间设定死了,无法更改
2、每次调用都要遍历字典,时间复杂度O(n),效率极低

import datetime,functools,time  

def make_key(key_tup,kwargs,typed=False):
    key = ()
    kw_key = ()

    if args:  # 遍历args元组,连接赋值给key
        key += key_tup

    if kwargs: # 字典不为空
        kw_key += tuple(kwargs) # 遍历字典的keys,转为tuple
        kw_key += tuple(kwargs.values())  # 遍历字典的values,转为tuple
        print(kw_key) # 打印查看kw_key
        if key == kw_key:  # 两者有可能相等?
            pass
        else:
            key += kw_key  # 连接赋值给key
                    
    if typed:  # 类型不为False
        key += tuple(type(i) for i in args)  # (新技巧:生成器得数据类型,tuple迭代)
		# 将args中的元素遍历,type()得到数据类型并连接。
        if kwargs: # 字典不为空
            key += (type(i) for v in kwargs.values()) # 遍历字典,得到数据类型

    elif len(key) == 1 :  # 特殊情况,key长度位1,直接返回元组里面的第一个值
        return key[0]

    return key

def cache(fn):
    cache = {} # 缓存字典,闭包不消亡
    cache_get = cache.get  # 将字典get方法赋值(源代码习得新技巧)
    sentienl = object()   # 哨兵,object(),祖宗对象(源代码习得新技巧)
    @functools.wraps(fn)
    def wrapper(*args,**kwargs):
        key = make_key(args,kwargs,typed=False) # 生成字典key
              
        lst = []   # 每次进入需要空列表,放删除的key,字典视图对象,遍历中不能改变size
        for k,v in cache.items(): # 循环需要放在此处,调用函数就遍历,发现超过时间的就删除(缺点:每次调用都遍历,时间复杂度O(n),效率极低)
            delta = (datetime.datetime.now() - v[1]).total_seconds()
            # 得到时间差timedelta对象
            if delta > 10: # 固定死,差值大于10秒,将删除的k追加到lst中
                lst.append(k)
                print(lst)

        for key in lst: # 遍历列表,删除cache中的key                                     
                del cache[key]  # 集合、字典在遍历的时候不能改变size
                
        result = cache_get(key,sentinel)  # 通过key取值          
        if result is not sentinel: # 如果不为哨兵,说明cache字典有key,有value
            return result[0]  # result二元组,0为值,1为datetime.datetime对象
        else: # 为哨兵,说明cache中无key
            start = datetime.datetime.now() # 生成时间
            result = fn(*args,**kwargs)  # fn函数调用
            cache[key] = (result,start)    # cache创建kv对

        return result,cache  # 查看cache结果
    return wrapper

解法二

1、函数的形参个数、位置都是固定的,得到函数的参数,组成前置的key(元组)
2、如果函数参数全有缺省值,则将缺省值按照形参顺序依次连接成key
3、进入make_key函数,判断实参是否在key中,在则不处理,不在则连接成key
4、此时的key元素为形参名、缺省值和实参,解决传入实参和缺省值一样的问题。
5、实参关键字传参位置打乱,但是形参顺序是固定的,只需要判断值在不在key里面即可
6、将duration柯里化,可以设置duration的时长,决定删除时间
缺点:
1、每次进入遍历字典,效率低下
2、可以将前置key的生成放在make_key中
3、没有实现(4,5) 和(5,4)一样的功能
优化:
给cache一个时间,调用之后再定点清空cache

import datetime,functools,time,inspect

def make_key(key_tup,kwargs,typed=False):

#     if key_tup:
#         key += key_tup

    if kwargs:  # 如果字典不为空
        lst = []   
        for _,v in kwargs.items(): # 遍历字典的value
            if v not in key_tup:   # 判断其是否在key_tup中,不在,则与key_tup连结,解决传入一个位置参数
                key_tup += (v,)    # 修正,可以直接和(v,)相连,(v,)为单个元组的表达方式,
#                 lst.append(v)      # 和一个关键字传参的问题
                                    # 因为v不能直接和tuple连结,所有使用了一个lst               
#         key_tup += tuple(lst)                
                    
    if typed:
        key_tup += tuple(type(i) for i in key_tup)
            
    key = key_tup
    print(key)

    if len(key) == 1 :
        return key[0]

    return key

def cache1(duration=100):
    def cache_inner(fn):
        cache = {}
        cache_get = cache.get
        sentinel = object()

        @functools.wraps(fn)
        def wrapper(*args,**kwargs):
            nonlocal hits,misses
            key_tup = ()   
            key_tup += tuple(inspect.signature(wrapper).parameters.keys())
            # 获得函数签名的parameters中的key,即形参的变量名,与空元组连结,组成前置的key
            # 优化点:可以将其移到make_key函数中
            if fn.__defaults__:   # 判断fn的缺省值是否为非空,非空则将缺省值与key_tup连结,组成key
                key_tup += fn.__defaults__
            else:                 # 如果为空,则将传入的实参与key_tup连结,组成key
                key_tup += args               
 
            key = make_key(key_tup,kwargs,typed=False)          
			# 解决kwargs的问题
            lst = []   # 每次进入就遍历,所以是空列表就可以
            for k,v in cache.items():      # 这个循环需要放在前面,进入字典就遍历,发现超过时间的就删除
                delta = (datetime.datetime.now() - v[1]).total_seconds()
                if delta > duration: # 遍历字典,查询v里面时间,并得到delta对象
                    lst.append(k)    # duration柯里化成函数的参数,可以设置删除时间
#                     print(lst)

            result = cache_get(key,sentinel)
            for key in lst:                                      
                    del cache[key]  # 集合在遍历的时候也不能改变size

            if result is not sentinel: 
                return result[0]   # result是一个二元组,索引0为值,索引1为时间
            else:
                start = datetime.datetime.now()
                result = fn(*args,**kwargs)
                cache[key] = (result,start)    

            return result,cache
        return wrapper
    return cache_inner
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值