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 = name
def __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,b
def __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)
...
1
1
2
3
5
...
46368
75025
getitem
可以通过定义 __getitem__()
方法,来使对象可索引化。
class Fib(object):
def __getitem__(self, n):
a, b = 1, 1
for x in range(n):
a, b = b, a + b
return 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, 1
for x in range(n):
a, b = b, a + b
return a
if isinstance(n, slice): # n是切片
start = n.start
stop = n.stop
if start is None:
start = 0
a, b = 1, 1
L = []
for x in range(stop):
if x >= start:
L.append(a)
a, b = b, a + b
return 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.score
99
好处是可以将对象的属性和方法的定义全部动态化,不需要手动一一生成。
练习
利用完全动态的getattr,写出一个链式调用:
class Chain(object):
def __init__(self, path=''):
self._path = path
def __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')