背景

近段时间笔者在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的更多相关文章

  1. [一起读源码]走进C#并发队列ConcurrentQueue的内部世界

    决定从这篇文章开始,开一个读源码系列,不限制平台语言或工具,任何自己感兴趣的都会写.前几天碰到一个小问题又读了一遍ConcurrentQueue的源码,那就拿C#中比较常用的并发队列Concurren ...

  2. Java读源码之ReentrantLock

    前言 ReentrantLock 可重入锁,应该是除了 synchronized 关键字外用的最多的线程同步手段了,虽然JVM维护者疯狂优化 synchronized 使其已经拥有了很好的性能.但 R ...

  3. Java读源码之ReentrantLock(2)

    前言 本文是 ReentrantLock 源码的第二篇,第一篇主要介绍了公平锁非公平锁正常的加锁解锁流程,虽然表达能力有限不知道有没有讲清楚,本着不太监的原则,本文填补下第一篇中挖的坑. Java读源 ...

  4. Java读源码之CountDownLatch

    前言 相信大家都挺熟悉 CountDownLatch 的,顾名思义就是一个栅栏,其主要作用是多线程环境下,让多个线程在栅栏门口等待,所有线程到齐后,栅栏打开程序继续执行. 案例 用一个最简单的案例引出 ...

  5. 阅读源码很重要,以logback为例,分享一个小白都能学会的读源码方法

    作为一个程序员,经常需要读一些开源项目的源码.同时呢,读源码对我们也有很多好处: 1.提升自己 阅读优秀的代码,第一可以提升我们自身的编码水平,第二可以开拓我们写代码的思路,第三还可能让我们拿到大厂 ...

  6. 读源码【读mybatis的源码的思路】

    ✿ 需要掌握的编译器知识 ★ 编译器为eclipse为例子 调试准备工作(步骤:Window -> Show View ->...): □ 打开调试断点Breakpoint: □ 打开变量 ...

  7. 跟大佬一起读源码:CurrentHashMap的扩容机制

    并发编程——ConcurrentHashMap#transfer() 扩容逐行分析 前言 ConcurrentHashMap 是并发中的重中之重,也是最常用的数据结构,之前的文章中,我们介绍了 put ...

  8. 跟着大彬读源码 - Redis 1 - 启动服务,程序都干了什么?

    一直很羡慕那些能读 Redis 源码的童鞋,也一直想自己解读一遍,但迫于 C 大魔王的压力,解读日期遥遥无期. 相信很多小伙伴应该也都对或曾对源码感兴趣,但一来觉得自己不会 C 语言,二来也不知从何入 ...

  9. 跟着大彬读源码 - Redis 3 - 服务器如何响应客户端请求?(下)

    继续我们上一节的讨论.服务器启动了,客户端也发送命令了.接下来,就要到服务器"表演"的时刻了. 1 服务器处理 服务器读取到命令请求后,会进行一系列的处理. 1.1 读取命令请求 ...

随机推荐

  1. Java 中Enum的使用与分析

    https://www.cnblogs.com/softidea/p/3760235.html https://blog.csdn.net/qq_27093465/article/details/52 ...

  2. 很全很全的 JavaScript 模块讲解

    模块通常是指编程语言所提供的代码组织机制,利用此机制可将程序拆解为独立且通用的代码单元.所谓模块化主要是解决代码分割.作用域隔离.模块之间的依赖管理以及发布到生产环境时的自动化打包与处理等多个方面. ...

  3. Linux系统添加新用户

    Linux系统中一般不直接使用root用户进行操作,需要添加新的用户. 首先,查看当前系统已有的用户 cat /etc/passwd 查看用户组 cat /etc/group 其次,添加想要的用户组和 ...

  4. R语言的xtabs函数

    今天在做一个列联表独立性检验的时候,总是无法处理好要求的数据类型,偶然的机会,看到了xtabs()函数,感觉很适合用来做列联表,适合将一列数据转换成列联表. shifou <- c(" ...

  5. VMware虚拟机里安装CentOS 6.3图文教程

    著名服务器版本 CentOS 6.3 已经发布 http://www.centoscn.com/CentosSoft/iso/2013/0720/370.html CentOS 6.3 网易镜像下载: ...

  6. Python---11模块

    在计算机程序的开发过程中,随着程序代码越写越多,在一个文件里代码就会越来越长,越来越不容易维护. 为了编写可维护的代码,我们把很多函数分组,分别放到不同的文件里,这样,每个文件包含的代码就相对较少,很 ...

  7. MyBatis之pageHelper分页插件

    1.先导入Maven,jar包依赖 <dependency> <groupId>com.github.pagehelper</groupId> <artifa ...

  8. Annotation标注

    # View more python tutorials on my Youtube and Youku channel!!! # Youtube video tutorial: https://ww ...

  9. Gnome Ubuntu16安装Nvidia显卡396驱动,CUDA9.2以及cudnn9.2

    深度学习环境配置,安装Nvidia显卡驱动,CUDA以及cudnn OS:ubuntu 16.04;driver: nvidia 396;CUDA: 9.2cudnn: 9.2 卸载原有Nvidia驱 ...

  10. CSS的五种定位方式

    CSS中一共有五种定位: position:static:默认值 position:absolute:绝对定位 position:relative:相对对定位 position:fixed:固定定位 ...