Python高级特性(二)——记录、结构体和纯数据对象

一、简介

记录中的字段数目固定,并且每一个都含有一个名称,类型可以不相同。
下面会介绍几种Python中的记录,结构体和纯数据对象(指没有任何方法和行为的对象,不同于类对象,含有方法或属性,纯数据对象只用作存储和传递数据),这些都是Python标准库中含有的内置数据类型和类。

二、数据类型

1、字典——简单数据对象

字典能存储任意数量的对象,每个对象都由一个键来标识,也称为映射关联数组,能快速的按照指定的键进行查找、插入和删除关联的对象。

字典可以当做记录数据类型或数据对象来使用,创建字典很方便,Python中有较多的语法糖支持字典的使用。但字典对象可变,可以任意删除和添加字段,并未对字段名称做限制,所以有时候可能会产生bug。

car2 = {'color': 'blue', 'mileage': 40231, 'automatic': False}

# __repr__方法返回一个字符串,包含了字典的所有内容
print(car2)

# 获取mileage的值
print(car2['mileage'])

# 字典是可变的,可以添加新的键值对
car2['mileage'] = 12
car2['windshield'] = 'broken'
print(car2)

# 对于错误,缺失和额外的字段名称没有保护措施
car3 = {
    'color': 'green',
    'automatic': False,
    'windshield': 'broken',
}
print(car3)

以上代码的输出:

{'color': 'blue', 'mileage': 40231, 'automatic': False}
40231
{'color': 'blue', 'mileage': 12, 'automatic': False, 'windshield': 'broken'}
{'color': 'green', 'automatic': False, 'windshield': 'broken'}
2、自定义类——手动精细控制

类可以用来为数据对象定义可重用的存储结构,确保每个对象都拥有相同的字段。普通的Python类可以作为记录数据类型,但需要完成一些其他的便利功能,比如实现构造函数__init__和字符串化函数__repr__。

在类上进行存储的字段是可以变化的,但是需要进行访问权限控制,使用@property可以创建只读字段。并且可以编写其他适合类的业务逻辑函数,但这就意味着这些对象不是纯数据对象了。

class Car:
    def __init__(self, color, mileage):
        self.color = color
        self.mileage = mileage

    def __repr__(self):
        return f'Car({self.color}, {self.mileage})'


if __name__ == '__main__':
    car1 = Car('red', 37281)
    car2 = Car('blue', 40231)

    # 获取mileage的值
    print(car2.mileage)

    # 类属性可变
    car2.mileage = 12
    car2.color = 'red'

    print(car2)
3、collections.namedtuple——方便的数据对象

namedtuple 是从Python2.6之后添加的数据类型。与自定义类相似,namedtuple 可以让记录重用,并确保每次都使用正确的字段名,与tuple一样,namedtuple不可变,在初始化之后不能再添加和修改现有字段。

除此之外,namedtuple 相当于具有名称的元组。存储在其中的每个对象都可以通过唯一标识符访问。namedtuple对象在内部是作为普通的Python类实现的,内存占用优于普通的类,和普通元组一样高效。

用namedtuple替换普通的元组和字典可以减轻同事的负担,因为使用namedtuple传递的数据在某种程度上能做到“自说明”

Car = namedtuple('Car', 'color mileage automatic')
car1 = Car('red', 3812.4, True)

# namedtuple内部实现了__repr__方法
print(car1)

# 访问字段
print(car1.color)
# 字段是只读的
# car1.mileage = 12
# 抛出异常 AttributeError: can't set attribute
# car1.windshield = 'broken'
# 抛出异常 AttributeError: 'Car' object has no attribute 'windshield'

以上代码运行结果:

Car(color='red', mileage=3812.4, automatic=True)
red
4、typing.NamedTuple——改进版namedtuple

这个类自Python3.6添加,是collections模块中namedtuple类的姊妹。它与namedtuple非常相似,主要的区别就是支持用新语法来定义记录类型并支持类型注解(type hint)

