<jupyter_start><jupyter_text>Constructors, Destructors, public, private and protected variables and methods:   cotd.. of oops:<jupyter_code>class Name():
    def Hello(self):                       #If self is not given: TypeError: Hello() takes 0 positional arguments but 1 was given
        print("Machine Learning")
ob = Name()
ob.Hello
ob.Hello()<jupyter_output>Machine Learning<jupyter_text>Constructors: ( __init__ )<jupyter_code>#__init__ is the constructor name, should always construct a constructor in a class

class Person:
    def __init__(self,name,surname,yob):        #self is a reference to its own object
        self.name=name
        self.surname=surname
        self.yob=yob
ob=Person("Sanjay","Prabhu",1999)
print(ob)
print("My name is {}{} and my yob is: {}".format(ob.name,ob.surname,ob.yob))
#Another example:
class Department:
    def __init__(self,name,num_of_stu):
        self.name = name
        self.num_of_stu = num_of_stu    
cse = Department("Comp. Sci.",240)
it = Department("Information Technology",144)
cce = Department("Comp. Comm. and Elec.",144)
mech = Department("Mechanical",200)
chem = Department("Chemical",150)
civil = Department("Civil",200)
biomed = Department("Bio. Med.",100)

a = [cse,it,cce,mech,chem,civil,biomed]
for i in a:
    print("Name of the branch: ",i.name)
    print("Num of Stu:         ",i.num_of_stu)
#Global variables and local/class variables:
a = 10

class Demo():
    b = 100
print(a)
print(Demo.b)
#cannot change the local variable, but the global variable can be changed

a = 100
b = 120
print(a)
print(Demo.b)
#Another example
class Person:
    def __init__(self,name,surname,yob):
        self.name = name
        self.surname = surname
        self.yob = yob
    
    def age(self,curr_yr):
        return curr_yr-self.yob
    
    def __str__(self):
        return "{} {} was born in {}".format(self.name,self.surname,self.yob)

Indian = Person("Sanjay","Prabhu",1999)
American = Person("Michael","McLarken",1997)
English = Person("Timothy","Bradshire",1998)
Spanish = Person("Jesus","Escobar",1995)
a = [Indian,American,English,Spanish]
for i in a:
    print(i)   # works based on the __str__
    print("{}'s' age is: {}".format(i.name,i.age(2019)))
dir(Indian)
print(Indian.__dict__.keys())
#Another way of writing a class using a set method, but a bad practice

class Person:
    def set_name(self,name):
        self.name = name
    def set_surname(self,surname):
        self.surname = surname
    def set_yob(self,yob):
        self.yob = yob
    def age(self,curr_yr):
        return curr_yr-self.yob
    def __str__(self):
        return "{} {} was born in: {}".format(self.name,self.surname,self.yob)
ob = Person()
ob.set_name("Mani")
ob.set_surname("Gupta")
ob.set_yob(1978)
ob.name
ob.surname
ob.yob
ob.age(2018)<jupyter_output><empty_output><jupyter_text>Advanced topics: Abstraction, Encapsulation, Polymorphism, Inheritance<jupyter_code>''' private,public,protected '''
#public variables (anyname)
class Person:
    def __init__(self,name,surname,yob):
        self.name = name
        self.surname = surname
        self.yob = yob
    
    def age(self,curr_yr):
        return curr_yr-self.yob
    
    def __str__(self):
        return "{} {} was born in {}".format(self.name,self.surname,self.yob)
ob = Person("Anony","mous",1976)
print(ob)
print(ob.__dict__.keys())
#protected variables (_anyname)
#Used for financial and banking applications

class Person:
    def __init__(self,name,surname,yob):    #these are the parameters, hence there is no need of adding _ here
        self._name = name                   #Here on, we are protecting the variables, hence, we add _ before the var name
        self._surname = surname
        self._yob = yob
    
    def age(self,curr_yr):
        return curr_yr-self._yob
    
    def __str__(self):
        return "{} {} was born in {}".format(self._name,self._surname,self._yob)
ob = Person("Anony","mous",1976)
print(ob)
print(ob.__dict__.keys())
ob.name                 #usage of protected hence giving an error
ob._name
ob._surname
ob._yob
ob.age(2019)                                #no change here since it only return a statement
#private variables( __anyname)

class Person:
    def __init__(self,name,surname,yob):
        self.__name = name
        self.__surname = surname
        self.__yob = yob
    
    def age(self,curr_yr):
        return curr_yr-self.__yob
    
    def __str__(self):
        return "{} {} was born in {}".format(self.__name,self.__surname,self.__yob)
ob = Person("Anony","mous",1976)
print(ob)
print(ob.__dict__.keys())
ob.name
ob._name
ob.__name                #Usage of private variable hence giving an error
ob._Person__name
ob._Person__surname
ob._Person__yob
ob.age(2019)              #no change here since it only return a statement<jupyter_output><empty_output><jupyter_text>Difference between public,protected and private:<jupyter_code>class Diff():
    def __init__(self):
        self.pub = ("Public variable")
        self._pro = ("Protected variable")
        self.__pri = ("Private variable")
    def public_method(self):
        print("Public")
    def _protected_method(self):
        print("Protected")
    def __private_method(self):
        print("Private")
