3

I have this code:

class Employee:
    def __init__(self, name, pay, gender):
        self.name = name
        self.pay = pay
        self.gender = gender

    def add_raise(self):
        self.pay = int(self.pay*1.10)

    def __str__(self):
        if self.gender == "Female" or self.gender == "female":
            return f"{__class__.__name__} name: {self.name}, and she earns {self.pay} Pound"
        else:
            return f"{__class__.__name__} name: {self.name}, and he earns {self.pay} Pound"

    def __repr__(self):
        if self.gender == "Female" or self.gender == "female":
            return f"{__class__.__name__} name: {self.name}, and she earns {self.pay} Pound"
        else:
            return f"{__class__.__name__} name: {self.name}, and he earns {self.pay} Pound"

class Supervisor(Employee):
    def __init__(self, name, pay, gender, department):
        super().__init__(name, pay, gender)
        self.department = department

Now, when I try to run

emp1 = Employee("Ron", 1000, "male")
emp10 = Supervisor("Hermoine", 3000, "female", "General")
print(emp1, emp10)

I only get "Employee name" at the beginning. How do I change it so it reflects that "Hermoine" is a Supervisor and not just an Employee, without re-writing both the __str__ and __repr__ methods?

brightstar2100
  • 117
  • 1
  • 8
  • 2
    Honestly, I'd make the "role" explicit in an instance attribute, rather than trying to reuse the class name. Add `self.role = "Employee` or `self.role = "Supervisor"` in the appropriate `__init__` methods. – chepner Sep 09 '21 at 12:38
  • 1
    (Or a class attribute, `role = "Employee"` directly in the `class` statement namespace.) – chepner Sep 09 '21 at 12:39
  • 1
    @chepner, the `self.__class__.__name__` approach suggested by @U12-Forward is a pretty common pattern for `__repr__` methods. See, for example, `collections.OrderedDict`, `collections.Counter`, `collections.namedtuple` and `collections.ChainMap`. https://github.com/python/cpython/blob/main/Lib/collections/__init__.py – Alex Waygood Sep 09 '21 at 13:02
  • 1
    It's not one that I like, especially for `str`. It's fine for `repr`, which is only intended as a debugging tool anyway. For `__str__`, I'd rather use explicit data rather than a value inferred from *code*. – chepner Sep 09 '21 at 13:04
  • 1
    Well, fair enough — but I think it's useful to note that it's arguably the norm in the standard library to define `__repr__` methods like this, and to leave `__str__` undefined (meaning that calls to `__str__` will fall back to the `__repr__` method). I'm also not entirely sure why you don't consider the class name to be explicit data — it's very rare that you're dynamically setting or modifying a class name at runtime. – Alex Waygood Sep 09 '21 at 13:21

1 Answers1

9

Solution:

Try changing your first class to:

class Employee:
    def __init__(self, name, pay, gender):
        self.name = name
        self.pay = pay
        self.gender = gender

    def add_raise(self):
        self.pay = int(self.pay*1.10)

    def __str__(self):
        if self.gender == "Female" or self.gender == "female":
            return f"{self.__class__.__name__} name: {self.name}, and she earns {self.pay} Pound"
        else:
            return f"{self.__class__.__name__} name: {self.name}, and he earns {self.pay} Pound"

    def __repr__(self):
        if self.gender == "Female" or self.gender == "female":
            return f"{self.__class__.__name__} name: {self.name}, and she earns {self.pay} Pound"
        else:
            return f"{self.__class__.__name__} name: {self.name}, and he earns {self.pay} Pound"

Try self.__class__.__name__ instead of __class__.__name__ in all strings.

And now:

emp1 = Employee("Ron", 1000, "male")
emp10 = Supervisor("Hermoine", 3000, "female", "General")
print(emp1, emp10)

Output:

Employee name: Ron, and he earns 1000 Pound Supervisor name: Hermoine, and she earns 3000 Pound

Explanation:

The reason why self returns the actual class name is because it will extract the class name from the current self instance. Without self would only get the class name of the original parent class, not the actual class it's currently in, that's the reason self is so important.

Also as @MadPhysicist mentioned, it's "because __class__ is lexically scoped at class execution time".

mkrieger1
  • 19,194
  • 5
  • 54
  • 65
U13-Forward
  • 69,221
  • 14
  • 89
  • 114