Python 新式类介绍 (包含 new-style-and-classic-classes 文档翻译)

我们使用 Python 开发时, 会遇到 class A 和 class A(object) 的写法,
这在 Python2 里是有概念上和功能上的区别, 即经典类(旧式类)与新式类的区别,
英文上分别描述为 old-style(classic-style) 与 new-style.

通过搜索, 先查阅了三个资料链接:
官方文档
stackoverflow 解答
Python Types and Objects

根据 stackoverflow 答案引出的语言发明者 Guido 写的一篇文章:
The Inside Story on New-Style Classes
其总结可以作为官方解释:


  • low-level constructors named __new__() – 低级别的构造函数.
    Note: Python 的 class __init__ 并不是其他语言意义上的构造函数,
    在 new 创建实例后对实例属性初始化的函数.

  • descriptors, a generalized way to customize attribute access – 描述符.
    或者说描述符协议支持.descriptor protocol __get__, __set__ ,__delete__ 等,
    可以阅读 descriptor 文档

  • static methods and class methods - 静态方法和类方法

  • properties (computed attributes) – 属性访问 setter getter.

  • decorators (introduced in Python 2.4) – 装饰器.
    现在装饰器语法糖遍布各Python框架.

  • slots – 用户设置后可以限定实例的属性.
    在 Python2 中替代 __dict__, 可以节省近 2/3 内存, Python3 中可以
    不因为优化内存使用率而使用 slots, 因为 __dict__ 结构内存做了优化,
    Note: __dict__ 并不是 Python 意义上的内置的 dict, 其实是一个 proxy 类.

  • a new Method Resolution Order (MRO) – MRO 方法解析次序改变
    (由左递归改为C3算法)

另一个答案增加了一点:

  • super added – super 方法支持

关于C3MRO介绍, 可以点击链接

以下是翻译官方文档的资料 (Python2的内容)


Classes and instances come in two flavors: old-style (or classic) and new-style.
类和实例有两种方式: 旧式(或者经典) 和 新式类.

Up to Python 2.1 the concept of class was unrelated to the concept of type,
and old-style classes were the only flavor available. For an old-style class,
the statement x.__class__ provides the class of x, but type(x) is always
<type ‘instance’>. This reflects the fact that all old-style instances, independent
of their class, are implemented with a single built-in type, called instance.

一直到 Python 2.1 class 的概念不与 type 不一样, 旧式类是唯一的风格. 对于一个
旧式类里, 陈述句 x.__class__ 提供 x 的 class, 但是 type(x) 一直是 <type ‘instance’>.
它反应了所有旧式类实例, 独立于它们的 class, 它们通过一个简单的内建 type 实现,
被称作 实例 instance.

New-style classes were introduced in Python 2.2 to unify the concepts of class
and type. A new-style class is simply a user-defined type, no more, no less.
If x is an instance of a new-style class, then type(x) is typically the same as
x.__class__ (although this is not guaranteed – a new-style class instance is
permitted to override the value returned for x.__class__).

新式类在Python 2.2 引入, 用于统一 class 和 type 的概念. 一个新式类简化了用户
自定义的 type, 不多不少. 如果 x 是一个新式类的实例 instance , type(x) 典型地
与 x.__class__ (尽管这并不能保证 – 一个新式类被允许重写 x.__class__ 的值)一致.

Continue reading

求机器人可用跳跃方式 (Python 代码实现)

今天在 segmentfault 看到有趣的 编程题
描述如下:
” 一救援机器人有三种跳跃模式,可分别跳跃1m,2m,3m的距离,
请用程序实现该机器人行进n米路程时可用的跳跃方式。

程序语言不限,当距离n值足够大时,程序执行时间尽量小。

例:当距离为4米时,输出结果有7种:

1m,1m,1m,1m
1m,1m,2m
1m,2m,1m
1m,3m
2m,1m,1m
2m,2m
3m,1m

根据另一个用户 AUV_503516 提供的思路,
转会为数学的思考方式:
由条件定义 f(n) 为 n米的可用步骤序列.
f(1) = ( (1), )
f(2) = ( (1, 1), (2,) )
f(3) = ( (1, 1, 1), (1, 2), (2, 1), (3,) )

求 f(n), 每一跳跃可以使用的距离是 1, 2, 3
f(n) = f(1) + f(n-1) = f(2) + f(n-2) = f(3) + f(n-3)
= f(n-1) + f(1) = f(n-2) + f(2) = f(n-3) + f(3)

另外 f(delta) + f(n – delta) 有可能与 f(n – delta) + f(delta) 是用一个序列,
求 f(n) 需要去重.

由公式定义, 使用 Python代码实现如下:

Continue reading

Python3 使用 nonlocal keyword 实现保存状态的嵌套函数

前言:
最近阅读一本书《代码之髓》, 其中篇章:《7.3 静态作用域是完美的吗》
介绍了 Python3 的 nonlocal 关键字, 从12月将工作语言升级到Python3后,
才从一本讲述编程语言概念的书籍里知晓这个 nonlocal -_-b

首先写个小Demo:

