一摞Python风格的纸牌

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

以上代码执行的结果为:

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

  那么排序呢?我们按照常规,用点数来判定扑克牌的大小,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)

以上代码执行的结果为:

  1. Card(rank='', suit='clubs')
  2. Card(rank='', suit='diamonds')
  3. Card(rank='', suit='hearts')
  4. ... (46 cards ommitted)
  5. Card(rank='A', suit='diamonds')
  6. Card(rank='A', suit='hearts')
  7. Card(rank='A', suit='spades')

如何使用特殊方法

模拟数值类型

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

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

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

一个简单的二维向量类

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

以上代码执行的结果为:

  1. v1 + v2的和: Vector(4, 5)
  2. v abs的结果: 5.0
  3. 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__ 更高效,可以采用这种实现:

  1. def __bool__(self):
  2. 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__ 

  











Python 数据模型的更多相关文章

  1. Python数据模型及Pythonic编程

    Python作为一种多范式语言,它的很多语言特性都能从其他语言上找到参照,但是Python依然形成了一套自己的“Python 风格”(Pythonic).这种Pythonic风格完全体现在 Pytho ...

  2. 第1章 Python数据模型

    #<流畅的Python>读书笔记 # 第一部分 序幕 # 第1章 Python数据模型 # 魔术方法(magic method)是特殊方法的昵称.于是乎,特殊方法也叫双下方法(dunder ...

  3. gj3 Python数据模型(魔法函数)

    3.1 什么是魔法函数 类里面,实现某些特性的内置函数,类似 def __xx__(): 的形式. 不要自己定义XX,并不是和某个类挂钩的 class Company(object): def __i ...

  4. python高级(一)—— python数据模型(特殊方法)

    本文主要内容 collections.namedtuple __getitem__ 和 __len__ __repr__和__str__ __abs__.__add__和__mul__ __bool_ ...

  5. Python数据模型与Python对象模型

    数据模型==对象模型 Python官方文档说法是"Python数据模型",大多数Python书籍作者说法是"Python对象模型",它们是一个意思,表示&quo ...

  6. Python数据模型

    引言 像大多数人一样,我在对一直传统的面向过程语言C一知半解之后,走进了面向对象的世界,尽管对OOP一无所知,还好Python还保留有函数式编程,这使得我才不那么抵触,直到现在,习惯了面向对象之后,也 ...

  7. Python数据模型建立

    基本结构AutoField(Field) - int自增列,必须填入参数 primary_key=True BigAutoField(AutoField) - bigint自增列,必须填入参数 pri ...

  8. (一)python 数据模型

    1.通过实现特殊方法,自定义类型可以表现的跟内置类型一样: 如下代码,实现len, getitem,可使自定义类型表现得如同列表一样. import collections from random i ...

  9. 流畅的python第一章python数据模型学习记录

    python中有些特殊的方法,以双上下划线开头,并以双下划线结束的方法.如__getitem__,这些方法是特殊的方法,供python解释权内部使用,一般来说不需要调用 还有一种是以双下划线开头的,如 ...

随机推荐

  1. wget下载站点文件

    非常简单的指令,只需要: wget -c -r -p -k -np [URL] 下面解释下个参数的意义: -c 断点续传 -r 递归下载,可遍历整个站点的结构 -p 网页显示所需要的素材(图片\css ...

  2. C实现dos图文菜单程序实例

      前言 公司一台服务器是novell环境,文件管理是基于dos6.22的,客户端启动需要一个图文菜单.   实现    编程环境:汉化版TC2.0 菜单基本功能:显示提示项.显示dbf中的行情信息. ...

  3. js通过Date获取日期

    获取当前系统时间 var myDate = new Date();//获取系统当前时间 获取特定格式日期 myDate.getYear(); //获取当前年份(2位) myDate.getFullYe ...

  4. 【使用WCF,发布服务端浏览报错】未能从程序集“System.ServiceModel, Version=3.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089” 中加载类型 “System.ServiceModel.Activation.HttpModule”。

    问题: 在WIN7中的IIS服务器中部署WCF服务程序时,通过浏览器访问报出如下错误: 未能从程序集"System.ServiceModel, Version=3.0.0.0, Cultur ...

  5. firefox插件开发及源码下载

    在个别情况下,由于数据量巨大,造成显示性能的明显下降,此时使用c++开发firefox插件,可以提高用户使用体验. test.html: 在插件中,我们导出3个函数给js:AddStr, Pause, ...

  6. docker~使用阿里加速器安centos

    回到目录 上一篇说了hub.docker.com里拉个镜像太,而阿里云为我们做了不少本国镜像,这样下载的速度就很惊人了,下面看一下在centos7下配置阿里云加速器的方法 打开服务配置文件 vi /e ...

  7. 配置ssh免密码登录——集群学习日记

    度过了难熬的考试月时期之后,最近和小伙伴一起参加的的比赛进入了紧张的准备时期.在进行工作的时候,发现有很多基础的知识点,自己不是很清楚以及了解,所以在想,要不就边学习的时候边写下学习日记,以供自己后来 ...

  8. sqlserver提高篇

    Microsoft SQL Server2008复习提高 一.Microsoft SQL Server 系统的体系结构 1.Microsoft SQL Server2008由4个主要的部分组成,即4个 ...

  9. 基于jmeter,jenkins,ANT接口,性能测试框架

    背景 公司计划推接口和性能测试,搭建这个性能测试框架框架是希望能够让每个人(开发人员.测试人员)都能快速的进行性能,接口测试,而不需要关注性能测试环境搭建过程.因为,往往配置一个性能环境可能需要很长的 ...

  10. Sql for Oracle基本操作关键字

    Sql for Oracle基本操作关键字 +SQL TOP子句 TOP 子句用于规定要返回的记录的数目 SELECT column_name(s) FROM table_name WHERE ROW ...