In case you missed the first part of this series, you can read it here Objects, Types and Values; Python Data Model — Part 1.

We divided this “All about Pythonic Class” particular title into two parts.

— All about Pythonic Class: The Birth and Style

All about Pythonic Class: The Life Cycle

The title of these chapters in the end might not unambiguously reflect the idea a reader just has depicted into her/his mind. We will try to cover the very basic concepts of how a Python class is formed and destructed with a simplified narrative. The creation and destruction of a Python class, more specifically a Python 3.x class, inevitably juxtaposes with a number of seemingly discrete topics.

More importantly, a reader needs at least moderate knowledge of the Python data model, special methods, metaprogramming, and how the interpreter works behind the scene. Also, there are some repetitions, circular discussion, and oversimplifications that are inevitable — e.g. we had to stick with objects to explain the behavior of a class.

1. How to write a basic class

Let’s write a basic class for Her Excellency Miss Foo, a famed post-rock guitar practitioner in our locality, who has requested us to write a program that will keep track of her students’ **name**s, money that they will pay her and assign an email address with her domain name.

She is a developer herself but with a tight daily schedule and asked lazy programmers like us for help. She will, however, use this blueprint or class into her own script. Example of real-life code reuse, huh! Let’s help her.

>>> class Student():     ## ...[1]
       pass>>> student_1 = Student() ## ... [2]
>>> student_2 = Student() ## ... [3]>>> student_1, student_2 ## ... [4]
(<__main__.Student object at 0x7fec1b6387b8>, <__main__.Student object at 0x7fec1b638780>)>>> print(id(student_1), id(student_2)) ## ... [5]
140652048517048 140652048516992>>> type(student_1)  ## .......... [6]
<class '__main__.Student'>

We just have written a skeleton Student class and passed the interpreter a pass statement telling her to keep it intact until we assign anything to it. We also create two Student class entity in the statements [2] and [3]. Two most important points to note here that despite being derived from the same class Student, student_1 and student_2 have different identities and locations in the RAM-- statements [5] and [4].

On a Sunday morning, Andrew Thompson got admitted into her class. Miss Foo will charge him 5,000 bucks every month. Let’s add his name

>>> student_1.firstname = "Andrew"
>>> student_1.lastname = "Thompson"
>>> student_1.pays = 5000
>>> student_1.mail = student_1.firstname.lower()+'_'+student_1.lastname.lower()+"@msfooguitar.edu"
>>> student_1.mail
'andrew_thompson@msfooguitar.edu'

On a Friday morning, Marc Byrd, a friend of Andrew, enrolled in the class. As he is a beginner, Miss Foo has to pay more effort and attention to him. Marc will pay 6000 bucks a month. Let’s log his name into the registry book.

>>> student_2.firstname = "Marc"
>>> student_2.lastname = "Byrd"
>>> student_2.pays = 6000
>>> student_2.mail = student_2.firstname.lower()+'_'+student_2.lastname.lower()+"@msfooguitar.edu"
>>> student_2.mail
'marc_byrd@msfooguitar.edu'

Notice the approach we have adopted in the aforementioned example serves our purpose but makes it complex to go further.

  • We are assigning instances to each object repetitively. This will create further complexity when the class will produce more objects.
  • We will need to define the class whenever we need them. The current example is not helping us.

We can make the situation a bit more flexible by avoiding repetition and ensuring code reusability by literally writing a blueprint of what properties the Student class and its objects will get.

class Student():
   def __init__(self, fname, lname, pays): ## ... [1] an initialiser.
      self.fname = fname
      self.lname = lname
      self.pays = pays
      self.mail = f"{self.fname.lower()}@msfooguitar.edu"

student_1 = Student("Andrew", "Thompson", 5000)
student_2 = Student("Marc", "Byrd", 6000)

Now what we have here is,

  • The class Student now acts like a form with three mandatory fields-- first name, last name, and the amount of money to pay along with an additional email address-- to be filled up by each new student or object when (s)he enrolls in a course. This way a coder can avoid repetitions.
  • The __init__ magic method (will discuss within minutes) initializes the variables, fields, or properties each new Student object/instance will get. This method is called at the creation of each new object of a class. Remember that __init__ is not a constructor. In Python, __new__ is the constructor
  • Have you noticed the self parameter is passed in the initializer? When an object is created from a class, the interpreter will first pass the object itself to __init__ the method which is conventionally denoted by the self parameter. We can use any parameter name other than self.
class Student():
  def __init__(self, fname, lname, pays): ## ... [1] an initialiser.
    self.fname = fname
    self.lname = lname
    self.pays = pays
    self.mail = f"{self.fname.lower()}@msfooguitar.edu"

  def details(self):
    print(f"name: {self.fname} {self.lname} pays: {self.pays} mail:\
     {self.mail}")

student_1 = Student("Andrew", "Thompson", 5000)## ... [2]
student_2 = Student("Marc", "Byrd", 6000)print(student_1.details(), student_2.details())
## Output
'''
name: Andrew Thompson pays: 5000 mail: andrew_thompson@msfooguitar.edu
name: Marc Byrd pays: 6000 mail: marc_byrd@msfooguitar.edu
'''

The statement [2] can be rewritten as Class.objectMethod(object). In our case, it is equivalent to Student.details(student_1).

An interesting note that we can override an instance method like a variable on the fly. For example, we can change details() the callable function into a new string.

student_1.details = "A new string."
print(student_1.__dict__)

## Output
'''
{'fname': 'Andrew',
'lname': 'Thompson',
'pays': 5000,
'mail': 'andrew_thompson@msfooguitar.edu',
'details': 'A new string.'}
'''

Notice that details has now become a string object which is not callable. The dict is a dictionary that contains what variables the object has.

Notice that details has now become a string object which is not callable.

print(student_1.details())
## Output
'''
Traceback (most recent call last)
...
---> 19 print(student_1.details())
TypeError: 'str' object is not callable
'''

#python #machine-learning #web-development #programming #developer

All About Pythonic Class: The Birth and Style
1.65 GEEK