在此例中,我是否违反了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
类的函数?(嗯,从技术上讲,你可以这样做,但这不是一个好主意)。