工厂方法:它用于创建类的实例,例如一些预处理。如果使用@staticmethod代替,那我们不得不硬编码Pizza类名在函数中,这使得任何继承Pizza的类都不能使用我们这个工厂方法给它自己用。
class Pizza(object): def __init__(self, ingredients): self.ingredients = ingredients @classmethod def from_fridge(cls, fridge): return cls(fridge.get_cheese() + fridge.get_vegetables())
调用静态类:如果你把一个静态方法拆分成多个静态方法,除非你使用类方法,否则你还是得硬编码类名。使用这种方式声明方法,Pizza类名明永远都不会在被直接引用,继承和方法覆盖都可以完美的工作。
class Pizza(object): def __init__(self, radius, height): self.radius = radius self.height = height @staticmethod def compute_area(radius): return math.pi * (radius ** 2) @classmethod def compute_volume(cls, height, radius): return height * cls.compute_area(radius) def get_volume(self): return self.compute_volume(self.height, self.radius) 抽象方法抽象方法是定义在基类中的一种方法,它没有提供任何实现,类似于Java中接口(Interface)里面的方法。
在Python中实现抽象方法最简单地方式是:
class Pizza(object): def get_radius(self): raise NotImplementedError任何继承自Pizza的类必须覆盖实现方法get_radius,否则会抛出异常。
这种抽象方法的实现有它的弊端,如果你写一个类继承Pizza,但是忘记实现get_radius,异常只有在你真正使用的时候才会抛出来。
>>> Pizza() <__main__.Pizza object at 0x7fb747353d90> >>> Pizza().get_radius() Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 3, in get_radius NotImplementedError还有一种方式可以让错误更早的触发,使用Python提供的abc模块,对象被初始化之后就可以抛出异常:
import abc class BasePizza(object): __metaclass__ = abc.ABCMeta @abc.abstractmethod def get_radius(self): """Method that should do something."""使用abc后,当你尝试初始化BasePizza或者任何子类的时候立马就会得到一个TypeError,而无需等到真正调用get_radius的时候才发现异常。
>>> BasePizza() Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: Can't instantiate abstract class BasePizza with abstract methods get_radius 混合静态方法、类方法、抽象方法当你开始构建类和继承结构时,混合使用这些装饰器的时候到了,所以这里列出了一些技巧。
记住,声明一个抽象的方法,不要凝固方法的原型,这就意味着你必须实现它,但是我可以用任何参数列表来实现:
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这同样是正确的,因为它遵循抽象类BasePizza设定的契约。事实上get_ingredients方法并不需要知道返回结果是什么,结果是实现细节,不是契约条件。
因此,你不能强制抽象方法的实现是一个常规方法、或者是类方法还是静态方法,也没什么可争论的。从Python3开始(在Python2中不能如你期待的运行,见issue5867),在abstractmethod方法上面使用@staticmethod和@classmethod装饰器成为可能。
import abc class BasePizza(object): __metaclass__ = abc.ABCMeta ingredient = ['cheese'] @classmethod @abc.abstractmethod def get_ingredients(cls): """Returns the ingredient list.""" return cls.ingredients别误会了,如果你认为它会强制子类作为一个类方法来实现get_ingredients那你就错了,它仅仅表示你实现的get_ingredients在BasePizza中是一个类方法。
可以在抽象方法中做代码的实现?没错,Python与Java接口中的方法相反,你可以在抽象方法编写实现代码通过super()来调用它。(译注:在Java8中,接口也提供的默认方法,允许在接口中写方法的实现)
import abc class BasePizza(object): __metaclass__ = abc.ABCMeta default_ingredients = ['cheese'] @classmethod @abc.abstractmethod def get_ingredients(cls): """Returns the ingredient list.""" return cls.default_ingredients class DietPizza(BasePizza): def get_ingredients(self): return ['egg'] + super(DietPizza, self).get_ingredients()这个例子中,你构建的每个pizza都通过继承BasePizza的方式,你不得不覆盖get_ingredients方法,但是能够使用默认机制通过super()来获取ingredient列表。
下面关于Python的文章您也可能喜欢,不妨看看:
CentOS上源码安装Python3.4
《Python核心编程 第二版》.(Wesley J. Chun ).[高清PDF中文版]