AttrDict
字典转换为可以用属性访问的模式

1
2
3
4
5
6
> from attrdict import AttrDict
> a = AttrDict({'foo': 'bar'})
> a.foo
'bar'
> a['foo']
'bar'

Web 框架

molten: 用于使用 Python 3.6 及更高版本构建 HTTP API 的最小,可扩展,快速且高效的框架。
responder:

Flask

Sanic

GitHub stars

Vibora

GitHub stars

实用工具

You-Get: 优酷、YouTube 等网站视频下载

lulu: 各网站视频下载,fork 自 you-get。其作者新开 Go 语言编写同功能库 annie

代码调试

icecream:打印调试,优化 print() 函数。

数据验证

voluptuous:数据验证库 GitHub stars

Shell

xonsh: xonsh 是一种 shell 语言和命令提示符。 与其他 shell 不同,xonsh 基于 Python,添加了额外的语法,可以轻松调用子进程命令,操作环境和处理文件系统。 xonsh 命令提示符为用户提供对 xonsh 语言的交互式访问。

在 Python 中一切皆对象,就是否能通过函数操作符 () 来调用,我们可将对象分为可调用对象和不可调用对象。我们通过 def 自定义的函数是可调用对象;一般情况下,类的实例不是可调用对象。可以用内置函数 callable() 来检测目标是否可调用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
>>> class Test: pass
>>> callable(Test)
True
>>> hasattr(Test, '__call__')
True
>>> t = Test()
>>> callable(t)
False
>>> t()
TypeError: 'Test' object is not callable
>>> def func(): pass
>>> callable(func)
>>> True
>>> dir(func)
[...more...,__call__,...more...]
>>> dir(func.__call__)
[...more...,__call__,...more...]
>>>
阅读全文 »

在《流畅的 Python》一书杂谈中推荐使用类来编写装饰器。就我的理解来说,简单的装饰器仍然使用传统的函数来定义比较好。而复杂的、涉及很多状态的装饰器,用类的方法定义,结构会更加清晰,容易扩展。

阅读全文 »

asyncio 模块提供了使用协程编写单线程并发代码,通过套接字和其他资源复用 I​​/O 访问,运行网络客户端和服务器以及其他相关原语的基础结构。

异步编程比传统的 “顺序” 编程更复杂。

官方文档:asyncio - 异步 I/O、事件循环、协程和任务

Python 中的并发主要涉及三种:多进程、多线程、协程。

相信提到多线程和多进程大家首先想到的是标准库 threadingmultiprocessing。在 Python 中,这两个标准库几乎成了 Python 并发编程的代名词。对于这两个标准库,无论网络还是书籍,可查阅的资料非常丰富,我们不多做介绍。本文主要介绍从 Python3.2 开始被纳入标准库的 concurrent.futures,它是对 threadingmultiprocessing 进一步的封装和高级别的抽象,并暴露出统一的接口,帮助开发者非常方便的实现异步调用。最初的提案见于 PEP 3148

阅读全文 »

于控制台中运行如下例子:

例 ①:无闭包

1
2
3
4
5
6
7
8
var x = [];
for (var i = 0; i < 9; i++) {
setTimeout(function() {
x[i] = i;
}, 1000);
}
console.log(i);
console.log(x); // ▶(10) [empty × 9, 9]

例 ②:闭包

1
2
3
4
5
6
7
8
9
10
11
var y = [];
function doSetTimeout(i) {
setTimeout(function() {
y[i] = i;
}, 1000);
}
for (var i = 0; i < 9; i++) {
doSetTimeout(i);
}
console.log(i);
console.log(y); // ▶(9) [0, 1, 2, 3, 4, 5, 6, 7, 8]
阅读全文 »

概述

Python 3.5 之前的协程是靠 yield 实现的,和生成器 yield 共用关键字,语义不明确,使用比较晦涩,很少有人使用(起码大多数爬虫程序用的是多线程)。Python 3.5 增加了 asyncawait 关键字(保留关键字,未正式确定,Python 3.7 正式确定),作为定义协程的专用关键字。协程才正式变得优雅可用,不过它的基础仍是基于 yield 的协程。作为基础,我们对其做一下简述。

与生成器的不同

协程、线程、进程的区别不在赘述。简述协程和生成器的区别:

  • 生成器是用于生成供迭代的数据
  • 协程是数据的消费者
  • 虽然在协程中会使用 yield 产生值,但这与迭代无关。

也就是说,协程只是和生成器 “碰巧” 共用了 yield 关键词,其他无任何关联。

协程基础

下面我们来分析下《流畅的 Python》中协程的一个例子:

简单实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
>>> def simple_coroutine():     # ①
... print('-> 协程开始')
... x = yield # ②
... print('-> 协程接收:', x)
...
>>> my_coro = simple_coroutine()
>>> my_coro # ③
<generator object simple_coroutine at 0x0000023E1AC54150>
>>> next(my_coro) # ④
-> 协程开始
>>> my_coro.send(42) # ⑤
-> 协程接收: 42
Traceback (most recent call last): # ⑥
File "<stdin>", line 1, in <module>
StopIteration
>>>
  • ① 协程使用生成器函数定义:定义体重有 yield 关键字。
  • ④ 首选要调用 next(...) 函数,因为生成器还没有启动,没在 yield 语句处暂停,所以一开始发送数据。也可以用给生成器发送 None 代替:my_coro,send(None),专业术语叫预激生成器

