Chapter 11: Classes and Objects
This article is part of the Python General Programming tutorial series, which has been fully completed. Its goal is to take you from zero to mastery of the Python programming language. This tutorial does not skim over Python superficially; instead, it offers refined, in-depth explanations across 5 stages and 25 chapters. So you need to study patiently to really gain something. Although it does not involve the use of any framework, it gives a global explanation of operating systems and network communication, and even rewrites some open-source modules and servers. After finishing it, you will not only have mastered the Python programming language, but also gained the ability to learn other programming languages quickly, read any Python source code without difficulty, and a comprehensive understanding of computers and networks. For complete beginners, it is an excellent textbook for entering the field of computing and mastering a programming language. For those who already have some Python foundation, I believe this tutorial will take your skills to the next level.
1. The Origin of Object-Oriented Programming
Procedure-oriented programming: the core is the word “procedure”. A procedure refers to the steps for solving a problem, that is, what to do first and what to do next… Procedure-oriented design is like carefully designing an assembly line; it is a mechanical way of thinking.
Advantages:
It turns complex problems into a process and thereby simplifies them (a complex problem is broken down into small steps, and implementing the small steps is very simple)
Disadvantages:
An assembly line or process is built to solve one problem. An assembly line that produces soda cannot produce cars; even if it could, it would need a major overhaul, and changing one component affects the whole system.
Use cases:
Scenarios that rarely change once completed. Famous examples include the Linux kernel, git, and the Apache HTTP Server.
面向对象的程序设计:核心是对象二字。
To understand what an object is, you must think of yourself as God. In God’s eyes, everything that exists in the world is an object, and whatever does not exist can be created. Object-oriented programming is like the Buddha designing Journey to the West. The problem the Buddha needs to solve is delivering the scriptures to the Great Tang in the East. The Buddha thought about it and decided that solving this problem required four people: Tang Seng, Sha Heshang, Zhu Bajie and Sun Wukong. Each of them has their own characteristics and skills (this is the concept of an object; characteristics and skills correspond to an object’s data attributes and method attributes respectively). But that wouldn’t be much fun, so the Buddha also arranged a horde of demons and monsters, and to keep the master and his three disciples from being killed on the road to the scriptures, he also arranged a host of immortals to escort and protect them. All of these are objects. Then the journey begins, and the master and his disciples interact with the demons, monsters and immortals until they finally obtain the true scriptures. The Buddha does not care at all what process the four follow to get them.
对象是特征与技能的结合体
Designing a program in an object-oriented way is like creating a world. You are the God of this world: whatever exists is an object, and whatever does not exist can be created. In sharp contrast to the mechanical thinking of procedure-oriented programming, object-oriented programming focuses more on simulating the real world; it is a “God-like” way of thinking.
Advantages:
It solves the problem of program extensibility. Modifying a single object is immediately reflected throughout the whole system; for example, it is easy to modify the characteristics and skills of a character’s parameters in a game.
Disadvantages:
1. The programming complexity is much higher than procedure-oriented programming. If you start designing programs with it right away without understanding object orientation, it is very easy to over-engineer. In some scenarios with low extensibility requirements, using object orientation only adds to the programming difficulty; for example, shell scripts for managing Linux systems are not suited to object-oriented design, and procedure orientation is actually more suitable.
2. Unlike the assembly-line style of procedure-oriented programming, it cannot predict the processing flow and results of a problem very precisely. Once an object-oriented program starts, problems are solved by the interactions between objects, and even God cannot accurately predict the final result. That’s why we often see in battle games that when a new character is added, a buggy skill may show up during battles, killing 3 people with one slash. Such situations cannot be accurately foreseen; only through the interactions between objects can the final result be known for sure.
Use cases:
Software whose requirements change frequently. Changes in requirements are generally concentrated in the user layer; internet applications, internal enterprise software, games and so on are all great places for object-oriented programming to shine.
Object-oriented programming is not everything. As far as software quality is concerned, object-oriented programming only addresses extensibility.

