1、_ _ slots _ _
限制实例的属性,只允许对Student实例添加name和age属性。
为了达到限制的目的,Python允许在定义class的时候,定义一个特殊的__slots__变量,来限制该class实例能添加的属性:
class Student(object):
__slots__ = ('name', 'age') # 只允许有name和age属性
在子类中也定义__slots__,这样,子类实例允许定义的属性就是自身的__slots__加上父类的__slots__
2、 _ _ 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)
如果不使用print,还是不行啊
s = Student('Michael')
>>> s
<__main__.Student object at 0x109afb310>
str()返回用户看到的字符串,而__repr__()返回程序开发者看到的字符串,也就是说,repr()是为调试服务的。
class Student(object):
def __init__(self, name):
self.name = name
def __str__(self):
return 'Student object (name=%s)' % self.name
def __repr__(self):
return 'Student object (name=%s)' % self.name
__repr__ = __str__ # 等价于上面函数的定义
2、 _ _ iter _ _
直接看代码
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 > 10: # 退出循环的条件
raise StopIteration()
return self.a # 返回下一个值
for n in Fib(): # 调用一次返回一次
print(n)
输出
1
1
2
3
5
8
3、_ _ getitem_ _
上面不支持Fib()[5]
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()
print(f[6])
输出
8
但是没有list的切片功能,安排
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:3]
# 这个是__getitem__的特异功能,我用n = [0:2]报错
# 不可以这么搞,程序中打印出来是这个slice(0, 3, None)
输出
[1, 1, 2]
4、_ _ getattr_ _
正常情况下,当我们调用类的方法或属性时,如果不存在,就会报错。
class Student(object):
def __init__(self):
self.name = 'Michael'
def __getattr__(self, attr):
if attr=='score':
return 99
s = Student()
print(s.score)
输出
99
没有找到属性的情况下,才调用__getattr__,__getattr__也没有,则返回none,也可以搞个异常,
class Student(object):
def __getattr__(self, attr):
if attr=='age':
return lambda: 25
raise AttributeError('\'Student\' object has no attribute \'%s\'' % attr)
这种完全动态调用的特性的作用就是可以针对完全动态的情况作调用。 - 不懂
class Chain(object):
def __init__(self, path=''):
print("a")
self._path = path
def __getattr__(self, path):
print("path", path)
return Chain('%s/%s' % (self._path, path)) # 很关键,这里返回的是对象
def __str__(self):
print("c")
return self._path
__repr__ = __str__
# path = Chain().status.user.timeline.list 等价于下面两句
# 拆开
link = Chain() # 初始化一个对象
path = link.status.user.timeline.list # 每次返回来的都是对象,所以可以一直点下去
print(path) # 其实这是是对象,打印类会触发__str__
结果
a
path status
a
path user
a
path timeline
a
path list
a
c
/status/user/timeline/list
所以这个很好用的是这样
path = Chain('Unchang/path').change.path
#输出
Unchang/path/change/path
不明白,这样不就行了吗?干嘛那么麻烦
path = 'Unchang/path'+'/'+'change/path'
评论
class Chain(object):
def __init__(self, path=''):
self.__path = path
def __getattr__(self, path):
return Chain('%s/%s' % (self.__path, path))
def __call__(self, path):
return Chain('%s/%s' % (self.__path, path))
def __str__(self):
return self.__path
__repr__ = __str__
print(Chain().users('michael').repos)
# /users/michael/repos
# 简直完美
5、_ _ call _ _
任何类,只需要定义一个__call__()方法,就可以直接对实例进行调用
class Student(object):
def __init__(self, name):
self.name = name
def __call__(self):
print('My name is %s.' % self.name)
s = Student('Michael')
s() # self参数不要传入
输出
My name is Michael.