模版与模式#

1. 模板化#

1.1. 继承#

通过继承,子类对象会拥有父类的属性,并可调用父类的方法,但父类中的私有方法和属性不能被继承。

Python 会按照特定的顺序遍历继承图。这个顺序叫方法解析顺序(Method Resolution Order,MRO)。类都有一个名为 __mro__ 的属性,它的值是一个元组,按照方法解析顺序列出各个超类,从当前类一直向上,直到 object 类。

class Cat():

    def __init__(self, name, color="white"):
        self.name = name
        self.color = color

    def run(self):
        print(f"{self.name} is running")


class Bosi(Cat):

    def setNewName(self, newName):
        self.name = newName

    def eat(self):
        print(f"{self.name} is eating")


bs = Bosi("Indian")
print(f'Name: {bs.name}') # Name: Indian
print(f'Color: {bs.color}') # Color: white
bs.eat() # Indian is eating
bs.setNewName('Persian')
bs.run() # Persian is running

1.2. 多态#

多态:父类定义一个方法但不去实现,让其子类去实现,每个子类有不同的表现。

class Dog:
    def show(self):
        pass


class bigDog(Dog):
    def show(self):
        print("Hi, I am a big dog")


class littleDog(Dog):
    def show(self):
        print("Hi, I am a little dog")


dog1 = bigDog()
dog2 = littleDog()

dog1.show() # Hi, I am a big dog
dog2.show() # Hi, I am a little dog

2. 重载#

2.1. 运算符#

在某些圈子中,运算符重载的名声并不好。这个语言特性已经被滥用,导致缺陷和意料之外的性能瓶颈。但若使用得当,API 会变得好用,代码会变得易于阅读。Python 施加了一些限制,做好了灵活性、可用性和安全性方面的平衡:

  • 不能重载内置类型的运算符

  • 不能新建运算符,只能重载现有的

  • 不能重载判断运算符(isandornot

位运算符(&|~)可以被重载

2.2. 类方法#

若子类中,有一个和父类相同名字的方法,在子类中的方法会覆盖掉父类中同名的方法。

父类中被重写的方法的调用:

  • 途径 1: parentClassName.func(self)

  • 途径 2: super().func() ,相当于将子类替换为父类,直接对父类操作。

class MyBaseClass:

    def __init__(self, value):
        self.value = value


class TimesSeven(MyBaseClass):

    def __init__(self, value):
        MyBaseClass.__init__(self, value)
        self.value *= 7


class PlusNine(MyBaseClass):

    def __init__(self, value):
        MyBaseClass.__init__(self, value)
        self.value += 9


class ThisWay(TimesSeven, PlusNine):

    def __init__(self, value):
        TimesSeven.__init__(self, value)
        print(self.value)
        PlusNine.__init__(self, value)
        print(self.value)

foo = ThisWay(5)

class TimesSevenCorrect(MyBaseClass):

    def __init__(self, value):
        super().__init__(value)
        self.value *= 7


class PlusNineCorrect(MyBaseClass):

    def __init__(self, value):
        super().__init__(value)
        self.value += 9


class GoodWay(PlusNineCorrect, TimesSevenCorrect):

    def __init__(self, value):
        super().__init__(value)
        print(self.value)


foo = GoodWay(5)
mro_str = '\n'.join(repr(cls) for cls in GoodWay.mro())
print(mro_str)

3. 改进#

3.1. 限制属性#

创建了大量的(比如百万级)实例,为此占用了大量的内存。对于那些主要用作简单数据结构的类,通常可以在类定义中增加 __slot__ 属性,之后的实例不能再有 __slots__ 中所列名称之外的其他属性。

  • 每个子类都要定义 __slots__ 属性,因为解释器会忽略继承的 __slots__ 属性。

  • 实例只能拥有 __slots__ 中列出的属性,除非把 __dict__ 加入 __slots__ 中(这样做就失去了节省内存的功效)。

  • 若不把 __weakref__ 加入 __slots__,实例就不能作为弱引用的目标。

class Date:
    __slots__ = ['year', 'month', 'day']

    def __init__(self, year, month, day):
        self.year = year
        self.month = month
        self.day = day

当定义了 __slots__ 属性时,Python 就会针对实例采用一种更加紧凑的内部表示。不再让每个实例都创建一个 __dict__ 字典,现在的实例是围绕着一个固定长度的小型数组来构建的,这和一个元组或者列表很相似。在 __slots__ 中列出的属性名会在内部映射到这个数组的特定索引上。

4. 模式#

4.1. 工厂模式#

工厂模式:定义 1 个创建对象的接口(表现为函数),但由子类决定要实例化的类是哪一个。

# 1. 定义一个容器父类
class CarStore():
    # 仅定义这个方法,不实现,具体功能在子类中实现
    def createCar(self, typeName):
        pass

    # 定义指令方法
    def order(self, typeName):
        # 调用子类方法
        self.car = self.createCar(typeName)
        self.car.move()
        self.car.stop()


# 2. 定义容器子类
class MyCarStore(CarStore):
    # 定义元素生产方法,调用工厂类,返回工厂类返回值
    def createCar(self, typeName):
        self.carFactory = CarFactory()
        return self.carFactory.createCar(typeName)


# 3. 定义工厂类
class CarFactory():
    # 定义元素生产方法,关联元素类参数
    def createCar(self, typeName):
        self.typeName = typeName
        if self.typeName == 'Elant':
            self.car = Elantra()
        elif self.typeName == 'Sonata':
            self.car = SuonataCar()
        return self.car


# 4. 定义元素类
class Elantra():
    def move(self):
        print('Moving')

    def stop(self):
        print('Parking')


class SuonataCar():
    def move(self):
        print('Moving')

    def stop(self):
        print('Parking')


suonata = MyCarStore()
suonata.order('Sonata')

4.2. 单例模式#

单例类即不管怎么创建,只会生成一个对象的类,其生成的对象的属性相互绑定。换句话说,单例只执行 1 次 __init__ 方法,不管怎么实例化,均指向同一个 id。创建单例需要使用 __new__ 方法。

__new__ 是类中执行的第一个方法,无论在类中什么位置,且,必须要有返回值,表示创建出的对象的引用。__new__ 返回值可是 object.__new__(cls) ,也可是 className.__new__(cls) 。若没有返回值,将仅执行 __new__(cls) 方法, cls 指向类对象。

__init__ 的参数 self ,就是 __new__ 返回的实例, __init__ 不需要返回值。 __init__ 的其他参数,需要传入 __new__

__new__() 方法接收到的参数依次是:

  1. 当前准备创建的类的对象;

  2. 类的名字;

  3. 类继承的父类集合;

  4. 类的方法集合。

class Dog():
    # 建立 1 个类属性,用于保存对象的引用次数,初始值为空
    __instance = None
   # 建立 1 个类属性,用于判断是否初始化,初始值为假
    __init_flag = False
    # 约定创建方法,创建需要传入 2 个参数
    def __new__(cls, age, name):
    # 若对象的引用为 0,则调用创建方法
        if cls.__instance == None:
            cls.__instance = object.__new__(cls)
            return cls.__instance
    # 若对象的引用不为 0,则不调用创建方法
        else:
            return cls.__instance
    def __init__(self, age, name):
        if Dog.__init_flag == False:
            self.age = age
            self.name = name
            Dog.__init_flag = True

a = Dog(7, "A")
b = Dog(8, "B")
print(a.age)
print(b.age)
a.age = 9
print(b.age)