特殊方法

特点:
特殊方法定义在class内
不需要直接调用
Python的某些函数或操作附会调用对应的特殊方法

__str__和 __repr__

print
python把任意变量变成str,因为任意数据类型的实例都有一个特殊方法 __str__()

__str__()用于显示给用户,而__repr__()用于显示给开发人员。
__repr__的目标是准确性,%r打印时能够重现它所代表的对象
__str__的目标是可读性,%s

import datetime  
d = datetime.date.today()  
print "%s" % d  
print "%r" % d

2015-12-23
datetime.date(2015, 12, 23)

__cmp__

对 int、str 等内置数据类型排序时,Python的 sorted() 按照默认的比较函数 cmp 排序,但是,如果对一组类的实例排序时,就必须提供我们自己的特殊方法 __cmp__()

sorted默认由小到大,cmp()
sorted(iterable, cmp=None, key=None, reverse=False)

类实现了__cmp__()方法,__cmp__用实例自身self和传入的实例 s 进行比较,如果 self 应该排在前面,就返回 -1,如果 s 应该排在前面,就返回1,如果两者相当,返回 0

修改 Student 的 __cmp__ 方法,让它按照分数从高到底排序,分数相同的按名字排序

class Student(object):
    def __init__(self,name,score):
        self.name = name
        self.score = score
    def __str__(self):
        return '(%s:%s)' % (self.name,self.score)
    def __cmp__(self,s):
        if self.score == s.score:
            return cmp(self.name,s.name)
        return -cmp(self.score,s.score)
L = [Student('Tim',79),Student('Bob',82),Student('Tom',99)]
print sorted(L)

__len__

如果一个类表现得像一个list,要获取有多少个元素,就得用 len() 函数。类必须提供一个特殊方法__len__(),它返回元素的个数。
打印斐波那契数列,并返回数列和个数

class Fib(object):
    def __init__(self,num):
        a,b,L = 0,1,[]
        for i in range(10):
            L.append(a)
            a,b = b,a+b
        self.num = L
    def __str__(self):
        return str(self.num)
    __repr__ = __str__
    def __len__(self):
        return len(self.num)
f = Fib(10)
print f
print len(f)

数学运算

用于有理数的运算

def gcd(a,b):
    if b == 0:
        return a
    return gcd(b,a%b)
class Rational(object):
    def __init__(self,p,q):
        self.p = p
        self.q = q
    #'+'
    def __add__(self,r):
        return Rational(self.p*r.q+self.q*r.p,self.q*r.q)
    #'-'
    def __sub__(self,r):
        return Rational(self.p*r.q-self.q*r.p,self.q*r.q)
    #'*'
    def __mul__(self,r):
        return Rational(self.p*r.p,self.q*r.q)
    #'/'
    def __div__(self,r):
        return Rational(self.p*r.q,self.q*r.p)
    def __str__(self):
        g = gcd(self.p,self.q)
        return '%s/%s' % (self.p/g,self.q/g)
    __repr__ = __str__

r1 = Rational(1,2)
r2 = Rational(1,4)
print r1 + r2
print r1 - r2
print r1 * r2
print r1 / r2

@property

用装饰器(@property)函数把 get/set 方法“装饰”成属性调用
@property—这是关键字,固定格式,能让方法当“属性”用。
@score.setter—前面的”score”是@property紧跟的下面定义的那个方法的名字,”setter”是关键字,这种“@+方法名字+点+setter”是个固定格式与@property搭配使用。

class Student(object):
    def __init__(self,name,score):
        self.name = name
        self.__score = score
    @property
    def score(self):
        return self.__score
    @score.setter
    def score(self,score):
        if score < 0 or score > 100:
            raise ValueError('invalid score')
        self.__score = score
    @property
    def grade(self):
        if self.__score < 60:
            return 'C'
        if self.__score < 80:
            return 'B'
        return 'A'
s = Student('Tom',80)
print s.grade
s.score = 62
print s.grade
s.score = 98
print s.grade

__slots__

由于Python是动态语言,任何实例在运行期都可以动态地添加属性。
如果要限制添加的属性,例如,Student类只允许添加 name、gender和score 这3个属性,就可以利用Python的一个特殊的__slots__来实现。
顾名思义,__slots__是指一个类允许的属性列表

class Student(object):
    __slots__=('name','gender','score')
    def __init__(self,name,gender,score):
        self.name = name
        self.score = score
        self.gender = gender

__slots__的目的是限制当前类所能拥有的属性,如果不需要添加任意动态的属性,使用__slots__也能节省内存。

__call__

在Python中,函数其实是一个对象.所有的函数都是可调用对象。一个类实例也可以变成一个可调用对象,只需要实现一个特殊方法__call__()。

class Fib(object):
    def __call__(self,num):
        a,b,L = 0,1,[]
        for n in range(num):
            L.append(a)
            a,b = b,a+b
        return L
f = Fib()
print f(10)