Python Python3
2021-01-28
Type Hint, 英文直译应该是输入提示。
动态类型语言有一个优点,同时也是缺点:不好做静态类型检查,IDE 或者其他开发工具很难根据代码去准确判断一个变量的类型。
Python 3.0 开始引入并逐渐完善类型注解(Type Annotation)则给 Python 静态类型检查提供了可能性。
PS: Python 运行时会忽略类型注解,不会给任何提示或警告。
PS: 之前的一些工具可以通过注释来做类型检查 (Type Comment),起到相同的作用。
PEP
SF 3107 [2006-12-02] (3.0 ) Function Annotations 开始引入函数注解
SF 3141 [2007-04-23] ( ) A Type Hierarchy for Numbers
SF 424 [2012-07-14] (3.4 ) A method for exposing a length hint
SF 451 [2013-08-08] (3.4 ) A ModuleSpec Type for the Import System
SP 484 [2014-09-29] (3.5 ) Type Hints 类型提示
IF 483 [2014-12-19] ( ) The Theory of Type Hints
IF 482 [2015-01-08] ( ) Literature Overview for Type Hints
SF 526 [2016-08-09] (3.6 ) Syntax for Variable Annotations
SA 544 [2017-03-05] (3.8 ) Protocols: Structural subtyping (static duck typing)
SA 560 [2017-09-03] (3.7 ) Core support for typing module and generic types
SA 563 [2017-09-08] (3.7 ) Postponed Evaluation of Annotations
SA 561 [2017-09-09] (3.7 ) Distributing and Packaging Type Information
SA 585 [2019-03-03] (3.9 ) Type Hinting Generics In Standard Collections
SA 586 [2019-03-14] (3.8 ) Literal Types
SA 591 [2019-03-15] (3.8 ) Adding a final qualifier to typing
SA 589 [2019-03-20] (3.8 ) TypedDict: Type Hints for Dictionaries with a Fixed Set of Keys
SA 593 [2019-04-26] (3.9 ) Flexible function and variable annotations
SA 604 [2019-08-28] (3.10) Allow writing union types as ``X | Y``
SA 613 [2020-01-21] ( ) Explicit Type Aliases 引入类型别名
SA 647 [2020-10-07] (3.10) User-Defined Type Guards 引入 TypeGuard 类型,缩小类型检查时的范围
S 649 [2021-01-11] ( ) Deferred Evaluation Of Annotations Using Descriptors
S 655 [2021-01-30] (3.10) Marking individual TypedDict items as required or potentially-missing
基础用法
如果担心类型检查会,可以使用 @no_type_check 装饰器。
def greeting(name: str) -> str:
return 'Hello ' + name
Vector = list[float]
def scale(scalar: float, vector: Vector) -> Vector:
return [scalar * num for num in vector]
Union
from typing import NoReturn
Address = tuple[str, int]
def connect(Union[Address, str]) -> NoReturn:
pass
Optional[T] 是 Union[T, None] 的简写。
def get_argument(name:str, default:Optional[str]=None) -> Union[str, None]:
pass
其他常用类型
Any
Callable
ClassVar
NewType
stub 文件
https://github.com/python/typeshed
Collection of library stubs for Python, with static types
类型检查工具
IDE,比如 PyCharm,可以配置类型检查。
VSCode 或者 Atom 之类的编辑器也可以通过插件支持类型检查。
- mypy

- pyright

- pytype

- pyre

