zoukankan      html  css  js  c++  java
  • 读源码

    背景

    近段时间笔者在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}
    ',
                # 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

    积跬步,至千里
  • 相关阅读:
    LeetCode Binary Tree Inorder Traversal
    LeetCode Populating Next Right Pointers in Each Node
    LeetCode Construct Binary Tree from Inorder and Postorder Traversal
    LeetCode Reverse Linked List II
    LeetCode Populating Next Right Pointers in Each Node II
    LeetCode Pascal's Triangle
    Palindrome Construct Binary Tree from Preorder and Inorder Traversal
    Pascal's Triangle II
    LeetCode Word Ladder
    LeetCode Binary Tree Zigzag Level Order Traversal
  • 原文地址:https://www.cnblogs.com/freedomlidi/p/12538459.html
Copyright © 2011-2022 走看看