1. 修改实例的字符串表示,使结果更有意义
如果不做特殊的处理,一般情况我们打印一个对象的实例时输出的结果为对象的内存地址类似以下:
<__main__.Animal object at 0x000000000085B0B8>
有时候我们需要在打印实例的时候让其输出一些更有意义的内容以帮助调试,那么我们就需要在类中定义__str__和__repr__方法来实现:
class Person():
def __init__(self,name,age,gender):
self.name = name
self.age = age
self.gender = gender
def __repr__(self):
#定义其返回内容为实例的代码表示,这样我们就能根据返回的字符串文本来重新创建这个实例
return 'Person({0.name!r},{0.age!r},{0.gender!r})'.format(self)
def __str__(self):
#将实例转换为一个字符串
return '({0.name!s},{0.age!s},{0.gender!s})'.format(self)
对于上例中的格式化部分中的{0.name}用来表示0的name属性,而这个0实际代表的就是实例self,其实我们也可以通过以下形式实现:
return 'Person(%r, %r, %r)'%(self.name, self.age, self.gender)
然后我们再去打印实例就会有以下的结果:
>>>teacher = Person('laoli',18,'male')
>>>teacher
Person('laoli',18,'male')
>>>print(teacher)
(laoli,18,male)
>>>laoli = eval(repr(teacher))
#obj == eval(repr(obj)),原对象和后面创建的对象在字符串表示方式上是一致的
>>>laoli.name
'laoli'
>>>laoli.age
18
由上可知我们可以通过一个实例的repr方法来重新创建这个实例。
上边的__str__和__repr__方法定义时用到了格式化输出时应该对应使用的字符串表示。注意特殊的格式化代码 !r 表示应该使用 __repr__()的输出,而不是默认的__str__()的输出,以下内容可以帮助理解:
>>>print('teacher is {0!r}'.format(teacher))
>>>teacher is Person('laoli',18,'male')
>>>print('teacher is {0}'.format(teacher))
#等于print('teacher is {0!s}'.format(teacher))
>>>teacher is (laoli,18,male)
对于__repr__(),标准的做法一般是让它产生的字符串文本能够满足eval(repr(obj)) == obj。如果不可能办到或者说不希望实现这种行为的话,那么我们可以让实例产生一段有帮助意义的文本,并且以<>括起来,例如文件操作对象的字符串表示:
>>>f = open('D:\github\dirbot\scrapy.cfg','r')
>>>f.__next__()
'[settings]\n'
>>>f
<_io.TextIOWrapper name='D:\\github\\dirbot\\scrapy.cfg' mode='r' encoding='cp936'>
如果没有定义__str__()可以将__repr__()作为备份输出
2. 让对象支持上下文管理协议
通过使用with语句触发,我们可以让对象支持上下文管理协议(context-management protocol)
要让对象能够兼容with语句,我们需要实现__enter__()和__exit__()方法。
要编写一个上下文管理器,主要的原则就是我们的代码需要包含在由 with 语句定义的代码块中。当遇到with语句的时候__enter__()方法首先被触发执行。__enter__()的返回值被放置在由as限定的变量之中。之后才开始执行with代码块中的语句,最后__exit__()方法被触发用来执行清理工作。
比如有一个网络连接的类和调用:
#coding:utf-8
'''
file:numpyy.py
date:2017/9/7 22:26
author:lockey
email:lockey@123.com
desc:
'''
from socket import socket,AF_INET,SOCK_STREAM
from functools import partial
class LazyConnection():
def __init__(self, address, family=AF_INET, type=SOCK_STREAM):
self.address = address
self.family = family
self.type = SOCK_STREAM
self.connections = []
#为了建立多个互不相干的连接
def __enter__(self):
sock = socket(self.family, self.type)
sock.connect(self.address)
self.connections.append(sock)
return sock
def __exit__(self, exc_type, exc_val, exc_tb):
self.connections.pop().close()
conn = LazyConnection(('www.python.org',80))
with conn as s1:
s1.send(b'GET /index.html HTTP/1.0\r\n')
s1.send(b'HOST:www.python.org\r\n')
s1.send(b'\r\n')
resp = b''.join(iter(partial(s1.recv,8192),b''))
print(resp)
with conn as s2:
s2.send(b'GET /index.html HTTP/1.0\r\n')
s2.send(b'HOST:www.python.org\r\n')
s2.send(b'\r\n')
resp = b''.join(iter(partial(s2.recv, 8192), b''))
print(resp)