python的数据模型_python高级(一)—— python数据模型(特殊方法)
本文主要内容
collections.namedtuple
__getitem__ 和 __len__
__repr__和__str__
__abs__、__add__和__mul__
__bool__
本文内容的表格式总结
语 法
调用的方法(按照顺序寻找)
备注
list[2]
__getitem__(2)
list[1:3:2]
__getitem__(slice(1,3,2))
切片时传入的参数是slice类型
for i in object:
__iter__()、__getitem__()
__iter__需要返回迭代器,并不断调用next()
in object
__contains__()、__iter__()、__getitem__()
__iter__()、__getitem__()会按照顺序搜索
print(object)
__str__()、__repr__()
if object:
__bool__()、__len__()
使用if、while等判断句时,会调用__bool__()
如果没有这两个方法,一般情况下,自定义的类总认为是真的
何为python的数据模型
本文所指的python数据模型,也可成为python中内置的对象模型(一切皆为对象),其包含的一些方法为特殊方法,在java中也称“魔术方法”。由于python文档里面喜欢使用“数据模型”这个词,所以本文依此称数据模型。
简单来说,数据模型就是python自有的数据类型,及其包含的特殊方法。例如:使用len()时会调用__len__特殊方法;使用list[]时会调用__getitem__方法;使用各类运算符也会调用其相对应的方法。从根本上而言,list[]、+、-、*、/、for i in x这些写法只是为了更简洁和更具有可读性,但内部跟其他操作一下,也是通过方法实现的,这就是特殊方法。
可命名元组(namedtuple)
#导入可命名元组
from collections importnamedtuple#创建的两种方法 (创建股票模型,每只股票包括name和price)
Stock_1= namedtuple("stock", ("name", "price")) #方法1:第二个参数传入可迭代对象(元组、数组等都可)
Stock_2= namedtuple("stock", "name price") #方法2:字符串之间用空格隔开
#生成多只股票
stock01 = Stock_1("SH000001", 1)
stock02= Stock_1("SH000002", 12)
stock03= Stock_1("SH000003", 123)
stock04= Stock_1("SH000004", 1234)#访问股票信息
print(stock01.name) #属性形式 SH000001
print(stock04[1]) #列表形式 1234
__getitem__ 和 __len__
1、__len__
classFoo:def __len__(self): #重写__len__方法
print("method __len__")return 1
if __name__ == "__main__":
foo=Foo()
n= len(foo) #使用len()时会自动调用__len__方法:method __len__
print(n) #1
2、__getitem__
from collections importnamedtuple
Stock= namedtuple("stock", ["name", "price"])classFoo:def __init__(self):
self._stock= [Stock(name, price) forname, pricein zip(range(1, 100), range(1, 100))]def __len__(self):returnlen(self._stock)def __getitem__(self, item):print(item)returnself._stock[item]if __name__ == "__main__":
foo=Foo()print(len(foo))print(foo[3]) #使用foo[3]时会调用__getitem__方法,解释器会将3传递给__getitem__(self, item)中的item参数
#stock(name=4, price=4)
print(foo[3:6]) #使用切片操作时也会调用__getitem__方法,解释器会传递slice(3, 6, None)item参数
#[stock(name=4, price=4), stock(name=5, price=5), stock(name=6, price=6)]
重写__getitem__后就可直接遍历对象:
if __name__ == "__main__":#此时可直接用for循环对foo进行遍历
for i infoo:print(i)#由于实现了__getitem__方法,foo实例就变成了可迭代对象
#不仅可以使用for循环正向迭代,也可反向迭代;还可以使用in判断
for i inreversed(foo):print(i) #反向迭代
print(Stock(name=2, price=2) in foo) #in判断会先调用__contains__方法,但是如果没有该方法,则调用__getitem__按顺序迭代搜索
#True (调用了2次getitem)
print(Stock(name=2, price=3) in foo) #False (调用了100次getitem方法,最后一次foo[99]发现不存在而停止迭代)
3、继续说说for i in x: 语句
刚刚我们使用for i in foo时发现可以正常迭代,如果在Foo类中重写__iter__方法,则无法正确迭代了:
from collections importnamedtuple
Stock= namedtuple("stock", ["name", "price"])classFoo:def __init__(self):
self._stock= [Stock(name, price) forname, pricein zip(range(1, 100), range(1, 100))]def __len__(self):returnlen(self._stock)def __getitem__(self, item):print(item)returnself._stock[item]def __iter__(self):pass
if __name__ == "__main__":
foo=Foo()for i in foo: #报错:TypeError: iter() returned non-iterator of type 'NoneType'
print(i)
如果我们把以上__iter__方法改成如下,那么又可使用for语句了:
def __iter__(self):
return iter(self._stock)
事实上我们在使用for i in foo语句时,会先调用__iter__方法,返回一个迭代器,然后for循环会不断使用next()进行遍历;如果foo里面没有该方法,则会调用__getitem__,并会从0开始依次读取相应的下标,直到发生IndexError为止,这是一种旧的迭代协议。
同样的,使用in判断时,解释器会依次寻找__contains__、__iter__、__getitem__方法。
from collections importnamedtuple
Stock= namedtuple("stock", ["name", "price"])classFoo:def __init__(self):
self._stock= [Stock(name, price) forname, pricein zip(range(1, 100), range(1, 100))]def __len__(self):returnlen(self._stock)def __getitem__(self, item):print(item)returnself._stock[item]def __iter__(self):returniter(self._stock)#def __contains__(self, item):
#print(item)
#return False
if __name__ == "__main__":
foo=Foo()for i in foo: #重写了__iter__(self)后解释器自动执行iter(foo)
print(i)
x= iter(foo) #手动执行
print(next(x)) #stock(name=1, price=1)
print(next(x)) #stock(name=2, price=2)
print(next(x)) #stock(name=3, price=3)
print(Stock(name=4, price=4) in foo) #按照__contains__、__iter__、__getitem__顺序寻找:True
__repr__和__str__
#接下来的例子引用自《流畅的python》#创建一个二维向量的类Vector,慢慢给它添加一些运算
classVector:def __init__(self, x=0, y=0):
self.x=x
self.y=ydef __repr__(self):return 'repr: Vector(%r, %r)' %(self.x, self.y)#def __str__(self): # 如果类中同时有__str__和__repr__,则调用print是会先使用__str__
#return "str: Vector(%r, %r)" % (self.x, self.y)
#这个类中现在只实现了__repr__方法
if __name__ == "__main__":
v= Vector(2, 3)print(v) #此时打印出来的不是这种形式
#打印出来的是Vector(2, 3)
#如果类中实现了__str__同样有此作用
#__repr__和__str__的区别在于,后者是在str()函数中被使用,或是在用print打印函数打印一个对象的时候才被#调用。如果你只想实现这两个特殊方法中的一个,__repr__是更好的选择,因为如果一个对象没有__str__函数#而python又需要调用它的时候,解释器会用__repr__作为代替
#故使用print()函数时,解释器会按照__str__、__repr__的顺序寻找
__abs__、__add__和__mul__
#接上面的二维向量的例子
from math importhypotclassVector:def __init__(self, x=0, y=0):
self.x=x
self.y=ydef __repr__(self):return 'Vector(%r, %r)' %(self.x, self.y)def __abs__(self): #abs本来是绝对值,在二维向量中指模
returnhypot(self.x, self.y)def __add__(self, other):
x= self.x +other.x
y= self.y +other.yreturnVector(x, y)def __mul__(self, scalar):return Vector(self.x * scalar, self.y *scalar)if __name__ == "__main__":
v= Vector(4, 3)#使用abs()求模,解释器自动调用__abs__方法
print(abs(v)) #5.0
#使用+求向量加法,解释器自动调用__add__方法
v2 = Vector(1, 5)print(v + v2) #Vector(5, 8)
#ps: __add__方法返回的是Vector对象,然后print函数会调用__repr__
#使用*求向量与数的乘法,解释器自动调用__mul__方法
print(v * 3) #Vector(12, 9)
#这里只实现了向量的数乘, 并且未实现 3*v
__bool__
#继续在上面列子中添加__bool__
from math importhypotclassVector:def __init__(self, x=0, y=0):
self.x=x
self.y=ydef __repr__(self):return 'Vector(%r, %r)' %(self.x, self.y)def __abs__(self):returnhypot(self.x, self.y)def __bool__(self):returnbool(abs(self))if __name__ == "__main__":
v= Vector(0, 3)if v: #调用__bool__
print(abs(v)) #3.0
#使用if或while语句,或者and\or\not运算符,为了判定一个对象v是真还是假,python会调用bool(v),这个函数只能返回True或者False#默认情况下,自定义的类的实例总被认为是真的,除非这个类对__bool__或者__len__函数有自己的实现。#bool(v)后面是调用v.__bool__()的结果;如果不存在__bool__方法,那么bool(v)会尝试调用v.__len__(),若返回0,则bool返回False,否则为True
#python 3.6的官方文档如下介绍
'''By default, an object is considered true unless its class defines either a __bool__() method that
returns False or a __len__() method that returns zero, when called with the object.
Here are most of the built-in objects considered false:
constants defined to be false: None and False.
zero of any numeric type: 0, 0.0, 0j, Decimal(0), Fraction(0, 1)
empty sequences and collections: '', (), [], {}, set(), range(0)
Operations and built-in functions that have a Boolean result always return 0 or False for false
and 1 or True for true, unless otherwise stated. (Important exception: the Boolean operations or and and
always return one of their operands.)'''
python中的全部特殊方法
python中一共有83个特殊方法,其中47个用于算术运算、位运算和比较操作。我根据《流畅的python》中的整理,摘录如下两个表格
表1:跟运算符无关的特殊方法
类 别
方法名
字符串/字节序列表示形式
__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__
表2:跟运算符相关的特殊方法
类 别
方法名和对应的运算符
一元运算符
__neg__ -、__pos__ +、__abs__ abs()
众多比较运算符
__lt__ 、__ge__>=
算数运算符
__add__ +、__sub__ -、__mul__ *、__truediv__ /、__floordiv__ //、
__mod__ %、__divmod__ divmod()、__pow__ **或pow()、__round__ round()
反向算数运算符
__radd__、__rsub__、__rmul__、__rtruediv__、__rfloordiv__、__rmod__、__rdivmod__、__rpow__
增量赋值算术运算符
__iadd__、__isub__、__imul__、__itruediv__、__ifloordiv__、__imod__、__ipow__
位运算符
__invert__ ~、__lshift__ <>、__and__ &、__or__ |、__xor__ ^
反向位运算符
__rlshift__、__rrshift__、__rand__、__rxor__、__ror__
增量赋值位运算符
__ilshift__、__irshift__、__iand__、__ixor__、__ior__
如何使用特殊方法:
1、特殊方法的调用是隐式的,通常你的代码无需直接使用特殊方法。除非有大量的元编程存在,直接调用特殊方法的频率应该远远低于你去实现它们的次数。唯一的例外可能是__init__方法,你的代码里可能经常会用到它,目的是在你的子类的__init__方法中调用超类的构造器。
2、通过内置的函数(例如len、iter、str等)来使用特殊方法是最好的选择。这些内置函数不仅会调用特殊方法,通常还提供额外的好处,而且对于内置的类来说,它们的速度更快。
3、不要自己想当然地随意添加特殊方法,比如__foo__之类的,因为虽然现在这个名字没有被python内部使用,以后就不一定了。
——《流畅的Python》
python高级系列文章目录
python的数据模型_python高级(一)—— python数据模型(特殊方法)相关推荐
- python generator长度_Python 高级特性之:生成器(generator)和迭代器(Iterator)
前言: 之前学习Python自动化,接触了不少python的学习,对生成器印象尤其深,网上也看了很多介绍,下面主要是这些概念的个人学习整理(如侵删). 正文: 如要创建一个非常大的列表,受到内存限制, ...
- python app服务器_Python应用02 Python服务器进化
作者:Vamei 出处:http://www.cnblogs.com/vamei 欢迎转载,也请保留这段声明.谢谢! **注意,在Python 3.x中,BaseHTTPServer, SimpleH ...
- python的使用说明_Python教程:Python中__init__.py的使用用法说明
Python中的Module是比较重要的概念.常见的情况是,事先写好一个.py文 件,在另一个文件中需要import时,将事先写好的.py文件拷贝 到当前目录,或者是在sys.path中增加事先写好的 ...
- python dicom 测量_python对DICOM图像的读取方法详解
DICOM介绍 DICOM3.0图像,由医学影像设备产生标准医学影像图像,DICOM被广泛应用于放射医疗,心血管成像以及放射诊疗诊断设备(X射线,CT,核磁共振,超声等),并且在眼科和牙科等其它医学领 ...
- python数组展示_python 显示数组全部元素的方法
python 显示数组全部元素的方法 如下所示: import numpy as np np.set_printoptions(threshold='nan') 以上这篇python 显示数组全部元素 ...
- python打印日历_python输出指定月份日历的方法
python输出指定月份日历的方法 本文实例讲述了python输出指定月份日历的方法.分享给大家供大家参考.具体实现方法如下: #!/usr/bin/python import calendar ca ...
- python自动化库_Python操作自动化库PyAutoGUI的方法
Python操作自动化库PyAutoGUI的方法 发布时间:2020-07-23 17:24:46 来源:亿速云 阅读:73 作者:小猪 这篇文章主要讲解了Python操作自动化库PyAutoGUI的 ...
- python 函数修饰_python修饰函数 python 函数有多个修饰符
define的意思,用来定义函数. 如: def 函数名(参数1, 参数2, --, 参数N): 执行语句 # 例:简单的函数使用# 定义函数def hello(): print 'hello pyt ...
- python论文摘要_Python实现提取文章摘要的方法
本文实例讲述了Python实现提取文章摘要的方法.分享给大家供大家参考.具体如下: 一.概述 在博客系统的文章列表中,为了更有效地呈现文章内容,从而让读者更有针对性地选择阅读,通常会同时提供文章的标题 ...
最新文章
- 关于中台的思考与尝试
- python中in的底层实现_python中print和input的底层实现
- Eigen矩阵的运算(二)
- 安徽计算机中专学校有哪些,安徽2021年中专学校里面都有什么专业
- c语言return 11,二级C语言教程章节测试11.对函数的进一步讨论
- CondenseNet总结
- php使用http请求头实现文件下载
- 使用静态容器防止并发修改同一对象
- 计算机科学常见工具书清单、项目开发清单
- 1、CSS 盒子模型,2、边框样式,3、CSS 轮廓(outline),
- 由深圳的大树所想到的
- windows NT的安全性
- 数据库之SQL更新语句中update set from用法
- 老Java程序员花两天做了个消消乐(天天爱消除)
- 截止失真放大电路_反馈/反馈电路/反馈类型的判别方法
- 天牛群,天牛须结合粒子群算法BAS-PSO。研究生阶段毕生所 学,低价出售!可定制pid参数整定。
- python箭头_箭头函数
- android中常见的异常总结
- .9图片报错Execution failed for task ':app:mergeDebugResources'. Crunching Cruncher scrollbar_thumb.9.p
- VS2015 还是VS2017 好用_强烈推荐:2020年12款Visual Studio 好用的工具
热门文章
- java后台代码添加超链接_Java 添加超链接至Excel文档
- Eclipse安装后启动出现error:could not create the java machine.
- Linux系统中源码包tomcat的管理
- java坐标代码_java实现计算地理坐标之间的距离
- 使用html-table模块生成html格式
- java telnet 交换机_华为交换机 telnet 配置(极简版)
- callback回调使用 vue_Vue 数据更新了但页面没有更新的 7 种情况汇总及延伸
- springboot 事务_第六章:springboot开启声明式事务
- 累加List对象中的某一个值
- MISCONF Redis is configured to save RDB snapshots, but is currently not able to persist