Python学习(七)—— 面向对象高级编程

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/q1449516487/article/details/87912200

一、使用__slots__

众所周知,Python的动态绑定允许我们在程序运行的过程中动态给class或实例加上功能。但是如果我们想要限制实例的属性,则可以在定义class的时候,定义一个特殊的__slots__变量,来限制该class实例能添加的属性:

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

需要注意的是,__slots__定义的属性仅对当前类实例起作用,对继承的子类是不起作用的。除非在子类中也定义__slots__,这样子类实例允许定义的属性就是自身的__slots__加上父类的__slots__

二、使用@property

在class中定义一个属性时,直接将属性暴露出去的方法会使得属性可被任意修改,这显然是不可取的。一般情况下,我们可以通过定义get和set的方法来加以限制:

class Student(object):
    def get_score(self):
        return self._score
    def set_score(self, value):
        if not isinstance(value, int):
            raise ValueError('scoer must be an integet!')
        if value < 0 or value > 100:
            raise ValueError('score must between 0 ~ 100!')
        self._score = value

而Python提供了另一种简洁的方法:@property装饰器负责把一个方法变成属性调用

class Student(object):
    @property               #相当于get
    def score(self):
        return self._score
    @score.setter           #相当于set
    def score(self, value):
        if not isinstance(vlaue, int):
            raise ValueError('score must be an integer!')
        if value < 0 or value > 100:
            raise ValueError('score must between 0 ~ 100!')
        self._score = value
    #score方法可以当作属性一样调用

三、多重继承

在设计类的继承关系时,通常主线都是单一继承下来的。但是,如果需要“混入”额外的功能,通过多重继承就可以实现。这种设计通常称之为Mixln。

Python自带的很多库也使用了Mixln。举个例子,Python自带了TCPServerUDPServer这两类网络服务,而要同时服务多个用户就必须使用多进程或多线程模型,这两种模型由ForkingMixInThreadingMixIn提供。通过组合,我们就可以创造出合适的服务来。

比如,编写一个多进程模式的TCP服务,定义如下:

class MyTCPServer(TCPServer, ForkingMixIn):
    pass

编写一个多线程模的UDP服务,定义如下:

class MyUDPServer(UDPServer, ThreadingMixIn):
    pass

这样一来,我们不需要复杂而庞大的继承链,只要选择组合不同的类的功能,就可以快速构造出所需的子类。

四、定制类

看到类似__slots__这种形如__xxx__的变量或者函数名就要注意,这些在Python中是有特殊用途的。Python的class中有许多这样有特殊用途的函数,可以帮助我们定制类。

函数或变量(特殊) 说明 用法
__str__() 返回用户看到的字符串

>>> 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循环,类似list或tuple那样,就必须实现__iter__()方法。该方法返回一个迭代对象,for循环就会不断调用该迭代对象的__next__()方法拿到循环的下一个值,知道遇到StopIteration错误时退出循环。

斐波那契数列:

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          #返回下一个值

__getitem__

(1)让实例获得索引功能

(2)让实例获得切片功能

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

__getattr__ 动态返回一个属性

class Student(objcet):

    def __init__(self, attr):

          if attr = 'score':

              return 99

          if attr='age':

              return lambda: 25

          raise AttributeError('\'Student\' object has no attribute \'%s\'' % attr)

__call__ 任何类,只需要定义一个__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.

以上只介绍了最常用的几个定制方法,还有很多可定制的方法,请参考Python官方文档

五、使用枚举类

在Python中要定义枚举类型,可以定义一个class类型,每个常量都是class的一个唯一实例。Python提供了Enum类来实现这个功能:

from enum import Enum
Month = Enum('Month', ('Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'))

这样我们就获得了Month类型的枚举类,可以直接使用Month.Jan来引用一个常量,或者枚举它的所有成员:

for name, member in Month.__members__.items():
    print(name, '=>', member, ',', member.value)   #value属性则是自动赋给成员的int常量,默认从1开始计数

如果需要更精确地控制枚举类型,可以从Enum派生出自定义类:

from enum import Enum, unique
@unique        #@unique装饰器可以帮助我们检查保证没有重复值
class Weekday(Enum):
    Sun = 0 #Sun的value被设定为0
    Mon = 1
    Tue = 2
    Wed = 3
    Thu = 4
    Fri = 5
    Sat = 6

访问这些枚举类型可以有若干种方法:

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

可见,既可以用成员名称引用枚举常量,又可以直接根据value的值获得枚举常量。

猜你喜欢

转载自blog.csdn.net/q1449516487/article/details/87912200
今日推荐