Git Product home page Git Product logo

chinapnr / fishbase Goto Github PK

View Code? Open in Web Editor NEW
94.0 94.0 29.0 6.18 MB

自主开发、整理的一套 Python 基础函数库,涵盖 system 系统增强包、logger 日志记录增强包、file 文件处理增强包、 date 日期处理函数包、data 数据信息处理函数包、csv 处理增强函数包、crypt 加密/编码增强包等,可减少程序开发工作量、降低引用复杂度。

License: MIT License

Python 100.00%

fishbase's People

Contributors

halfapple avatar itaa avatar leochengkx avatar miaotianshi avatar mindjun avatar renmu123 avatar shenganzhang avatar wingfish avatar zhangmuqing avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

fishbase's Issues

根据请求参数字典拼接一个 HTTP 协议 GET 请求的地址参数

# URL ?与参数部分的地址拼接
# 输入:
# data: ? 参数名与 value 值组合的 dict
# 输出:
# urls:? 与参数拼接后的字符串
# 2018.3.8 edit by qingqing.xiang
def parse_url(data):
    item = data.items()
    urls = "?"
    for key, value in item:
        temp_str = key + "=" + value
        urls = urls + temp_str + "&"
    # 去掉最后一个&字符
    urls = urls[:len(urls) - 1]
    return urls

get_random_str,获取不同规则的随机字符串

背景

  • 在创建验证码,默认密码,文件名时需要生成随机字符串

步骤

  • random.sample(seq, n) 从序列seq中选择n个随机且独立的元素;
  • 使用string模块,获取不同类型的所有字符串,例如:string.ascii_letters、string.digits

举例

  • 获得长度为6,包含大小写和数字的字符串,可以使用如下方法:
>>> ''.join(random.sample(string.ascii_letters+string.digits,6)) 
lt5nCQ

判断整数的位数、字符串的长度否在限定长度内

判断整数的位数、字符串的长度否在限定长度内

jman和fishbase中都有判断是否参数为数字的方法,有时候还需要进一步判断长度,而且不同参数的长度规则不一样,所以抽象参数长度判断。检查的最大和最小长度均可以通过参数传入,通过len来进行进一步判断。
参考代码如下:

def check_number_len(check_param, min_length=None, max_length=None):
    """
    check_number_len,判断变量是否为数字,以及大小是否符合在要求长度内
    :param
        * check_param: (string) 需要判断的变量
        * min_length: (int) 变量的最小长度,默认为None
        * max_length: (int) 变量的最大长度,默认为None
    :return:
        * True 长度在要求长度内
        * False 长度不在要求长度内
    
    举例如下::
        print('--- check_number_len demo ---')
        # 定义待代理字符串
        check_param = '1234567890'
        # 最小长度
        min_length = 0
        # 最大长度
        max_length = 64
        check_number_len_result = check_number_len(check_param, min_length, max_length)
        print('check_number_len_result right result is : {}'.format(check_number_len_result))
        print('---'*5)
        
        check_param = 1234567890
        check_number_len_result = check_number_len(check_param, min_length, max_length)
        print('check_number_len_result right result is : {}'.format(check_number_len_result))
        print('---'*5)
        
        check_param = {'a':'123'}
        check_number_len_result = check_number_len(check_param, min_length, max_length)
        print('check_number_len_result type error result is : {}'.format(check_number_len_result))
        print('---'*5)
        
        check_param = '1234567890'*10
        check_number_len_result = check_number_len(check_param, min_length, max_length)
        print('check_number_len_result max_length error result is : {}'.format(check_number_len_result))
        print('---'*5)
        
        min_length = 20
        check_number_len_result = check_number_len(check_param, min_length, max_length)
        print('check_number_len_result min_length error result is : {}'.format(check_number_len_result))
        print('---'*5)
    执行结果::
        --- check_number_len demo ---
        check_number_len_result right result is : True
        ---------------
        check_number_len_result right result is : True
        ---------------
        check_number_len_result type error result is : False
        ---------------
        check_number_len_result max_length error result is : False
        ---------------
        check_number_len_result min_length error result is : False
        ---------------
    """
    # 判断如果不为数字类型,则返回False
    if type(check_param) != int and (not str(check_param).isdigit()):
        return False

    length = len(str(check_param))
    # 参数最大值不为 None 则判断是否超过最大值
    if max_length is not None and length > max_length:
        return False
    # 参数最小值不为 None 则判断是否小于最小值
    if min_length is not None and length < min_length:
        return False
    return True

