7.6 混合使用静态方法、类方法和抽象方法

这些装饰器各有各的用处,但有时可能会需要同时使用。下面介绍一些相关的小技巧。

抽象方法的原型并非一成不变。在实际实现方法的时候,可以根据需要对方法的参数进行扩展。

广告:个人专属 VPN,独立 IP,无限流量,多机房切换,还可以屏蔽广告和恶意软件,每月最低仅 5 美元

import abc

class BasePizza(object):
  __metaclass__ = abc.ABCMeta

  @abc.abstractmethod
  def get_ingredients(self):
     """Returns the ingredient list."""

class Calzone(BasePizza):
  def get_ingredients(self, with_egg=False):
     egg = Egg() if with_egg else None
     return self.ingredients + [egg]

这里可以任意定义Calzone的方法,只要仍然支持在基类BasePizza中定义的接口。这包括将它们作为类方法或静态方法进行实现:

import abc

class BasePizza(object):
  __metaclass__ = abc.ABCMeta

  @abc.abstractmethod
  def get_ingredients(self):
     """Returns the ingredient list."""

class DietPizza(BasePizza):
  @staticmethod
  def get_ingredients():
     return None

尽管静态方法get_ingredients没有基于对象的状态返回结果,但是它仍然满足在基类BasePizza中定义的抽象接口,所以它仍然是有效的。

从Python 3开始(在Python 2中有问题,详见issue 5867,http://bugs.python.org/issue5867),可能会支持在@abstractmethod之上使用@staticmethod@classmethod装饰器,如示例7.13所示。

示例 7.13 混合使用@classmethod@abstractmethod

import abc

class BasePizza(object):
  __metaclass__ = abc.ABCMeta

  ingredients = ['cheese']

  @classmethod
  @abc.abstractmethod
  def get_ingredients(cls):
     """Returns the ingredient list."""
     return cls.ingredients

注意,像这样在BasePizza中定义get_ingredients为类方法并不会强迫其子类也将其定义为类方法。将其定义为静态方法也是一样,没有办法强迫子类将抽象方法实现为某种特定类型的方法。

但是等一下,这里我们在抽象方法中居然是有实现代码的。真的可以吗?是的,在Python中完全没问题!不同于Java,Python中可以在抽象方法中放入代码并使用super()调用它,如示例7.14所示。

示例 7.14 通过抽象方法使用super()

import abc

class BasePizza(object):
  __metaclass__ = abc.ABCMeta

  default_ingredients = ['cheese']

  @classmethod
  @abc.abstractmethod
  def get_ingredients(cls):
     """Returns the default ingredient list."""
     return cls.default_ingredients

class DietPizza(BasePizza):
  def get_ingredients(self):
    return [Egg()] + super(DietPizza, self).get_ingredients()

在这个例子中,每一个新的继承自BasePizza基类的Pizza子类都必须重写get_ingredients方法,但它可以通过基类的默认机制访问原料表。