0%

缘起

最近在开发关于大模型相关的应用,涉及到文生图的功能。接入大模型的 API 调用后会返回 Base64 编码后的数据,通常情况下返回的图片数据都是 PNG 格式,所以后续默认按照此格式处理即可。但是图片生成后通常需要进行展示,有两种解决方案,第一种返回相关的 Base64 数据让前端渲染,这样需要遵循 Data URI scheme 标准,第二种是上传到对象存储上返回图片的 URL 。 无论那种方案,都需要知道返回数据的 MIME type(通俗理解上的文件格式),虽然可以按照默认值 PNG 进行处理,但是在使用共享的 Base64 文件预览的时候,发现一个 Base64在线预览网站 上能够正确展示编码数据的 MIME type, 我就比较好奇我没有按照 Data URI scheme 标准的数据如何实现的 MIME type 检测的。

静态网站评论系统对比

由于博客年久失修,很久没有产出文章了,之前的主题已经很久没有更新(自己不太会前端),突然心血来潮想要更新一下,并且增加评论功能,便产生了写一篇文章的想法,本文不包含软件使用上的教学内容,仅仅是写一下自身了解的信息。

Python 描述符

什么是描述符(Descriptor)

定义了 __get__(),__set__(),__delete__()中任意一个描述符协议的对象成为描述符。简单来说,描述符就是可重用的属性。描述符具有诸多优点,诸如:保护属性不受修改、属性类型检查和自动更新某个依赖属性的值等。

Python 属性访问顺序

实例属性访问顺序

  • 类的 __getattribute__() 方法
  • 类的数据描述符:(定义了描述符方法)(若人为的重载了该 __getattribute__() 方法,可能会导致无法调用描述符)
  • 实例的属性字典(若与描述符对象同名,会被覆盖哦)
  • 类的非数据描述符(只定义了 __get__() 方法,而没有定义 __set__(), __delete__() 方法)
  • 类的属性字典
  • 类的 __getattr__() 方法

类属性访问顺序

假设类为元类的实例,相比于实例多了一个类数据描述符的查找。

LRU(Least Recent Used)缓存淘汰策略

LRU 算法根据数据的历史访问记录来进行淘汰数据,其核心思想是“如果数据最近被访问过,那么将来被访问的几率也更高”。

实现细节

核心思想使用 map 结构做到 save 和 get key的时间都是 O(1),配合双向链表完成 O(1) 时间将节点放置在缓存头部。

二分查找

二分查找模版

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
def binary_search(arr, target):
    if not arr:
        return -1
    left = 0
    right = len(arr) - 1
    while left <= right:
        mid = left + ((right - left)>>1)
        pivot = arr[mid]
        if pivot < target:
            left = mid + 1
        elif pivot > target:
            right = mid - 1
        else
            pass  # ???
    ret = -1  # ???
    return ret

二分查找实现

  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
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
#!/usr/bin/env python3
# -*- coding:utf-8 -*-

import bisect


def binary_search(arr, target):
    """ 二分查找不存在返回 -1 """
    ret = -1
    if not arr:
        return ret
    arr_len = len(arr)
    left = 0
    right = arr_len
    while left < right:
        mid = left + ((right - left)>>1)
        v = arr[mid]
        if v < target:
            left = mid + 1
        elif v > target:
            right = mid
        else:
            return mid
    return ret


def low_bound(arr, target):
    """ 返回左边界 第一个大于等于 """
    ret = -1
    if not arr:
        return ret
    arr_len = len(arr)
    left = 0
    right = arr_len
    while left < right:
        mid = left + ((right - left)>>1)
        v = arr[mid]
        # v >= target right = mid
        if v < target:
            left = mid + 1
        elif v > target:
            right = mid
        else:
            right = mid
    ret = left
    return ret


def upper_bound(arr, target):
    """ 返回边界 第一个大于"""
    ret = -1
    if not arr:
        return ret
    arr_len = len(arr)
    left = 0
    right = arr_len
    while left < right:
        mid = left + ((right - left)>>1)
        v = arr[mid]
        # v <= target left = mid + 1
        if v < target:
            left = mid + 1
        elif v > target:
            right = mid
        else:
            left = mid + 1
    ret = right
    return ret


def low_bound_reverse(arr, target):
    """ 逆序数组,返回左边界 第一个小于等于 """
    ret = -1
    if not arr:
        return ret
    arr_len = len(arr)
    left = 0
    right = arr_len
    while left < right:
        mid = left + ((right - left)>>1)
        v = arr[mid]
        # v >= target left = mid + 1
        if v >= target:
            left = mid + 1
        elif v < target:
            right = mid
    ret = left
    return ret


def upper_bound_reverse(arr, target):
    """ 逆序数组,返回边界 第一个小于"""
    ret = -1
    if not arr:
        return ret
    arr_len = len(arr)
    left = 0
    right = arr_len
    while left < right:
        mid = left + ((right - left)>>1)
        v = arr[mid]
        # v <= target right = mid
        if v > target:
            left = mid + 1
        elif v <= target:
            right = mid
    ret = right
    return ret


