本文主要内容

  collections.namedtuple

__getitem__ 和 __len__

  __repr__和__str__

  __abs__、__add__和__mul__

  __bool__

python高级——目录

  文中代码均放在github上:https://github.com/ampeeg/cnblogs/tree/master/python高级

本文内容的表格式总结

 语 法 调用的方法(按照顺序寻找)  备注 
 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 import namedtuple # 创建的两种方法 (创建股票模型,每只股票包括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__

class Foo:
def __len__(self): # 重写__len__方法
print("method __len__")
return 1 if __name__ == "__main__":
foo = Foo()
n = len(foo) # 使用len()时会自动调用__len__方法:method __len__
print(n) #

  2、__getitem__

from collections import namedtuple

Stock = namedtuple("stock", ["name", "price"])

class Foo:
def __init__(self):
self._stock = [Stock(name, price) for name, price
in zip(range(1, 100), range(1, 100))] def __len__(self):
return len(self._stock) def __getitem__(self, item):
print(item)
return self._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 in foo:
print(i) # 由于实现了__getitem__方法,foo实例就变成了可迭代对象
# 不仅可以使用for循环正向迭代,也可反向迭代;还可以使用in判断
for i in reversed(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 import namedtuple

Stock = namedtuple("stock", ["name", "price"])

class Foo:
def __init__(self):
self._stock = [Stock(name, price) for name, price
in zip(range(1, 100), range(1, 100))] def __len__(self):
return len(self._stock) def __getitem__(self, item):
print(item)
return self._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 import namedtuple

Stock = namedtuple("stock", ["name", "price"])

class Foo:
def __init__(self):
self._stock = [Stock(name, price) for name, price
in zip(range(1, 100), range(1, 100))] def __len__(self):
return len(self._stock) def __getitem__(self, item):
print(item)
return self._stock[item] def __iter__(self):
return iter(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,慢慢给它添加一些运算 class Vector: def __init__(self, x=0, y=0):
self.x = x
self.y = y def __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 object at 0x0000003>这种形式
# 打印出来的是Vector(2, 3)
# 如果类中实现了__str__同样有此作用 # __repr__和__str__的区别在于,后者是在str()函数中被使用,或是在用print打印函数打印一个对象的时候才被
# 调用。如果你只想实现这两个特殊方法中的一个,__repr__是更好的选择,因为如果一个对象没有__str__函数
# 而python又需要调用它的时候,解释器会用__repr__作为代替 # 故使用print()函数时,解释器会按照__str__、__repr__的顺序寻找

__abs__、__add__和__mul__

# 接上面的二维向量的例子

from math import hypot

class Vector:

    def __init__(self, x=0, y=0):
self.x = x
self.y = y def __repr__(self):
return 'Vector(%r, %r)' % (self.x, self.y) def __abs__(self): # abs本来是绝对值,在二维向量中指模
return hypot(self.x, self.y) def __add__(self, other):
x = self.x + other.x
y = self.y + other.y
return Vector(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 import hypot

class Vector:

    def __init__(self, x=0, y=0):
self.x = x
self.y = y def __repr__(self):
return 'Vector(%r, %r)' % (self.x, self.y) def __abs__(self):
return hypot(self.x, self.y) def __bool__(self):
return bool(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中的全部特殊方法

  本部分内容可以参考官方网站 https://docs.python.org/3/reference/datamodel.html#special-method-names

  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__ <、__le__ <=、__eq__ ==、__ne__ !=、__gt__ >、__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__ <<、__rshift__ >>、__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数据模型(特殊方法)的更多相关文章

  1. Python高级编程-Python一切皆对象

    Python高级编程-Python一切皆对象 Python3高级核心技术97讲 笔记 1. Python一切皆对象 1.1 函数和类也是对象,属于Python的一等公民 ""&qu ...

  2. 第九章:Python高级编程-Python socket编程

    第九章:Python高级编程-Python socket编程 Python3高级核心技术97讲 笔记 9.1 弄懂HTTP.Socket.TCP这几个概念 Socket为我们封装好了协议 9.2 cl ...

  3. 为什么用Python,高级的Python是一种高级编程语言

    Python特性 如果有人问我Python最大的特点是什么,我会毫不犹豫地告诉他:它简单易学,功能强大.作为一个纯自由软件,Python有许多优点: 很简单.基于"优雅".&quo ...

  4. Python高级编程技巧(转)

    译文:http://blog.jobbole.com/61171/ 本文展示一些高级的Python设计结构和它们的使用方法.在日常工作中,你可以根据需要选择合适的数据结构,例如对快速查找性的要求.对数 ...

  5. python高级——目录

    目  录 python高级(一)—— python数据模型(特殊方法) python高级(二)—— python内置序列类型 python高级(三)—— 字典和集合(泛映射类型) python高级(四 ...

  6. python 高级之面向对象初级

    python 高级之面向对象初级 本节内容 类的创建 类的构造方法 面向对象之封装 面向对象之继承 面向对象之多态 面向对象之成员 property 1.类的创建 面向对象:对函数进行分类和封装,让开 ...

  7. python高级之生成器&迭代器

    python高级之生成器&迭代器 本机内容 概念梳理 容器 可迭代对象 迭代器 for循环内部实现 生成器 1.概念梳理 容器(container):多个元素组织在一起的数据结构 可迭代对象( ...

  8. python高级之面向对象高级

    python高级之面向对象高级 本节内容 成员修饰符 特殊成员 类与对象 异常处理 反射/自省 单例模式 1.成员修饰符 python的类中只有私有成员和公有成员两种,不像c++中的类有公有成员(pu ...

  9. python高级之网络编程

    python高级之网络编程 本节内容 网络通信概念 socket编程 socket模块一些方法 聊天socket实现 远程执行命令及上传文件 socketserver及其源码分析 1.网络通信概念 说 ...

随机推荐

  1. Nmap扫描参数解析

    Nmap是一款开源免费的网络发现(Network Discovery)和安全审计(Security Auditing)工具.软件名字Nmap是Network Mapper的简称.Nmap最初是由Fyo ...

  2. pymsql的简单实用方法

    在进行本文以下内容之前需要注意: 1.你有一个MySQL数据库,并且已经启动. 2.你有可以连接该数据库的用户名和密码 3.你有一个有权限操作的database 连接数据库 #导入pymsql imp ...

  3. MVC各个层的作用

    (1)控制器的作用是调用模型,并调用视图,将模型产生的数据传递给视图.并让相关视图去显示.(2)模型的作用是获取数据并处理数据.(3)视图的作用是将取得的数据进行组织.美化等,并最终向用户终端输出.

  4. MongoDBAppender

    分三步: 1. client 2. appender 3. 配置文件 logback-test.xml <configuration> <appender name="MO ...

  5. SciTE编写lua的快捷键整理

    SciTE快捷键整理 常用键整理: Ctrl + Q: 多行注释 Ctrl + L:删除一行或多行 Ctrl+T :和上一行换位置 Ctrl+D :复制高亮选中字符. 如果没有高亮选择字符, 则复制光 ...

  6. 11、Semantic-UI之分割线

    11.1 分割线的定义 示例:定义分割线 分割线 <div class="ui divider"></div> 竖线并加入or <div class= ...

  7. MongoDB默认配置

    mongodb使用了yaml格式定义的配置文件(http://www.yaml.org/) 默认为: # mongod.conf #where to log logpath=/var/log/mong ...

  8. Android-ListView-(BaseAdapter初步)

    在Android中就提供了专门列表显示条目的控件,ListView控件,ListView控件不是一次性加载全部数据,他是只加载用户在屏幕看得到的数据,当用户滑动的过程中在去加载新的数据,同时会自动销毁 ...

  9. JavaScript 放置在文档最后面可以使页面加载速度更快

    JavaScript 放置在文档最后面可以使页面加载速度更快

  10. centos 查看mysql数据库命令

    使用MySQL时,需要了解当前数据库的情况,例如当前的数据库大小.字符集.用户等等.下面总结了一些查看数据库相关信息的命令 1:查看显示所有数据库 mysql> show databases; ...