美文网首页
读源码 - stagesepx

读源码 - stagesepx

作者: 葛木瓜 | 来源:发表于2020-03-21 11:59 被阅读0次

    背景

    近段时间笔者在GitHub发现一个有意思的工具:stagesepx,经过这段时间的学习和研究,也基本将其应用于实际项目中,并产生了不错的效果,而本文旨在于记录stagesepx源码中诸多优雅的用法和思路,作以学习、积累

    一、概述

    stagesepx

    轻量化的、基于图像处理与机器学习的、全自动的视频分析工具。它提供了丰富的可定制性,能够根据你的实际需求分析视频并将其拆分为一系列阶段。在此之后,你可以清晰地得知视频包含了几个阶段、以及每个阶段发生了什么。而这一切都是自动完成的。

    更多内容请移步GitHub项目地址,此外stagesepx作者也很贴心的给出了开箱即用的实例:work_with_stagesepx

    二、源码中优雅的用法

    优雅的赋值/返回

    # 赋值/返回,当输入为空时,赋一个固定值,否则将输入赋值/返回
    def foo(input):
        return input or ‘null'
        # return input if input else ’null'
    

    递归方法新建多级目录

    import os
    # target_dir - 目标目录
    # exist_ok - 默认为False,表示当新建目录存在时抛异常
    os.makedirs(target_dir, exist_ok=True)
    

    更简洁的日志输出

    from loguru import logger
    
    logger.debug('this is a debug log')
    logger.info('this is a info log')
    logger.warning('this is a warning log')
    logger.error('this is a error log')
    logger.success('this is a success log')
    
    >>> output
    2020-02-26 17:55:12.681 | DEBUG    | __main__:<module>:38 - this is a debug log
    2020-02-26 17:55:12.681 | INFO     | __main__:<module>:39 - this is a info log
    2020-02-26 17:55:12.681 | WARNING  | __main__:<module>:40 - this is a warning log
    2020-02-26 17:55:12.681 | ERROR    | __main__:<module>:41 - this is a error log
    2020-02-26 17:55:12.681 | SUCCESS  | __main__:<module>:42 - this is a success log
    

    格式化输出的另一种姿势

    s = 'hello'
    print(f'{s}, world !')
    

    基于生成器读取多个文件

    上下文管理的生成器常常与with联合使用

    import contextlib
    
    # 上下文管理生成器
    @contextlib.contextmanager
    def op(fp):
        res = open(fp, 'rb')
        try:
            yield res
        finally:
            res.close()
    
    for ff in ['./ss.py', './test_adbutils.py']:
        with op(ff) as rf:
            res = rf.readline()
            while res:
                print(res)
                res = rf.readline()
    

    sorted进阶

    # key - 可自定义排序的基准,通常与lambda结合
    # reverse - bool类型,默认为False表示不颠倒(升序)
    l = [(1,3), (-1, 0), (3, 7)]
    l0 = sorted(l, key=lambda x: x[0])
    l1 = sorted(l, key=lambda x: x[1])
    
    >>> output
    l
    Out[21]: [(1, 3), (-1, 0), (3, 7)]
    l0
    Out[22]: [(-1, 0), (1, 3), (3, 7)]
    l1
    Out[23]: [(-1, 0), (1, 3), (3, 7)]
    

    入参类型限制

    python3.5引入
    通常方法的入参类型限制的格式如下

    # 不带初始值
    def func(a: int, b: str, c: bool):
       ......
    
    # 带初始值
    def func_with_args(
        a: int = 0,
        b: str = None,
        ):
        ......
    

    可引入typing,提供更多的类型

    # typing.Union[a, b, c] - 表示path入参类型为a/b/c其中一种即可,否则IDE warn提示
    
    # target_size: typing.Tuple[int, int] = None - 表示target_size为元组类型,且只能有两个整形
    
    # _hook_list: typing.List[BaseHook] = list() - 表示_hook_list为BaseHook的列表类型
    
    # As follows: 
    import typing
    
    class VideoObject(object):
        def __init__(
            self,
            path: typing.Union[bytes, str, os.PathLike],
            pre_load: bool = None,
            fps: int = None,
            *_,
            **__,
        ):
            self.path: str = str(path)
            self.data: typing.Optional[typing.Tuple[VideoFrame]] = tuple()
       ......
    

    绝对安全的路径

    # method 1
    import os
    
    PATH = lambda p: os.path.abspath(
        os.path.join(os.path.dirname(__file__), p)
    )
    
    # methon 2
    from pathlib import Path
    
    P = lambda p: Path(p).resolve()
    

    __file__ - 返回当前python文件所在的路径
    os.path.dirname(__file__) - 返回当前python文件所在的目录
    os.path.dirname/basename() - 分别为获取目标所在的目录和目标名
    os.path.split() - 拆分路径为:[目录/路径的头, 目录/路径的尾]
    os.path.splitext('./demo.log') - ('./demo', '.log')

    # os.path.dirname/basename()实现
    def basename(p):
    """Returns the final component of a pathname"""
    return split(p)[1]
    
    # Return the head (dirname) part of a path.
    def dirname(p):
        """Returns the directory component of a pathname"""
        return split(p)[0]
    

    josn.dumps/dump()进阶

    
    d = {'1': 2, '3': 4, '0': 3, '中国': 'ss', 'b': 'inf'}
    res = json.dumps(
        d,
        ensure_ascii=False, # 是否使用ascii
        skipkeys=True,  # 跳过非int str float bool None的key
        indent=None,    # 可设置缩紧层级
        separators=(', ', ': '),    # key vaules的分离格式
        sort_keys=True  # 是否按照key排序,注意排序时要求key类型统一
    )
    print(res)
    

    pathlib库

    参考:https://www.cnblogs.com/huangm1314/p/11318514.html

    from pathlib import Path
    
    Path.cwd()
    Out[23]: PosixPath('/Users/ssfanli/Myfolder/pyproj/owner/stagesepx')
    Path.home()
    Out[24]: PosixPath('/Users/ssfanli')
    res = Path('./demo.mp4')
    print(res, type(res))
    demo.mp4 <class 'pathlib.PosixPath'>
    
    # 属性
    # .nane - 文件名,包含拓展名
    # .stem - 仅文件名,不包含拓展名
    # .parent - 上一级目录名
    # .suffix - 获取文件的拓展名
    # .anchor - 哪个盘
    
    abs_p = p.resolve()
    abs_p
    PosixPath('/Users/ssfanli/Myfolder/pyproj/work/YSPTimeConsuming/main.py')
    abs_p.name
    'main.py'
    abs_p.stem
    'main'
    abs_p.parent
    PosixPath('/Users/ssfanli/Myfolder/pyproj/work/YSPTimeConsuming')
    abs_p.suffix
    '.py'
    abs_p.anchor
    '/'
    
    # iterdir(‘./’) - 返回'./'路径下的所有内容的生成器
    p = Path('./')
    all_p = p.iterdir()
    for i in all_p:
        print(i)
    >>> output
    pyrightconfig.json
    LICENSE
    test
    ......
    

    any & all

    any - 检查可迭代对象,只要有一个为真则返回True,否则返回False
    all - 检查可迭代对象,全部为真则返回True,否则返回False

    # 源码
    def all(*args, **kwargs): # real signature unknown
        """
        Return True if bool(x) is True for all values x in the iterable.
        
        If the iterable is empty, return True.
        """
        pass
    
    def any(*args, **kwargs): # real signature unknown
        """
        Return True if bool(x) is True for any x in the iterable.
        
        If the iterable is empty, return False.
        """
        pass
    
    

    while...continue <=> for...if

    # while循环
    a = 1
    while a < 10:
        if a % 2:
            print(a)
            a += 1
            continue
        print(f'{a} is even number')
        a += 1
    
    # for循环
        for a in range(10):
            if a % 2:
                print(a)
            else:
                print(f'{a} is even number')
            
    >>> output
    1
    2 is even number
    3
    4 is even number
    5
    6 is even number
    7
    8 is even number
    9
    
    # 注意⚠️
    # 无continue while循环依然继续,但是if循环之后会继续往下走,产生bug
    # continue,在此处的作用时结束if循环,重新回到while大循环中
    a = 1
    while a < 10:
        if a % 2:
            print(a)
            a += 1
            # continue
        print(f'{a} is even number')
        a += 1
    

    python私有变量

    “单下划线” 开始的成员变量叫做保护变量,意思是只有类对象和子类对象自己能访问到这些变量

    “双下划线” 开始的是私有成员,意思是只有类对象自己能访问,连子类对象也不能访问到这个数据,但依然可以通过如:a._A__a方式访问

    参考
    https://blog.csdn.net/debugm/article/details/8179482?depth_1-utm_source=distribute.pc_relevant.none-task&utm_source=distribute.pc_relevant.none-task

    class A:
        def __init__(self):
            self.a = 'a'
            self._a = '_a'
            self.__a = '__a'
            
    a = A()
    a.a
    Out[66]: 'a'
    a._a
    Out[67]: '_a'
    a.__a
    Traceback (most recent call last):
      File "/usr/local/lib/python3.7/site-packages/IPython/core/interactiveshell.py", line 3331, in run_code
        exec(code_obj, self.user_global_ns, self.user_ns)
      File "<ipython-input-68-52cb5731dd97>", line 1, in <module>
        a.__a
    AttributeError: 'A' object has no attribute '__a'
    a._A__a
    Out[69]: '__a'
    

    获取当前类名和方法名

    参考:https://www.cnblogs.com/yoyoketang/p/9231320.html

    import sys
    import inspect
    
    # inspect模块动态获取当前运行的函数名(或方法名称)
    def get_cur_func_name_2():
        return inspect.stack()[1][3]
    
    class A:
        def get_cul_cls_name(self):
            print(self.__class__.__name__)
        
        @staticmethod
        def get_cul_func_name():
            print(sys._getframe().f_code.co_name)
            print(f'cur_func_name: {get_cur_func_name_2}')
    

    父类调用子类重写的方法

    import inspect
    
    def get_cur_func_name():
        return inspect.stack()[1][3]
    
    
    class A:
        def __init__(self):
            self.a = 'a'
            self._a = '_a'
            self.__a = '__a'
    
        @staticmethod
        def t():
            print(f'cul_func_name_by_method 1: {sys._getframe().f_code.co_name}: ')
            print(f'cul_func_name_by_method 2: {get_cur_func_name()}')
    
        def tt(self):
            # TODO: 此时父类直接调用的是子类中重写的t()方法?
            self.t()
            print(self.__class__.__name__)
    
    
    class B(A):
        def __init__(self):
            super().__init__()
            self.b = 'b'
            self._b = '_b'
            self.__b = '__b'
    
        def t(self):
            super().t()
            ab_a_b = self.a + self.b + self._a + self._b
            # 子类无法访问父类中`__`开头的属性或方法,除非这样访问`self._A__a`
            # __a__b = self.__a + self.__b
            print(
                f'ab_a_b = {ab_a_b}\n',
                # f'__a__b = {__a__b}'
            )
    
    
    if __name__ == '__main__':
        b = B()
        b.tt()
    

    python图像比较

    参考:https://www.jianshu.com/p/0c3ac46af8fd
    SSIM的值范围[-1, 1],1代表完全相同

    hook特性

    非常nice的逻辑

    __str__ & __repr__

    https://baijiahao.baidu.com/s?id=1596817611604972751&wfr=spider&for=pc

    相关文章

      网友评论

          本文标题:读源码 - stagesepx

          本文链接:https://www.haomeiwen.com/subject/ylfqgctx.html