2. Using Classes
1. Constructing a class
A class means a category or kind, and it is the most important concept in object-oriented design. An object is a combination of characteristics and skills, while a class is a combination of the characteristics and skills that a series of objects have in common.
So here’s the question: which came first, the individual, concretely existing objects (such as a specific, real person), or the concept of the human class? This question needs to be looked at in two cases.
在现实世界中:先有对象,再有类
In the world, all kinds of actually existing things certainly appeared first, and then, as human civilization developed, humans summarized different categories from different perspectives, such as the concepts of humans, animals and plants.
In other words, objects exist concretely, while a class is merely a concept and does not really exist.
在程序中:务必保证先定义类,后产生对象
This is similar to using functions: you define a function first and then call it. Classes are the same; in a program you need to define the class first and then call it.
The difference is that calling a function executes the function body and returns the result of executing it, while calling a class produces an object and returns that object.
Following the steps above, let’s define a class (we look at it from the perspective of the deepshare school, and all of you readers are students)
#In the real world: objects come first, then classes
对象1:李坦克
特征:
学校=deepshare
姓名=李坦克
性别=男
年龄=18
技能:
学习
吃饭
睡觉
对象2:王大炮
特征:
学校=deepshare
姓名=王大炮
性别=女
年龄=38
技能:
学习
吃饭
睡觉
对象3:牛榴弹
特征:
学校=deepshare
姓名=牛榴弹
性别=男
年龄=78
技能:
学习
吃饭
睡觉
现实中的deepshare学生类
相似的特征:
学校=deepshare
相似的技能:
学习
吃饭
睡觉
In a program, define the class first, then produce objects
#In a program, always make sure to: define (the class) first, then use it (produce objects)
PS:
1. 在程序中特征用变量标识,技能用函数标识
2. 因而类中最常见的无非是:变量和函数的定义
3. 定义类,使用关键字class+空格+类名,类名用大驼峰命名,变量名推荐使用“_”这种方式就是为了区分
#A class in the program
class DeepshareStudent:
# Use variables to represent characteristics
school='deepshare'
# Use functions to represent skills
def learn(self):
"""
self是你使用Pycharm自动添加的,你写成xxx也是一样的,
他就是一个位置参数,必须被传值,具体意义请看下文
"""
print('is learning')
def eat(self):
print('is eating')
def sleep(self):
print('is sleeping')
print('=======>')
# A class is a combination of characteristics and skills, so variables and functions are the most common things in a class, but a class can also contain any Python code
# Run the code above and you will see that the line of arrows is printed, which shows that the preceding code also ran,
# which shows that the code in the class body is executed immediately when the class is defined,
# and since it was executed, with variables and functions, the names produced are stored in the class namespace
2. Inspecting a class
So how do we inspect the variables and functions in a class?
# You can inspect them with the class.__dict__ attribute
name_and_func = DeepshareStudent.__dict__
print(name_and_func)
# Output
"""
{ '__module__': '__main__',
'school': 'deepshare',
'learn': <function DeepshareStudent.learn at 0x103698268>,
'eat': <function DeepshareStudent.eat at 0x105c46488>,
'sleep': <function DeepshareStudent.sleep at 0x1036797b8>,
'__dict__': <attribute '__dict__' of 'DeepshareStudent' objects>,
'__weakref__': <attribute '__weakref__' of 'DeepshareStudent' objects>,
'__doc__': None}
"""
# Note:
# 1. This is clearly in dictionary format: there are names and memory addresses in one-to-one correspondence, and a dictionary is the best way to store them
# 2. Leaving out the key-value pairs built into Python, we only look at the parts related to the class we defined
print(name_and_func['school'])
print(name_and_func['learn'])
# Output of running the code
"""
deepshare
<function DeepshareStudent.learn at 0x103bb0268>
我们可以看到name_and_func['learn']是一个函数的内存地址,那么我加()应该就能调用他
"""
name_and_func['learn']()
It turns out we get an error

