Python理解Liskov代换原理



在此例中,我是否违反了LSP?因为直接用子类的实例替换最后两行会给我一个错误(因为工资没有初始化)?

person_1 = Employee('Brad')
person_1.print_name()
@dataclass
class Person:
    name: str
    def print_name(self):
        print(self.name)
@dataclass
class Employee(Person):
    wage: int
person_1 = Person('Brad')
person_1.print_name()

如果是这样,那么在扩展类的构造函数时(除了此后放置可选属性之外),如何能够不违反LSP呢?

LSP表示,如果Person对象为真(例如,它有一个名称,该名称是一个字符串,它可以打印它的名称),那么对于Employee对象也必须为真。换句话说,每个Employee也是一个Person。

它没有说明Employee对象必须以与Person对象相同的方式创建。每个员工不仅仅是一个人。它不仅有名字,还有工资。


第二个问题:

如果Employee.print_name()方法被重新定义为不打印名称,而是以字符串形式返回,这将违反原则。

请注意,破坏LSP不需要更改代码,例如,如果Person的名称格式从"first_name last_name"到last_name, first_name;,这会导致程序给出错误的输出。

我知道已经有人回答了,但我想强调:我们需要区分两种关系。其一是Person的实例与Employee的实例之间的关系。二是type的两个实例(Person类本身和Employee类本身)之间的关系

在你的例子中,LSP只处理前者(我们可以对Person实例做的一切,我们需要能够以完全相同的方式对Employee实例做)。它没有说明类本身。

现在,因为python是非常动态的,你可以在技术上说&嘿,等一下!有一件事我能做,另一件不行!"看一下下面的例子:

# Assume we have an instance of either Person or Employee here
instance = _
# The following will work with Person instances but will raise an exception for Employee instances
instance_copy = type(instance)(instance.name)

我会说你不应该计算这种东西。我们称之为"不合理的使用预期"。在绝大多数用例中考虑代码结构的有效性时,不应该考虑这一点。

要记住的最重要的事情是:B inherits from A != A (the class object itself) can be substituted by B (the class itself)

这取决于你对LSP的定义。

它是否意味着严格的LSP,就像Barbara Liskov的原始论文中那样,程序的行为应该通过类型替换而保持不变?(即使这样,它也是一个期望的属性,而不是绝对的要求)

或者它是否意味着遵循Person接口,在这种情况下,它不会违反,因为你不能在Employee类中删除Person类的函数?(嗯,从技术上讲,你可以这样做,但这不是一个好主意)。

最新更新