Continue reading

Tornado底层学习 (1) — tornado ioloop start 的过程

写在分享前:
基于 tornado 开发 http 服务已经 3个月了, 通过 F2E.im 项目模仿, 也开始重新
重写整个业务代码(现成的开源代码并不适用复杂的使用场景, 数据库和缓存的
模块就需要使用新的模块组件), 在这个过渡阶段中, 也遇到诸如
tornado-restful 封装的库在 tornado.gen.coroutine 搭配并不成功的坑, 因此
有必要在使用第三方开源库时, 理解 tornado 底层的代码, 以便修改第三方源码
或者做代码结构调整, 合理开发. 在 Write Less, Do More 之前, 学会
Read and Appreciate Other’s work.

每一个 tornado 应用都会把 tornado ioloop 导入到代码中, 通过 ioloop 事件触发
机制, 处理 http request, 或者其他的协议的连接消息. tornado 在 Linux 系统中优先
使用 epoll 的封装, 基于 epoll 做事件处理.

Continue reading

Tornado web.authenticated 用户认证浅析

在Web服务中会有用户登录后的一系列操作, 如果一个客户端的http
请求要求是用户登录后才能做得操作, 那么 Web服务器接收请求时
需要判断该请求里带的数据是否有用户认证的信息.

使用 Tornado 框架开发Web服务, 框架里提供了tornado.web.authenticated
的 decorator 的辅助开发者做用户登录认证, 即开发者在实现一个 handler
(对应一个url资源, 继承于tornado.web.RequestHandler)时,
该 url的资源操作需要有用户认证或者登录为前提, 那么在资源请求的方法
覆写时(overwritten), 例如在 get 与 post 方法定义前以
tornado.web.authenticated 装饰,并且同时覆写 get_current_user
方法(RequestHandler只是定义空函数, 默认放回None). 在覆写之后,
RequestHandler 类的实例里 current_user 就会有值. current_user
在 tornado源码中是 getter setter的实现, 真正的成员变量是 _current_user
(稍后解析tornado里的源码). authenticated 即实现了 current_user 判断
这一过程来验证用户.

Continue reading

Python gevent + zmq + redis 搭配小实践

前些日子与 darkforday 交流时听说了 Python 中
基于 libev 封装的 gevent 库, 编码和性能上也是值得关注的.

简单介绍下 gevent (来自 tutorial ):

gevent is a concurrency library based around libev.
It provides a clean API for a variety of concurrency
and network related tasks.

翻译即:
gevent 是一个基于 libev的并发库, 提供了大量清楚简洁的并发和网络
相关任务的API

Continue reading

Redis set 何时会 Failed ( set 操作失败)

在使用 tornado-redis 库 开发时, set 操作总是返回 True,
除非连接异常, 现在就有一个疑问, 什么时候会返回 False
( Python 代码中).

首先来几个测试实验:
1. 存储其他类型, eg: lpush test_key 1
此时对 test_key 使用 set 操作, set test_key 1
2. 根据实验1步骤逆着来, 看看 push 操作是否成功

实验结果:
1. set操作可以覆盖 list操作

2. list操作无法覆盖set操作

仍然没有找到直接的依据, 那么就看看 stackoverflow 的答案.
当中提到文档里有说明:
” Status code reply: always OK since SET can’t fail. “

接着找 Redis SET 官方文档 阅读.
文档中并没有出现上面提到的话, 而说明的是:

Simple string reply: OK if SET was executed correctly.
Null reply: a Null Bulk Reply is returned if the SET
operation was not performed becase the user specified
the NX or XX option but the condition was not met.

即SET 操作没有执行时的情况下, 返回Null 回复 而在使用
NX 或者 XX 参数操作时:
set test_key 1 nx # 当 test_key 已存在时设置失败
set test_key 1 xx # 当 test_key 之前不存在时设置失败

如果仅仅是 set test_key 1 呢, 这个情况有没有失败的时候?
在上面提到的 stackoverflow 答案中, 提到了 redis.c 源码
maxmemory 选项如果设置, 则会限制 redis 存储的容量. 比如设置
一个极端条件 maxmemory 1 bytes, 假如之前有 redis 存储数据,
那么此时再使用 set 设置新值时, 效果如下图:

Redis set的文档:
Redis SET
Redis SETNX
Redis SETEX


最后提醒自己一下, 学会看文档, 不要猜测 -_-b

Python generator 与 yield 关键字

初次使用 tornado 开发服务器, 也进一步学习使用 Python 这门语言.
开始对 Python 的语法糖 decorator 感兴趣, tornado 的异步事件
处理使用了 tornado.gen.coroutine decorator, 要求代码中异步
执行的代码需要添加 yield 关键字在前面.
于是开始补基础, 使用 yield 这个关键字前需要先了解 Generator:

Python Generators 生成器, 参考自 Generators 文档

首先看简单的代码, 计算一个 100w 长度数组的和:

代码目的很明显, 而这里有个问题是, 100w 的整形数据
在内存中也不够用(假设自己的机器是4G内存).

重新调整下代码:

Continue reading