使用装饰器为函数计时

背景

  • 需要判断某个方法执行时间,以便知道瓶颈所在,进行优化
  • 装饰器能在不改变原方法的基础上增加额外的计时功能

步骤

  • 在被装饰的函数调用前后分别计时,打印计时结果
  • 使用该装饰器来装饰被计时的函数

参考代码如下

import time
from functools import wraps


def fn_timer(fn):
    @wraps(fn)
    def wrapper(*args, **kwargs):
        start_time = time.time()
        
        result = fn(*args, **kwargs)
        
        end_time = time.time() - start_time
        
        print("func {0} run time is {1}".format(fn.__name__, str(end_time - start_time)))
        return result

    return wrapper

"""
    举例如下::
    
        print('--- fn_timer demo---')
        @fn_timer
        def get_sum(n):
            return sum(range(n))

        get_sum(100000)
        print('---')
        
    执行结果::

        --- fn_timer demo---
        func get_sum run time is -1528297140.3855693
        ---
"""

根据键ascii值升降序拼接字典值的列表

代码如下:

def get_sorted_values_list_from_dict(param_dict, order='asc'):
    """
    get_sorted_values_list_from_dict,根据键ascii值升降序拼接字典值的列表
    :param
        * param_dict: (dict) 字典信息
        * order: (string) 键排序规则 enum ('asc', 'desc'),默认 'asc' 升序
    :return:
        * sorted_values_list: (list) 字典值排序列表
    
    举例如下::
        print('--- get_sorted_values_str_from_dict demo ---')
        # 定义待处理字典
        dict2 = {'a_key':'a_value','1_key':'1_value','A_key':'A_value', 'z_key':'z_value'}
        # 升序结果
        order = 'asc'
        sorted_values_str = get_sorted_values_str_from_dict(dict2, order)
        print('sorted_values_list with asc order result is : {}'.format(sorted_values_str))
        # 降序结果
        order = 'desc'
        sorted_values_str = get_sorted_values_str_from_dict(dict2, order)
        print('sorted_values_list with desc order result is : {}'.format(sorted_values_str))
        print('---')
        
    执行结果::
        --- get_sorted_values_str_from_dict demo ---
        sorted_values_list with asc order result is :['1_value', 'A_value', 'a_value', 'z_value']
        sorted_values_list with desc order result is : ['z_value', 'a_value', 'A_value', '1_value']
        ---
    """
    key_list = list(param_dict.keys())
    reverse_flag = False if order == 'asc' else True
    key_list.sort(reverse=reverse_flag)

    sorted_values_list = [param_dict[x] for x in key_list]
    return sorted_values_list

remove_duplicate_elements,从序列中删除重复的项并且保持元素间的顺序不变

背景

#63 中只能删除列表中的重复元素,并且因为使用set,导致去重后的列表中的元素是无序的

步骤

  • 添加一个key用以指定一个函数将序列中的元素转换为可哈希类型,这样就可以检测重复元素了
  • 新建一个set类型,如果发现不是重复的元素,就将它加入到set中,以保持有序

举例

>>> list1 = [1,23,4,5,6,1,7,23]
>>> list(remove_duplicate_elements(list1))
[1, 23, 4, 5, 6, 7]
>>> list2 = [{'x':1, 'y':2}, {'x':1, 'y':3}, {'x':1, 'y':2}]
>>> list(remove_duplicate_elements(a, key=lambda d:(d['x'],d['y'])))
[{'x': 1, 'y': 2}, {'x': 1, 'y': 3}]

find_files 获取指定路径下所有文件的完整路径

获取指定路径下所有文件的完整路径

写脚本的过程中,经常遇到需要递归的获取某个路径下所有文件长路径,
该方法使用os模块的walk方法,获取目标路径下所有文件的长路径并以列表的形式返回。
参考代码如下:

