模版与模式#
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 施加了一些限制,做好了灵活性、可用性和安全性方面的平衡:
不能重载内置类型的运算符
不能新建运算符,只能重载现有的
不能重载判断运算符(
is
、and
、or
和not
)
位运算符(
&
、|
和~
)可以被重载
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__()
方法接收到的参数依次是:
当前准备创建的类的对象;
类的名字;
类继承的父类集合;
类的方法集合。
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)