参考:
mypy
参考资料与拓展阅读
Python Python3
2021-01-25
以前的字符串格式换方法
1. format 方法格式化
这种方法用的不多(对应 string.Formatter)。
Format String Syntax
replacement_field ::= "{" [field_name] ["!" conversion] [":" format_spec] "}"
field_name ::= arg_name ("." attribute_name | "[" element_index "]")*
arg_name ::= [identifier | digit+]
attribute_name ::= identifier
element_index ::= digit+ | index_string
index_string ::= <any source character except "]"> +
conversion ::= "r" | "s" | "a"
format_spec ::= [[fill]align][sign][#][0][width][grouping_option][.precision][type]
fill ::= <any character>
align ::= "<" | ">" | "=" | "^"
sign ::= "+" | "-" | " "
width ::= digit+
grouping_option ::= "_" | ","
precision ::= digit+
type ::= "b" | "c" | "d" | "e" | "E" | "f" | "F" | "g" | "G" | "n" | "o" | "s" | "x" | "X" | "%"
print('{} {}'.format('Hello', 'World'))
print('There are three people in my family: {0}, {1}, and I, and I love my {0} a litte more.'.format('father', 'mother'))
2. 模板字符串格式化
这种方法我只在文档中看到,从没真的用过。
PS: 被废弃的 PEP 215 曾建议采用 $'a = $a, b = $b' 这种语法。
Template strings
from string import Template
Template('$who likes $what').substitute(who='tim', what='kung pao')
Template('$who likes $what').safe_substitute({'who': 'time'})
3. 百分号格式化
这应该是现在的最主流的字符串格式化方式。
printf-style String Formatting
print('Hello %s' % 'World')
print('action %s cost %.3f seconds' % ('download', 0.123456789))
print('%(language)s has %(number)03d quote types.' % {'language': "Python", "number": 2})
Python 3.6 新加入 f-string
Formatted string literals
f_string ::= (literal_char | "{{" | "}}" | replacement_field)*
replacement_field ::= "{" f_expression ["="] ["!" conversion] [":" format_spec] "}"
f_expression ::= (conditional_expression | "*" or_expr)
("," conditional_expression | "," "*" or_expr)* [","]
| yield_expression
conversion ::= "s" | "r" | "a"
format_spec ::= (literal_char | NULL | replacement_field)*
literal_char ::= <any code point except "{", "}" or NULL>
从 format 方法格式化语法中复用了很多 (格式化和 conversion 部分), 不过变得更强大了。
主要是里面支持条件语句,表达式 (包括 yield)。
a = 3.1415926
f'{a}'
f'{a:.2f}'
f"{1 + 1}", f"{{1 + 1}}", f"{{{1 + 1}}}"
# ('2', '{1 + 1}', '{2}')
注意:f-sting 里面不能使用反斜杠转义!
f'{John\'s}'
# SyntaxError: f-string expression part cannot include a backslash
r, s, a
!r -> repr()
!s -> str()
!a -> ascii()
PS: ascii 方法是 Python 3 引入,和 repr 相似,但是 ascii 方法仅使用 ASCII 字符。
例如:a = '中国'; print(f'{a!a} {ascii(a)}') 输出 '\u4e2d\u56fd' '\u4e2d\u56fd'。
print(f'{a!r}')
=
有人提议加入 !d 表示输出表达式本身,然后加上等于号,加上计算值,例如 f'1 + 1!d' => 1 + 1=2。
后来实现成了这样:
a = 3.14
b = 1
print(f'a + b=') # a + b=4.140000000000001
print(f'a + b = ') # a + b = 4.140000000000001
很有趣!
参考资料与拓展阅读
Python Python3
2021-01-24

Python SQLAlchemy asyncio 协程
2020-11-22
这是大神 zzzeek 2015 年发表的一篇文章,详细介绍了关于 SQLAlchemy 与异步编程的一些事情。解答了我关于如何实现异步编程的一些疑惑。
我曾反复阅读这篇文章好多遍,以求能够更加准确地领会到大佬阐述的意思。我认为每个 Python 的使用者都应该阅读阅读。
Python JobQueue
2020-10-19

RQ (Redis Queue) is a simple Python library for queueing jobs and processing them in the background with workers. It is backed by Redis and it is designed to have a low barrier to entry. It can be integrated in your web stack easily.
翻译:RQ (Redis Queue)是一个简单的 Python 库,用于将作业排队并在后台与 worker 一起处理它们。它由 Redis 支持,其设计具有较低的进入门槛。它可以很容易地集成到您的 web 堆栈中。
This project has been inspired by the good parts of Celery, Resque and this snippet, and has been created as a lightweight alternative to existing queueing frameworks, with a low barrier to entry.
示例
启动 worker 进程:
rq worker
rq worker --url redis://:secrets@example.com:1234/9
项目中添加任务:
from redis import Redis
from rq import Queue
# 队列
q = Queue(connection=Redis())
# 添加任务
from my_module import count_words_at_url
result = q.enqueue(count_words_at_url, 'http://nvie.com')
# 关于重试
from rq import Retry
# 失败之后立即重试
queue.enqueue(say_hello, retry=Retry(max=3))
# 失败之后间隔指定时间重试
queue.enqueue(say_hello, retry=Retry(max=3, interval=[10, 30, 60]))
获取结果:
参考资料与拓展阅读
Python
2020-09-27
平时删除文件都是 os.unlink 和 os.remove 中随便选一个,今天突然想看看这两个方法有什么不一样。
remove 和 unlink 实际上来自 Modules/posixmodule.c
可以看到这两个方法实际上相同。
/*[clinic input]
os.remove = os.unlink
Remove a file (same as unlink()).
If dir_fd is not None, it should be a file descriptor open to a directory,
and path should be relative; path will then be relative to that directory.
dir_fd may not be implemented on your platform.
If it is unavailable, using it will raise a NotImplementedError.
[clinic start generated code]*/
static PyObject *
os_remove_impl(PyObject *module, path_t *path, int dir_fd)
/*[clinic end generated code: output=a8535b28f0068883 input=e05c5ab55cd30983]*/
{
return os_unlink_impl(module, path, dir_fd);
}
Python 3 的 Path 对象中也有一个 unlink 方法(pathlib.Path.unlink):
def unlink(self, missing_ok=False):
"""
Remove this file or link.
If the path is a directory, use rmdir() instead.
"""
try:
os.unlink(self)
except FileNotFoundError:
if not missing_ok:
raise
顺便对删除目录做一个整理:
# os.remove(path: StrOrBytesPath, *, dir_fd: int | None = ...)
# os.unlink(path: StrOrBytesPath, *, dir_fd: int | None = ...)
os.mkdir(path: StrOrBytesPath, mode: int = ..., *, dir_fd: int | None = ...)
os.rmdir(path: StrOrBytesPath, *, dir_fd: int | None = ...)
os.makedirs(name: StrOrBytesPath, mode: int = ..., exist_ok: bool = ...)
os.removedirs(name: StrOrBytesPath)
pathlib.Path.rmdir -> os.rmdir
shutil.rmtree(path, ignore_errors=False, onerror=None, *, dir_fd=None)
Python Tornado
2020-09-09

Python
2020-07-01
来自 Quora,原文标题:Why is Python so popular despite being so slow?
说明:这里谈的 Python,很大程度上说的是 CPython 这个标准实现,而不是 Python 这门语言。
Python GUI
2020-05-29
gooey
可以快速实现命令行 GUI 化。
Python 日志 logging
2020-05-21
logging 内部的服务级别:
DEBUG 10
INFO 20
WARNING 30
ERROR 40
CRITICAL 50
根据使用习惯,INFO 是重要信息,DEBUG 是普通信息。线上也是开到 DEBUG 级别。
然后调试信息也是通过 DEBUG 服务打印,然后通过 conf.DEBUG_MODE 来区分是不是要打印这种 DEBUG 级别的调试信息。
觉得不甚方便,想了一下,有两种思路:
- 将普通信息也通过 INFO 日志打印,在日志内容中插入部分标识来表示是重要信息,比如 “&NOTICE”。
- 增加一个 TRACE 级别日志。
方案一感觉相对合理一些,但是对于已有项目还是方案二好。
实现
def trace(self, message, *args, **kwargs):
if self.isEnabledFor(TRACE):
self._log(TRACE, message, args, **kwargs)
TRACE = logging.TRACE = 5
logging.addLevelName(TRACE, 'TRACE')
logging.Logger.trace = trace
参考:syslog 日志级别
- EMERG:系统不可用
- ALERT:需要立即采取行动
- CRIT:关键错误
- ERR:一般错误
- WARNING:警告
- NOTICE:一般通知
- INFO:信息性消息
- DEBUG:调试级别的消息
参考:nodejs winston 日志级别
- error:错误
- warn:警告
- info:一般信息
- http:HTTP 请求
- verbose:详细信息
- debug:调试信息
- silly:非常详细的调试信息
参考:java log4j 日志级别
- FATAL:致命
- ERROR:错误
- WARN:警告
- INFO:信息
- DEBUG:调试
- TRACE:跟踪