def file_complete_path(path):
    """
    file_complete_path,获取指定路径下所有文件的完整路径
    
    :param
        * path: (string) 绝对路径
    :return:
        * file_list: (list) 文件的完整路径列表
    
    使用场景::
        测试脚本中,需要获取某个目录的所有文件
    
    举例如下::
    
        print('--- file_complete_path demo---')
        print('on windows')
        print(file_complete_path(os.path.split(os.path.realpath(__file__))[0]))
        print('on linux')
        print(file_complete_path(os.path.split(os.path.realpath(__file__))[0]))
        print('---')
        
    执行结果::
        
        --- file_complete_path demo---
        on windows
        ['E:\\Practice\\fishbase_issue\\jman_issue.py', 'E:\\Practice\\fishbase_issue\\path_test\\__init__.py']
        on linux
        ['/root/script/test.png', '/root/script/result.txt','/root/script/path_test/__init__.py']
        ---
        
    """
    file_list = []
    for root, dirs, files in os.walk(path):
        for name in files:
            file_list.append(os.path.join(root, name))
    return file_list

重写 TimedRotatingFileHandler 类,让logging支持多进程日志记录

背景

Python logging 是线程安全的,也就是说,在一个进程内的多个线程同时往同一个文件写日志是安全的。但是多个进程往同一个文件写日志不是安全的,参见官方文档: https://docs.python.org/2/library/logging.html#thread-safety

遇到的问题

kyoto项目多进程启动服务,在进行日志记录时常常会出现莫名少了一些日志,并且日志文件日志写入时间跟文件名不一致。

原因

当文件需要分割时,一个进程先更改了文件名,例如:test.log.2018.5.7,其他进程在进行文件分割时发现 test.log.2018.5.7 已经存在,就会删除,然后重新命名文件为 test.log.2018.5.7,这样之前进程记录的日志就丢失了。
具体进程不安全的代码如下,参见 Python logging模块中 TimedRotatingFileHandler类中 按照时间分割日志文件的部分

    def doRollover(self):
        """
        Do a rollover, as described in __init__().
        """
        if self.stream:
            self.stream.close()
            self.stream = None
        if self.backupCount > 0:
            for i in range(self.backupCount - 1, 0, -1):
                sfn = self.rotation_filename("%s.%d" % (self.baseFilename, i))
                dfn = self.rotation_filename("%s.%d" % (self.baseFilename,
                                                        i + 1))
                if os.path.exists(sfn):
                    if os.path.exists(dfn):
                        os.remove(dfn)
                    os.rename(sfn, dfn)
            dfn = self.rotation_filename(self.baseFilename + ".1")
            if os.path.exists(dfn):
                os.remove(dfn)
            self.rotate(self.baseFilename, dfn)
        if not self.delay:
            self.stream = self._open()

上面文件删除代码有多进程问题

    if os.path.exists(dfn):
        os.remove(dfn)

解决方法

参见 http://python.jobbole.com/87300/ 的方法,重写 TimedRotatingFileHandler 类中删除文件的逻辑改为:

#判断当 test.log.2018.5.7 不存在时重命名,而不是存在时删除文件
#dfn 为将要切割的文件名,eg test.log.2018.5.7, baseFilename 为当前记录日志文件名, eg test.log
if not os.path.exists(dfn) and os.path.exists(self.baseFilename):
         os.rename(self.baseFilename, dfn)

最终实现,通过子类SafeRotatingFileHandler,继承TimedRotatingFileHandler,实现以上修改,然后在fish_base模块中logger.py使用SafeRotatingFileHandler即可