def _main():
    # arr = list(range(1, 11))
    # 数组从小到大
    arr = list(range(11))
    pivot = 7
    arr[8] = pivot
    print('origin: {}'.format(arr))
    for i in (-1, 5, 7, 8, 11):
        ret1 = binary_search(arr, i)
        ret2 = bisect.bisect_left(arr, i)
        print('{} diff {} {}'.format(i, ret1, ret2))

    for i in (-1, 5, 7, 8, 11):
        ret1 = low_bound(arr, i)
        ret2 = bisect.bisect_left(arr, i)
        print('{} diff {} {}'.format(i, ret1, ret2))

    for i in (-1, 5, 7, 8, 11):
        ret1 = upper_bound(arr, i)
        ret2 = bisect.bisect_right(arr, i)
        print('{} diff {} {}'.format(i, ret1, ret2))

    arr.sort(reverse=True)

    print('sort origin: {}'.format(arr))
    for i in (-1, 5, 7, 8, 11):
        ret1 = low_bound_reverse(arr, i)
        # ret2 = bisect.bisect_left(arr, i)
        # ret3 = bisect.bisect_right(arr, i)
        # ret2 = ret2 if ret2 < ret3 else ret3
        ret2 = 0
        print('{} diff {} {}'.format(i, ret1, ret2))

    for i in (-1, 5, 7, 8, 11):
        ret1 = upper_bound_reverse(arr, i)
        # ret2 = bisect.bisect_left(arr, i)
        # ret3 = bisect.bisect_right(arr, i)
        # ret2 = ret2 if ret2 > ret3 else ret3
        ret2 = 0
        print('{} diff {} {}'.format(i, ret1, ret2))


if __name__ == '__main__':
    _main()

Note

二分查找的三个步骤: 1. 预处理:如果序列未排序,则先进行排序 2. 二分查找:使用循环或递归将中间值元素与目标元素进行比较,将区间划分为两个子区间,然后再符合条件的其中一个子区间内进行寻找,直至循环或递归结束。 3. 后处理:在循环或递归完成后,需要对剩余区间的元素中确定符合条件的元素

面向对象之设计模式

创建型模式

创建型模式提供了创建对象的机制, 能够提升已有代码的灵活性和可复用性。

工厂方法 Factory Method

意图

父类中提供创建对象的接口,但是允许子类修改需要创建对象的类型

数据结构与算法

小顶堆

1
2
3
4
5
6
7
8
9
import heapq

nums = [1, 8, 2, 23, 7, -4, 18, 23, 42, 37, 2]
heap = list(nums)
heapq.heapify(heap)
heap
# [-4, 2, 1, 23, 7, 2, 18, 23, 42, 37, 8]
print(heapq.nlargest(3, nums)) # Prints [42, 37, 23]
print(heapq.nsmallest(3, nums)) # Prints [-4, 1, 2]

nlargest() 和 nsmallest() 适合查找元素数量少的情况,如果查找数量较多,通常先排序后切片更快。sorted(items)[:N]

Python 浅拷贝、深拷贝

  • 深复制,即将被复制对象完全再复制一遍作为独立的新个体单独存在。所以改变原有被复制对象不会对已经复制出来的新对象产生影响。

  • 等于赋值,并不会产生一个独立的对象单独存在,他只是将原有的数据块打上一个新标签,所以当其中一个标签被改变的时候,数据块就会发生变化,另一个标签也会随之改变。

Python 运行时间

在很多的时候我们需要计算我们程序的性能,这个时候我们常常需要统计程序运行的时间。下面我们就来说说怎么统计程序的运行时间。

datetime

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
#! /usr/bin/env python

# -*- coding:utf-8 -*-

import datetime
from functools import wraps
import logging

def run_time(func):
    @wraps(func)
    def __wrapper(*args, **kwargs):
        logging.getLogger(__name__)
        start = datetime.datetime.now()
        ret = func(*args, **kwargs)
        end = datetime.datetime.now()
        logging.debug('running time: {}'.format(end-start))
        return ret
    return __wrapper

time.now

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
#! /usr/bin/env python

# -*- coding:utf-8 -*-

import time
from functools import wraps
import logging

def run_time(func):
    @wraps(func)
    def __wrapper(*args, **kwargs):
        logging.getLogger(__name__)
        start = time.time()
        start_2 = time.clock()
        ret = func(*args, **kwargs)
        end = time.time()
        end_2 = time.clock()
        logging.debug('running time: {}'.format(end-start))
        logging.debug('running time2: {}'.format(end_2-start_2))
        return ret
    return __wrapper

timeit

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
#! /usr/bin/env python

# -*- coding:utf-8 -*-

import timeit
from functools import wraps
import logging

def run_time(func):
    @wraps(func)
    def __wrapper(*args, **kwargs):
        logging.getLogger(__name__)
        start = timeit.default_timer()
        ret = func(*args, **kwargs)
        end = timeit.default_timer()
        logging.debug('running time: {}'.format(end-start))
        return ret
    return __wrapper

方法对比

  • 方法二的精度比较高。方法一基本上是性能最差的。
  • **uix 平台方法 datetime.now() 和 time.time() 都包含了其他程序使用CPU的时间。方法 time.clock() 只计算了程序运行CPU的时间。
  • *nix 使用 time.time(),windows 使用 time.clock()。
  • timeit 可跨平台,推荐使用