Day-9: 面对对象高级编程

  数据封装、继承和多态只是面向对象编程中最基础的3个概念。

  下面整理面向对象高级编程的更为强大的技巧。

  使用__slots__:Python属于动态语言,可以允许已创建好的类动态地绑定任何属性和方法。但是,给实例绑定后,由该类创建的其他其他实例是没有绑定的;不过,可以给类绑定,那么有该类创建的实例均会拥有该属性和方法。

>>> class Student(object):
...     pass
...
>>> s = Student()
>>> s.name = 'Michael' # 动态给实例绑定一个属性
>>> print s.name
Michael
>>> def set_age(self, age): # 定义一个函数作为实例方法
...     self.age = age
...
>>> from types import MethodType
>>> s.set_age = MethodType(set_age, s, Student) # 给实例绑定一个方法
>>> s.set_age(25) # 调用实例方法
>>> s.age # 测试结果
25
>>> def set_score(self, score):
...     self.score = score
...
>>> Student.set_score = MethodType(set_score, None, Student)

  同时,正因为动态语言的这种特性,为了避免过于属性绑定,class类中使用__slots__来限制允许的属性。

>>> class Student(object):
...     __slots__ = ('name', 'age') # 用tuple定义允许绑定的属性名称
...
>>> s = Student() # 创建新的实例
>>> s.name = 'Michael' # 绑定属性'name'
>>> s.age = 25 # 绑定属性'age'
>>> s.score = 99 # 绑定属性'score'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'Student' object has no attribute 'score'

__slots__属性只对父类起作用,但继承的子类是不起作用的,除非子类中也定义__slots__,这样,子类允许定义的属性就是自身的__slots__加上父类的__slots__。

  使用@property装饰器:

  在实际使用中,为了检查修改参数的合法性,会定义一个get和set函数,对set函数进行一系列的严格验证。

class Student(object):

    def get_score(self):
        return self._score

    def set_score(self, value):
        if not isinstance(value, int):
            raise ValueError('score must be an integer!')
        if value < 0 or value > 100:
            raise ValueError('score must between 0 ~ 100!')
        self._score = value
>>> s = Student()
>>> s.set_score(60) # ok!
>>> s.get_score()
60
>>> s.set_score(9999)
Traceback (most recent call last):
  ...
ValueError: score must between 0 ~ 100!

  为了简化上述调用和设置的过程,将两个方法直接变成调用属性一样简单,使用@property装饰器。

class Student(object):

    @property
    def score(self):
        return self._score

    @score.setter
    def score(self, value):
        if not isinstance(value, int):
            raise ValueError('score must be an integer!')
        if value < 0 or value > 100:
            raise ValueError('score must between 0 ~ 100!')
        self._score = value
>>> s = Student()
>>> s.score = 60 # OK,实际转化为s.set_score(60)
>>> s.score # OK,实际转化为s.get_score()
60
>>> s.score = 9999
Traceback (most recent call last):
  ...
ValueError: score must between 0 ~ 100!

就能像调用普通属性一样方便了!

  也可以只定义只读属性:

class Student(object):

    @property
    def birth(self):
        return self._birth

    @birth.setter
    def birth(self, value):
        self._birth = value

    @property
    def age(self):
        return 2014 - self._birth
  •  多重继承

  在继承时,由于对象会有多个属性,一般按照继承关系来说,选取其中一个大的类别作为主线,即主线是单一继承下来的。除主线外,其他的属性可以作为功能,多重继承下来。这种设计称为Mixin。

  例如,下面中,Mammal和Bird作为主线,Runnable和Flyable作为Mixin功能增加进去,构成多重继承。

class Animal(object):
    pass

# 大类:
class Mammal(Animal):
    pass

class Bird(Animal):
    pass
class Runnable(object):
    def run(self):
        print('Running...')

class Flyable(object):
    def fly(self):
        print('Flying...')
class Dog(Mammal, Runnable):
    pass
class Bat(Mammal, Flyable):
    pass
  • 定制类

  之前提到过的以双下划线开头和结尾的属性,是属于特殊属性,它们是用来定制类的。

  __str__():显示print该类的实例的名称

>>> 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)

  __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('Michael')
>>>s
Student object (name: Michael)

  __iter__():服务于for...in的迭代循环,该方法返回一个迭代对象,然后,Python的for循环会一直调用next()方法得到循环的下一个值,直到遇到StopIteration错误后退出循环。

lass 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 > 100000: # 退出循环的条件
            raise StopIteration();
        return self.a # 返回下一个值
>>> for n in Fib():
...     print n
...
1
1
2
3
5
...
46368
75025

  __getitem__():允许像list一样按索引取值和切片。

class Fib(object):
    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
            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]
1
>>> f[1]
1
>>> f[2]
2
>>> f[3]
3
>>> f[10]
89
>>> f[100]
573147844013817084101
>>> f = Fib()
>>> f[0:5]
[1, 1, 2, 3, 5]
>>> f[:10]
[1, 1, 2, 3, 5, 8, 13, 21, 34, 55]

  __getattr__():当调用类的属性或者方法不存在时,运行__getattr__()方法。

class Student(object):

    def __init__(self):
        self.name = 'Michael'

    def __getattr__(self, attr):
        if attr=='score':
            return 99
>>> s = Student()
>>> s.name
'Michael'
>>> s.score
99

  __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()
My name is Michael.

通过callable()函数,可以判断一个对象是否是“可调用”对象。

  更多定制类的方法,参考Python文档

 注:本文为学习廖雪峰Python入门整理后的笔记

posted @ 2017-09-13 20:30  倔强的小蚂蚁  阅读(204)  评论(0编辑  收藏  举报