空类与数据记录
Python 的类可以非常轻量——甚至只包含 pass。这种"空类"在需要简单数据容器时非常有用,它通过属性访问语法(obj.x)提供了比字典更好的可读性。
空类作为数据容器
class Employee(object):
pass
john = Employee()
john.name = "John Doe"
john.dept = "computer lab"
john.salary = 1000
print john.name # John Doe
print john.dept # computer lab
空类实例的 __dict__ 存储所有属性:
print john.__dict__
# {'salary': 1000, 'dept': 'computer lab', 'name': 'John Doe'}
与字典的对比
| 特性 | 空类 | 字典 |
|---|---|---|
| 语法 | obj.name | obj["name"] |
| 键检查 | 无(动态创建) | KeyError |
| 迭代 | obj.__dict__ | 直接迭代 |
| 内存 | 稍大 | 稍小 |
| 可读性 | 更好(模拟 struct) | 一般 |
# 字典写法
employee = {}
employee["name"] = "John Doe"
employee["dept"] = "computer lab"
# 空类写法
john = Employee()
john.name = "John Doe"
john.dept = "computer lab"
空类的 obj.name 语法更接近 C 的 struct 或 JavaScript 的对象,在表示结构化数据时更直观。
模拟抽象数据类型
空类可以模拟其他语言的数据结构:
class Point(object):
pass
class Rectangle(object):
pass
p1 = Point()
p1.x = 0
p1.y = 0
p2 = Point()
p2.x = 10
p2.y = 20
rect = Rectangle()
rect.top_left = p1
rect.bottom_right = p2
print rect.bottom_right.x # 10
作为函数参数
空类可以替代需要特定接口的参数:
class FileLike(object):
"""模拟文件对象。"""
pass
# 给空类添加必要的方法
content = StringIO("line1\nline2\nline3")
# StringIO 有 read() 和 readline(),可以传给需要文件对象的函数
def process_file(f):
for line in f:
print line.strip()
process_file(content)
Python 的"鸭子类型"哲学:不检查对象类型,只检查它是否有需要的方法。空类可以动态添加方法,实现这种灵活性。
命名元组 namedtuple
对于不可变的数据记录,collections.namedtuple 比空类更好:
from collections import namedtuple
Point = namedtuple("Point", ["x", "y"])
p = Point(3, 4)
print p.x # 3
print p.y # 4
print p[0] # 3,也支持索引
# 不可变
p.x = 10 # AttributeError: can't set attribute
namedtuple 自动生成 __init__、__repr__、__eq__ 等方法,内存占用也比普通类小,适合大量创建的数据记录。
使用建议
- 需要可变数据记录 → 空类或普通类
- 需要不可变数据记录 →
namedtuple - 需要动态键 → 字典
- 需要方法行为 → 完整类定义
- 性能敏感且只读 →
namedtuple或__slots__