一、抽象父类
首先看一个例子specialize.py:
class Super:
def method(self):
print('in Super.method')
def delegate(self):
self.action()
class Provider(Super):
def action(self):
print('in Provider.action')
if __name__ == "__main__":
x = Provider()
x.method()
x.delegate()
%python specialize.py
in Super.method
in Provider.action
我们来思考一下,当我们通过Provider实例调用delegate方法时,会发生两次独立的继承搜索:
- 在x.delegate调用一开始,Python会搜索Provider实例和类树中更上层的类对象,直到在Super中找到delegate的方法。实例x会照常传给该方法的self参数。
- 在Super.delegate方法中,self.action会对self以及它上层的对象发起另一次新的继承搜索。因为self引用了一个Provider的实例,所以action方法会在Provider子类中找到。
我们从delegate方法的角度来看,这个例子的父类就可以被称为抽象父类,也就是类的部分行为预期由其子类来提供。如果所预期的方法没有在子类中定义,那么当继承搜索失败的python就会引发名称未定义的异常。
class Super:
def method(self):
print('in Super.method')
def delegate(self):
self.action()
class Provider(Super):
pass
if __name__ == "__main__":
x = Provider()
x.method()
x.delegate()
%python specialize.py
Traceback (most recent call last):
File "/Users/unity/projects/python-node/specialize.py", line 16, in <module>
x.delegate()
File "/Users/unity/projects/python-node/specialize.py", line 6, in delegate
self.action()
AttributeError: 'Provider' object has no attribute 'action'
in Super.method
在python中提供特殊的语法来实现抽象父类(抽象基类)
Python3的实现:abstract.py
from abc import ABCMeta, abstractmethod
class Super(metaclass=ABCMeta):
def delegate(self):
self.action()
@abstractmethod
def action(self):
pass
class Sub(Super):
pass
if __name__=="__main__":
x = Sub()
% python abstract.py
Traceback (most recent call last):
File "D:/WeChatProjects/pythonCode/simplecode/abstract.py", line 18, in <module>
x = Sub()
TypeError: Can't instantiate abstract class Sub with abstract methods action
子类中没有定义action方法就不能进行实例化操作。
from abc import ABCMeta, abstractmethod
class Super(metaclass=ABCMeta):
def delegate(self):
self.action()
@abstractmethod
def action(self):
pass
class Sub(Super):
def action(self):
print("sub.action")
if __name__=="__main__":
x = Sub()
x.delegate()
% python abstract.py
sub.action
采用这种方式编写代码后,除非子类中的所有抽象方法都已经定义,否则带有抽象方法的类是不能进行实例化的。
二、OOP设计模式:继承。“is-a”关系。
故事:开一家披萨店,需要员工为顾客服务、准备食物等。当然,作为一名程序员,我们要高级一点,除了普通员工外决定开发一个机器人来制作披萨,出于礼貌和正确控制,我们决定把机器人做成有薪水成本的功能齐全的员工。
设计:最通用的类Employee提供共同行为,例如加薪(giveRaise)和打印(__repr__)。员工有两种,所以Employee有两个子类:Chef和Server。这两个子类都会继承work方法来打印更详细的信息。而我们的披萨机器人PizzaRobot是一种chef,也是一种Employee。我们称为“is-a”链:机器人是一个主厨,而主厨是一个员工。
以下是employees.py文件:
class Employee:
def __init__(self, name, salary=0):
self.name = name
self.salary = salary
def giveRaise(self, percent):
self.salary = self.salary + (self.salary * percent)
def work(self):
print(self.name, "does stuff")
def __repr__(self):
return "<Employee:name={},salary={}>".format(self.name, self.salary)
class Chef(Employee):
def __init__(self, name):
Employee.__init__(self, name, 50000)
def work(self):
print(self.name, "makes food")
class Server(Employee):
def __init__(self,name):
Employee.__init__(self, name, 40000)
def work(self):
print(self.name, "interfaces with customer")
class PizzaRobot(Chef):
def __init__(self,name):
Chef.__init__(self, name)
def work(self):
print(self.name, "makes pizza")
if __name__ == "__main__":
bob = PizzaRobot("bob")
print(bob)
bob.work()
bob.giveRaise(0.20)
print(bob)
% python employees.py
<Employee:name=bob,salary=50000>
bob makes pizza
<Employee:name=bob,salary=60000.0>
创建一个名为bob的机器人,打印bob会执行Employee.__repr__方法,而给bob加薪则会运行Employee.giveRaise方法,因为继承会在employee中找到方法。
从程序员的角度看,继承是由属性点号操作启动的,并由此触发实例、类以及任何父类中的变量名搜索。从类的设计者的角度看,继承是一种指明集合成员关系的方式:类定义了一个属性集合,可由更具体的集合(子类)继承和定制。
三、OOP设计模式:组合。“has-a”关系
故事:披萨店是一个组合对象:有一个烤炉,也有服务生和主厨这些员工。当顾客来店里下单时,店里的组件就会开始行动:服务生接单,主厨制作披萨。
设计:PizzaShop类是容器和控制器。它的构造函数创建并嵌入员工类,以及定义Oven类的实例。
以下是pizzashop.py文件:
from employees import PizzaRobot, Server
class Customer:
def __init__(self, name):
self.name = name
def order(self, server):
print(self.name, "orders from", server)
def pay(self, server):
print(self.name, "pays for item to", server)
class Oven:
def bake(self):
print("oven bakes")
class PizzaShop:
def __init__(self):
self.server = Server('Pat')
self.chef = PizzaRobot('Bob')
self.oven = Oven()
def order(self, name):
customer = Customer(name)
customer.order(self.server)
self.chef.work()
self.oven.bake()
customer.pay(self.server)
if __name__ == "__main__":
scene = PizzaShop()
scene.order("Homer")
print("...")
scene.order("shaggy")
% python pizzashop.py
Homer orders from <Employee:name=Pat,salary=40000>
Bob makes pizza
oven bakes
Homer pays for item to <Employee:name=Pat,salary=40000>
...
shaggy orders from <Employee:name=Pat,salary=40000>
Bob makes pizza
oven bakes
shaggy pays for item to <Employee:name=Pat,salary=40000>
在测试程序中,调用PizzaShop的order方法时,内嵌对象会按照顺序进行工作。每份工作订单都会创建一个新的Customer对象,并且把内嵌的Server对象传给Customer的方法。虽然顾客是流动的,但是服务员是披萨店的组件。
从程序员的角度看,组合涉及把其他对象嵌入容器对象内,并促使其实现容器方法。对类的设计者来说,组合是在一个问题领域中另一呈现关系的方式。但是,组合不是集合的成员关系,而是组件,也是整体的组成部分。
四、OOP设计模式:委托。“包装器”代理对象
委托通常是指控器对象内嵌其他对象,并把操作请求传递那些内嵌的对象。控制器能够负责管理类活动,例如记录日志和验证访问,为接口组件添加额外步骤,或监视活跃实例。
在python中,通常使用__getattr__方法钩子来实现委托。因为这个运算符重载方法会拦截对不存在属性的访问,所以包装器类可以使用__getattr__方法来把任意的访问转发给被包装的对象。因为该方法允许一般性地路由属性请求,所以包装器类保有被包装对象的接口,并可以添加其他的附加操作。
class Wrapper:
def __init__(self, object):
self.wrapped = object
def __getattr__(self, attrname):
print("Trace:" + attrname)
return getattr(self.wrapped, attrname)
if __name__ == "__main__":
x = Wrapper([1, 2, 3])
x.append(4)
print(x.wrapped)
% python trace.py
Trace:append
[1, 2, 3, 4]
对x.append(4)操作会被__getattr__拦截,getattr内置函数——通过名称字符串获取被包装对象的属性:getattr(X,N)就像X.N,只不过这里N时在运行时求值为字符串的表达式,而不是变量。
小结
委托:把对象包装在代理类内
组合:控制内嵌的对象
继承:从其他类中获取行为
网友评论