不过,只有像mypy这样的类型检查工具才会在意注解。不过即使没有使用工具支持,类型注解也可以帮助其他程序员更好的理解代码。

class Car(NamedTuple):
    color: str
    mileage: float
    automatic: bool

car1 = Car('red', 3812.4, True)

# namedtuple内部实现了__repr__方法
print(car1)

# 访问字段
print(car1.color)
# 字段是只读的
# car1.mileage = 12
# 抛出异常 AttributeError: can't set attribute
# car1.windshield = 'broken'
# 抛出异常 AttributeError: 'Car' object has no attribute 'windshield'

# 不会抛出异常,只有mypy这样的类型检查工具才会落实类型注解
print(Car('red', 'NOT_A_FLOAT', 99))

以上代码运行结果

Car(color='red', mileage=3812.4, automatic=True)
red
Car(color='red', mileage='NOT_A_FLOAT', automatic=99)
5、struct.Struct——序列化C结构体

struct.Struct类用于在Python值和C结构体之间转换,并将其序列化为Python字节对象。例如可以用来处理存储在文件中或来自网络连接的二进制数据。
结构体使用与格式化字符串类似的语法来定义,能够定义并组织各种C数据类型(如char, int, long,以及对应的无符号的变体)。
序列化结构体一般不用来表示只在Python代码中处理的数据对象,而是主要用作数据交换格式。
在某些情况下,与其他类型相比,将原始数据类型打包到结构体中占的内存较少,但大多数情况下这都属于高级的优化。

MyStruct = Struct('i?f')
data = MyStruct.pack(23, False, 42.0)

# 得到的是一团内存中的数据
print(data)

# 数据可以再次解包
print(MyStruct.unpack(data))

以上代码运行结果:

b'\x17\x00\x00\x00\x00\x00\x00\x00\x00\x00(B'
(23, False, 42.0)
6、types.SimpleNamespace——多样的属性访问

types.SimpleNamespace类添加自Python3.3,可以使用属性访问的方式访问其名字空间。也就是说,SimpleNamespace实例所有的键都公开为类属性。访问时可以使用obj.key这样的点式语法,不需要用obj[‘key’]方括号索引语法。
正如其名,SimpleNamespace很简单,基本就是一个扩展版的字典,能够很好的访问属性并打印,还能自由的添加,删除和修改属性。

car1 = SimpleNamespace(color='red', mileage=3812.4, automatic=True)

# __repr__方法返回一个字符串,包含了实例的所有属性
print(car1)
# 实例支持修改,增加和删除属性
car1.mileage = 12
car1.windshield = 'broken'
del car1.automatic
print(car1)

以上代码运行结果

namespace(color='red', mileage=3812.4, automatic=True)
namespace(color='red', mileage=12, windshield='broken')

三、总结

以上介绍的数据对象类型,在不同场景有不同选择, 大致可以总结为以下几点:

  • 如果只有两三个字段,字段顺序容易记忆并且无需使用字段名称,则使用简单元组对象,例如,二维坐标(x, y)。
  • 如果想锁定字段名称避免输入错误,可以使用collections.namedtuple或者typing.NamedTuple这样的简单元组
  • 如果希望保持简单,建议使用简单的字典对象,语法方便,易于和json对象转换
  • 如果需要对数据结构完全掌控,可以使用含有getter和setter等权限控制方法的自定义类实现。
  • 如果需要向对象添加行为(方法),则应该从头开始编写自定义类,或者通过扩展collections.namedtuple或typing.NamedTuple来编写自定义类
  • 如果想严格打包数据以将其序列化到磁盘或者通过网络发送,建议使用struct.Struct。

一般情况下,如果想在Python中实现一个普通的记录、结构体或数据对象,建议在Python2.x中使用collections.namedtuple,在Python3中使用typing.NamedTuple。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值