Python数据模型深度解析

一. 什么是数据模型?

Python数据模型是Python对象系统的抽象,通过一组特殊方法​(如__init____len__等)和协议​(如迭代协议、上下文管理协议),定义了对象如何与语言的内置功能(如len()for循环等)交互。

核心思想

  • 统一性:所有对象(如列表、字典、自定义类)的行为都通过相同的特殊方法实现。
  • 灵活性:通过实现特殊方法,可以让自定义对象支持内置操作(如+in、切片等)。
  • 例子

    import collections
    Card = collections.namedtuple('Card', ['rank', 'suit'])
    class FrenchDeck:
        ranks = [str(n) for n in range(2, 11)] + list('JQKA')
        suits = 'spades diamonds clubs hearts'.split()
        def __init__(self):
            self._cards = [Card(rank, suit) for suit in self.suits
            for rank in self.ranks]
        def __len__(self):
            return len(self._cards)
        def __getitem__(self, position):
            return self._cards[position]
    解释
    1. 模块导入

      import collections

      导入了 Python 标准库中的 collections 模块。

    2. 命名元组定义:Card

      Card = collections.namedtuple('Card', ['rank', 'suit'])

      使用 namedtuple 创建了一个名为 Card 的简单类,表示扑克牌的一张牌。每个 Card 对象有两个属性:

    3. rank:表示牌的点数(如 '2''J''A' 等)。
    4. suit:表示牌的花色(如 'spades''diamonds' 等)。
    5. 类定义:FrenchDeck

    6. 定义了一个名为 FrenchDeck 的类,表示一副标准的 52 张扑克牌。
    7. 类属性:

      ranks = [str(n) for n in range(2, 11)] + list('JQKA') suits = 'spades diamonds clubs hearts'.split()

    8. ranks:表示牌的所有点数,包括数字牌('2' 到 '10')和字母牌('J''Q''K''A')。
    9. suits:表示牌的所有花色('spades' 黑桃、'diamonds' 方片、'clubs' 梅花、'hearts' 红心)。
    10. 构造函数 __init__

      def __init__(self): self._cards = [Card(rank, suit) for suit in self.suits for rank in self.ranks]

      初始化时创建了一副完整的扑克牌,存储在 _cards 列表中。通过列表推导式生成所有可能的牌组合。

    11. 方法 __len__

      def __len__(self): return len(self._cards)

      实现了特殊方法 __len__,使得可以通过 len(deck) 获取扑克牌的数量。

    12. 方法 __getitem__

      def __getitem__(self, position): return self._cards[position]

      实现了特殊方法 __getitem__,使得可以通过索引访问扑克牌,例如 deck[0]

    常用特殊方法

    方法 对应操作
    __init__ 对象初始化
    __repr__ / __str__ 字符串表示
    __len__ len(obj)
    __getitem__ obj[key](支持索引和切片)
    __iter__ for x in obj(迭代)
    __enter__ / __exit__ with语句(上下文管理)

    迭代协议

    实现__iter____next__方法,让对象支持for循环:

    class CountDown:
        def __init__(self, start):
            self.current = start
    
        def __iter__(self):
            return self
    
        def __next__(self):
            if self.current <= 0:
                raise StopIteration
            else:
                self.current -= 1
                return self.current + 1
    
    for num in CountDown(3):
        print(num)  # 输出: 3 2 1

     

     解释
    1. 类定义:CountDown

    2. 定义了一个名为 CountDown 的类,用于实现倒计时功能。

    3. 构造函数 __init__

      def __init__(self, start): self.current = start

      初始化时接收一个参数 start,表示倒计时的起始值,并将其赋值给实例属性 self.current

    4. 方法 __iter__

      def __iter__(self): return self

      实现了可迭代协议,使得该类的实例可以作为迭代器使用。返回自身(self)。

    5. 方法 __next__

      def __next__(self): if self.current <= 0: raise StopIteration else: self.current -= 1 return self.current + 1

      实现了迭代器协议中的 __next__ 方法:

    6. 如果当前值 self.current 小于或等于 0,则抛出 StopIteration 异常,表示迭代结束。
    7. 否则,将 self.current 减 1,并返回减 1 前的值(即当前值)。
    8. 使用场景

    9. 该类可以通过 for 循环或手动调用 next() 方法进行倒计时。
    10. 示例:
      countdown = CountDown(5)
      for value in countdown:
          print(value)

      输出结果:

      5 4 3 2 1

    上下文管理协议

    实现__enter____exit__方法,支持with语句:

    class Timer:
        def __enter__(self):
            self.start = time.time()
            return self
    
        def __exit__(self, exc_type, exc_val, exc_tb):
            self.end = time.time()
            print(f"耗时: {self.end - self.start:.2f}秒")
    
    with Timer():
        time.sleep(1)  # 输出: 耗时: 1.00秒
     解释
    1. 类定义:Timer

    2. 定义了一个名为 Timer 的类,用于测量代码块的执行时间。

    3. 方法 __enter__

      def __enter__(self): self.start = time.time() return self

    4. 实现了上下文管理协议中的 __enter__ 方法。
    5. 在进入 with 语句块时,记录当前时间(以秒为单位)并存储在实例属性 self.start 中。
    6. 返回自身(self),以便在 with 语句块中可以访问该对象。
    7. 方法 __exit__

      def __exit__(self, exc_type, exc_val, exc_tb): self.end = time.time() print(f"耗时: {self.end - self.start:.2f}秒")

    8. 实现了上下文管理协议中的 __exit__ 方法。
    9. 在退出 with 语句块时,记录当前时间(以秒为单位)并存储在实例属性 self.end 中。
    10. 计算执行时间(self.end - self.start),并以两位小数的格式打印到控制台。
    11. 使用场景

    12. 通过 with 语句使用 Timer 类来测量代码块的执行时间。
    13. 示例:

      with Timer(): time.sleep(1) # 输出: 耗时: 1.00秒

    14. 在 with 语句块中调用了 time.sleep(1),模拟了一个耗时 1 秒的操作。
    15. Timer 类会自动计算并打印出这段代码的执行时间。 

    让自定义类支持切片

    class MySequence:
        def __init__(self, data):
            self.data = data
    
        def __len__(self):
            return len(self.data)
    
        def __getitem__(self, index):
            if isinstance(index, slice):
                return self.data[index.start : index.stop : index.step]
            else:
                return self.data[index]
    
    seq = MySequence([0, 1, 2, 3, 4])
    print(seq[1:3])  # [1, 2](支持切片!)

    鸭子类型(Duck Typing)​

    Python不检查对象的类型,而是检查对象是否实现了特定的方法(即是否“像鸭子一样叫”)。

    示例
    class FakeList:
        def __len__(self):
            return 10
    
        def __getitem__(self, index):
            return index * 2
    
    fake = FakeList()
    print(len(fake))       # 10(调用__len__)
    print(fake[5])         # 10(调用__getitem__)
    print(isinstance(fake, list))  # False,但行为类似列表!

     

     

     

     

     

    作者:愚戏师

    物联沃分享整理
    物联沃-IOTWORD物联网 » Python数据模型深度解析

    发表回复