类的特殊方法
类的特殊方法是为了 Python 解释器调用的,你自己并不需要调用它,也就是说没有 myobject.__len__()
这种写法,应该使用 len(myobject)
。
在执行 len(myobject)
的时候,如果 myobject 是一个自定义类的对象,那么python自己会去调用由你实现的 __len__
方法。
如果 myobject 是一个list, str 或者字节序列(bytearray)的话,那么 Cpython 会抄个近路,__len__
会直接返回 PyVaryObject 里的 ob_size 属性。 PyVaryObject 是表示内存中长度可变的内置对象的 C语言结构体。直接读取此值会比调用一个方法快得多
str
定义一个类
class Student(object):
def __init__(self, name):
self.name = name
当我们打印这个类的实例时:
>>> print(Student('Arya'))
<Student object at 0x00000211DE165DD8>
这样很不方便观看,怎么按我们想要的格式打印出实例的信息呢?这时可以用到__str__
方法
class Student(object):
def __init__(self, name):
self.name = name
def __str__(self):
return '%s object, name: %s' %(self.__class__, self.name)
这时,再打印:
>>> s = Student('Arya')
>>> print(s)
<class 'Student'> object, name: Arya
可以按我们想要的格式展示了,可以打印实例所属的类,实例的一些属性、方法。
但是,当我们直接输入此实例时,显示的还是以前的值。
>>> s
<Student object at 0x00000211DE165DD8>
这是因为,打印和直接显示变量调用的不是同一个函数,如上,打印调用的是实例的__str__
方法,后者调用的是实例内置的__repr__
方法。
当我们对以上两个方法进行重新定义时,便改变了打印和直接显示时显示的内容。
当重新定义了__str__
时,我们可以直接让__repr__
与其相等,即可改变直接显示变量的内容。
当定义中无 __str__
, 而 python 又需要调用它时,解释器会用定义的 __repr__
方法代替
class Student(object):
def __init__(self, name):
self.name = name
def __str__(self):
return '%s object, name: %s' %(self.__class__, self.name)
__repr__ = __str
getattr
class Student(object):
def __init__(self, name, id, score):
self.name = name
self.id = id
self.score = score
定义类的几种属性,当我们尝试调用时:
>>> s = Student('Arya', '007', 95)
>>> s.name
'Arya'
>>> s.id
'007'
>>> s.score
95
>>> s.city
Traceback (most recent call last):
File "<input>", line 1, in <module>
AttributeError: 'Student' object has no attribute 'city'
当调用不存在的属性时,会报错,要想不报错,返回指定的内容,就可以用__getattr__
。注意,只有访问不存在的属性时,才会调用这个函数。
还有一个类似的特殊方法__getattribute__
,定义了这个方法以后,当调用实例属性时,无论其存不存在,都将执行这个方法。如果同时定义了__getattribute__
和__getattr__
,那么调用属性时后者不会被被调用,除非在前者中调用了后者。
class Student(object):
def __init__(self, name, id, score):
self.name = name
self.id = id
self.score = score
def __getattribute__(self, item):
return object.__getattribute__(self, item)
另外,在__getattribute__
的定义中,返回值不能是__self.__dict__[item]
,因为如果这样定义,还会调用这个__getattribute__
函数,会形成无限递归,形成死循环。
class Student(object):
def __init__(self, name, id, score):
self.name = name
self.id = id
self.score = score
def __getattr__(self, item):
if item == 'city':
return 'Beijing'
这时,我们再调用
>>> s = Student('Arya', '007', 95)
>>> s.city
'Beijing'
>>> s.sex
就会返回指定的内容,当我们再调用未指定的属性时,会返回None,要想只返回特定的内容,就要抛出AttributeError
class Student(object):
def __init__(self, name, id, score):
self.name = name
self.id = id
self.score = score
def __getattr__(self, item):
if item == 'city':
return 'Beijing'
raise AttributeError('Student object has no attribute %s' %(self.__class__, item))
setattr
如果我在程序运行期间想为其增加某种属性怎么办,可以使用__setattr__
:
class Student(object):
def __init__(self, name, id, score):
self.name = name
self.id = id
self.score = score
def __setattr__(self, key, value):
self.__dict__[key] = value
这时我们就可以使用s.key = value
了:
>>> s = Student('Arya', '007', 95)
>>> s.sex = 'girl'
>>> s.sex
'girl'
增加的属性是实例属性,存储在实例的__dict__
中。
call
如果想让创建的实例可以当作函数调用,可以使用__call__
方法:
class average(object):
def __init__(self, *args):
self.count = 0
self.add = 0
for x in args:
self.count += 1
self.add += x
def __call__(self):
return self.add/self.count
如此:
>>> s = average(10, 9, 8, 7)
>>> s.count
4
>>> s.add
34
>>> s()
8.5
还可以在__call__
定义中加参数
getitem
class Fib(object):
"""
>>> Fib()[0]
1
>>> Fib()[1]
1
>>> Fib()[2]
2
>>> Fib()[3]
3
>>> Fib()[4]
5
>>> Fib()[10]
89
"""
def __getitem__(self, n):
a, b = 1, 1
for x in range(n):
a, b = b, a+b
return a
if __name__ == '__main__':
import doctest
doctest.testmod()
如果想实现按照下标访问元素的方法就要使用 __getitem__
方法,
另外,需要注意的是,传入的参数可能是索引数字,也可能是切片,如果是未对切片作相应处理会报错:
class Fib(object):
"""
>>> Fib()[0]
1
>>> Fib()[1]
1
>>> Fib()[2]
2
>>> Fib()[3]
3
>>> Fib()[4]
5
>>> Fib()[10]
89
>>> Fib()[0:5]
[1, 1, 2, 3, 5]
"""
def __getitem__(self, n):
if isinstance(n, int):
a, b = 1, 1
for x in range(n):
a, b = b, a+b
return a
if isinstance(n, slice):
start = n.start
stop = n.stop
if start == None:
start = 0
L = []
a, b = 1, 1
for x in range(stop):
L.append(a)
a, b = b, a+b
L = L[start:stop]
return L
if __name__ == '__main__':
import doctest
doctest.testmod()
同时,把对象视为 dict , __getitem__
的参数也可能是一个可以作为 key 的对象,比如 str
class Student(object):
def __init__(self, name, gender):
self.name = name
self.gender = gender
def __getitem__(self, key):
return self.__dict__[key]
>>> s = Student('Arya', 'girl')
>>> s.name
'Arya'
>>> s['name']
'Arya'
- tips: `__getattr__` 和 `__getattribute__` 是用来访问实例对象属性的, `__getitem__`是可以通过索引访问对应值,也可以将对象视为 dict, 访问其 key
与其对应的是 `__setitem__` ,可以给对象新增 key 。
网友评论