Don’t panic. The error says that the function learn() is missing a positional argument ‘self’. Go back up and look at the learn function in our class: the positional parameter self must be passed a value, but we didn’t pass one
name_and_func['learn']('albert')
Run the code again and you’ll find that the learn function runs normally, but it has nothing to do with the argument we passed. Can we modify it?
class DeepshareStudent:
school = 'deepshare'
def learn(self):
print('%s is learning' % self) # Let us change this function slightly
def eat(self):
print('is eating')
def sleep(self):
print('is sleeping')
Run it again
name_and_func['learn']('albert')
Output
albert is learning
OK, the function call here is just a small digression. The point is to tell you that once you see a function’s memory address, you should think: adding () to the function name calls the function.
Now let’s talk about what this self refers to.
self指的是类的对象本身
You’ll come to understand it gradually through the code that follows. We just said that a class has the dict attribute, which gives us a dictionary
So
name_and_func = DeepshareStudent.__dict__
print(name_and_func['school'])
print(name_and_func['learn'])
print(name_and_func['eat'])
print(name_and_func['sleep'])
Every time we want to get a skill or attribute of a class, we can do it this way. Do you find it tedious?
The clever Guido naturally has a better way of handling this for the sophisticated Python language
# Directly use the object followed by . and the variable name or function name
print(DeepshareStudent.school)
print(DeepshareStudent.learn)
print(DeepshareStudent.eat)
print(DeepshareStudent.sleep)
# The output is the same as the code above; in fact, when we use this approach, it is essentially executing the code above
# Names that start with __ and end with __ are triggered automatically under certain conditions; we generally do not need to write them ourselves
So what do we call it when we use the class name, a dot, and a variable name or function name?
print(DeepshareStudent.abc)
In this line of code I accessed a variable or function name that doesn’t exist. Look at the error message

It says the class DeepshareStudent has no attribute abc, so all of these are attributes. Unlike some other languages, Python does not distinguish between kinds of attributes; they are all simply called attributes, and you can get an attribute’s value with a dot followed by the attribute name.
3. Modifying a class
# Modify the value of a class attribute
DeepshareStudent.school = '深度之享'
print(DeepshareStudent.school)
# Add an attribute to the class
DeepshareStudent.country = 'China'
print(DeepshareStudent.country)
# Delete an attribute of the class
del DeepshareStudent.country
print(DeepshareStudent.country)
2. Using Objects
The core of the object-oriented programming mindset is the object, and in the end what we use is objects. In the previous subsection we only built the class. A class is really like a template or a factory, and next we will use the class to mass-produce objects.
在程序中:先定义类====>调用类====>产生对象
Define the class; here is the code we used to define it earlier:
class DeepshareStudent:
school = 'deepshare'
def learn(self):
print('%s is learning' % self)
def eat(self):
print('is eating')
def sleep(self):
print('is sleeping')
A class is called the same way as a function: put parentheses after the class name
DeepshareStudent()
# Running the code produces no output
Calling a class produces an object at the same time; we just need to assign it to a variable
stu1 = DeepshareStudent()
stu2 = DeepshareStudent()
stu3 = DeepshareStudent()
print(stu1)
print(stu2)
print(stu3)
Output
<__main__.DeepshareStudent object at 0x10ca88a90>
<__main__.DeepshareStudent object at 0x10ca88d68>
<__main__.DeepshareStudent object at 0x10ca88d30>
Each call makes the class’s mold turn out one object, but is there any difference between the objects turned out this way? If you don’t tell the factory, every object it makes will be the same, but the objects we need are certainly not all exactly the same
#In the real world: objects come first, then classes
对象1:李坦克
特征:
学校=deepshare
姓名=李坦克
性别=男
年龄=18
技能:
学习
吃饭
睡觉
对象2:王大炮
特征:
学校=deepshare
姓名=王大炮
性别=女
年龄=38
技能:
学习
吃饭
睡觉
对象3:牛榴弹
特征:
学校=deepshare
姓名=牛榴弹
性别=男
年龄=78
技能:
学习
吃饭
睡觉
现实中的deepshare学生类
相似的特征:
学校=deepshare
相似的技能:
学习
吃饭
睡觉
Besides sharing similar skills, objects also have different characteristics, and we need to customize each object’s unique characteristics
It’s like making people. Do you have any experience? Everyone has a head, a body, arms and legs, but some people are tall and some are short, some are fat and some are thin, and so on; some things are shared and some are unique. How do we implement this in a program?
class DeepshareStudent:
school = 'deepshare'
print('=====>')
def __init__(self): # Define an __init__ function in the program
print('===init run====>') # For now we write nothing else, just print something
def learn(self):
print('%s is learning' % self)
def eat(self):
print('is eating')
def sleep(self):
print('is sleeping')
Run the code: the first print executes, because the code in the class body is executed when the class is defined, as we discussed above. But the print inside the init function doesn’t execute. So when is the init function triggered?
class DeepshareStudent:
school = 'deepshare'
print('=====>')
def __init__(self):
print('====init run===>')
def learn(self):
print('%s is learning' % self)
def eat(self):
print('is eating')
def sleep(self):
print('is sleeping')
DeepshareStudent()
Output
=====>
====init run===>
We call a function with its name followed by parentheses, but in the code above, did we ever actively call the init function? Obviously not, yet the function did run, which shows that this function is triggered automatically when we call the class
So what do we use this feature for?
Look at its name, init; look it up in the Youdao dictionary and it means “initialize”. Got it?
How do we use init to give objects different attributes?
class DeepshareStudent:
school = 'deepshare'
def __init__(self,x,y,z): # Add three positional parameters x, y, z after self
self.NAME = x # Write it like this for now; we will explain what it does in a moment
self.AGE = y
self.GENDER = z
def learn(self):
print('%s is learning' % self)
def eat(self):
print('is eating')
def sleep(self):
print('is sleeping')
DeepshareStudent()
Run the code and you get an error

