Python 中的异常

在 Python 中,EAFP 的风格很受青睐,这种写法能让代码更加简洁,还可以避免一些重复判断和多线程竞争的问题。为此,了解并熟练使用异常是很重要的。 异常类 首先来看一下 Python 内置的异常类(有省略): BaseException +-- SystemExit +-- KeyboardInterrupt +-- GeneratorExit +-- Exception +-- StopIteration +-- StopAsyncIteration +-- ArithmeticError +-- AssertionError +-- AttributeError +-- BufferError +-- EOFError +-- ImportError +-- LookupError +-- MemoryError +-- NameError +-- OSError +-- ReferenceError +-- RuntimeError +-- SyntaxError +-- SystemError +-- TypeError +-- ValueError +-- Warning BaseException 是所有异常的老祖宗,但很少会用到,通常我们只需要 Exception,比如自定义一个错误类型: # 命名习惯一般以 Error 结尾 class CustomError(Exception): def __init__(self, message, status): # 这里最好把参数都放进去,之后会统一存在 e....

09-15 · 3 min

Python Import 源码阅读

浅析一下 Python 中的 Import 机制。代码版本:3.11.0 - 4dd8219。 Module & Package Import 的对象就是各种各样的模块,即 Module。如何定义呢?官方文档这样描述: A module is a file containing Python definitions and statements. 实际上,Module 可以是 Builtin,也可以是 C extension,但最常见的存在形式还是 *.py 文件。 一个 Module 同时也可能是 Package,此时它从文件升级为目录,就可以拥有 Submodule 了。Package 分为两种: Regular package:这类 Package 必须包含一个 __init__.py 文件,代表了这个 Module(Package) 本身。 Namespace package:如果不存在 __init__.py,Python 会将其创建为此类 Package。Namespace package 的特殊之处在于同名的 Package 可以出现在多个目录下,而 Import 完成之后又可以统一使用。 不管是哪一种 Package,都会有 __path__ 属性,指向目录的路径。属性值是个列表,这对于 Namespace package 尤为重要。除此之外,一个 Module 还有: __name__:模块名称。 __file__:文件位置。 __package__:主要是为了在 Relative import 时计算起点位置。 如果是 Package 则设置为 __name__。 如果非 Package 则设置为 Parent package 的名称(Top-level 的 Module 应为空字符串)。 如果以脚本执行,那么取值为 None。 How to Import 一般情况下 Import 都是通过 import 关键字完成的,可分为两大类:...

09-10 · 6 min

Bottle 框架源码阅读

写这篇文章最开心的一点是终于可以用这张截图了: 相比名声在外的 Django/Flask/FastAPI,Bottle 可以说是非常不起眼了,甚至很多人并不知道它的存在。其实在很多方面,这个框架都极其优秀: 速度:截止到 2022-04-13,Bottle 在一众 Python Web 框架的测评中名列第二,要知道这可是十年以上的老前辈了。 易用性:Bottle 早在 Flask 之前就使用了装饰器来定义路由,此外还有全局可用的 Request/Response 对象。 文档:不仅将框架本身的使用讲得很清楚,还总结了很多 Web 场景下的解决方案。 代码质量:虽然为了 Python 2 做了不少兼容,但是代码很精炼,而且 Pythonic。 其他:Bottle 坚持单模块以及无第三方库依赖;仓库仍然在积极维护中。 换作几年前,我会一开始就使用并将 Bottle 研究透彻,而不是让自己淹没在 Django 浩瀚如烟的文档中。下面开始梳理 Bottle 源码的阅读理解。因为代码量不大,所以就直接看最新的版本了:0.11.1 - 5a6c620。 Web 框架的基本元素 参考 The Hitchhiker’s Guide to Python 的说法,一个 Web 框架要满足的基本功能: URL Routing Request and Response Objects Template Engine Development Web Server 从后端的角度来讲更重要的是 1、2、4 三项,其中 1 负责转发请求到对应的视图函数,2 是对 HTTP 协议元素的解析处理,而 4 决定了服务的部署方式和基础性能。 Bottle 在这几方面都做了很好的实现:路由上提供了通配符匹配和装饰器接口;请求和响应对象作为全局对象存在并保证了线程安全;Server 部署除了 Python 自带的 wsgiref 还支持绝大多数的 WSGI Server。...

04-22 · 6 min

Python 中的 TLS 是如何实现的

TLS(Thread Local Storage),或者说 Threadlocal,可以说是一种并发编程的常用模式,既实现了线程之间的资源隔离,又满足了全局变量的使用。 从 TLS 出发,这篇文章研究了 Python 中的 Threadlocal 是如何实现的,比如自带的 threading.local,再比如 Flask 框架中 Local 对象。 Why Threadlocal 先思考一下为什么要用 Threadlocal,这就不得不提到线程安全。Race condition 说到底是因为数据共享和非原子操作,这可以体现在函数的两种基本写法:一种是显式地传参(参数对象也可能变化?这也是为什么最好不要传递可变对象),没有共享自然安全;另一种就是全局对象,这么写既简化了函数签名,代码也比较清晰,缺点就是很容易出现线程不安全的问题,所以经常会和锁配合使用。 而 Threadlocal 就结合了两者的优点,在共享全局变量的同时,保证每个线程操作的都是自己独有的数据对象。 对比一下 Django 和 Flask 两大框架就会发现,前者总是在视图函数中显式声明 request 参数,而后者的只需要 import 一次就可以到处使用。在 Flask 的文档中,Armin Ronacher 也提及了这一点: For example, Flask uses thread-local objects internally so that you don’t have to pass objects around from function to function within a request in order to stay threadsafe. 不过 Flask 并没有直接使用 Python 内置的 threading....

04-11 · 4 min

Python Logging 源码分析

阅读了源码之后,我对 Python Logging 模块的几大疑惑都得到了解答: 为什么 Logger 和 Handler 都有 setLevel 方法? Logging 中会出现 Race condition 吗?(感觉都是很直接的 write 操作) 正式环境中想看日志又没办法动态调整 logLevel,感觉很鸡肋。 用起来好像还不如 print 方便。 会有性能问题吗? 日常使用 首先要了解下 Logging 的用法。 1. 配置 基本上有三种方式,代码、文件和字典。先看下如何用代码设置: import logging # create logger logger = logging.getLogger('simple_example') logger.setLevel(logging.DEBUG) # create console handler and set level to debug ch = logging.StreamHandler() ch.setLevel(logging.DEBUG) # create formatter formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') # add formatter to ch ch....

04-09 · 5 min