Reputation: 310
In this example, am I violating LSP? Since straight up replacing the last two lines with an instance of a subclass will give me an error(as wage isn't initialised)?
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()
If so, then how can there ever be a non-violation of LSP when extending the constructor of a class (aside from placing optional attributes thereafter)?.
Upvotes: 1
Views: 983
Reputation: 1
From my understanding of LSP, it states that objects or rather methods the parent class should be replaceable with objects or methods in the child class without compromise(Altering the program)
Example
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()
In this example, we have a Bird class with a fly
method. Then, we have a Penguin
class that is a subclass of Bird. The Penguin class overrides the fly method to indicate that penguins cannot fly.
So if we call the bird instance;
bird_instance = Bird()
print(make_bird_fly(bird_instance)) # Output: "I can fly"
The penguin instance;
penguin_instance = Penguin()
print(make_bird_fly(penguin_instance)) # Output: "Sorry, I can't fly"
The make_bird_fly
function takes a Bird object as an argument and calls its fly
method. Since Penguin is a subclass of Bird, we can pass an instance of Penguin to this function without affecting the correctness of the program. This adheres to the Liskov Substitution Principle.
Hope this helps! :)
Upvotes: 0
Reputation: 11
I know it's been answered already but i wanted to emphasize:
We need to differentiate between 2 relationships. One is the relationship between instances of Person
and instances of Employee
. The second one is the relationship between the 2 instances of type
(The Person
class itself, and the Employee
class itself.)
In your case, LSP deals only with the former (everything that we can do with Person
instances, we need to be able to do in the exact same way with Employee
instances). It says nothing about the classes themselves.
Now, since python is very dynamic you could technically argue "Hey just wait second! There is something I can do with one and not the other!". Take a look at the following example:
# 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)
I would say that you shouldn't count this kind of stuff. Let's call it an "unreasonable expectation of usage" that shouldn't be accounted for when considering the validity of your code structure in the vast vast majority of use cases.
The most important thing to remember is this:
B inherits from A
!= A (the class object itself) can be substituted by B (the class itself)
Upvotes: 1
Reputation: 17342
LSP says, that if something is true for a Person object (e.g. it has a name, the name is a string, it can print its name), it must be also true for an Employee object. In other words, every Employee is also a Person.
It does not state that an Employee object must be created the same way as a Person object. Every Employee is not only a Person. It has not only the name, but also a wage.
The second question:
If the Employee.print_name() method were redefined not to print the name, but for instance to return it as a string, that would break the principle.
Note that breaking the LSP does not require a code change, for instance if the Person's name format were changed from e.g. "first_name last_name" to "last_name, first_name" in the Employee, and that would cause the program to give incorrect output.
Upvotes: 2
Reputation: 414
It depends on what you mean by the LSP.
Does it mean the strict LSP, like in Barbara Liskov's original paper, where the behaviour of the program should be unchanged by type substitution? (and even then it's a desired property, not an absolute requirement)
Or does it mean following the Person
interface, in which case it would not be a violation, since you can't remove functions for the Person
class in the Employee
class? (Well, you technically could, but it's not a good idea to do that).
Upvotes: 0