文章目录
【 注意】出现的
__slot__
和
__init__
和
__sstr__
等,都是
双下划线
1. __slots__
Python允许在定义class
的时候,定义一个特殊的__slots__
变量,来限制该class
实例能添加的属性:
class Student(object):
__slots__ = ('name', 'age') # 用tuple定义允许绑定的属性名称
也就是说创建新的实例的时候,只能绑定限定的属性,否则就会出错:
s = Student()
s.name = 'Tom'
s.age = 22
s.score=100
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-10-3ee075cc4fd4> in <module>()
2 s.name = 'Tom'
3 s.age = 22
----> 4 s.score=100
AttributeError: 'Student' object has no attribute 'score'
因为限定了属性,所以绑定属性score的时候就报错了。
2. @property
既能检查参数,又可以用类似属性这样简单的方式来访问类的变量。Python内置的property
饰器就是负责把一个方法变成属性调用的:
class Student(object):
@property
def name(self):
return self._name
@name.setter
def name(self, s):
if isinstance(s, str):
self._name = s
else:
raise ValueError('Name must be a string')
s = Student()
s.name='hello'
print(s.name)
hello
可以看到在函数定义前加上@property,就把一个函数变成了一个属性在使用,而且这个属性还能被检查。因为用@同名.setter就起到了设置的作用,在设置中就可以进行检查,那么@property就起到了一个getter的作用,用于获取一个属性的值。
【特殊用法】只定义getter方法,不定义setter方法就是一个只读属性:
class Student(object):
@property
def birth(self):
return self._birth
@birth.setter
def birth(self, value):
self._birth = value
@property
def age(self):
return 2015 - self._birth
3. __str__
直接打印类的时候,如print(Student('Tom'))
,输出的结果是<__main__.Student object at 0x109afb190>
,就不太好看,于是只需要定义好_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)
如果用变量的方式,打出来还是不好看:
s = Student(‘Michael’)
<main.Student object at 0x109afb310>
这是因为直接显示变量调用的不是__str__()
,而是__repr__()
,两者的区别是__str__()
返回用户看到的字符串,而__repr__()
返回程序开发者看到的字符串,也就是说,__repr__()
是为调试服务的。
解决办法是再定义一个__repr__()
。但是通常__str__()
和__repr__()
代码都是一样的,所以,有个偷懒的写法:
class Student(object):
def __init__(self, name):
self.name = name
def __str__(self):
return 'Student object (name=%s)' % self.name
__repr__ = __str__
s = Student('Tom')
print(s)
Student object (name=Tom)
4. __iter__
如果一个类想被用于for ... in
循环,类似list
或tuple
那样,就必须实现一个__iter__()
方法,该方法返回一个迭代对象,然后,Python的for循环就会不断调用该迭代对象的__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 > 100: # 退出循环的条件
raise StopIteration()
return self.a # 返回下一个值
print([n for n in Fib()])
[1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]
5. __getitem__
Fib实例虽然能作用于for循环,看起来和list有点像,但是,把它当成list来使用还是不行,比如,取第5个元素:Fib()[5]就会报错。要表现得像list那样按照下标取出元素,需要实现__getitem__()
方法:
class Fib(object):
def __getitem__(self, n):
a, b = 1, 1
for x in range(n):
a, b = b, a + b
return a
print(Fib()[5])
8
6. __getattr__
python中有一个__getattr__()
方法,可以动态返回一个属性。当调用不存在的属性时,比如score,Python解释器会试图调用__getattr__(self, 'score')
来尝试获得属性,这样,我们就有机会返回score的值:
class Student(object):
def __init__(self):
self.name = 'Michael'
def __getattr__(self, attr):
if attr=='score':
return 99
s = Student()
print('s.name=', s.name)
print('s.score=', s.score)
print('s.age=', s.age)
s.name= Michael
s.score= 99
s.age= None
【注意】只有在没有找到属性的情况下,才调用__getattr__
,已有的属性,比如name
,不会在__getattr__
中查找。此外,注意到任意调用如s.age
都会返回None
,这是因为我们定义的__getattr__
默认返回就是None
。要让class
只响应特定的几个属性,我们就要按照约定,抛出AttributeError
的错误:
class Student(object):
def __init__(self):
self.name = 'Michael'
def __getattr__(self, attr):
if attr=='score':
return 99
raise AttributeError('This object has no attribute named ',attr)
s = Student()
print('s.name=', s.name)
print('s.score=', s.score)
print('s.age=', s.age)
s.name= Michael
s.score= 99
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-27-4721b134eb99> in <module>()
12 print('s.name=', s.name)
13 print('s.score=', s.score)
---> 14 print('s.age=', s.age)
<ipython-input-27-4721b134eb99> in __getattr__(self, attr)
7 if attr=='score':
8 return 99
----> 9 raise AttributeError('This object has no attribute named ',attr)
10
11 s = Student()
AttributeError: ('This object has no attribute named ', 'age')
7. __call__
任何类,只需要定义一个__call__()
方法,就可以直接对实例进行调用:
class Student(object):
def __init__(self, name):
self.name = name
def __call__(self):
print('My name is %s.' % self.name)
s = Student('Tom')
print(s()) # 直接调用
My name is Tom.
None
__call__()
还可以定义参数。对实例进行直接调用就好比对一个函数进行调用一样,所以你完全可以把对象看成函数,把函数看成对象,因为这两者之间本来就没啥根本的区别。
那么,怎么判断一个变量是对象还是函数呢?其实,更多的时候,我们需要判断一个对象是否能被调用,能被调用的对象就是一个Callable
对象,比如函数和我们上面定义的带有__call__()
的类实例:
callable(Student('Tom'))
True