将要覆盖的实现方法分开是pythonic吗?
Posted
技术标签:
【中文标题】将要覆盖的实现方法分开是pythonic吗?【英文标题】:Is it pythonic to separate implementation method to be overridden? 【发布时间】:2013-11-24 20:19:10 【问题描述】:我发现将抽象方法分成两种方法似乎很有用,一种用于公共接口,另一种用于子类覆盖。
通过这种方式,您可以为输入和输出添加前置条件/后置条件检查,使其能够抵御人为错误。
但我关心的是它是否在 python 上可接受,因为根据我的经验,我从未见过这样的代码。
正常多态性
import abc
class Shape:
"""Abstract base class for shapes"""
__metaclass__ = abc.ABCMeta
@abc.abstractmethod
def get_area(self, scale):
"""Calculates the area of the shape, scaled by a factor.
Do not blame for a silly example.
"""
pass
class Rectangle(Shape):
def __init__(self, left, top, width, height):
self.left = left
self.top = top
self.width = width
self.height = height
def get_area(self, scale):
return scale * self.width * self.height
print(Rectangle(10, 10, 40, 40).get_area(3))
# Gosh!... gets tons of 3's
print(Rectangle(10, 10, 40, 40).get_area((3,)))
实现方法分离
import abc
class Shape:
"""Abstract base class for shapes"""
__metaclass__ = abc.ABCMeta
def get_area(self, scale):
"""Calculates the area of the shape, scaled by a factor"""
# preconditions
assert isinstance(scale, (int,float))
assert scale > 0
ret = self._get_area_impl(scale)
# postconditions
assert isinstance(ret, (int,float))
assert ret > 0
return ret
@abc.abstractmethod
def _get_area_impl(self, scale):
"""To be overridden"""
pass
class Rectangle(Shape):
def __init__(self, left, top, width, height):
self.left = left
self.top = top
self.width = width
self.height = height
def _get_area_impl(self, scale):
return scale * self.width * self.height
print(Rectangle(10, 10, 40, 40).get_area(3))
print(Rectangle(10, 10, 40, 40).get_area((3,))) # Assertion fails
【问题讨论】:
我不是说它是或不是,但是给被覆盖的方法一个“私有”前导下划线名称绝对是很奇怪的。 在 Python 中,通常由 API 的 消费者 来获取正确的参数。如果消费者想给你一个scale
这是一个元组,那就这样吧,那是他们的头,不是你的。
我不知道它是不是pythonic...不是说实现是不是pythonic;正如martijn所说,只是python程序员不倾向于这种严谨性。在 C 等语言中你不需要它,因为你有静态类型检查。它肯定会对性能产生重大影响。
【参考方案1】:
除了您描述的之外,还有我看到的替代方案:
-
使用“实现方法分离”技术,但给它一个更友好的名称(没有下划线,可能是一个可以将它与
get_area_from_validated_scale
等公共 API 区分开来的名称)。我在我的项目中使用了这种技术。
执行“实现方法分离”技术,用户覆盖该方法但调用进行验证的基类方法。 (不过,如果您需要验证输入和输出,则会变得更加复杂。)
在基类上创建 2 个方法 get_area_validate_input
和 get_area_validate_output
,并要求实现者在每次实现 get_area
时调用它们。
#2 和 #3 的变体,但使用装饰器完成,因此实现者只需将 @area_validation
放在其方法定义之上。
【讨论】:
以上是关于将要覆盖的实现方法分开是pythonic吗?的主要内容,如果未能解决你的问题,请参考以下文章