美文网首页
Python 面向对象高级编程

Python 面向对象高级编程

作者: _YZG_ | 来源:发表于2017-12-24 19:05 被阅读9次

    使用__slots__

    限制实例的属性只允许对Student实例添加name和age
    class Student(object):
        __slots__ = ('name', 'age') # 用tuple定义允许绑定的属性名称
    
    使用__slots__要注意,__slots__定义的属性仅对当前类实例起作用,对继承的子类是不起作用的
    
    
    >>> class GraduateStudent(Student):
    ...     pass
    ...
    >>> g = GraduateStudent()
    >>> g.score = 9999
    除非在子类中也定义__slots__,这样,子类实例允许定义的属性就是自身的__slots__加上父类的__slots__。
    

    使用@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 Dog(Mammal, RunnableMixIn, CarnivorousMixIn):
        pass
    
    

    定制类

    __str__
    
    class Student(object):
        def __init__(self, name):
            self.name = name
        def __str__(self):
            return 'Student object (name=%s)' % self.name
        __repr__ = __str__
    
    
    >>> print(Student('Michael'))
    Student object (name: Michael)
    
    >>> s = Student('Michael')
    >>> s
    
    
    
    __iter__
    
    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 > 100000: # 退出循环的条件
                raise StopIteration()
            return self.a # 返回下一个值
    
    >>> for n in Fib():
    ...     print(n)
    ...
    1
    1
    2
    3
    5
    ...
    46368
    75025
    
    
    __getitem__
    
    Fib实例虽然能作用于for循环,看起来和list有点像,但是,把它当成list来使用还是不行。
    比如去第5个元素
    
    class Fib(object):
        def __getitem__(self, n):
            a, b = 1, 1
            for x in range(n):
                a, b = b, a + b
            return a
    
    list有个神奇的切片方法,__getitem__()传入的参数可能是一个int,也可能是一个切片slice,所以判断
    class Fib(object):
        def __getitem__(self, n):
            if isinstance(n, int): # n是索引
                a, b = 1, 1
                for x in range(n):
                    a, b = b, a + b
                return a
            if isinstance(n, slice): # n是切片
                start = n.start
                stop = n.stop
                if start is None:
                    start = 0
                a, b = 1, 1
                L = []
                for x in range(stop):
                    if x >= start:
                        L.append(a)
                    a, b = b, a + b
                return L
    
    这里没有对step参数做处理,也没有对非负数做处理,所以,要正确实现一个__getitem__()还有很多工作要做
    
    
    __getattr__
    class Student(object):
    
        def __init__(self):
            self.name = 'Michael'
    
        def __getattr__(self, attr):
            if attr=='score':
                return 99
    
    返回函数
    class Student(object):
    
        def __getattr__(self, attr):
            if attr=='age':
                return lambda: 25
    
    只是调用方式要变为:
    
    >>> s.age()
    25
    
    
    只有在没有找到属性的情况下,才调用__getattr__,已有的属性,比如name,不会在__getattr__中查找
    任意调用如s.abc都会返回None,因为__getattr__默认返回就是None,要让class只显影特定的几个属性,我们要按照约定,抛出AttributeError的错误
    class Student(object):
    
        def __getattr__(self, attr):
            if attr=='age':
                return lambda: 25
            raise AttributeError('\'Student\' object has no attribute \'%s\'' % attr)
    
    链式调用
    class Chain(object):
    
        def __init__(self, path=''):
            self._path = path
    
        def __getattr__(self, path):
            return Chain('%s/%s' % (self._path, path))
    
        def __str__(self):
            return self._path
    
        __repr__ = __str__
    
    
    >>> Chain().status.user.timeline.list
    '/status/user/timeline/list'
    
    
    __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() # self参数不要传入
    My name is Michael.
    
    通过callable()函数,我们就可以判断一个对象是否是“可调用”对象。
    >>> callable(Student())
    True
    >>> callable(max)
    True
    >>> callable([1, 2, 3])
    False
    >>> callable(None)
    False
    >>> callable('str')
    False
    

    使用枚举类

    
    Enum可以把一组相关常量定义在一个class中,且class不可变,而且成员可以直接比较。
    
    from enum import Enum
    
    Month = Enum('Month', ('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'))
    
    
    for name, member in Month.__members__.items():
        print(name, '=>', member, ',', member.value)
    
    value属性自动赋给成员的int常量,默认从1开始计数
    
    如果需要更精确地控制枚举类型,可以从Enum派生出自定义类
    
    from enum import Enum, unique
    
    @unique
    class Weekday(Enum):
        Sun = 0 # Sun的value被设定为0
        Mon = 1
        Tue = 2
        Wed = 3
        Thu = 4
        Fri = 5
        Sat = 6
    
    @unique装饰器可以帮助我们检查保证没有重复值。
    
    访问这些枚举类型可以有若干种方法:
    
    >>> day1 = Weekday.Mon
    >>> print(day1)
    Weekday.Mon
    >>> print(Weekday.Tue)
    Weekday.Tue
    >>> print(Weekday['Tue'])
    Weekday.Tue
    >>> print(Weekday.Tue.value)
    2
    >>> print(day1 == Weekday.Mon)
    True
    >>> print(day1 == Weekday.Tue)
    False
    >>> print(Weekday(1))
    Weekday.Mon
    >>> print(day1 == Weekday(1))
    True
    >>> Weekday(7)
    Traceback (most recent call last):
      ...
    ValueError: 7 is not a valid Weekday
    >>> for name, member in Weekday.__members__.items():
    ...     print(name, '=>', member)
    ...
    Sun => Weekday.Sun
    Mon => Weekday.Mon
    Tue => Weekday.Tue
    Wed => Weekday.Wed
    Thu => Weekday.Thu
    Fri => Weekday.Fri
    Sat => Weekday.Sat
    

    使用元类

    class Hello(object):
        def hello(self, name='world'):
            print('Hello, %s.' % name)
    
    
    >>> from hello import Hello
    >>> h = Hello()
    >>> h.hello()
    Hello, world.
    >>> print(type(Hello))
    <class 'type'>
    >>> print(type(h))
    <class 'hello.Hello'>
    
    
    >>> def fn(self, name='world'): # 先定义函数
    ...     print('Hello, %s.' % name)
    ...
    >>> Hello = type('Hello', (object,), dict(hello=fn)) # 创建Hello class
    >>> h = Hello()
    >>> h.hello()
    Hello, world.
    >>> print(type(Hello))
    <class 'type'>
    >>> print(type(h))
    <class '__main__.Hello'>
    
    要创建一个class对象,type()函数依次传入3个参数:
    
    class的名称;
    继承的父类集合,注意Python支持多重继承,如果只有一个父类,别忘了tuple的单元素写法;
    class的方法名称与函数绑定,这里我们把函数fn绑定到方法名hello上。
    
    通过type()函数创建的类和直接写class是完全一样的,因为Python解释器遇到class定义时,仅仅是扫描一下class定义的语法,然后调用type()函数创建出class。
    

    相关文章

      网友评论

          本文标题:Python 面向对象高级编程

          本文链接:https://www.haomeiwen.com/subject/qnyawxtx.html