-1
dir/
  |
  |___ __init__.py
  |
  |___ Base_class.py
  |
  |___ Subclass.py

__init__.py is empty(as mentioned here)


/* Base_class.py


class Employee:

    numOfEmployees = 0 # Pure class member, no need to override
    raiseAmount = 1.04 # Could not be pure class member, so, can it be overidden by object?
                           # Yes. make sense
                           # This is why we use self.raiseAmount in methods
    def __init__(self, firstName, lastName, pay):
        self.firstName = firstName
        self.lastName = lastName
        self.pay = pay
        self.email = firstName + '.' + lastName + '@email.com'

        Employee.numOfEmployees += 1

    def fullName(self):
        return '{} {}'.format(self.firstName, self.lastName)

    def appyRaise(self):
        self.pay = int(self.pay * self.raiseAmount)

        @classmethod
        def setRaiseAmt(cls, amount):
            cls.raiseAmount = amount

        @classmethod
        def createEmployee(cls, employeeStr):
            firstName, lastName, pay = employeeStr.split('-')
            return cls(firstName, lastName, pay)

        @staticmethod
        def isWorkDay(day):
            if day.weekday() == 5 or day.weekday() == 6:
                return False
            return True


emp1 = Employee('AAA', 'BBB', 50000)
emp2 = Employee('CCC', 'DDD', 40000)


print Employee.raiseAmount # 1.04
print emp1.raiseAmount     # 1.04
print emp2.raiseAmount     # 1.04

# Regular methods
emp1.fullName()          # Executing fullName(<__main__.Employee object at 0xb7dbef0c>) 
Employee.fullName(emp1)  # same as above 


# With classmethods, the class of the object instance is implicitly passed as the first argument instead of self.

Employee.setRaiseAmt(1.05) # class variable's cls member raiseAmount will get updated

print Employee.raiseAmount # 1.05
print emp1.raiseAmount     # 1.05
print emp2.raiseAmount     # 1.05

emp1.setRaiseAmt(1.05) # Invokes as, setRaise(<class '__main__.Employee'>,1.05)

# Application of class methods as constructors
employeeStr = 'John-Doe-70000'
newEmployee = Employee.createEmployee(employeeStr);

print newEmployee.email
print newEmployee.pay


# With static methods, neither self (the object instance) nor  cls (the class) is implicitly passed as the first argument. They behave like plain functions except that you can call them from an instance or the class:

emp1 = Employee('AAA', 'BBB', 50000)
emp2 = Employee('CCC', 'DDD', 40000)

import datetime
myDate = datetime.date(2016, 7, 10)

print emp1.isWorkDay(myDate) # Executing isWorkDay(myDate)

/* Subclass.py */

from  Base_class import Employee

class Developer(Employee):
    pass

Question:

On just inheriting Employee class:

> python Subclass.py

why this below output? How to inherit base class?

$ python Subclass.py
1.04
1.04
1.04
1.05
1.05
1.05
John.Doe@email.com
70000
False
Community
  • 1
  • 1
overexchange
  • 15,768
  • 30
  • 152
  • 347
  • 3
    When you import `Employee` python `runs` it. Thus all of `print` statements at the end of the file will be executed. – Stephen Rauch May 11 '17 at 03:00
  • @StephenRauch All the print statements are not part of `Employee`. Am importing `Employee` only – overexchange May 11 '17 at 03:01
  • 2
    No you are not only importing Employee. The entire file is evaluated.... What you are *only* doing is putting `Employee` into your namespace – Stephen Rauch May 11 '17 at 03:02
  • @StephenRauch If entire file has 100 classes, does python evaluates all 100 non related classes to import only `Employee`? What is the need to evaluate entire file? This is weird. Firstly, If I write `class Developer(Employee)`, am expecting `Developer.__dict__` to have all members of `Employee`. What is going on here? – overexchange May 11 '17 at 03:08
  • 1
    Yes, and all of the imports and imports in the imports.... – Stephen Rauch May 11 '17 at 03:09
  • @overexchange that's because each object is it's own entity. When you set a variable to be a class that object contains the values for itself not for another object. Creating a subclass doesn't carry forward all objects associated with that class. It allows you to create a new object for that subclass with all the underlying structure of the base class. But it doesn't carry all the information from all instances of that base class that has been created, just the structure or constants you have configured in the base class. – Aklys May 11 '17 at 03:14
  • @Aklys Not carrying the information about **instances** of base class(`Employee`) but expecting to carry information about `Employee` attributes & methods into `Developer` class. This is what inheritance mean? – overexchange May 11 '17 at 03:17
  • Maybe reading through this might help you. http://www.jesshamrick.com/2011/05/18/an-introduction-to-classes-and-inheritance-in-python/ – Aklys May 11 '17 at 03:25

