str
>>> class Student(object):... def __init__(self, name):... self.name = name... def __str__(self):... return 'Student object (name: %s)' % self.name...>>> print(Student('Michael'))Student object (name: Michael)
repr
虽然字符化的结果修改类,但将实例直接输出的结果并不好看
>>> s = Student('Michael')>>> s<__main__.Student object at 0x109afb310>
这是因为直接显示变量调用的不是 __str__() ,而是 __repr__() , __repr__() 返回程序开发者看到的字符串,解决方法只需将__repr__() 指向 __str()__ 即可。
class Student(object):def __init__(self, name):self.name = namedef __str__(self):return 'Student object (name=%s)' % self.name__repr__ = __str__
iter
甚至可以通过 __iter__ 方法定义一个可迭代的类型,同时我们还需定义 __next()__ 方法,在迭代到最后时报出 StopIteration 错误。
class Fib(object):def __init__(self):self.a, self.b = 0, 1 # 初始化两个计数器a,bdef __iter__(self):return self # 实例本身就是迭代对象,故返回自己def __next__(self):self.a, self.b = self.b, self.a + self.b # 计算下一个值if self.a > 100000: # 退出循环的条件raise StopIteration()return self.a # 返回下一个值>>> for n in Fib():... print(n)...11235...4636875025
getitem
可以通过定义 __getitem__() 方法,来使对象可索引化。
class Fib(object):def __getitem__(self, n):a, b = 1, 1for x in range(n):a, b = b, a + breturn a>>> f = Fib()>>> f[0]1>>> f[1]1>>> f[2]2
定义切片方法:
class Fib(object):def __getitem__(self, n):if isinstance(n, int): # n是索引a, b = 1, 1for x in range(n):a, b = b, a + breturn aif isinstance(n, slice): # n是切片start = n.startstop = n.stopif start is None:start = 0a, b = 1, 1L = []for x in range(stop):if x >= start:L.append(a)a, b = b, a + breturn L>>> f = Fib()>>> f[0:5][1, 1, 2, 3, 5]
getattr
当用不存在的属性时,python会通过 __getattr__ 方法来定义该属性
class Student(object):def __init__(self):self.name = 'Michael'def __getattr__(self, attr):if attr=='score':return 99>>> s = Student()>>> s.name'Michael'>>> s.score99
好处是可以将对象的属性和方法的定义全部动态化,不需要手动一一生成。
练习
利用完全动态的getattr,写出一个链式调用:
class Chain(object):def __init__(self, path=''):self._path = pathdef __getattr__(self, path):return Chain('%s/%s' % (self._path, path))def __str__(self):return self._path__repr__ = __str__
执行命令
Chain().status.user.timeline.list
输出为:
/status/user/timeline/list
程序执行过程如下:
- 初始化
Chain(),path="",self._path='' - 由于
Chain().status未定义,因此执行__getattr__(),其中:self._path=''(这由第1步可知)path="status"(注意 status 就是__getattr__()输入参数中的 path)- 返回
Chain("/status")
- 初始化
Chain(),path="/status",该 Chain 实例的_path="/status" - 由于
Chain().user未定义,因此执行__getattr__(),其中:self._path="/status"path="user"- 返回
Chain("/status/user")
- 初始化
Chain(),path="/status/user",该 Chain 实例的_path="/status/user" - 类似的流程一直递归运行到最后,返回
Chain("/status/user/timeline/list") - 再次初始化
Chain(),self._path="/status/user/timeline/list" - 执行
__str__(),定义str(self)=self._path - 由于
__repr__()指向__str__(),因此Chain().status.user.timeline.list的输出为该Chain 实例的_path,为"/status/user/timeline/list"
当我们执行:
Chain().users('michael').repos
结果会报错:TypeError: 'Chain' object is not callable
这是因为在执行完 Chain().user 的定义后,Chain().users('michael') 相当于执行 Chain("/user")('michael'),
很明显,我们的Chain类型并不是可调用的,所以需要再额外定义一个__call__()方法:
def __call__(self, path):return Chain('%s/%s' % (self._path,path))
这样就能顺利得到输出:
Chain().users('michael')