ob = Diff()
ob.pub
ob._pro
ob._Diff__pri
ob.public_method()
ob._protected_method()
ob._Diff__private_method()
ob.__dict__<jupyter_output><empty_output><jupyter_text>Nested private method- is it possible? [check]<jupyter_code>#Example for private method:

class World:
    def __India(self):
        def __Telangana(self):
            def __Hyderabad(self):
                def __Ameerpet(self):
                    print("You have reached Ameerpet")
earth = World()
earth._World__India
#Example to call a private variable inside a private method
class Diff():
    def __init__(self,to_be_printed):
        self.pub = ("Public variable")
        self._pro = ("Protected variable")
        self.__pri = ("Private variable")
        self.__to_be_printed = to_be_printed
    def public_method(self):
        print("Public")
    def _protected_method(self):
        print("Protected")
    def __private_method(self,__to_be_printed):
        return __to_be_printed
    def __private_method_2(self,__not_defined_in_constructor):
        return __not_defined_in_constructor
ob = Diff(2019)
ob._Diff__private_method(2019)
ob._Diff__private_method("constucted outside the code")
#If private method can be written inside a private method?<jupyter_output><empty_output><jupyter_text>Can a method be written inside another method? [check]<jupyter_code>#If method can be written inside another method?

class World:
    def India(self):
        print("You have reached India")
        def Telangana(self):
            print("You have reached Telangana")
            def Hyderabad(self):
                print("You have reached Hyderabad")
                def Ameerpet(self):
                    print("You have reached Ameerpet")
ob = World()
ob.India()
#If method can be written inside another method?

class World:
    def India(self,yo):
        print("You have reached India")
        self.yo = yo
        def Telangana():
            print(yo)
            def Hyderabad():
                print(yo)
                def Ameerpet():
                    print(yo)
ob = World()
ob.India(12)
ob.India(12).Telanagana(12)<jupyter_output>You have reached India<jupyter_text>Can we use a Constructor twice?<jupyter_code>class Diff():
    def __init__(self,to_be_printed):
        self.pub = ("Public variable")
        self._pro = ("Protected variable")
        self.__pri = ("Private variable")
        self.__to_be_printed = to_be_printed
    def public_method(self):
        print("Public")
    def _protected_method(self):
        print("Protected")
    def __private_method(self,__to_be_printed):
        return __to_be_printed
    def __init__(self):
        self.pub = ("Overwritten public")
        self._pro = ("Overwritten protected")
        self.__pri = ("Overwritten private")
ob = Diff()
ob.pub
ob._pro
ob._Diff__pri
ob._Diff__private_method(2021)<jupyter_output><empty_output><jupyter_text>Final example of public,protected and private:<jupyter_code>#Another Example of a class without constructor and a settera

class mlearn:
    pub = ("Data Science Public")
    _pro = ("Data Science protected")
    __pri = ("Data Science private")
    def SetCourse(self,name):
        self.name = name
    def GetCourse(self):
        return self.name
    def _SetCourse2(self,name):
        self._name = name
    def _GetCourse2(self):
        return self._name
    def __SetCourse3(self,name):
        self.__name = name
    def __GetCourse3(self):
        return self.__name
ob1 = mlearn()<jupyter_output><empty_output><jupyter_text>public,protected,private variable access<jupyter_code>ob1.pub
ob1._pro
ob1._mlearn__pri<jupyter_output><empty_output><jupyter_text>pulic,protected and private method set<jupyter_code>ob1.SetCourse("public")
ob1._SetCourse2("protected")
ob1._mlearn__SetCourse3("private")<jupyter_output><empty_output><jupyter_text>public,protected and priavte method get<jupyter_code>ob1.GetCourse()
ob1._GetCourse2()
ob1._mlearn__GetCourse3()<jupyter_output><empty_output><jupyter_text>Destructor(__del__):<jupyter_code>class Test:
    ''' module here '''
    def __init__(self):
        print("Constructor")
    def __del__(self):
        print("Destructor")        
ob = Test()
ob.__del__()
print(Test.__name__)
print(Test.__doc__)
print(Test.__module__)
print(Test.__bases__)<jupyter_output>(<class 'object'>,)<jupyter_text>Deletion of an object created:<jupyter_code>ob
if __name__ == "__main__":
    ob = Test()
    del ob
ob<jupyter_output><empty_output><jupyter_text>Deletion of more than 1 object:<jupyter_code>class Test:
    ''' module here '''
    def __init__(self):
        print("Constructor still not deleted")
    def __del__(self):
        print("Destructor still not deleted")   
ob1 = Test()
ob2 = Test()
ob3 = Test()
if __name__ == "__main__":
    ob1 = Test()
    del ob1
ob1
ob2
ob3<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.11659726353361094, 'fraction_numerical': 0.013087447947650209, 'mean_word_length': 3.4711879432624113, 'pattern_counts': {'":': 2, '<': 55, '<?xml version=': 0, '>': 55, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 1, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '14', 'dir': 'jupyter-structured-clean-dedup', 'id': '535436', 'license': 'MIT', 'n_tokens_mistral': 3437, 'n_tokens_neox': 3246, 'n_words': 792, 'path': 'Data-Science-HYD-2k19/Day-based/Day 13.ipynb', 'repo_name': 'Sanjay9921/Python', 'size': '10086'}