4 Answers4

2

The issue you seem to be having is that when you import a module, all of its code is run, even if you're using from module import name syntax to just import a part of the module's contents. This is why you get the same output from running the Subclass.py file as you would if you ran Base_class.py.

If you don't want the testing code to run when you import the Base_class module, you should put it inside of an if __name__ == "__main__": block. The global variable __name__ is usually the name of the module (such as "Base_class"). It is "__main__" when you run a module as a script. Thus, testing for that string lets you run some code only when your module is the main script, and not when it's being imported by some other module.

You may also be confused about how subclasses see their parent class's attributes. This is somewhat magical in Python. When you look up an attribute on an instance, first the instance's own dictionary is checked, then it's class's dictionary, then the dictionaries of each of the base classes in its MRO (Method Resolution Order, generally the chain of parent classes in order unless you're doing complicated multiple inheritance). So an inherited attribute of Employee won't show up in Developer.__dict__ unless you explicitly set a new value for it on the Developer class.

As far as I can see, your code should work just fine if you create some Developer instances and call some of the methods they'll inherit. The only error in see is that the decorated methods in Employee are not indented the same as the other methods, but I suspect (based on the output you say you're getting) that that's an issue from copying the code to Stack Overflow, rather than a real bug. You may want to double check that you're not mixing spaces and tabs for your indents, which can lead to subtle errors that are hard to see (and mixing them is not allowed in Python 3).

Blckknght
  • 100,903
  • 11
  • 120
  • 169
1

You've declared setRaiseAmt to be a class method, which means it will update the class variable raiseAmount whether you invoke it via the class or an instance. There's no need for this class method; if you want to change the default raise amount, just do it directly:

Employee.raiseAmount = ...

If you want to set an individual employee's value, also do it directly, but via the instance:

emp1.raiseAmount = ...

This always creates (or updates) a variable in the instance's attribute dictionary. When reading the value of raiseAmount, you will get the value of the instance attribute if it exists, otherwise you'll get the value of the class attribute.

If you must provide setters (for instance, it is a requirement for a class assignment), provide separate class and instance methods:

@classmethod
def set_default_raise_amount(cls, amount):
    cls.raise_amount = amount

def set_raise_amount(self, amount):
    self.raise_amount = amount
chepner
  • 497,756
  • 71
  • 530
  • 681
1

When you import the Employee base class the Base_class.py file is read into memory and processed first, then the Employee class is imported into the name space of Subclass.py

To test whether you've successfully subclassed you can in Subclass.py try to instantiate a Employee class, though you will need to make a constructor.

Add these to your Developer class.

from Base_class import Employee

class Developer(Employee):
     def __init__(self):
          Super(Employee, self).__init__()

test_dev = Developer('Bob', 'Marley', 1000)

test_dev.fullName()
Mike Tung
  • 4,735
  • 1
  • 17
  • 24
0

When you import Employee from Base_class.py it read the whole Base_class.py data into memory. So because you create objects within that file and print them inside of Base_class.py you will execute them when setting up the inherit.

You probably want to create those Employee objects via another file like main.py or something. This will stop you from processing the print and object creation requests when you import the Employee class.

Aklys
  • 461
  • 1
  • 4
  • 15