It says the init function is missing three positional arguments x, y, z
Analyzing this, we can see that calling the class automatically triggers the execution of the init function. The function has positional parameters, but we didn’t pass any argument values. How do we pass them? There’s nowhere else to pass them; you can probably guess that the only way is like this
DeepshareStudent('李坦克',18,'male')
Run the code: no error, and the program produces no output. Next, let’s first summarize what calling a class does
- First, an empty object stu1 is created
- The init function inside the class is triggered automatically
- Then the empty object stu1 and the arguments in the parentheses of the class call are put together as (stu1,“李坦克”,18,‘male’), and these four arguments are passed together to the init function
DeepshareStudent('李坦克',18,'male')
# DeepshareStudent.__init__(stu1,"李坦克",18,'male')
Passed to the init function
def __init__(self,x,y,z): # self=stu1 name= "李坦克" age=18
self.NAME = x # stu1.NAME = '李坦克'
self.AGE = y # stu1.AGE = 18
self.GENDER = z # stu1.GENDER = 'male'
stu1 goes to self, “李坦克” goes to x, 18 goes to y, and ‘male’ goes to z
Moving on, does the syntax obj.NAME look familiar? In the section on classes above, we said this accesses an attribute of the class. It works the same way on objects, except that here it adds an attribute to the object and assigns it a value
class DeepshareStudent:
school = 'deepshare'
def __init__(self,x,y,z):
self.NAME = x
self.AGE = y
self.GENDER = z
def learn(self):
print('%s is learning' % self)
def eat(self):
print('is eating')
def sleep(self):
print('is sleeping')
stu1 = DeepshareStudent('李坦克',18,'male')
print(stu1.NAME)
print(stu1.AGE)
print(stu1.GENDER)
print(stu1.school)
print(stu1.learn)
stu2 = DeepshareStudent('王大炮',38,'female')
print(stu2.NAME)
print(stu2.AGE)
print(stu2.GENDER)
print(stu2.school)
print(stu2.learn)
This meets our requirement: objects have both shared attributes and their own attributes. Still, let’s see whether the code can be improved
class DeepshareStudent:
school = 'deepshare'
# We first wrote x, y, z to help you understand; to make the parameters more meaningful, we write it this way
def __init__(self,name,age,gender):
self.name = name # The first name is the attribute name of the object and the second name is the variable name; using the same name is for convenience
self.age = age
self.gender = gender
def learn(self):
print('%s is learning' % self)
def eat(self):
print('is eating')
def sleep(self):
print('is sleeping')
Note that self is the object itself, and its value is passed automatically when the class is called to produce an object. When writing code, PyCharm adds this self automatically. You could also call it aaa, though the meaning would be unclear, but leaving it out is not allowed
class DeepshareStudent:
school = 'deepshare'
def __init__(name,age,gender): # The error is raised on this line
self.name = name # The code never gets to this line before the error is raised
self.age = age
self.gender = gender
def learn(self):
print('%s is learning' % self)
def eat(self):
print('is eating')
def sleep(self):
print('is sleeping')
stu1 = DeepshareStudent('李坦克',18,'male')
The program raises an error

It says the program takes three arguments but you passed 4, which shows that one of them is passed automatically
Summary:
The init function initializes the object: when the class creates an object, it gives the object’s attributes their distinct characteristics