在这个例子中,我是否违反了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.print_name() 方法被重新定义为不打印姓名,而是将其作为字符串返回,那么就会违反原则。
请注意,破坏 LSP 不需要更改代码,例如,如果人员的姓名格式从例如“将员工中的“first_name last_name”更改为“last_name,first_name”,这会导致程序给出不正确的输出。
我知道已经回答了,但我想强调: 我们需要区分两种关系。一是
Person
的实例与Employee
的实例之间的关系。第二个是 type
的 2 个实例之间的关系(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
接口,在这种情况下,这不会违反,因为您无法在 Person
类中删除 Employee
类的函数? (嗯,技术上可以,但这样做不是一个好主意)。
根据我对LSP的理解,它指出父类的对象或方法应该可以用子类中的对象或方法替换,而无需妥协(改变程序)
示例
class Bird:
def fly(self):
return "I can fly"
class Penguin(Bird):
def fly(self):
# Penguins cannot fly, so we override the fly method
return "Sorry, I can't fly"
def make_bird_fly(bird):
return bird.fly()
在这个例子中,我们有一个带有“fly”方法的 Bird 类。然后,我们有一个“Penguin”类,它是 Bird 的子类。 Penguin 类重写了 Fly 方法以指示企鹅不能飞。 所以如果我们调用 Bird 实例;
bird_instance = Bird()
print(make_bird_fly(bird_instance)) # Output: "I can fly"
企鹅实例;
penguin_instance = Penguin()
print(make_bird_fly(penguin_instance)) # Output: "Sorry, I can't fly"
“make_bird_fly”函数将 Bird 对象作为参数并调用其“fly”方法。由于Penguin是Bird的子类,因此我们可以将Penguin的实例传递给这个函数,而不会影响程序的正确性。这遵循里氏替换原则。
希望这有帮助! :)