• Python 数据模型


    一摞Python风格的纸牌

     1 from collections import namedtuple
     2 from random import choice
     3 
     4 #命名元祖
     5 Card = namedtuple('Card', ['rank', 'suit'])
     6 
     7 
     8 class FrenchDeck:
     9     ranks = [str(n) for n in range(2, 11)] + list('JQKA')
    10     suits = 'spades diamonds clubs hearts'.split()
    11 
    12     def __init__(self):
    13         self._cards = [Card(rank, suit) for suit in self.suits for rank in self.ranks]
    14 
    15     def __len__(self):
    16         return len(self._cards)
    17 
    18     #变为可迭代的对象
    19     def __getitem__(self, position):
    20         return self._cards[position]
    21 
    22 deck = FrenchDeck()
    23 #扑克牌的张数
    24 print('扑克牌的长度:', len(deck))
    25 
    26 #随机选择扑克牌
    27 print('随机选择的扑克牌:', choice(deck))
    28 
    29 #选择扑克牌最上面的三张牌
    30 print('扑克牌上最上面的三张牌:', deck[:3])
    31 
    32 #选择大老A
    33 print('先选择第一张A', deck[12])
    34 
    35 #每一个花色有12张牌,可以通过步长来获取
    36 print('取所有的大老A:', deck[12::13])
    37 
    38 #判断成员关系
    39 print(Card('Q', 'hearts') in deck)
    40 print(Card('7', 'beasts') in deck)
    41 
    42 #反向输出结果
    43 for card in reversed(deck):
    44     print(card)

    以上代码执行的结果为:

    扑克牌的长度: 52
    随机选择的扑克牌: Card(rank='J', suit='clubs')
    扑克牌上最上面的三张牌: [Card(rank='2', suit='spades'), Card(rank='3', suit='spades'), Card(rank='4', suit='spades')]
    先选择第一张A Card(rank='A', suit='spades')
    取所有的大老A: [Card(rank='A', suit='spades'), Card(rank='A', suit='diamonds'), Card(rank='A', suit='clubs'), Card(rank='A', suit='hearts')]
    True
    False
    Card(rank='A', suit='hearts')
    Card(rank='K', suit='hearts')
    Card(rank='Q', suit='hearts')
    Card(rank='J', suit='hearts')
    Card(rank='10', suit='hearts')
    Card(rank='9', suit='hearts')
    Card(rank='8', suit='hearts')
    Card(rank='7', suit='hearts')
    .......................
    .......................

      那么排序呢?我们按照常规,用点数来判定扑克牌的大小,2 最小、A最大;同时还要加上对花色的判定,黑桃最大、红桃次之、方块再次、梅花最小。下面就是按照这个规则来给扑克牌排序的函数,梅花 2 的大小是 0,黑桃 A 是 51:

     1 #牌颜色的权重
     2 suit_values = dict(spades=3, hearts=2, diamonds=1, clubs=0)
     3 
     4 def spades_high(card):
     5     #获取2.....A的索引的值
     6     rank_value = FrenchDeck.ranks.index(card.rank)
     7     return rank_value * len(suit_values) + suit_values[card.suit]
     8 
     9 for card in sorted(deck, key=spades_high):
    10     print(card)

    以上代码执行的结果为:

    Card(rank='2', suit='clubs')
    Card(rank='2', suit='diamonds')
    Card(rank='2', suit='hearts')
    ... (46 cards ommitted)
    Card(rank='A', suit='diamonds')
    Card(rank='A', suit='hearts')
    Card(rank='A', suit='spades')

    如何使用特殊方法

    模拟数值类型

    利用特殊方法,可以让自定义对象通过加号“+”(或是别的运算符)进行运算

    们来实现一个二维向量(vector)类,这里的向量就是欧几里得几何中常用的概念,常在数学和物理中使用的那个

    一个二维向量加法的例子,Vector(2,4) + Vextor(2,1) =Vector(4,5)

    一个简单的二维向量类

     1 from math import hypot
     2 
     3 
     4 class Vector:
     5 
     6     def __init__(self, x=0, y=0):
     7         self.x = x
     8         self.y = y
     9 
    10     def __repr__(self):
    11         return 'Vector({!r}, {!r})'.format(self.x, self.y)
    12 
    13     def __abs__(self):
    14         return hypot(self.x, self.y)
    15 
    16     def __bool__(self):
    17         return bool(abs(self))
    18 
    19     def __add__(self, other):
    20         x = self.x + other.x
    21         y = self.y + other.y
    22         return Vector(x, y)
    23 
    24     def __mul__(self, scalar):
    25         return Vector(self.x * scalar, self.y * scalar)
    26 
    27 
    28 v1 = Vector(2, 4)
    29 v2 = Vector(2, 1)
    30 
    31 #向量v1 + v2的结果
    32 print('v1 + v2的和:', v1+v2)
    33 
    34 #向量v abs的结果
    35 v = Vector(3, 4)
    36 print('v abs的结果:', abs(v))
    37 
    38 #向量的惩罚
    39 print('v 的标量乘法abs的结果:', abs(v * 3))

    以上代码执行的结果为:

    v1 + v2的和: Vector(4, 5)
    v abs的结果: 5.0
    v 的标量乘法abs的结果: 15.0

     

    字符串表示形式

      Python 有一个内置的函数叫 repr,它能把一个对象用字符串的形式表达出来以便辨认,这就是“字符串表示形式”。repr 就是通过 __repr__这个特殊方法来得到一个对象的字符串表示形式的。如果没有实现__repr__,当我们在控制台里打印一个向量的实例时,得到的字符串可能会是 <Vector object at 0x10e100070>。 

      交互式控制台和调试程序(debugger)用 repr 函数来获取字符串表示形式;在老的使用 % 符号的字符串格式中,这个函数返回的结果用来代替 %r 所代表的对象;同样,str.format 函数所用到的新式字符串格式化语法(https://docs.python.org/2/library/string.html#format-stringsyntax也是利用了 repr(),才把 !r 字段变成字符串。

      __repr__ 所返回的字符串应该准确、无歧义,并且尽可能表达出如何用代码创建出这个被打印的对象。因此这里使用了类似调用对象构造器的表达形式(比如 Vector(3, 4) 就是个例子)

      __repr__ 和 __str__ 的区别在于,后者是在 str() 函数被使用,或是在用 print 函数打印一个对象的时候才被调用的,并且它返回的字符串对终端用户更友好。如果你只想实现这两个特殊方法中的一个,__repr__ 是更好的选择,因为如果一个对象没有 __str__ 函数,而 Python 又需要调用它的时候,解释器会用 __repr__ 作为替代

    算术运算符

      通过 __add__ 和 __mul__,示例向量类带来了 + 和 * 这两个算术运算符。值得注意的是,这两个方法的返回值都是新创建的向量对象,被操作的两个向量(self 或 other)还是原封不动,代码里只是读取了它们的值而已。中缀运算符的基本原则就是不改变操作对象,而是产出一个新的值。

    自定义的布尔值

      尽管 Python 里有 bool 类型,但实际上任何对象都可以用于需要布尔值的上下文中(比如 if 或 while 语句,或者 and、or 和 not 运算符)。为了判定一个值 x 为真还是为假,Python 会调用 bool(x)这个函数只能返回 True 或者 False

      默认情况下,我们自己定义的类的实例总被认为是真的,除非这个类对 __bool__ 或者 __len__ 函数有自己的实现。bool(x) 的背后是调x.__bool__() 的结果;如果不存在 __bool__ 方法,那么 bool(x) 会尝试调用 x.__len__()。若返回 0,则 bool 会返回 False;否则返回True。

      我们对 __bool__ 的实现很简单,如果一个向量的模是 0,那么就返回False,其他情况则返回 True。因为 __bool__ 函数的返回类型应该是布尔型,所以我们通过 bool(abs(self)) 把模值变成了布尔值。

      如果想让 Vector.__bool__ 更高效,可以采用这种实现:

    def __bool__(self):
        return bool(self.x or self.y)

      它不那么易读,却能省掉从 abs 到 __abs__ 到平方再到平方根这些中间步骤。通过 bool 把返回类型显式转换为布尔值是为了符合__bool__ 对返回值的规定,因为 or 运算符可能会返回 x 或者 y本身的值:若 x 的值等价于真,则 or 返回 x 的值;否则返回 y 的值。

    特殊方法一览

    跟运算符无关的特殊方法

    类别 方法名
    字符串字/字节序列表示形式  __repr__、__str__、__format__、__bytes__
    数值转换   __abs__、__bool__、__complex__、__int__、__float__、__hash__、__index__
    集合模拟   _len__、__getitem__、__setitem__、__delitem__、__contains__  
    迭代枚举  __iter__、__reversed__、__next__ 
    可调用模拟   __call__
    上下文管理  __enter__、__exit__
    实例的创建和销毁   __new__、__init__、__del__
    属性管理  __getattr__、__getattribute__、__setattr__、__delattr__、__dir__
    属性描述符   __get__、__set__、__delete__ 
    跟类相关的服务  __prepare__、__instancecheck__、__subclasscheck__

     

     

     

     

     

     

     

     

     

     

    跟运算符相关的特殊方法

    类别 方法名
    一元运算符  __neg__ -、__pos__ +、__abs__ abs()
    众多比较运算符  __lt__ <、__le__ <=、__eq__ ==、__ne__ !=、__gt__ >、__ge__ >=
    算数运算符   __add__ +、__sub__ -、__mul__ *、__truediv__ /、__floordiv__ //、__mod__ %、  __divmod__divmod()、__pow__ ** 或pow()、__round__ round()
    反向算术运算符  __radd__、__rsub__、__rmul__、__rtruediv__、__rfloordiv__、__rmod__、__rdivmod__
    增量赋值算术运算符   __iadd__、__isub__、__imul__、__itruediv__、__ifloordiv__、__imod__、__ipow__
    位运算符  __invert__ ~、__lshift__ <<、__rshift__ >>、__and__ &、__or__ |、__xor__ ^ 
    反向位运算符  __rlshift__、__rrshift__、__rand__、__rxor__、__ror__
    增量赋值为运算符   __ilshift__、__irshift__、__iand__、__ixor__、__ior__ 

      

  • 相关阅读:
    freemarker时间转换197
    用FileZilla服务器端和客户端实现本机与虚拟机之间文件上传和下载197
    springboot pom 详解197
    eclipse svn197
    eclipse 安装git197
    listen()函数中backlog参数分析
    linux 函数库使用
    gdb调试问题汇总
    gdb调试多线程
    linux-C 多线程编程
  • 原文地址:https://www.cnblogs.com/demon89/p/7365742.html
Copyright © 2020-2023  润新知