可用 inspect.getgeneratorstate(...) 查看协程状态:

1
2
import inspect
inspect.getgeneratorstate(...)

具体有协程从创建到结束有四种状态:

  • GEN_CREATED: 等待执行
  • GEN_RUNNING: 解析器正在执行
  • GEN_SUSPENDED: 在 yield 表达式出暂停执行
  • GEN_CLOSED: 执行结束

只有在多线程应用中才能看到 GEN_RUNNING 状态。此外,生成器对象在自己身上调用 getgeneratorstate 函数能看到,可自行测试。

阅读全文 »

在《流畅的 Python》一书的 7.4 节讲《变量作用域规则》一节,提到如下例子:

1
2
3
4
5
6
7
8
9
>>> def f1(a):
print(a)
print(b)
>>> f1(3)
3
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 3, in f1
NameError: name 'b' is not defined

上述示例中错误很明显。我们再看下一个例子:

1
2
3
4
5
6
7
8
9
10
11
12
>>> b = 6
>>> def f2(a):
... print(a) # ①
... print(b) # ②
... b = 9
...
>>> f2(3)
3
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 3, in f
UnboundLocalError: local variable 'b' referenced before assignment
阅读全文 »

一个管理系统一般需要附带一套权限系统,来控制什么样的人可以访问什么资源。如果你的系统是一个网站后台,如公司的 OA 系统,那么权限系统的设计应该在前后端分别设计。

前端权限,主要来控制页面是否可访问,页面上组件元素是否显示、是否可点击、可编辑、可输入等。根据颗粒度不同:

  • 路由(或页面)权限
  • 页面组件权限

后端权限,主要来控制 API 是否可访问,在细化到底层,API 对应的数据库的增、删、查、改。根据颗粒度不同:

  • Restful API 权限
  • 数据权限

对于数据权限,如果我们以 提交人 为标准来思考权限:

  • 增:是否可以添加数据,可以添加数据的那些字段。
  • 删:是否可以删除数据,是否可删除自己添加的数据,还是可以删除别人(任何人、某类人)添加的数据也可以删除。
  • 查:是否可以查看数据,是可以查看自己添加的数据,还是可以查看别人(任何人、某类人)添加的数据;可以查看数据的哪些字段,禁止查看数据的哪些字段。
  • 改:是否可以更改数据,是可以更改自己添加的数据,还是可以更改别人(任何人、某类人)添加的数据;可以更改数据的哪些字段,禁止查看数据的哪些字段。

一般情况下,权限系统的设计部分根据人员来分配,而会根据给人员分配角色,通过给不同角色赋予不同权限,来限定不同人员的权限范围。

以上是粗略的分析,针对具体的业务还要具体分析。最近做一个学校教务 CRM 系统,除了 admin,里面按照工作逻辑,角色有如下划分:

  • 网站管理员:统筹网站控制,基础信息录入
  • 管理者(领导):可以查看所有统计数据
  • 班主任:需要录入大量数据,查看本班级数据,公共数据
  • 教师:需要录入大量数据,查看自己录入的数据,公共数据
  • 其他:需要录入少量数据,查看自己录入的数据,公共数据

在数据层面主要划分为:

  • 自己录入的数据
  • 班级数据集合
  • 公共数据集合

同时,大部分数据集,增删查改有时间限制,一般过了某个时间点数据只能查看,不能增删改,若需变动,向 管理者 申请通过 网站管理员 变动。

以此为背景,设计背后的权限逻辑,及具体的代码实现,做到代码和权限控制低耦合。

(开坑,待续……)

术语:

  • MRO: 方法解析顺序 (Method Resolution Order, MRO)

环境:Python 3(Python 2 会有差别)

super()

首选可以参看 Python 3 版本中 super() 说明文档: PEP 3135 – New Super
开篇即说:

1
super()

等价于:

1
super(__class__, <firstarg>)

super 指的是 MRO 中的下一个类!

1
2
3
4
5
6
7
8
9
10
11
class A:
def spam(self):
print('A.spam')
super().spam()

class B:
def spam(self):
print('B.spam')

class C(A, B):
pass

下面我们测试说明。首先实例化 A 类,然后调用它的 spam 方法,很显然,会出错:

1
2
3
4
5
6
7
>>> a = A()
>>> a.spam()
A.spam
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 4, in spam
AttributeError: 'super' object has no attribute 'spam'

那么,我们再做如下测试:

1
2
3
4
>>> c = C()
>>> c.spam()
A.spam
B.spam

你可以看到在 A 类中使用 super().spam() 实际上调用的是跟 A 类毫无关系的 B 类中的 spam() 方法。 这个用 C类 的 MRO 列表就可以完全解释清楚了:

1
2
>>> C.__mro__
(<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>)

在 MRO 顺序中, B 类时 A 类的下一个类即为 B 类。

参考:

0%