class SafeRotatingFileHandler(TimedRotatingFileHandler):
    def __init__(self, filename, when='h', interval=1, backupCount=0, encoding=None, delay=False, utc=False):
        TimedRotatingFileHandler.__init__(self, filename, when, interval, backupCount, encoding, delay, utc)

    def doRollover(self):
        """
        do a rollover; in this case, a date/time stamp is appended to the filename
        when the rollover happens.  However, you want the file to be named for the
        start of the interval, not the current time.  If there is a backup count,
        then we have to get a list of matching filenames, sort them and remove
        the one with the oldest suffix.
        """
        if self.stream:
            self.stream.close()
            self.stream = None
        # get the time that this sequence started at and make it a TimeTuple
        currentTime = int(time.time())
        dstNow = time.localtime(currentTime)[-1]
        t = self.rolloverAt - self.interval
        if self.utc:
            timeTuple = time.gmtime(t)
        else:
            timeTuple = time.localtime(t)
            dstThen = timeTuple[-1]
            if dstNow != dstThen:
                if dstNow:
                    addend = 3600
                else:
                    addend = -3600
                timeTuple = time.localtime(t + addend)
        dfn = self.rotation_filename(self.baseFilename + "." +
                                     time.strftime(self.suffix, timeTuple))
        if not os.path.exists(dfn) and os.path.exists(self.baseFilename):
            os.rename(self.baseFilename, dfn)
        self.rotate(self.baseFilename, dfn)
        if self.backupCount > 0:
            for s in self.getFilesToDelete():
                os.remove(s)
        if not self.delay:
            self.stream = self._open()
        newRolloverAt = self.computeRollover(currentTime)
        while newRolloverAt <= currentTime:
            newRolloverAt = newRolloverAt + self.interval
        #If DST changes and midnight or weekly rollover, adjust for this.
        if (self.when == 'MIDNIGHT' or self.when.startswith('W')) and not self.utc:
            dstAtRollover = time.localtime(newRolloverAt)[-1]
            if dstNow != dstAtRollover:
                if not dstNow:  # DST kicks in before next rollover, so we need to deduct an hour
                    addend = -3600
                else:           # DST bows out before next rollover, so we need to add an hour
                    addend = 3600
                newRolloverAt += addend
        self.rolloverAt = newRolloverAt

判断参数是否为Unix时间戳

代码如下:

def is_timestamp(timestamp):
    """
    检查时间戳
    :param timestamp: 时间戳
    :return: 整形长度为10或者数字字符串并且长度为10时返回True
             其他情况返回False
    """
    if isinstance(timestamp, int):
        return True if len(str(timestamp)) == 10 else False
    elif isinstance(timestamp, str):
        if timestamp.isdigit() and len(timestamp) == 10:
            return True
    else:
        return False

增加 get_long_filename_with_sub_dir() 函数

增加 get_long_filename_with_sub_dir() 函数。

用于生成当前路径下的一级子路径的某个文件的长文件名。

\aaa.py
\bbb\ccc.conf

use in aaa.py

get_long_filename_with_sub_dir('bbb', 'ccc.conf')

then you can get

C:....\bbb\ccc.conf

改写get_md5方法,新增“文件”、“大文件”的MD5获取功能

相关代码如下:

def get_md5(string, mode='S'):
    """
    获取MD5方法
    :param string: 待hash的字符串,或者是文件的路径,
                如果是文件路径的时候需要设置mode='F'
    :param mode: 模式,'S', 获取字符串的MD5
                'F', 传入的str需要是文件的路径
                'B', 大文件的MD5,传入的str需要是文件的路径
    :return: 32位小写MD5值
    """
    if mode.upper() == 'S':
        return _get_str_md5(string)
    if mode.upper() == 'F':
        return _get_file_md5(string)
    if mode.upper() == 'B':
        return _get_big_file_md5(string)


def _get_str_md5(string):
    """
    获取一个字符串的MD5值
    :param string 待hash的字符串
    :return: 32位小写MD5值
    """
    m0 = hashlib.md5()
    m0.update(string.encode('utf-8'))
    result = m0.hexdigest()
    return result


def _get_file_md5(file_path):
    """
    获取一个文件的MD5值
    :param file_path 待hash的文件路径
    :return: 32位小写MD5值
    """
    with open(file_path, 'rb') as f:
        md5obj = hashlib.md5()
        md5obj.update(f.read())
        hash_o = md5obj.hexdigest()
        return hash_o


def _get_big_file_md5(file_name):
    """
    获取一个较大文件的MD5值
    :param file_name 待hash的文件路径
    :return: 32位小写MD5值
    """
    if not os.path.isfile(file_name):
        return
    hash_o = hashlib.md5()
    f = open(file_name, 'rb')
    while True:
        b = f.read(8096)
        if not b:
            break
        hash_o.update(b)
    f.close()
    return hash_o.hexdigest()

Remove useless file

Remove useless file

Such as:

.cache/* # 缓存文件
.coverage # coverage命令 自动生成的缓存文件
.coveragerc 
fishbase.egg-info/* # 使用 python setup.py sdist 打包时自动生成的,用来存放package信息的文件
readme # 空文件

remove_duplicate_elements,删除列表中的重复元素

背景

  • 需要删除列表中的重复元素,避免重复计算
  • 例如在权限控制中,需要删除一个角色权限列表中的重复权限

步骤

  • list转换成set,去除掉重复元素,再转换成list返回

举例

>>> list1 = ['a', 1, 2, 'c', 'd', 'sting', 2, 'd', 'string']
>>> remove_duplicate_elements(list1)
[1, 2, 'd', 'c', 'a', 'string']

is_dict_value_none,检查字典中是否有空的值

背景

  • post请求中需要判断body的参数字典是否存在空的值
  • 空的值包括空字符换、空数组,None等Pythonif判断为False的值

步骤

  • 使用 dict.keys() 方法取得所有的键,根据dict.get()来进行判断是否值为空

举例

>>> dict1 = {'A_key': 'A_value', 'a_key': 'a_value', 'z_key': 'z_value'}
>>> is_dict_value_none(dict1)
False
>>> dict1.update({'non_str':''})
>>> is_dict_value_none(dict1)
True
>>> dict1.update({'non_list':[]})
>>> is_dict_value_none(dict1)
True

GetBase64 获取字符串、文件的base64编码

背景

  • Base64是网络上最常见的用于传输8Bit字节码的编码方式之一
  • 我们的服务中涉及到文件的传输时常使用 Base64 进行编码

步骤

  • 参考 GetMD5 实现
  • 编写类,提供filestring,两个静态方法,分别对filestring进行Base64编码

举例

>>> GetBase64.string('hello world')
b'aGVsbG8gd29ybGQ='
>>> GetBase64.file('./test.png')
b'IyEvYmluL2Jhc2gKCmNkIC9yb290L3d3dy9zaW5nbGVfcWEKCm5vaHVwIC9yb290L2FwcC9weXRob24zNjIvYmluL2d1bmljb3JuIC1jIGd1bmljb3JuLmNvbmYgc2luZ2xlX3NlcnZlcjphcHAK'

fish_common.GetMD5 类的改进

背景

fish_common.GetMD5 中方法filebig_file 重复,另外对于文件大小的划分没有标准,多大为大文件?

建议

计算文件的md5值时均使用分块读入,使用big_file替换file

resize_image 用来对图片进行缩放

resize_image 用来对图片进行缩放

函数命名及参数暂定如下:
resize_image(img_path, mini_size=480, jpeg_quality=80) mini_size为最小边像素数,默认为480,jpeg_quality为jpeg的图片质量,默认为 80,即0.8

应用场景

用来对图片进行缩放

实现

需要引入新的依赖wand包, 详见 https://pypi.org/project/Wand/#files

get_group_list_data, 获取列表偏移量数据

背景

  • web中需要根据分页获取数据
  • 设计分页数据时,需要根据页码数以及每页数据量获取相应的数据

步骤

  • 提供所有的数据:all_records,页码数:page_number和每页数据量:page_size作为参数
  • page_number默认值为1page_size默认值为10
  • all_records类型是列表,page_numberpage_size类型为整形

举例

>>> all_records = [1,2,3,4,5]
>>> get_list_offset_data(all_records)
 [1, 2, 3, 4, 5]
>>> all_records1 = list(range(100))
>>> get_list_offset_data(all_records1, page_number=5, page_size=15)
[60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74]
>>> get_list_offset_data(all_records1, page_number=7, page_size=15)
[90, 91, 92, 93, 94, 95, 96, 97, 98, 99]

在干净的 python 环境安装 fishbase 报错找不到 dateutil

在一个完全干净的 python 环境安装 fishbase 出错,比较奇怪。

Collecting fishbase
Downloading https://files.pythonhosted.org/packages/a4/2b/45c42409bbb090cb5df5fbabda0b13189402070d00174f06755c51839d25/fishbase-1.0.13.tar.gz
Complete output from command python setup.py egg_info:
Traceback (most recent call last):
File "", line 1, in
File "/private/var/folders/y5/rt54swp16rvg9q5vfsdwp9jr0000gn/T/pip-install-n1alx_0y/fishbase/setup.py", line 3, in
from fishbase import version
File "/private/var/folders/y5/rt54swp16rvg9q5vfsdwp9jr0000gn/T/pip-install-n1alx_0y/fishbase/fishbase/init.py", line 21, in
from .fish_date import *
File "/private/var/folders/y5/rt54swp16rvg9q5vfsdwp9jr0000gn/T/pip-install-n1alx_0y/fishbase/fishbase/fish_date.py", line 4, in
from dateutil.relativedelta import relativedelta
ModuleNotFoundError: No module named 'dateutil'

----------------------------------------

Command "python setup.py egg_info" failed with error code 1 in /private/var/folders/y5/rt54swp16rvg9q5vfsdwp9jr0000gn/T/pip-install-n1alx_0y/fishbase/


在 setup.py 中是有

install_requires=['python-dateutil']

应该会自动安装 python-dateutil,也就是 dateutil

需要验证是否哪里有问题

新增hmax_sha256方法,通过秘钥获取消息的hash值

新增hmax_sha256方法,通过秘钥获取消息的hash值

HMAC是密钥相关的哈希运算消息认证码,HMAC运算利用哈希算法,以一个密钥和一个消息为输入,生成一个消息摘要作为输出。其实和我们在计算md5中加入盐值的原理是一致的 md5(message + salt),只不过HMAC是一个标准的算法,支持各种hash算法。比如我们这里使用的hmac_sha256, 其实就是在计算sha256的时候加入一个盐值,通常运用在一些具有较高安全要求的hash认证中。

参考代码如下:

def hmac_sha256(secret, message):
    """
    hmac_sha256
    :param secret: 密钥
    :param message: 消息输入
    :return: 长度为64的小写hex string 类型的hash值
    """
    hash_o = hmac.new(secret.encode('utf-8'),
                      message.encode('utf-8'),
                      digestmod=hashlib.sha256).digest().hex()
    return hash_o

fish_date.get_date_range 方法的改进

背景

  • get_date_range 方法用于获取本月、上月的第一天和最后一天日期,函数名修改为get_month_date_range更加清晰
  • 函数只能获取上月和本月的日期范围,不够通用

建议

  • 添加参数表示年月,函数返回该年月的日期范围,默认值为当前月份
  • 该参数可以是一个表示年月的字符串,例如: 201809,也可以是datetime类型,例如:datetime.datetime.now()
  • 添加一个参数表示分隔符,用以控制月份时间的格式,可以选择 -/ 作为分隔符

举例

>>> Fday, Lday = get_month_date_range('201809', delimiter='/')
>>> Fday
'2018/09/01'
>>> Lday
'2018/09/30'
>>> make_month = datetime.date(day=1, month=2, year=2018)
>>> Fday1, Lday1 = get_month_date_range(make_month)
>>> Fday1
'2018-02-01'
>>> Lady1
'2018-02-28'

fish_common.splice_url_params 方法的改进

背景

  • fish_common.splice_url_params方法用于拼接urlquery参数
  • Python内建包urllib提供了丰富的方法用于url的解析

建议

  • 可以使用urllib.parse.urlencode来进行query参数的拼接
  • 同样使用OrderedDict来保持字典的顺序

举例

>>> dict1 = {'spam': 1, 'eggs': 2, 'bacon': 0}
>>> od = OrderedDict(sorted(dict1.items()))
>>> urllib.parse.urlencode(od)
'bacon=0&eggs=2&spam=1'

sorted_objs_by_attr,对原生不支持比较操作的对象根据属性排序

背景

  • 想要在同一个类的实例之间做排序,但是他们并不支持比较操作

步骤

  • 内建的sorted函数可以接受一个用来传递可调用对象的参数key
  • sorted_objs_by_attr提供参数key,用来标识排序的规则

举例

>>> class User(object):
        def __init__(self, user_id):
            self.user_id = user_id
>>> users = [User(23), User(3), User(99)]
>>> [user.user_id for user in users]
[23, 3, 99]
>>> sorted_result = sorted_objs_by_attr(users, key='user_id')
>>> [user.user_id for user in sorted_result]
[3, 23, 99]

获得n个月前的日期(年月)

获得n个月前的日期(年月)

参考datetime.timedelta写法,方法获取月份增量后年月字符串。
主要用于根据年月信息查询数据时,年月信息的计算。
参考代码如下:

# months of a year
MONTH = 12


def previous_months_date(n):
    """
    previous_months_date,获得n个月前的日期(年月)
    
    :param
        * n: (int) n个月前,正整数
    :return:
        * date: (string) n个月前的年月
    
    使用场景::
        获取n个月前的日期,以便查询n个月前的数据
    
    举例如下::
    
        print('--- previous_months_date demo---')
        print('last month:', previous_months_date(1))
        print('10 months ago:', previous_months_date(10))
        print('---')
        
    执行结果::
        
        --- previous_months_date demo---
        last month: 201804
        10 months ago: 201707
        ---
        
    """
    now = datetime.now()
    all_months = now.year * MONTH + now.month
    all_months -= n
    year, month = divmod(all_months, MONTH)
    if month == 0:
        year -= 1
        month = MONTH
    date = ''.join(['%04d' % year, '%02d' % month])
    return date

查看字符串中是否含有中文

查看字符串中是否含有中文

字符串的校验需要检查特殊字符,目前封装中文和数字类型,后续预留扩展。
检查字符串是否包含特殊字符,用正则表达式进行判断,如果包含特殊字符,正则search的结果不为空。
参考代码如下:

def if_any_char_is_zh(p_str):
    """
    if_any_char_is_letter,字符串变量是否含有中文
    
    :param
        * p_str: (string) 需要判断的字符串变量
    :return:
        * True 含有中文字符串
        * False 不含有中文字符串
        
    举例如下::
    
        print('--- if_any_char_is_zh demo---')
        non_zh_result = if_any_char_is_zh('meiyouzhongwen')
        zh_result = if_any_char_is_zh('没有zhongwen')
        print('non_zh_result result is {}'.format(non_zh_result))
        print('zh_result result is {}'.format(zh_result))
        print('---')
        
    执行结果::
    
        --- if_any_char_is_zh demo---
        non_zh_result result is False
        zh_result result is True
        ---
    
    """
    zh_pattern = re.compile(u'[\u4e00-\u9fa5]+')
    
    if zh_pattern.search(p_str):
        return True
    else:
        return False

get_obj_attr_dict,获得对象的属性,并以字典的形式返回

背景

  • sqlalchemy查询结果是一个orm的对象,在取到orm对象之后需要取出表对象的字段属性
  • 如果使用特定表字段作为属性取值的做法不通用

步骤

  • 将对象中的public属性取出
  • 根据getattr获得对象的属性

举例

>>> class Table(object):
        def __init__(self, a, b, c):
            self._d = 12
            self.a = a
            self.b = b
            self.c = c

>>> table = Table(1,2,3)
>>> get_obj_attr_dict(table)
{'a': 1, 'b': 2, 'c': 3}

增加基础类bitools

增加基础类bitools,用于放置bi分析的方法。包含up_init()、change_card_number_to_mcc两个方法。

fish_file.check_sub_path_create 方法的改进

背景

  • fish_file.check_sub_path_create 方法用于生成必要的文件夹
  • 进行单元测试时,单元测试脚本所在文件夹为test;调用该方法的脚本所在文件夹为config,并且test与config目录同级,执行单元测试脚本时,会在test文件夹下生成log文件夹,而log文件夹应与test同级

建议

  • 修改check_sub_path_create 方法参数sub_path含义为子文件夹长路径
  • 在单元测试脚本中修改当前工作目录

check_obj_length,合并issue #28 #35,检查Python对象长度是否在限定范围内

背景

  • #28 #35 #两个问题都是判断python对象是否在限定长度内,可以进行合并

步骤

  • 方法包含默认参数,min_len=0, max_len=sys.maxsize,分别表示最小长度为0,最大长度为当前环境支持最大Int值
  • 使用 hasattr(obj, '__len__') 方法判断校验对象是否能取长度值,不能则返回 TypeError
  • 使用 min_len < len(obj) < max_len判断长度

举例

>>> list1 = 'abdaca'
>>> int1 = 12
>>> check_obj_length(list1)
True
>>> check_obj_length(list1, min_len=10, max_len=20)
False
>>> check_obj_length(int1)
TypeError: object type of <class 'int'> has no len()

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.