我爱编程

python特殊方法(魔术方法)

2018-04-12  本文已影响75人  WilliamY

例子

特点

用法

__str__
把类的实例变成str。值得注意的是__str__显示给用户,与之类似的__repr__显示给开发人员。可以用偷懒的方法定义__repr__ = __str__

class Person(object):

    def __init__(self, name, gender):
        self.name = name
        self.gender = gender

class Student(Person):

    def __init__(self, name, gender, score):
        super(Student, self).__init__(name, gender)
        self.score = score

    def __str__(self):
        return '(student: %s, %s, %s)' % (self.name, self.gender, self.score)

s = Student('Bob', 'male', 88)
print s

__cmp__
对于int、str等类型数据,排序可以调用python默认的cmp函数。对于自己实现的类,sorted函数的参数要实现__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)

    __repr__ = __str__

        
    def __cmp__(self, s):
        if self.score == s.score:
            return cmp(self.name, s.name)
        return -cmp(self.score, s.score)
            

L = [Student('Tim', 99), Student('Bob', 88), Student('Alice', 99)]
print sorted(L)

__len__
返回个数。

任务
斐波那契数列是由 0, 1, 1, 2, 3, 5, 8...构成。
请编写一个Fib类,Fib(10)表示数列的前10个元素,print Fib(10) 可以打印出数列的前 10 个元素,len(Fib(10))可以正确返回数列的个数10。

class Fib(object):
    def __init__(self, num):
        a, b, L = 0, 1, []
        for i in range(num):
            L.append(a)
            a, b = b, a + b
        self.L = L
    def __str__(self):
        # return '['+','.join(map(str, self.L))+']'
        str(self.L)
    def __len__(self):
        return len(self.L)

f = Fib(10)
print f
print len(f)

数学运算
包括__add__, __sub__, __mul__, __div__等。例子:有理数(分数)

def gcd(a, b):
    while b:
        a, b = b, a % b
    return a

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

__int__和__float__
输出其整数(浮点数)类型。
例子:

class Rational(object):
    def __init__(self, p, q):
        self.p = p
        self.q = q

    def __int__(self):
        return self.p // self.q

    def __float__(self):
        # return 1. * self.p / self.q
        return float(self.p) / self.q

print float(Rational(7, 2))
print float(Rational(1, 3))

@property
这是最神奇的一个方法。
考察 Student 类:

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

当我们想要修改一个 Student 的 scroe 属性时,可以这么写:

s = Student('Bob', 59)
s.score = 60

但是也可以这么写:
s.score = 1000
显然,直接给属性赋值无法检查分数的有效性。

如果利用两个方法:

class Student(object):
    def __init__(self, name, score):
        self.name = name
        self.__score = score
    def get_score(self):
        return self.__score
    def set_score(self, score):
        if score < 0 or score > 100:
            raise ValueError('invalid score')
        self.__score = score

这样一来,s.set_score(1000) 就会报错。

这种使用 get/set 方法来封装对一个属性的访问在许多面向对象编程的语言中都很常见。

但是写 s.get_score() 和 s.set_score() 没有直接写 s.score 来得直接。

有没有两全其美的方法?----有。

因为Python支持高阶函数,在函数式编程中我们介绍了装饰器函数,可以用装饰器函数把 get/set 方法“装饰”成属性调用:

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

注意: 第一个score(self)是get方法,用@property装饰,第二个score(self, score)是set方法,用@score.setter装饰,@score.setter是前一个@property装饰后的副产品。

现在,就可以像使用属性一样设置score了:

>>> s = Student('Bob', 59)
>>> s.score = 60
>>> print s.score
60
>>> s.score = 1000
Traceback (most recent call last):
ValueError: invalid score

说明对 score 赋值实际调用的是 set方法。

任务
如果没有定义set方法,就不能对“属性”赋值,这时,就可以创建一个只读“属性”。
请给Student类加一个grade属性,根据 score 计算 A(>=80)、B、C(<60)。

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 >= 80:
            return "A"
        if self.__score < 60:
            return "C"
        return "B"

s = Student('Bob', 59)
print s.grade

s.score = 60
print s.grade

__slots__()
限制添加属性。__slots__()中添写允许的属性列表。比如:

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

操作:

>>> s = Student('Bob', 'male', 59)
>>> s.name = 'Tim' # OK
>>> s.score = 99 # OK
>>> s.grade = 'A'
Traceback (most recent call last):
  ...
AttributeError: 'Student' object has no attribute 'grade'

任务
假设Person类通过slots定义了name和gender,请在派生类Student中通过slots继续添加score的定义,使Student类可以实现name、gender和score 3个属性。

class Person(object):

    __slots__ = ('name', 'gender')

    def __init__(self, name, gender):
        self.name = name
        self.gender = gender

class Student(Person):

    __slots__ = ('score')

    def __init__(self, name, gender, score):
        super(Student, self).__init__(name, gender)
        self.score = score

s = Student('Bob', 'male', 59)
s.name = 'Tim'
s.score = 99
print s.score
上一篇 下一篇

猜你喜欢

热点阅读