Python理解里氏替换原理

问题描述 投票:0回答:4

在这个例子中,我是否违反了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(除了随后放置可选属性)?

python solid-principles liskov-substitution-principle
4个回答
2
投票

LSP 表示,如果某件事对于 Person 对象来说是正确的(例如,它有一个名字,名字是一个字符串,它可以打印它的名字),那么对于 Employee 对象来说它也必须是正确的。换句话说,每个员工也是一个人。

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


第二个问题:

如果 Employee.print_name() 方法被重新定义为不打印姓名,而是将其作为字符串返回,那么就会违反原则。

请注意,破坏 LSP 不需要更改代码,例如,如果人员的姓名格式从例如“将员工中的“first_name last_name”更改为“last_name,first_name”,这会导致程序给出不正确的输出。


1
投票

我知道已经回答了,但我想强调: 我们需要区分两种关系。一是

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)


0
投票

这取决于您所说的 LSP 的含义。

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

或者这是否意味着遵循

Person
接口,在这种情况下,这不会违反,因为您无法在
Person
类中删除
Employee
类的函数? (嗯,技术上可以,但这样做不是一个好主意)。


0
投票

根据我对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的实例传递给这个函数,而不会影响程序的正确性。这遵循里氏替换原则。

希望这有帮助! :)

© www.soinside.com 2019 - 2024. All rights reserved.