Object oriented design

1, Classes and objects

  process oriented and object-oriented are both problem-solving programming ideas. The realization of the function of the former mainly depends on "function", while the implementation of the latter needs to be realized with the help of "class" and "object". Therefore, object-oriented programming (OOP) is the rational use of "class" and "object", and it has three characteristics: encapsulation, inheritance and polymorphism. Object oriented is to treat programming as a thing. For the outside world, things can be used directly, and there is no need to care about its internal situation.

1.1 object oriented and process oriented

  suppose we now need to build a student dormitory on an open space, what is the difference between object-oriented and process-oriented methods to realize this requirement?

① Process oriented: more like an executor in his role, he works in sequence according to the construction process and construction drawings, such as land application, earth excavation, frame construction, etc;

  1. When realizing requirements, they prefer to process and steps, and do not pay attention to the division of responsibilities;
  2. According to the requirements, some function independent codes are encapsulated into one function after another;
  3. Organize data and functions together according to the logical order of execution, and treat data and functions separately;

② Object oriented: the role is more like a commander. Find a competent construction project manager and hand over the detailed construction steps to him;

  1. Pay attention to objects and responsibilities, and different objects undertake different responsibilities;
  2. According to the requirements, first determine the responsibilities, and then create different objects according to the responsibilities;
  3. Different attributes and methods are encapsulated inside the object, and different methods of different objects are called in sequence;

1.2 relationship between class and object

1.2.1 concept of class

   class is the general name of a series of things with the same characteristics and behavior. It is abstract and not real. For example, the drawing of aircraft manufacturing (below) is equivalent to a class, which specifies the characteristics (model, size, etc.) and behavior (take-off, flight, etc.) of aircraft produced in the future. However, it is not a specific aircraft, but an abstract concept of aircraft, that is, template.

  1. A feature is a variable, which we call an attribute in a class;
  2. Behavior is actually a function, which we call method in class;
  3. Class is essentially an abstract concept composed of attributes and methods, which can not be used directly;

1.2.2 concept of object

   objects are real things created by classes. You can directly use the public attributes and methods defined in the class, such as F22 (Raptor), F117 (Nightingale), etc. these different aircraft are objects created through classes (drawings). Note: in the actual development process, there are classes before objects, and the same class can instantiate multiple different objects.

1.3 simple object-oriented implementation

1.3.1 new and classic

    in Python, object is the parent class of all classes. It provides some built-in properties and methods, which can be viewed using Python built-in function dir(). However, its definition and use in Python 2.x and Python 3.x are different:

  • New class: a class with object as the base class. When defining a class after Python 3.x, if no parent class is explicitly specified, it inherits from object by default. They are also called new classes;
  • Classic classes: classes that do not take object as the base class. When defining classes in Python 2.x, if the inherited classes are not explicitly specified, objects will not be inherited, so they are also called old classes;

Create new classes in Python 3.x:

class Person(object):  # Explicitly specify inheritance from object;
	"""
	Do something!
	"""
	pass

class Person:  # It is not explicitly specified, but it still inherits from object by default;
	"""
	Do something!
	"""
	pass

Create classic classes in Python 2.x:

class Person(object):  # Explicitly specify the parent class as object;
	"""
	Do something!
	"""
	pass
	
class Person:  # If it is not explicitly specified, it does not inherit from object by default;
	"""
	Do something!
	"""
	pass

1.3.2 class definition and object creation

① Class definition: remember that there must be a class before an object, and in the later stage, unless it is explicitly declared that the currently defined class inherits from a class, all new classes will be used;

# New class definition format;
class Class name(object):  # The naming of class names should follow the principle of big hump (the first letter of each word is uppercase and the other letters are lowercase);
    # Attributes or characteristics;
    pass
    # Method or behavior;
    pass
# Define a simple aircraft model class;
class ModelPlane(object):
	# The aircraft has color and size attributes. The attributes here are class attributes (which will be described later), that is, all objects created based on this class have these attributes by default;
	color = "red"
	size = (100, 20)
	
	# The aircraft has flight behavior;
	def fly(self):
	    print("I'm flying!")
	
	# The aircraft has the behavior of shooting the enemy;
	def shot(self):
	    print("I'm shooting at the enemy!")

② Creating objects: in Python, the process of creating objects can also be called the process of instantiating objects, that is, the process of transforming abstract classes into actual things;

# Instantiation object format;
Object name = Class name()
# Create objects;
F117 = ModelPlane()

# Printing the object directly will call the class to which it belongs__ str__() Method, which outputs the memory address information of the object by default. It starts with 0x and represents hexadecimal;
print(F117)  # <__main__.ModelPlane object at 0x000001F25C6FA400>

③ Self parameter analysis: there is a formal parameter self in the methods fly() and shot() in the aircraft model class, and the Python interpreter will automatically point it to the object calling the function;

class ModelPlane(object):

    # The aircraft has flight behavior;
    def fly(self):
        print("I'm flying!")
        print("self point:", id(self))  # < -- you can directly view the memory address of an object by using the python bui lt-in function id();

    # The aircraft has the behavior of shooting the enemy;
    def shot(self):
        print("I'm shooting at the enemy!")
        print("self point:", id(self))  # < -- you can directly view the memory address of an object by using the python bui lt-in function id();
# Create Nightingale object F117
F117 = ModelPlane()
print("nightingale F117 Memory address of:", id(F117))  # Memory address of Nightingale F117: 1230523307008
F117.fly()  # The Python interpreter will automatically pass the object F117 to self in fly();
"""
I'm flying!
self point: 1230523307008
"""

# Create Raptor object F22; -- > ID (F22) is equal to id(self) in the method in the class;
F22 = ModelPlane()
print("raptor F22 The memory address of is:", id(F22))  # The memory address of Raptor F22 is 1230523558256
ModelPlane.shot(F22)  # We can also manually pass the object F22 to the formal parameter self in shot() through [class name. Method name (object name)];
"""
I'm shooting at the enemy!
self point: 1230523558256
"""

1.3.3 adding and accessing object properties

① Add and access object attributes outside the class: after the object is created successfully, a new attribute can be added to the object or the attribute value with the same name can be overwritten through the object name. Attribute name = attribute value;

class ModelPlane(object):
	
	# The aircraft has the behavior of flying;
	def fly(self):
	    print("I'm flying!")
	
	# The aircraft has the behavior of shooting the enemy;
	def shot(self):
	    print("I'm shooting at the enemy!")
# Instantiate the object;
F35 = ModelPlane()

# Add a new attribute to an object by clicking object name. Attribute name = attribute value;
F35.color = "black"
F35.size = (100, 20)

# You can access or obtain the attributes of an object through [object name. Attribute name];
print(f"Object outside class F35 Add attribute: color={F35.color}, size={F35.size}!")  # Add attributes for object F35 outside the class: color = black, size=(100, 20)!

# If the attribute of an object already exists, the operation of [object name. Attribute name = attribute value] is to modify the value of the attribute;
F35.color = "gules"
print(f"object F35 Current color color by:", F35.color)  # The color of object F35 is now red

② Access object properties in the class: all the exposed properties of the object can be called inside the class through the self. Property name;

class ModelPlane(object):

    # The aircraft has flight behavior;
    def fly(self):
        print("I'm flying!")

    # The aircraft has the behavior of shooting the enemy;
    def shot(self):
        print("Color is%s of%s Shooting at the enemy!" % (self.color, self.name))  # < -- self. Attribute name can use the attribute in the class;
# Instantiate object F35;
F35 = ModelPlane()
F35.name = "F35"
F35.color = "black"
F35.shot()  # F35 in red is shooting at the enemy!

1.4 common object-oriented functions

  throughout the world, everything from breaking through the ground and booming to the final sunset needs to follow the objective development law of nature. In the process of object-oriented programming, every object seems to face such a fate: creation, initialization, use, garbage collection. Like other object-oriented programming languages, classes in Python also support a variety of attributes and methods, such as object attributes and object methods, class attributes and class methods, private attributes and private methods, static methods and magic methods. The organic combination of these different attributes and methods ensures the normal operation of each stage of the object. Magic method, which starts with two underscores and ends with two underscores (__ xxx__()), They have special functions and will be activated at the appropriate time. Using them accurately and efficiently can greatly improve our programming efficiency.

1.4.1 instance attribute and method

   it is conceivable that if we set fixed attribute values when defining the class, such as color and size in the aircraft class, the attribute values of each object instantiated by this class are the same, that is, their color is red and their size is (100, 20). In addition, in real life, the attribute values of different objects are different. For example, everyone has his own name and age. In addition to adding and modifying attributes externally after creating an object, can we directly pass in attribute values for an object when it is created? Next, let's learn the first magic method:__ init__(self), which is used to initialize and set the properties of different objects.

① Parameterless__ init__(self): constructor, which is called by default when creating objects, and does not need to be manually invoked, in which self parameters point to the current calling object;

class Demo(object):

    def __init__(self):
        print("I'm a constructor for initializing objects!")

# The Python interpreter actively calls when an object is created__ init__() Method and pass the current object (demo) to the function parameter (self);
demo = Demo()  # I am a constructor for initializing objects!

② Parametric__ init__(self): a class can create multiple objects, and the properties of different objects are saved separately. The instance method is shared by all objects, occupying only one memory space;

class Student(object):
	
	# Constructor__ init__() Attributes in are called object attributes or instance attributes. Different objects save their own attributes and attribute values;
    def __init__(self, name, age): 
        self.name = name
        self.age = age

    def study(self):  # The object method is shared by all objects, and the Python interpreter will point self to the current function call object;
        print("I am%s,this year%d Years old, I'm eating Haidilao!" % (self.name, self.age))  # Class calls the attribute value through self;
# Instantiate the object stu1;
stu1 = Student("Haibodong", 30)  # When creating an object, you need to pass in values for the attributes name and age;
print("object stu1 The attribute value of is:", stu1.name, ",", stu1.age)  # The attribute value of object stu1 is: haibodong, 30
stu1.study()  # I'm haibodong. I'm 30 years old. I'm eating Haidilao!

# Instantiate object stu2;
stu2 = Student("Medusa", 25)
print("object stu2 The attribute value of is:", stu2.name, ",", stu2.age)  # The attribute value of object stu2 is Medusa, 25
stu2.study()  # I'm Medusa. I'm 25 years old. I'm eating Haidilao!

③ Built in properties__ slots__: It is used to specify the attributes that an object can exist, that is, the instantiated object can no longer add a new object attribute through the object name. Attribute = attribute value;

class Student(object):
    __slots__ = ("name", "age")  # __ slots__ Is a tuple, which can pass in attributes that need to be restricted. Each attribute is represented by a string, and multiple attributes are separated by commas;

    def __init__(self, name, age):
        self.name = name
        self.age = age

    def study(self):
        print("I am%s,this year%d Years old, I'm eating Haidilao!" % (self.name, self.age))
#  Instantiate the object stu3;
stu3 = Student("Haibodong", 30)
print("object stu3 Original attribute:", stu3.name, ",", stu3.age)  # Object stu3 original attribute: haibodong, 30

# Modify the existing attribute value outside the object stu3;
stu3.name = "Xiao Yan"
stu3.age = 20
print("object stu3 Externally modified properties:", stu3.name, ",", stu3.age)  # Object stu3 external modified attribute: Xiao Yan, 20
# Object stu3 adds non-existent attribute -- > attributeerror (attribute error)
stu3.spouse = "Yun Yun"

1.4.2 print object information

    as can be seen from the previous example, printing an instance object of a class directly using the print() function will return its class name and memory address information on the terminal. In fact, this is the way to call the object__ str__() The result of the magic method. Of course, we can also override this method to return descriptive information related to the object, such as its properties.

①__ str__(self): the first parameter self of the method represents the current calling object and does not need to pass other parameters, and it must return (return) the content of a string format.

class Figure(object):

    def __init__(self, name, age):
        self.name = name
        self.age = age
	
	# Rewrite__ str__() method;
    def __str__(self):
        return "I am{}, this year{}Years old!".format(self.name, self.age)

# Main program entry;
if __name__ == '__main__':
    # Instantiate the object;
    girl = Figure("Yun Yun", 25)
    print(girl)  # I'm Yun Yun. I'm 25 years old!

    # Instantiate the object;
    boy = Figure("Xiao Yan", 20)
    # Calling the built-in function str() triggers the of the object__ str__() method;
    print(str(boy))  # I'm Xiao Yan. I'm 20 years old!

②__ repr__(): The method is similar to__ str__() Can output the description information of the object. The difference is that this method can be called by directly inputting the object in the interactive environment without print();

class Figure(object):

    def __init__(self, name, age):
        self.name = name
        self.age = age
	
	# Rewrite__ repr__() method;
    def __repr__(self):
        return "I broke through the sky{}, this year{}year!".format(self.name, self.age)  # The return value must be in string format;

# Main program entry;
if __name__ == '__main__':
    # Instantiate the object;
    girl = Figure("Yun Yun", 25)
    print(girl)

    # Instantiate the object;
    boy = Figure("Xiao Yan", 20)
    print(repr(boy))  # Calling the built-in function repr() triggers the of the object__ repr__(self) method;
  • Note: if an object is both overridden__ str__() Method, rewritten again__ repr__() Method, the Python interpreter will execute it first__ str__() Method, and will print on the terminal;
class Figure(object):

    def __init__(self, name, age):
        self.name = name
        self.age = age

    # Rewrite__ str__() method;
    def __str__(self):
        return "I am__str__()Method, I was called!"

    # Rewrite__ repr__() method;
    def __repr__(self):
        return "I am__repr__()Method, I was called!"

# Main program entry;
if __name__ == '__main__':
    # Instantiate the object;
    girl = Figure("Yun Yun", 25)
    print(girl)  # I am__ str__() Method, I was called!

    # Instantiate the object;
    boy = Figure("Xiao Yan", 20)
    # Calling the built-in function repr() triggers the of the object__ repr__(self) method;
    print(repr(boy))  # I am__ repr__() Method, I was called!
  • Note: in the interactive environment, if a class is defined at the same time__ str__() Methods and__ repr__() Method, call the object to execute the latter, and print the object to execute the former, as shown below.

1.4.3 judge whether two objects are equal

    in Python, we can compare whether two objects belong to the same object through the identity operator is and the comparison operator = =. By default, they compare the memory addresses of the two objects. The difference is that the comparison operator = = can override the class to which the object belongs__ eq__() Methods come from defining comparison rules, which is the second magic method we want to learn.

① Memory address of the comparison object:

# First, define a function to compare two objects, which can print their comparison results and memory addresses;
def compare(ele1, ele2):
    print(f"object{ele1}and{ele2}of is The comparison result is:", ele1 is ele2)
    print(f"object{ele1}and{ele2}of==The comparison result is:", ele1 is ele2)
    print("{}and{}of id Respectively:".format(ele1, ele2), id(ele1), id(ele2))
  • Comparison of common Python objects: integer (int), string (str), list and Dictionary (dict);
# Call the function and pass in the object (here only take the string as an example, and the same is true for other objects)
compare("Hello", "hello")  # Two different objects;
"""
object Hello and hello of is The comparison result is: False
 object Hello and hello of==The comparison result is: False
Hello and hello of id Respectively: 2076256308336 2076256307952
"""

compare("Hello", "Hello")  # Two identical objects;
"""
object Hello and Hello of is The comparison result is: True
 object Hello and Hello of==The comparison result is: True
Hello and Hello of id Respectively: 2076256308336 2076256308336
"""
  • Custom object comparison: Here we specify that as long as the name and age in the object attribute are equal, we will consider them to be the same object;
# Define a Person class with name and age attributes;
class Person(object):
	
	def __init__(self, name, age):
		self.name = name
		self.age = age
# Instantiate two objects (different properties)
p1 = Person("Xiao Ming", 25)
p2 = Person("Xiao Hong", 26)
print(p1 is p2)  # False
print(p1 == p2)  # False
print("p1 and p2 The memory address of is:", id(p1), id(p2))  # The memory addresses of p1 and p2 are 2303743198208 and 2303747728720
# Instantiate two objects (same properties)
p3 = Person("Jack", 20)
p4 = Person("Jack", 20)
print(p3 is p4)  # False
print(p3 == p4)  # False
print("p3 and p4 The memory address of is:", id(p3), id(p4))  # The memory addresses of p3 and p4 are 14655322531841465532589392

② Custom comparison rule: obviously, the default comparison rule cannot compare the properties of the object, and it is necessary to override the properties in the class to which the object belongs__ eq__() Method, which belongs to the magic method of comparison operator;

# Override the in the Person class__ eq__() method;
class Person(object):

    def __init__(self, name, age):
        self.name = name
        self.age = age
	# As long as the contents (properties) of two objects are the same, they are considered equal;
    def __eq__(self, other):
        return (self.name == other.name) and (self.age == other.age)
# Retrieve the p3 and p4 objects above;
p3 = Person("Jack", 20)
p4 = Person("Jack", 20)
print(p3 is p4)  # False
print(p3 == p4)  # True
print("p3 and p4 The memory address of is:", id(p3), id(p4))  # The memory addresses of p3 and p4 are 2688001868800, 268800205008

1.4.4 class attributes and class methods

① Class attribute: in Python, a class can instantiate an object, and it is called class object. Class attribute is the attribute owned by class object, which is shared by all instance objects of the class;

class Person(object):
	"""
	When a recorded item of data is always consistent, you can define a class attribute because it only occupies one share of memory, which saves more memory space;
	"""
    type_ = "Human"  # Class attribute;

    def __init__(self, name, age):
        self.name = name
        self.age = age
  • Class attribute access: instance objects or class objects can call class attributes directly, but it is recommended to use class objects because using instance objects will be confused;
# Instantiate the object;
someone = Person("Nobody", 12)

# Calling class attributes through instance objects and class objects respectively;
print("Class attribute is:", someone.type_)  # Class attribute: Human
print("Class attribute is:", Person.type_)  # Class attribute: Human

# Instance properties can only be called through instance objects, and class objects cannot be used;
print("Instance properties are:", "Name = ", someone.name, ", Age = ", someone.age)  # The instance attribute is: name = nobody, age = 12
  • Class attribute modification: class attributes can only be modified through class objects, not instance objects, because the latter means to create an instance attribute for the class;
print("Class object before modifying class properties:", Person.type_)  # Class object before modifying class properties: Human
Person.type_ = "human beings"  # Modify class attributes through class objects;
print("Class object after modifying class properties:", Person.type_)  # Class object after modifying class attributes: Human
someone = Person("Nobody", 12)

print("Before modifying class properties of instance object:", someone.type_)  # Before modifying class properties of instance object: Human
someone.type_ = "human beings"  # Modify the class attribute through the instance object (essentially, create an attribute type_9;for the class to which it belongs);
print("After modifying class properties of an instance object:", Person.type_)  # Class object after modifying class properties: Human

print(someone.type_)  # human beings

② Class method: only class attribute methods are used in the class. The first parameter must be a class object (default cls), and the decorator @ classmethod needs to be used above the method for identification;

class Person(object):
    type_ = "Human"  # Class attribute;

    def __init__(self, name, age):
        self.name = name
        self.age = age

    # Use the identifier @ classmethod above the class method for identification;
    @classmethod
    def demo(cls):  # The first parameter is cls, not self;
        # Within a class, class attributes can only be called through class objects (cls) or class names, not instance objects (self);
        print("Hello, I'm%s!" % cls.type_)
        print("Hello, I'm%s!" % Person.type_)
  • Class method call: class methods can be called directly by instance objects or class objects, but class objects are actually recommended, because using instance objects will confuse others;
# Instantiate the object;
someone = Person("Nobody", 12)

# Use the instance object someone to call the class method;
someone.demo()
"""
Hello, I'm Human!
Hello, I'm Human!
"""

# Use the class object Person to call the class method;
Person.demo()  # The Python interpreter will automatically pass Person as cls to the demo function;
"""
Hello, I'm Human!
Hello, I'm Human!
"""

1.4.5 private attributes and methods

  in the actual development process, sometimes we need that some properties or methods of the object can only be used inside the class without external access. At this time, we can define private properties or methods;

  • Requirement: define a bank account class with account name and account balance money. It is specified that account balance can only be queried and cannot be recharged;
class BankAccount(object):

    def __init__(self, name, money):
        self.name = name
        self.money = money

# Instantiate a bank account;
someone = BankAccount("the other woman", 100)  # Initial 100 pieces;
print("Bank card account balance:", someone.money)  # Bank card account balance: 100

someone.money = 100000 
print("Bank card account balance:", someone.money)  # Bank card account balance: 100000
  • Analysis: it can be seen that the external can modify the amount arbitrarily. Obviously, this is contrary to our requirements, so we need to set it as a private attribute;

① Setting of private property or method: add two underscores _ before the private property or method, In the following, we take private attributes as an example, and private methods are the same;

class BankAccount(object):

    def __init__(self, name, money):
        self.name = name
        self.__money = money
	
	def __demo(self):
		print("")

# Instantiate a bank account;
someone = BankAccount("the other woman", 100)
print("Bank card account balance:", someone.money)

② Access to private attributes or methods: there is no absolute private in Python, but the difficulty of access is different;

  • Method 1: through the instance object_ Class name__ Private properties or methods can be accessed, and corresponding modifications can be made based on this;
# Instantiate a bank account;
someone = BankAccount("the other woman", 100)

# Private attribute external access;
print("Before modification:", someone._BankAccount__money)  # Before modification: 100

# External modification of private attributes;
someone._BankAccount__money = 500
print("After external modification of private property:", someone._BankAccount__money)  # Private attribute after external modification: 500
  • Method 2: by setting access and modification methods for private attributes within the class, the security of private attributes can be relatively guaranteed, which is also the most widely used one in practice;
class BankAccount(object):

    def __init__(self, name, money):
        self.name = name
        self.__money = money

    # Define the method to access the private property money. The general naming convention is: private property name_ getter() or private property name_ setter();
    def money_getter(self):
        return self.__money

    # Define the method to modify the private property money;
    def money_setter(self, new_money):
        # The practicability of this method is that it can filter the external assignment;
        if new_money > 10000:
            print("Please enter legal amount: 1-10000!")
            return  # In a function or method, if there is no return value after return, it represents the end of the function or method;
        self.__money = new_money
# Instantiate a bank account;
someone = BankAccount("the other woman", 100)
print("Account amount before modification:", someone.money_getter())  # Account amount before modification: 100

someone.money_setter(1000)
print("Legally modify account amount:", someone.money_getter())  # Legally modified account amount: 1000

someone.money_setter(20000)  # Please enter legal amount: 1-10000!
print("Illegal modification of account amount:", someone.money_getter())  # Illegal modification of account amount: 1000
  • Method 3: the property attribute can use a method in the class as an attribute, which can simplify the amount of code. It can be divided into two methods: Based on decorator and based on class attribute;
"""
Based on [decorator] property Property access and modification;
"""
class BankAccount(object):

    def __init__(self, name, money):
        self.name = name
        self.__money = money

    # Get private attribute: when getting the attribute, execute the following methods;
    @property
    def money(self):  # The method name must be consistent with the property name of the property decoration;
        return self.__money

    # Modify private property: when setting the property, execute the following methods;
    @money.setter
    def money(self, new_money):  # The method name must be consistent with the property name of the property decoration;
        if new_money > 10000:
            print("Please enter legal amount: 1-10000!")
            return
        self.__money = new_money

# Instantiate a bank account;
someone = BankAccount("the other woman", 100)
print("Account amount before modification:", someone.money)  # Account amount before modification: 100

someone.money = 5000
print("Modified account balance:", someone.money)  # Account amount before modification: 5000
"""
Based on class attribute property Property access and modification, which can be regarded as a simplified version of mode 2;
"""
class BankAccount(object):

    def __init__(self, name, money):
        self.name = name
        self.__money = money

    def money_getter(self):
        return self.__money

    def money_setter(self, new_money):
        if new_money > 10000:
            print("Please enter legal amount: 1-10000!")
            return
        self.__money = new_money

    money = property(money_getter, money_setter)  # It needs to be placed at the end, and the variable name needs to be consistent with the private attribute name;

# Instantiate a bank account;
someone = BankAccount("the other woman", 100)
print("Account amount before modification:", someone.money)  # Account amount before modification: 100

someone.money = 5000
print("Modified account balance:", someone.money)  # Account amount before modification: 5000

1.4.6 static method

   if a method in a class does not use instance attributes or class attributes, that is, it does not need formal parameters self or cls, we call it a static method, which is generally identified by the decorator @ staticmethod;

class Person(object):
    type_ = "Human"

    def __init__(self, name, age):
        self.name = name
        self.age = age

    # Use the identifier @ classmethod above the static method for identification;
    @staticmethod
    def greeting():
        print("I'm a static method!")
  • Static method call: static methods do not need instance attributes or class attributes, so they can be called directly through class objects to reduce unnecessary memory occupation and performance consumption;
# Instantiate the object and call the static method;
someone = Person("Tang San", 16)
someone.greeting()  # I'm a static method!

# Call static methods directly using class objects (recommended);
Person("Mubai", 15).greeting()  # I'm a static method!

1.4.7 memory application

  construction method__ new__(cls, *args, **kwargs), another magic method, which is the first method called by the Python interpreter when we create an object to apply for memory space. Its first formal parameter (cls) represents the current class, and other indefinite length parameters will be passed to the Python interpreter after the method is successfully called__ init__(self) method.

① With no return value__ new__(cls, *args, **kwargs): if the method has no return value or if the return value is None, then__ init__( The self () method is not executed 🤨;

class Demo(object):

    def __init__(self):
        print("Instantiate object step 2!")

# Instantiated object: if subclasses do not override__ new__() Method, the Python interpreter will call the method of the parent class by default to create and return an instance object;
demo = Demo()  # Instantiate object step 2!
print(demo)  # <__main__.Demo object at 0x00000167F8599400>
class Demo(object):

    def __new__(cls, *args, **kwargs):
        print("The first step of instantiating an object!")

    def __init__(self):
        print("Instantiate object step 2!")

# Instantiate object 1:__ new__() The function of is to return the instance object of the class, and__ init__() The premise of initialization is that the object already exists. If no object is generated, initialization will be lonely;
demo1 = Demo()  # The first step of instantiating an object!
print(demo1)  # None -- > object demo3 has no memory address, which means that it has not been created successfully at all!
# In this case, we can also apply for memory manually;
demo2 = object.__new__(Demo)
print(demo2)  # <__main__.Demo object at 0x000002195E497580>
Demo.__init__(demo2)  # Instantiate object step 2!

② With return value__ new__(cls, *args, **kwargs): the return value can only call the__ new__() Method cannot call this method in other unrelated classes;

class Demo(object):  # Inherited from string str class;

    # __ new__() A method always belongs to a class method, even if it does not have a class method decorator (@ classmethod)
    def __new__(cls, *args, **kwargs):  # __ new__() The first parameter of is the current class object, which belongs to the class level method;
        print("The first step of instantiating an object!")
        first_step = object.__new__(cls)
        print("first_step The memory address of is:", id(first_step))
        return first_step  # Create and return the instance object, save it with temporary variables, and pass it to__ init__() method;

    def __init__(self, name):  # __ init__() The first parameter is the instance object, which belongs to the instance level method;
        print("Instantiate object step 2!")
        print("self The memory address of is:", id(self))

# Instantiate the object;
demo3 = Demo("Unnamed")
print("Instantiate object demo3 The memory address of is:", id(demo3))
  • __ new__() Method can return an instance object of the current class, which will be internally passed to__ init__() Method so that the instance object can be successfully initialized;
  • __ init__() Other parameters defined in the method except the formal parameter self need to be associated with__ new__() The parameters in the method are consistent except the formal parameter cls;

③ Practical application 1: the singleton design pattern ensures that there is only one instance of a class. For example, there is only one recycle bin on the computer, which belongs to Object creation mode One kind of;

class Person(object):
	
    def __init__(self, name, age):
        self.name = name
        self.age = age

# Instantiate two objects separately: This is certainly not possible because__ new__() It will be called every time when instantiating to apply for new memory space;
stu1 = Person("Daming", 10000)
stu2 = Person("Er Ming", 99999)
print("object stu1 The memory address of is:", id(stu1))  # The memory address of object stu1 is 2213843342336
print("object stu2 The memory address of is:", id(stu2))  # The memory address of object stu2 is 2213847066992
# Idea: the first time__ new__() After instantiating an object and saving it, no matter how many times you call it later, only one object will be returned to realize the singleton mode;
class Person(object):
    __instance = None  # Define a private class attribute to hold__ new__() The returned instance object is called internally by the user class and cannot be directly modified and accessed externally;
    __is_first = True  # Private class attributes to ensure that the object attributes created by this class are consistent;

    @classmethod
    def __new__(cls, *args, **kwargs):  # If you do not override it, the of the object will be called automatically__ new__() Method to generate multiple instance objects;
        if not cls.__instance:
            cls.__instance = object.__new__(cls)
        return cls.__instance

    def __init__(self, name, age):  # The formal parameter self in init is the instance returned by new. Init only performs initialization based on it, and init does not need to return a value;
        if self.__is_first:  # Instance objects can only call class properties;
            self.name = name
            self.age = age
            self.__is_first = False  # The instance object cannot modify class properties, it just creates an object property__ is_first;

# Instantiate two objects respectively: one class creates only one instance;
stu1 = Person("Daming", 10000)  # It first goes in and then determines the memory space and attribute value;
stu2 = Person("Er Ming", 99999)  # During initialization, it calls the object properties created by stu1__ is_first is not the class attribute we define__ is_first, so it will not enter the if statement;

print("object stu1 The memory address of is:", id(stu1))  # The memory address of object stu1 is 2521520698752
print("object stu2 The memory address of is:", id(stu2))  # The memory address of object stu2 is 2521520698752

print("stu1 Your name and age are:", stu1.name, ",", stu1.age)  # stu1's name and age are: Daming, 10000
print("stu2 Your name and age are:", stu2.name, ",", stu2.age)  # stu2's name and age are: Daming, 10000

④ Practical application 2: record how many objects a class creates, which we can use__ new__() Methods can also be used__ init__() method;

class Person(object):
    __count = 0
    
    def __init__(self, name, age):
        Person.__count += 1
        self.name = name
        self.age =age
        
    def get_count(self):
        return "Total created{}second!".format(self.__count)  # self can obtain class attributes and cannot be modified;

# Instantiate the object;
p1 = Person("Glittering and translucent", 25)
p2 = Person("monkey", 26)
p3 = Person("Apple", 27)
p3.get_count()  # Created 3 times in total!
class Person(object):
    __count = 0
    
    @classmethod
    def __new__(cls, *args, **kwargs): 
        cls.__count += 1
        return object.__new__(Person)
    
    def __init__(self, name, age):
        self.name = name
        self.age = age
    
    @classmethod
    def get_count(cls):
        return "The class was called in total{}second!".format(cls.__count)

# Instantiate the object;
p7 = Person("Glittering and translucent", 26)
p8 = Person("bolt", 26)
p9 = Person("Treasure", 26)
p9.get_count()  # # Created 3 times in total!

1.4.8 memory destruction

  at present, we already know more or less about the creation, initialization and use of an object. Next is the twilight of the gods. Finally, when an object is deleted or the program is executed, the Python interpreter will automatically call__ del__(self) magic method, also known as destructor, which means that we can write operations before exiting in this method, such as saving and closing open files, disconnecting databases, etc.

① Automatic call__ del__(self): the Python interpreter automatically calls after the normal execution of the program__ del__() Delete the object by magic method (reference count is 0);

class Washer(object):

    def __new__(cls, *args, **kwargs):
        print("Object instantiation completed!")
        return object.__new__(cls)

    def __init__(self):
        print("Object initialization completed!")

    def __del__(self):
        print("The object has been deleted!")

# Main program entry;
if __name__ == '__main__':
    haier = Washer()
    """
    Object instantiation completed!
	Object initialization completed!
	The object has been deleted!
	"""
import time  # Import the time module in Python standard library;
class Washer(object):

    def __new__(cls, *args, **kwargs):
        print("Object instantiation completed!")
        return object.__new__(cls)

    def __init__(self):
        print("Object initialization completed!")

    def __del__(self):
        print("The object has been deleted!")

# Main program entry;
if __name__ == '__main__':
    haier = Washer()
    print("Program sleep for 2 seconds...") 
    time.sleep(2)  # The program hibernates for 2 seconds before the Python interpreter calls__ del__() Delete object;
    """
    Object instantiation completed!
	Object initialization completed!
	Program sleep for 2 seconds...
	The object has been deleted!
    """

② Manual call__ del__(self): we can manually call del method to delete the specified object during program execution;

import time  # Import time module;
class Washer(object):

    def __new__(cls, *args, **kwargs):
        print("Object instantiation completed!")
        return object.__new__(cls)

    def __init__(self):
        print("Object initialization completed!")

    def __del__(self):
        print("The object has been deleted!")

# Main program entry;
if __name__ == '__main__':
    haier = Washer()
    del haier  # Of the calling object__ del__() Method to delete the object, and the program sleeps for 2 seconds and then exits;
    time.sleep(2)
    print("The program needs to sleep for 2 seconds...")
    """
    Object instantiation completed!
	Object initialization completed!
	The object has been deleted!
	The program needs to sleep for 2 seconds...
	"""
  • Note: when you manually call del to delete an object, you can delete it completely only if the reference count of the object is 0;
import time  # Import time module;
class Washer(object):

    def __new__(cls, *args, **kwargs):
        print("Object instantiation completed!")
        return object.__new__(cls)

    def __init__(self):
        print("Object initialization completed!")

    def __del__(self):
        print("The object has been deleted!")
        
# Main program entry;
if __name__ == '__main__':
    haier = Washer()
    haier1 = haier  # Everything in Python is referenced. Through the assignment operation, the object haier1 and the object Haier now point to the same memory space (storing the Washer instance);
    del haier  # The first time del is called to delete the object Haier, it just breaks its connection with the memory space, and the object haier1 is connected to the space at this time;
    time.sleep(2)  # del is called to delete the object haier1 when the program exits after sleeping for 2 seconds;
    print("The program needs to sleep for 2 seconds...")
    """
    Object instantiation completed!
	Object initialization completed!
	The program needs to sleep for 2 seconds...
	The object has been deleted!
	"""
from copy import copy
import time  # Import time module;
class Washer(object):

    def __new__(cls, *args, **kwargs):
        print("Object instantiation completed!")
        return object.__new__(cls)

    def __init__(self):
        print("Object initialization completed!")

    def __del__(self):
        print("The object has been deleted!")
        
if __name__ == '__main__':
    haier = Washer()
    haier1 = copy(haier)  # The copy() method will copy the reference of the object Haier and the memory space it points to to the object haier1;
    del haier  # Call the del method to delete the Haier object (they do not affect each other now);
    time.sleep(2)  # When the program exits after sleeping for 2 seconds, call del to delete the object haier1;
    print("The program needs to sleep for 2 seconds...")
    """
    Object instantiation completed!
	Object initialization completed!
	Object instantiation completed!
	The object has been deleted!
	The program needs to sleep for 2 seconds...
	The object has been deleted!
	"""

1.4.9 using objects as dictionaries

    through the previous example, we already know that we can add attributes to objects or replace the values of attributes with the same name by means of object name. Attribute name = attribute value, and we have also learned that dictionaries can add new elements or overwrite the values of keys with the same name by means of variable name [key]=value. So, can we combine them to add or access properties for objects in the form of dictionaries?

# Define a dictionary that stores everyone's name and age, where name is the key of the dictionary and age is the value of the dictionary;
name_list = {"Jack": 25, "Rose": 20, "Lily": 25}
print(name_list)  # {'Jack': 25, 'Rose': 20, 'Lily': 25}

# Add new elements to the dictionary;
name_list["Yun Yun"] = 20
name_list["Little dance"] = 25
print(name_list)  # {'Jack': 25, 'Rose': 20, 'Lily': 25, 'yunyun': 20, 'Xiaowu': 25}

# Delete the specified key value pair from the dictionary;
print(name_list.pop("Rose"))
print(name_list)  # {'Jack': 25, 'Lily': 25, 'yunyun': 20, 'Xiaowu': 25}
class Animal(object):

    def __init__(self, name, age):
        self.name = name
        self.age = age

# Instantiate the object;
animal = Animal("Wangcai", 25)
# print("object animal The properties of are:", animal.name, ",", animal.age)  # The attribute of the object animal is: Wangcai, 25

# Add attributes to the object;
animal["age"] = 20  # The error information is as follows

①__ dict__: Python built-in attribute, which can return the attribute of an object in the form of a dictionary. The attribute name is the key of the dictionary, and the attribute value is the value of the dictionary;

class Figure(object):

    def __init__(self, name, age, color):
        self.name = name
        self.age = age
        self.color = color

# Main program entry;
if __name__ == '__main__':
    # Instantiate the object;
    girl = Figure("Yun Yun", 25, "black")
    print(girl.__dict__)  # {'name': 'yunyun', 'age': 25, 'color': 'Black'}
  • Note: if used in the class to which the object belongs__ slots__ The property is restricted, and then the object's__ dict__ An error will be reported when the object property is returned;
class Figure(object):
    __slots__ = ("name", "age", "color")  # Attribute restrictions;

    def __init__(self, name, age, color):
        self.name = name
        self.age = age
        self.color = color

# Main program entry;
if __name__ == '__main__':
    # Instantiate the object;
    girl = Figure("Yun Yun", 25, "black")
    print(girl.__dict__)  # Use__ slots__ After the object property is restricted, the object's__ dict__ Error will be reported;

② Accessing and setting object properties as a dictionary: magic methods__ setitem__() And__ getitem__() You can add and get object properties separately, and__ delitem__() Object attributes can be deleted;

class Animal(object):

    def __init__(self, name, age):
        self.name = name
        self.age = age

    # Add attributes to the object;
    def __setitem__(self, key, value):
        self.__dict__[key] = value

    # Access or obtain object properties;
    def __getitem__(self, item):
        return self.__dict__[item]

    # Delete object attributes;
    def __delitem__(self, key):
        self.__dict__.pop(key)

# Main program entry;
if __name__ == '__main__':
    animal = Animal("Wangcai", 25)
    print("object animal The original attribute is:{}, {}".format(animal.name, animal.age))  # The original attribute of the object animal is: Wangcai, 25

    # Modify the object attribute value;
    animal["name"] = "Bagong"

    # Add a new attribute to the object;
    animal["species"] = "Pastoral dogs"
    animal["spouse"] = False

    # View the specified attributes or all attributes of the object;
    print(animal["name"])  # Bagong
    print(animal.__dict__)  # {'name': 'Bagong', 'age': 25, 'species':' pastoral dog ',' house ': false}

    # Delete the specified attribute of the object;
    del animal["spouse"]  # Auto trigger object__ delitem__() Magic methods;
    print(animal.__dict__)  # {'name': 'Bagong', 'age': 25, 'specialties':' pastoral dog '}

1.5 other magic methods

  the magic methods in this part are not used as frequently as those described above. The following is only to expand the scope of knowledge and improve the knowledge system, so it will not be explained in too detail. Those interested can continue to read. In essence, the preceding and even the following magic methods are also called operator overloading, which means that a processing method corresponding to the operator is defined and implemented in the class, so that when the class object is operating the operator, the system will call the corresponding method in the class for processing.

1.5.1 comparison operator correlation

   comparison operator = = by defau lt, the memory addresses of two objects will be compared. For custom objects, you can also override the memory address of the object__ eq__() Method to customize the comparison rule. Similarly, if we need to use other comparison operators, such as >, <,! = ≤, ≥ and so on. If you can compare user-defined objects, don't mention whether Python really has this function.

class Person2(object):

    def __init__(self, name, age):
        self.name = name
        self.age = age

    def __eq__(self, other):  # self: points to the current calling object.
        return (self.name == other.name) and (self.age == other.age)  # The return value is a Boolean(True, False)

    def __ne__(self, other):  # (not equal to not equal) use= The operator automatically calls the method
        return not (self.__eq__(other))

    def __gt__(self, other):  # (greater than great than) using the > operator automatically calls the method
        return self.age > other.age

    def __ge__(self, other):  # (greater than or equal to great equal) using the > = operator automatically calls the method
        return self.age >= other.age

    def __lt__(self, other):  # (less than less than) using the < operator automatically calls the method
        return self.age < other.age

    def __gt__(self, other):  # (less than or equal to less equal) using the < = operator automatically calls the method
        return self.age <= other.age
# Main program entry
if __name__ == '__main__':
    P1 = Person2("Zhang San", 25)
    P2 = Person2("Zhang San", 25)
    P3 = Person2("Li Si", 26)

    print("object p1 And objects p2 Are they equal:", P1 == P2)  # Whether object p1 and object p2 are equal: True
    print("object p1 And objects p3 Are they equal:", P1 == P3)  # Whether object p1 and object p3 are equal: False
    print("object p1 And objects p2 Are they equal:", P1 is P2)  # Whether object p1 and object p2 are equal: False

    print("object P1 And objects P2 Unequal:", P1 != P2)  # Object P1 and object P2 are not equal: False
    print("object P1 Is it larger than the object P3:", P1 > P3)  # Whether object P1 is larger than object P3: True
    print("object P1 Is it greater than or equal to the object P3:", P1 >= P3)  # Whether object P1 is greater than or equal to object P3: False
    print("object P1 Is it smaller than the object P3:", P1 < P3)  # Whether object P1 is smaller than object P3: True
    print("object P1 Is it less than or equal to the object P3:", P1 <= P3)  # Whether object P1 is less than or equal to object P3: True

1.5.2 arithmetic operator correlation

   in addition to comparison operators, Python also supports us to rewrite various arithmetic operators, such as +, -, *, / /, / /,% and so on, so as to extend the functions of custom objects.

class Student(object):

    def __init__(self, name, age):
        self.name = name
        self.age = age

    def __add__(self, other):  # Plus (+)
        return self.age + other

    def __sub__(self, other):  # Minus (-)
        return self.age - other

    def __mul__(self, other):  # Multiply (*)
        return self.age * other

    def __truediv__(self, other):  # Integer division (/) [different from modulo (integer) / / (rounding down)]
        return self.age / other

    def __mod__(self, other):  # Surplus (%)
        return self.age % other

    def __pow__(self, other):  # Power (* *)
        return self.age ** other

# Main program entry;
if __name__ == '__main__':
    stu = Student("Zhang San", 25)
    print(stu + 2)  # 27
    print(stu - 2)  # 23
    print(stu * 2)  # 50
    print(stu / 2)  # 12.5
    print(stu % 2)  # 1
    print(stu ** 2)  # 625 
    
	# Supplement: rounding and remainder;
	integer, remainder = divmod(10, 3)
    print("10 The integer divided by 3 is{}, The remainder is{}!".format(integer, remainder))  # The integer of 10 divided by 3 is 3 and the remainder is 1!

1.5.3 type conversion related

   Python basic variable types, such as int, str, float, etc., can be converted to each other under the condition of meeting the rules. Does the user-defined object also support this operation?

class Animal(object):

    def __init__(self, name, age):
        self.name = name
        self.age = age

    def __int__(self):
        return self.age

    def __float__(self):
        return self.age * 1.0

    def __bool__(self):
        return self.age > 18

    def __str__(self):
        return "I am" + self.name

# Main program entry;
if __name__ == '__main__':
    animal = Animal("monkey", 3)
    print(animal)  # I'm a monkey

    print(int(animal))  # 3
    print(float(animal))  # 3.0
    print(bool(animal))  # False
    print(str(animal))  # I'm a monkey

2, Inherit

   in object-oriented programming, inheritance is a skill of designing classes, which mainly enables the dependency relationship between classes, and the subclass inherits all the attributes and methods of the parent class by default, which can greatly improve the reusability and simplicity of the code. In addition, in Python, subclasses (derived classes) can inherit from one or more parent classes (base or top-level classes) at the same time, so inheritance in Python can be divided into single inheritance, multi inheritance and multi-level inheritance.

Inheritance in real life:

2.1 single inheritance

General format: a subclass can only inherit from one parent class, and it will inherit all the properties and methods of the parent class by default;

class Subclass name(Parent class):  # The parent class or base class is generally enclosed by parentheses () after [class name];
	"""
	Do something!
	"""
	pass

The main line of the story: a pancake fruit teacher, who has worked hard in the industry for many years, summed up a set of exquisite pancake spreading technology, and wanted to pass it on to one of its most proud disciples;

# First create a master class
class Master(object):
	
	def __init__(self):
		self.kong_fu = "[Gufa pancake fruit formula]"
	
	def make_cake(self):
		print(f"use{self.kong_fu}Making pancake fruit!")
# Define an apprentice class, which inherits from the master class, and the subclass inherits all the attributes and methods of the parent class by default;
class Prentice(Master):
	
	# The sub category can also have its own methods: daqiu found that only selling cakes could not meet the needs of customers after market research, so he sold milk tea again;
	def make_drink(self):
		print("daqiu Original milk tea!")

# Instantiate a disciple object daqiu and call the master's properties and methods;
daqiu = Prentice()
print(daqiu.kong_fu)  # [Gufa pancake fruit formula]

daqiu.make_cake()  # Use [ancient recipe for pancake fruit] to make pancake fruit!
daqiu.make_drink()  # daqiu original milk tea!
  • Note: the subclass does not directly inherit the private properties and methods of the parent class, but as we said in section 1.4.5, there is no absolute private property, just the difficulty of access;
class Master(object):

    def __init__(self):
        self.kong_fu = "[Gufa pancake fruit formula]"
        self.__money = 100000  # Master's money is private, and external and subclasses cannot be used directly;

    def make_cake(self):
        print(f"use{self.kong_fu}Making pancake fruit!")

class Prentice(Master):

    # The sub category can also have its own methods: daqiu found that only selling cakes could not meet the needs of customers after market research, so he sold milk tea again;
    def make_drink(self):
        print("daqiu Original milk tea!")

# Main program entry;
if __name__ == '__main__':
    # Instantiate the object;
    daqiu = Prentice()
    print("Master, there are altogether{}element!".format(daqiu.money))
# 1.4.5 gives a detailed introduction to the private attribute, including its setting and access. Here we use the access and modification method of setting the private attribute for the outside;
class Master(object):

    def __init__(self):
        self.kong_fu = "[Gufa pancake fruit formula]"
        self.__money = 100000

    def make_cake(self):
        print(f"use{self.kong_fu}Making pancake fruit!")
	
	# Set private property access method for external;
    def money_getter(self):
        return self.__money
	
	# External setting private property modification method;
    def money_setter(self, new_money):
    	if new_money < self.__money:  # Add a judgment;
    		print("How dare you move your money!")
    		return
        self.__money = new_money

class Prentice(Master):

    # The sub category can also have its own methods: daqiu found that only selling cakes could not meet the needs of customers after market research, so he sold milk tea again;
    def make_drink(self):
        print("daqiu Original milk tea!")

# Main program entry;
if __name__ == '__main__':
    # Instantiate the object;
    daqiu = Prentice()
    print("Master, there are altogether:%s element!" % daqiu.money_getter())  # Master: 100000 yuan in total!

    # The dutiful daqiu saved another 100000 yuan for the master!
    daqiu.money_setter(200000)
    print("Master, there are altogether:%s element!" % daqiu.money_getter())  # Master: 200000 yuan in total!

2.2 multi inheritance

General format: a subclass can inherit from multiple parent classes at the same time, and will have the properties and methods of all parent classes by default;

class Subclass name(Parent class 1, Parent class 2, ..., Parent class n):  # The parent class or base class is generally enclosed by parentheses () after [class name];
	"""
	Do something!
	"""
	pass

Storyline: daqiu is a good child who loves learning. After learning, he wants to learn more pancake fruit technology, so he finds the dark horse programmer to sign up for class to learn technology;

 # Take the master class directly from above;
class Master(object):
	
	def __init__(self):
		self.kong_fu = "[Gufa pancake fruit formula]"
	
	def make_cake(self):
		print(f"use{self.kong_fu}Making pancake fruit!")
class School(object):
	
	def __init__(self):
		self.kong_fu = "[Dark horse pancake fruit formula]"
	
	def make_cake(self):
		print(f"use{self.kong_fu}Making pancake fruit!")

Thinking: multi inheritance enables subclasses to have all the properties and methods of the parent class, but different parent classes have the same method. When subclasses call this method, which parent class method will be called first?

① Subclass daqiu inherits Master first and then School;

# Construct a disciple class and instantiate the object daqiu;
class Prentice(Master, School): 
	"""
	Do something!
	"""
	pass

daqiu = Prentice()
print(daqiu.kong_fu)  # [Gufa pancake fruit formula]
daqiu.make_cake()  # Use [ancient recipe for pancake fruit] to make pancake fruit!

② Subclass daqiu inherits School first and then Master;

# Construct a disciple class and instantiate the object daqiu;
class Prentice(School, Master): 
	"""
	Do something!
	"""
	pass

daqiu = Prentice()
print(daqiu.kong_fu)  # [dark horse pancake fruit formula]
daqiu.make_cake()  # Use [dark horse pancake fruit formula] to make pancake fruit!

Conclusion: when subclasses call properties or methods with the same name of different parent classes, the properties and methods of the first parent class are used by default;

  by changing the order in which the subclass inherits the parent class and comparing their execution results, we have temporarily reached the above acceptable conclusion. However, because we can't enumerate all the possible situations, such results are always unconvincing. Therefore, in actual development, we generally use MRO(Method Resolution Order) to determine the order in which multiple inheritance subclasses call properties or methods with the same name as the parent class, and the Python interpreter will call properties or methods in this order. If found, it will be executed, otherwise the program will report an error. Note: this function needs to be called with [subclass name] inheriting multiple parent classes instead of [subclass instance object], that is, subclass name__ mro__. Even so, in practice, if multiple parent classes have properties or methods with the same name, you should try to avoid using multiple inheritance.

# Subclasses inherit Master first and then School
print(Prentice.__mro__)  # Return the result in the form of tuple;
# (<class '__main__.Prentice'>, <class '__main__.Master'>, <class '__main__.School'>, <class 'object'>)

# Subclasses inherit School first, and then Master
print(Prentice.__mro__)  # Return the result in the form of tuple;
# (<class '__main__.Prentice'>, <class '__main__.School'>, <class '__main__.Master'>, <class 'object'>)

2.3 multi level inheritance

General format: subclasses inherit from multiple parent classes, and subclasses inherit from multiple subclasses

class Subclass(Parent class 1, Parent class 2, ..., Parent class n):
	"""
	Do something!
	"""
	pass

class Subclass(Subclass 1, Subclass 2, ..., Subclass n):
	"""
	Do something!
	"""
	pass
......

storyline:

  1. The Animal class is the parent class of all animals and has the name and age attributes. The methods are eat, drink, run and sleep;
  2. Dog class and Cat class inherit from Animal class, add the variable attribute on the basis of the parent class, and add bar and catch to the method respectively;
  3. XiaoTianQuan class is integrated from Dog class, with fly method added, and the attributes remain unchanged;
# First construct an Animal class
class Animal(object):

    def __init__(self, name, age):
        self.name = name
        self.age = age

    def eat(self):
        print("I am%s,I was having dinner!" % self.name)

    def drink(self):
        print("I am%s,I'm drinking water !" % self.name)

    def run(self):
        print("I am%s,I'm running!" % self.name)

    def sleep(self):
        print("I am%s,I'm sleeping!" % self.name)
# Then define Dog class and Cat class respectively, which inherit from Animal class;
class Dog(Animal):

    def __init__(self, name, age, variety):
        super().__init__(name, age)  # Call the parent class method (explained later in 2.5)
        self.variety = variety

    def bark(self):
        print(f"I am{self.name},I'm barking!")

class Cat(Animal):

    def __init__(self, name, age, variety):
        super(Cat, self).__init__(name, age)  # Call the parent class method (explained later in 2.5)
        self.variety = variety

    def catch(self):
        print(f"I am{self.name},I'm climbing a tree!")
# Finally, XiaoTianQuan class is defined, which inherits from Dog class;
class XiaoTianQuan(Dog):

    def fly(self):
        print("I am{},I'm flying with Erlang God!".format(self.name))

if __name__ == '__main__':
	# Instantiate the object and call its internal methods;
    xiao_tian_quan = XiaoTianQuan("Howling dog", 25, "Erlang God mount")
    xiao_tian_quan.eat()
    xiao_tian_quan.drink()
    xiao_tian_quan.run()
    xiao_tian_quan.sleep()
    xiao_tian_quan.bark()
    xiao_tian_quan.fly()

Program execution results:

2.4 subclass [override] parent attribute or method

Plot: after daqiu mastered the skills of master and dark horse, he developed a new set of pancake fruit technology with unique formula;

# Take the master class directly;
class Master(object):
	
	def __init__(self):
		self.kong_fu = "[Gufa pancake fruit formula]"
	
	def make_cake(self):
		print(f"use{self.kong_fu}Making pancake fruit!")
# Bring black horses directly;
class School(object):
	
	def __init__(self):
		self.kong_fu = "[Dark horse pancake fruit formula]"
	
	def make_cake(self):
		print(f"use{self.kong_fu}Making pancake fruit!")
# Construct disciple class;
class Prentice(object):
	# Rewrite__ init__()
	def __init__(self):
		self.kong_fu = "[Daqiu Original pancake fruit formula]"
		
	# Rewrite make_cake()
	def make_cake(self):
		print(f"use{self.kong_fu}Making pancake fruit!")

# Instantiate a disciple object Daqiu;
daqiu = Prentice()
print(daqiu.kong_fu)  # [Daqiu's original recipe for pancake fruit]
daqiu.make_cake()  # Use [Daqiu original pancake fruit formula] to make pancake fruit!

Conclusion: the subclass and the parent class have the same name attributes or methods, and the subclass's attributes or methods with the same name are preferred;

2.5 subclass [call] parent class attribute or method

① Example: in actual development, a subclass is more about adding functions on the basis of the parent class's properties or methods. At this time, it involves how to call the parent class's properties or methods;

# First define a Person class, which has name and age attributes and eat() method;
class Person(object):

    def __init__(self, name, age):
        self.name = name
        self. age = age

    def eat(self):
        print("I am%s,this year%d Years old, I'm eating hot pot!" % (self.name, self.age))	

1. Use super (current class name, self). Attribute or method to call:

# The student class adds the school attribute on the basis of inheriting the Person class, and adds the print school information on the basis of the eat method of the parent class;
class Student(Person):

    def __init__(self, name, age, school):
        super(Student, self).__init__(name, age)  # The first parameter in super() is the current class name, and the second parameter is the current class instance object;
        self.school = school

    def eat(self):
        super(Student, self).eat()  # The first parameter in super() is the current class name;
        print("I am now in%s!" % self.school)

    def __str__(self):
        return "name = %s, age = %d, school = %s" % (self.name, self.age, self.school)
        
# Instantiate a student object;
stu = Student("Li Niang", 99, "Thinking Island")
print(stu)  # name = Li Niang, age = 99, school = thinking Island
stu.eat()  # The program execution results are as follows:;
"""
I'm Li Niang. I'm 99 years old. I'm eating hot pot!
I am now located on siliang island!
"""

2. Call with super(). Attribute or method:

# The ITCoder class adds hair on the basis of inheriting the Person class_ Style attribute, and override the eat method;
class ITCoder(Person):

    def __init__(self, name, age, hair_style):
        super().__init__(name, age)  # super() does not need to pass any parameters, and it will automatically call the corresponding methods according to the inheritance order;
        self.hair_style = hair_style

    def eat(self):
        print("Hello, I'm a code dog!")
        super().eat() # super() does not need to pass any parameters, and it will automatically call the corresponding methods according to the inheritance order;

    def __str__(self):
        return "name = %s, age = %d, hair_style = %s" % (self.name, self.age, self.hair_style)

# Instantiate a programmer object;
coder = ITCoder("side dish", 25, "Baldness")
print(coder)  # name = Li Niang, age = 99, hair_style = bald
coder.eat()  # The program execution results are as follows
"""
Hello, I'm a code dog!
I'm a small dish. I'm 25 years old. I'm eating hot pot!
"""

3. Call with parent class name, attribute or method:

class CivilServant(Person):

    def __init__(self, name, age, slogan):
        Person.__init__(self, name, age)  # Call through the [parent class name]. Method (self, *args, **kwargs);
        self.slogan = slogan

    def eat(self):
        print("Hello, I'm a civil servant!")
        Person.eat(self)  # Call through the [parent class name]. Method (self);

    def __str__(self):
        return "name = %s, age = %d, slogan = %s" % (self.name, self.age, self.slogan)
        
# Instantiate a civil servant object;
gov = CivilServant("Xiaobai", 25, "Serve the people")
print(gov)  # name = Xiaobai, age = 25, slogan = serving the people
gov.eat()  # The program execution results are as follows:;
"""
Hello, I'm a civil servant!
I'm Xiaobai. I'm 25 years old. I'm eating hot pot!
"""

Summary:

  1. Parent class name, attribute or method. If the parent class name changes, it will be very difficult to maintain the later code, so try not to use this method;
  2. Super (current class name, self) or super() have the same function, but if the current class name changes, maintenance is also troublesome, so it is recommended to use the latter. However, they are not suitable for the case where the parent class attribute or method has the same name in multiple inheritance, because their calling order depends on the order in MRO, so it is difficult to call accurately;

② Scenario reproduction: if many customers want to eat both daqiu's original pancake fruit and Master or School's Pancake fruit, how can they achieve it`

# Directly use Master class and School class;
class Master(object):
	
	def __init__(self):
		self.kong_fu = "[Gufa pancake fruit formula]"
	
	def make_cake(self):
		print(f"use{self.kong_fu}Making pancake fruit!")
		
class School(object):
	
	def __init__(self):
		self.kong_fu = "[Dark horse pancake fruit formula]"
	
	def make_cake(self):
		print(f"use{self.kong_fu}Making pancake fruit!")
# Define disciple class;
class Prentice(Master, School):

    def __init__(self):
        self.kong_fu = "[Original pancake fruit formula]"

    def make_cake(self):
        print(f"application{self.kong_fu}Making pancake fruit!")

    def make_master_cake(self):
        super().__init__()
        super().make_cake()

    def make_school_cake(self):
        School.__init__(self)  # Pass in the current class instance object;
        School.make_cake(self)
# Instantiate a disciple object;
daqiu = Prentice()
daqiu.make_cake()
daqiu.make_master_cake()
daqiu.make_school_cake()

Program execution results:

Think: is there a problem with the code defined in the subclass?

# Change the order of the above daqiu calling methods: first call mater, then call yourself, then call school;
daqiu = Prentice()
daqiu.make_master_cake()
daqiu.make_cake()
daqiu.make_school_cake()

Program execution result: you can see that after calling the Master method, its__ init__() Overrides the corresponding attribute in the subclass daqiu

Solution: subclasses call their own methods before calling their own methods__ init__ Initialize, otherwise it will be overwritten by the properties of the parent class;

class Prentice(Master, School):
	# The omitted part of the code is the same as above, and here is only to highlight the key points of modification;
    def make_cake(self):
    	# Call the initialization of your own subclass before calling the attribute, otherwise it will be overwritten by the attribute of the parent class;
        self.__init__()
        print(f"application{self.kong_fu}Making pancake fruit!")
daqiu = Prentice()
daqiu.make_master_cake()
daqiu.make_cake()
daqiu.make_school_cake()

Execution results of the modified program: obviously, the modified code is more robust and accurate;

2.6 type of judgment object

  in Python, built-in functions type() and isinstance() are often used to determine which type a variable or object belongs to. What are the differences between them? type() accepts an object, judges and returns its type, but thinks that the subclass instance object is different from the parent class type, while isinstance() can judge whether an instance object is created by a subclass or its inherited parent class, but cannot get its type.

① Common Python data types: both type() and isinstance() can accurately implement this requirement;

# Judge through the built-in function type(object): it will judge and return the type of the incoming object, so we can judge directly through its return value;
print(type(10) == int)  # True
print(type("Hello") == str)  # True
print(type(85.36) == float)  # True
print(type(1+2j) == complex)  # True

print(type([10, 20, 30]) == list)  # True
print(type((10,)) == tuple)  # True
print(type({"name": "Jack", "age": 20}) == dict)  # True
print(type({"name", "age", "hobby"}) == set)  # True

print(type({}) == set)  # False: in Python, an empty set can only be represented by set(), {} represents an empty dictionary;
print(type({}) == dict)  # True
# isinstance(object, type) judges according to the incoming object and type. If the object belongs to type, it returns True; otherwise, it returns False;
print(isinstance(10, int))  # True
print(isinstance("Hello", str))  # True
print(isinstance(85.26, float))  # True
print(isinstance(10+2j, complex))  # True

print(isinstance([10, 20, 30], list))  # True
print(isinstance((10,), tuple))  # True
print(isinstance({"name": "Jack", "age": 20}, dict))  # True
print(isinstance({"name", "age", "hobby"}, set))  # True

print(isinstance({}, set))  # False: in Python, an empty set can only be represented by set(), {} represents an empty dictionary;
print(isinstance({}, dict))  # True

② User defined object comparison: isinstance() can judge whether the instance object of a subclass belongs to its parent class, while type() does not have this ability;

# Customize an Animal class;
class Animal(object):

    def __init__(self, name, age):
        self.name = name
        self.age = age

    def run(self):
        print("Hello, I'm%s,this year%d Years old, I'm running!" % (self.name, self.age))

# Define a Dog subclass, which inherits from the Animal class;
class Dog(Animal):

    def __init__(self, name, age, variety):
        super(Dog, self).__init__(name, age)
        self.variety = variety

# Instantiate an Animal object and a Dog object respectively;
animal = Animal("Alive", 100)
dog = Dog("Bagong", 12, "Koki")
# Use type() to judge;
print(type(animal) == Animal)  # True
print(type(dog) == Dog)  # True
print(type(dog) == Animal)  # False: the subclass instance object dog does not belong to its parent class Animal;
# By object__ class__ You can view which class the object of the current operation is, which is functionally equivalent to type();
print(dog.__class__ == Dog)  # True

# Use isinstance() to judge;
print(isinstance(animal, Animal))  # True
print(isinstance(dog, Dog))  # True
print(isinstance(dog, Animal))  # True: the subclass instance object dog belongs to its parent class Animal;

③ Supplement: isubclass() can judge the inheritance relationship between two classes;

# Add a Student class, which inherits from the base class object;
class Student(object):

    def __init__(self, name, age):
        self.name = name
        self.age = age

# Judge the relationship between Animal, Dog and Student classes;
print(issubclass(Animal, object))  # True

print(issubclass(Dog, Animal))  # True
print(issubclass(Dog, object))  # True

print(issubclass(Student, object))  # True
print(issubclass(Student, Animal))  # False

3, Abstract classes and polymorphism

  in Python, the idea of abstract classes and polymorphic problem solving is very similar. Understanding and mastering their differences and similarities is very important to improve the simplicity, readability and extensibility of the code.

3.1 abstract classes

    so far, all the classes we have contacted belong to ordinary classes. They can directly generate instantiated objects, and this class can contain construction methods, instance methods, class methods, static methods, attributes and so on. On the contrary, an abstract class adds an abstract method to a common class, and an abstract method refers to a method that is only defined in the class but has no concrete implementation inside. Unlike other static languages, such as Java, you can directly implement abstract classes through the keyword abstract. In Python, you need to import abcmeta - the base class of all abstract classes from the abc module. At the same time, the module also includes abstract methods and abstract properties. Therefore, according to the definition of abstract class, it has the following characteristics:

  1. Abstract classes can contain normal methods and abstract methods. The abstract method needs to be identified by the decorator @ abstractmethod, and there is no realizable code in it;
  2. Abstract classes can only be inherited, not instantiated. Only subclasses that inherit and implement all its abstract methods can be instantiated;
  3. Member properties and abstract properties can be added to abstract classes, and a class cannot be instantiated as long as it contains one of the abstract methods or abstract properties;

① Judge whether the following classes belong to abstract classes:

from abc import ABCMeta  # Import ABCMeta from Python built-in module;
class TestClass(metaclass=ABCMeta):
    pass

cls = TestClass()  # Can be instantiated;
from abc import ABCMeta, abstractmethod  # Import abstractmethod from abc module;
class AbstractClass(metaclass=ABCMeta):
	# Generally, the methods under @ abstractmethod of decorator are abstract methods;
    @abstractmethod
    def abstract_method(self):
        pass  # Abstract methods cannot contain any implementable code;
        
demo = AbstractClass()  # Cannot be instantiated because it has abstract methods inside;

② For example, everyone has the function of eating, drinking and having fun, but the specific entertainment methods vary from Person to Person, which means that we can't write these functions when defining a Person class;

from abc import ABCMeta, abstractmethod, abstractproperty
# Define an abstract class containing two abstract methods;
class Person(metaclass=ABCMeta):

    def __init__(self, name, age):
        self.name = name
        self.age = age
        self.school = "Shrek College"
	# Abstract classes can contain instance methods;
    def run(self):
        print("I am%s,I'm running!" % self.name)

    @abstractmethod
    def eat(self):
        pass

    @abstractmethod
    def play(self):
        pass
# Define a [subclass 1]: it must implement all abstract methods of the parent class;     
class Subclass(Person):  # Ctrl+I in pychar can automatically implement abstract methods;

    def eat(self):
        print("I am%s,I'm eating hot pot in the star forest!" % self.name)

    def play(self):
        print("I am%s,I'm playing with Daming and Erming!" % self.name)

per = Subclass(name="Little dance", age=25)
per.eat()
# The execution result of the program is: I'm Xiaowu. I'm eating hot pot in the star forest!
per.play()
# The result of program execution is: I'm a small dance. I'm playing with Daming and Erming!
# Define a [subclass 2]: it must implement all abstract methods of the parent class;     
class Subclass(Person):  # Ctrl+I in pychar can automatically implement abstract methods;

    def eat(self):
        print("I am%s,I'm%s Sell sausage!" % (self.name, self.school))

    def play(self):
        print("I am%s,I'm chasing Ning Rongrong in Qibao Liuli sect!" % self.name)

per2 = Subclass(name="Oscar", age=26)
per2.eat()
# The result of the program is: I'm Oscar. I'm selling sausages at Shrek college!
per2.play()
# The result of program execution is: I'm Oscar. I'm chasing Ning Rongrong in Qibao Liuli sect!

3.2 polymorphism

Polymorphism refers to a class of things with multiple forms, which are commonly used in static languages, such as Java. Polymorphism allows functions with different functions to use the same function name, so that functions with different functions can be called through a function name to produce different execution results. In addition, the implementation of polymorphism is based on inheritance (non mandatory, but recommended) and overriding parent class methods, so it is only a skill to call methods and will not affect the internal design of the class. Therefore, this method can increase the flexibility of external calls, enhance compatibility, and better adapt to changing needs.

Programming implementation: anti drug dogs are used to track down drugs, military dogs are responsible for chasing the enemy, guide dogs are used to lead the way, and people let different dogs do different things;

Scheme 1: do not use polymorphic implementation;

"""
Define anti drug dogs, military dogs and guide dogs and their corresponding methods;
"""
class DrugDog(object):

    def search_drug(self):
        print("Drug dogs are searching for drugs!")

class PoliceDog(object):

    def attack_enemies(self):
        print("The military dog is chasing the enemy!")

class BlindDog(object):

    def load_road(self):
        print("The guide dog is leading the way!")
# Define the Person class, which has the attributes of name and dog. Different dogs perform different tasks;
class Person(object):

    def __init__(self, name):
        self.name = name
        self.dog = None

    def work_with_drug(self):  # Let the anti drug dog work;
        if self.dog:
            self.dog.search_drug()

    def work_with_police(self):  # Let military dogs work;
        if self.dog:
            self.dog.attack_enemies()

    def work_with_blind(self):  # Let the guide dog work;
        if self.dog:
            self.dog.load_road()
# Instantiate various classes defined above and execute code according to corresponding logic;
someone = Person("Tang San")

drug_dog = DrugDog()
someone.dog = drug_dog  # someone.dog --> drug_ Dog -- > call the method inside the class;
someone.work_with_drug()
# Program execution result: anti drug dog is searching for drugs!

police_dog = PoliceDog()
someone.dog = police_dog
someone.work_with_police()
# Program execution result: military dogs are chasing the enemy!

blind_dog = BlindDog()
someone.dog = blind_dog
someone.work_with_blind()
# Program execution result: the guide dog is leading the way!

  at present, this requirement can be realized without polymorphism. Ok, now if there is another dog breed, then we need to define a corresponding method in the Person class to manipulate the dog through this method. Obviously, the Person class will keep adding new functions, and its internal code needs to be changed every time. The program scalability is too poor and does not comply with the open and closed principle (open function, closed code).

Scheme 2: use polymorphism to realize this requirement. Its implementation can be divided into the following three steps.

  1. Define the parent class and provide public methods;
  2. Define subclasses, inherit and override parent methods;
  3. When you pass a subclass object to the caller, you can see that different subclasses have different implementation effects;

1. Define a parent class and provide public methods;

class Dog(object):

    def work(self):
        pass

2. Define subclasses, inherit and implement parent class methods;

class DrugDog(Dog):

    def work(self):
        print("Drug dogs are searching for drugs!")

class PoliceDog(Dog):

    def work(self):
        print("The military dog is chasing the enemy!")

class BlindDog(Dog):

    def work(self):
        print("The guide dog is leading the way!")
"""
At present, although subclasses can not inherit from the parent class, as long as they implement a method with the same name inside each class work(),Then the instantiation is passed to the caller, and it seems that it can also be executed successfully;
However, if an animal comes now, such as a cat, it also has work()method. Then, when it is instantiated and passed to the caller, it will not report an error, but execute successfully;
Obviously, this is not the result we want, so for the use of polymorphism in the actual development process, inheritance is recommended to ensure the correctness and consistency of the results;
"""
class Cat(object):

	def work(self):
		print("The shepherd is on duty!")

cat = Cat()  # When we instantiate Cat and pass it to the caller, no error will be reported, but the result is not what we want;
# Define human beings;
class Person(object):
	# Have name and age attributes;
    def __init__(self, name, dog=None):
        self.name = name
        self.dog = dog

    def work_with_dog(self):
        # If the caller does not pass in a dog, or the animal it passes in does not belong to a dog, the following code will not be executed;
        if self.dog and isinstance(self.dog, Dog):
            self.dog.work()  # You only need to call the public methods in the parent class (Dog) of all subclasses to ensure that subclasses achieve different effects;

3. Instantiate the above types and pass the subclass objects to the caller. You can see that their implementation effects are different;

# Instantiate a human, and instantiate dogs respectively;
someone = Person("Tang San")
drug_dog = DrugDog()
police_dog = PoliceDog()
blind_dog = BlindDog()

someone.dog = drug_dog
someone.work_with_dog()
# Program execution result: anti drug dog is searching for drugs!

someone.dog = police_dog
someone.work_with_dog()
# Program execution result: military dogs are chasing the enemy!

someone.dog = blind_dog
someone.work_with_dog()
# Program execution result: the guide dog is leading the way!

4, Practice makes true knowledge

  although Python's object-oriented syntax is relatively simple, its knowledge points are also cumbersome. If you want to use them accurately, efficiently and flexibly to solve practical problems, you need to practice and test them in practice, so as to truly master the idea and essence of object-oriented programming. Therefore, the focus of our problem-solving in this part is not to explain the code one by one, but to pay more attention to the process and ideas of solving the problem.

4.1 trial ox knife

Actual demand:

  1. Define student classes. Students have name, student number, age, python score, java score and c language score
    And learning and playing games;
  2. Create 5 student objects and add them to the student list;
  3. The student objects in the student list are sorted in descending order according to the average scores of python and c language;
# 1. Define a student class, which includes name, student number, age, Python score, java score and C language score;
class Student(object):

    def __init__(self, name, stu_id, age, python_score, c_score):
        self.name = name
        self.stu_id = stu_id
        self.age = age
        self.python_score = python_score
        self.c_score = c_score
        self.avg_score = (self.python_score+self.c_score) / 2

    def study(self):
        print("%s Learning, please don't disturb!" % self.name)

    def play_game(self):
        print(f"{self.name}He is playing games this year{self.age}Years old!")

    def __str__(self):  # Print object information;
        string = ""
        string += "I am%s, my Python The result is%d, C The language score is:%d" % (self.name, self.python_score, self.c_score)
        string += ",The average score is: %d" % self.avg_score
        return string
if __name__ == '__main__':
    # 2. Instantiate 5 objects and store them in the list;
    xiao_san = Student("the other woman", "123456", 25, 100, 100)
    xiao_wu = Student("Little dance", "456789", 26, 98, 97)
    rong = Student("Ning Rongrong", "134679", 25, 99, 98)
    mu_bai = Student("Dai mubai", "159753", 28, 93, 92)
    xiao_ao = Student("Oscar", "147896", 27, 95, 98)
    students = [xiao_san, xiao_wu, rong, mu_bai, xiao_ao]

    # 3. Sort [student objects] in [list] in descending order according to [average score of python and c language];
    students.sort(key=lambda x: (x.python_score + x.c_score) >> 1, reverse=True)  # sorted()
    for ele in students:
        print(ele)

Program execution results:

4.2 cultural differences

Actual demand:

  1. Define the Person class: the attributes are name and age; The method is eat, and the function is eat.
  2. Define the ZhHuman class to inherit the Person class: the attributes are name, age and color (skin color); The method is eat, and the function is [eating with chopsticks].
  3. Define the USHuman class to inherit the Person class: the attributes are name, age and color (skin color); The method is eat and the function is eat with knife and fork.
  4. Define AfricaHuman class to inherit Person class: the attributes are name, age and color (skin color); The method is eat and the function is eat by hand.
  5. Define Cat class: the attributes are name, age and variety; The method is eat, and the content is [Cat eats fish].
class Person(object):

    def __init__(self, name, age):
        self.name = name
        self.age = age

    def eat(self):
        print("I am eating!")

class ZhHuman(Person):

    def __init__(self, name, age, color):
        super().__init__(name, age)
        self.color = color

    def eat(self):
        print("Chinese people eat with chopsticks!")

class USHuman(Person):

    def __init__(self, name, age, color):
        super(USHuman, self).__init__(name, age)
        self.color = color

    def eat(self):
        print("Americans eat with knives and forks!")

class AfricanHuman(Person):

    def __init__(self, name, age, color):
        Person.__init__(self, name, age)
        self.color = color

    def eat(self):
        print("Africans eat with their hands!")

class Cat(object):

    def __init__(self, name, age, variety):
        self.name = name
        self.age = age
        self.variety = variety

    @staticmethod
    def eat():
        print("Cats eat fish!")

def start_eat(human):
    human.eat()
p1 = Person('Zhang San',30)
p2 = ZhPerson('Li Si',30,'yellow race')
p3 = USPerson('Wang Wu',30,' white person')
p4 = AfricaPerson('Li Si',30,'black')
cat = Cat('Li Si',30,'Persian cat')
start_eat(cat)

start_eat(p1)
start_eat(p2)
start_eat(p3)
start_eat(p4)

Program execution results:

4.3 warm home

① Demand and analysis: put furniture smaller than the room area into the room. The room area here refers to the remaining usable area of the room;

  • Furniture: each furniture has its own name and floor area;
  • Room class: it has the properties of house type, total area and furniture list; Having a method of adding and displaying furniture;

② Code implementation:

# Furniture;
class Furniture(object):

    def __init__(self, name, area):
        self.name = name
        self.area = area
# Rooms;
class House(object):

    def __init__(self, type_, total_area):
        self.type_ = type_
        self.total_area = total_area
        self.remain_area = total_area * 0.7
        self.furniture_list = []

    # Add furniture;
    def add_furniture(self, furniture: Furniture):  # Objects can also be passed as method parameters;
        # Before adding furniture each time, judge whether the remaining area of the room is greater than the area of the furniture;
        if self.remain_area < furniture.area:
            print(f"{furniture.name}The area of the is{furniture.area}, The remaining area of the room is{self.remain_area},It's too big to fit!")
            return
        self.furniture_list.append(furniture.name)
        # After adding, the remaining area of the room should be updated;
        self.remain_area -= furniture.area

    # Display furniture;
    def __str__(self):
        return "The house type is:{}, The total area is:{}square metre, The remaining area is:{}square metre, Furniture list is:{}".format(self.type_, self.total_area,
                                                             self.remain_area, self.furniture_list)
# Main program entry;
if __name__ == '__main__':
    # Instantiate a room object first;
    my_house = House("One room and one living room", 144)
    print(my_house)  # House type: one bedroom and one living room, total area: 144 square meters, remaining area: 100.8 square meters, furniture list: []

    # Instantiate multiple furniture objects;
    bed = Furniture("spring mattress bed", area=32)
    chest = Furniture("wardrobe", area=28)
    table = Furniture("Table", area=25)
    sofa = Furniture("sofa", area=21)

    # Put the created furniture into the room;
    my_house.add_furniture(bed)
    my_house.add_furniture(chest)
    my_house.add_furniture(table)
    my_house.add_furniture(sofa)  # The area of the sofa is 21, and the remaining area of the room is 15.799999999997. It's too big to fit!

    # Print room information;
    print(my_house)  # House type: one bedroom and one living room, total area: 144 square meters, remaining area: 15.799999999997 square meters, furniture list: [Simmons bed ',' wardrobe ',' table ']

4.4 class student information management

① Demand and analysis:

  • Student category: it has the attributes of student number, name, age, gender and achievement;
  • Class class: it has class name and student information attributes in the class (use list to store student objects). The corresponding methods are as follows
  1. It has the function of adding student information (student name cannot be repeated);
  2. View the information of all students in the class;
  3. View the information of students with specified student number;
  4. View the information of students who fail (less than 60 points) in the class;
  5. Sort the students in the class in descending order according to their grades;

② Code implementation:

# First define a student class;
class Student(object):

    def __init__(self, stu_id, stu_name, stu_age, stu_gender, stu_score):
        self.stu_id = stu_id
        self.stu_name = stu_name
        self.stu_age = stu_age
        self.stu_gender = stu_gender
        self.stu_score = stu_score

    def __str__(self):
        return f"Student number is:{self.stu_id}, Name is:{self.stu_name}, Age is:{self.stu_age}, Gender:{self.stu_gender}, The result is{self.stu_score}!"
# Then define a class class;
class Class(object):

    def __init__(self, name):
        self.name = name
        self.stu_infos = []

    # Add student objects to the list;
    def add_stu(self, student: Student):
        # We do not allow duplicate student names, so we need to traverse the student list to judge one by one before adding student objects each time;
        for stu in self.stu_infos:
            if stu.stu_name == student.stu_name:
                print("The student already exists, please re-enter!")
                break
        else:  # Execute else statement after the for...in loop ends normally: if the newly added student does not exist in the list, it can be added;
            self.stu_infos.append(student)

    # View the information of all students in the class;
    def query_all(self):
    	# Non empty detection. If the class student list is empty, the user will be prompted to add student information!
        if not self.stu_infos:
            print("There are no students in this class. Please add student information first!")
            return
        print(f"[{self.name}All student information is as follows]".center(46, "-"))
        for stu in self.stu_infos:
            print(stu)

    # View the information of students with specified student number;
    def query_stu(self, stu_id):
        stu_len = len(self.stu_infos)
        i = 0
        while i < stu_len:
            if self.stu_infos[i].stu_id == stu_id:
                print("The student number you inquired is{}Your student information is:".format(stu_id), self.stu_infos[i])
                break
            i += 1
        else:  # The usage of while...else is the same as that of for...else. The information returned after unsuccessful query;
            print("The student information you queried does not exist, Please re-enter!")

    # View the information of students whose grades fail in the class;
    def query_score(self):
        # Here, we use the Python built-in function filter() to directly filter out the information of unqualified students and display it (you can also traverse the filter);
        result = filter(lambda x: x.stu_score < 60, self.stu_infos)  # The filter() function returns an iteratable object;
        print("[The information of students with failed grades is as follows]".center(48, "-"))
        for ele in result:
            print(ele)

    # Sort the students in the class in descending order according to their grades;
    def sort(self):
        # Here, you can use either list.sort() or sorted(). The difference is that the former is in place, that is, sort directly on the source list, and the latter returns a new list after sorting;
        # Of course, we can also use quick sort, merge sort, bubble sort, heap sort or other sorting algorithms for manual descending;
        self.stu_infos.sort(key=lambda x: x.stu_score, reverse=True)  # reverse=Fasle is in ascending order by default;
        self.query_all()  # Return the sorted student information to the user;
# Main program entry (the executability has been verified, and the results are not shown here due to the long time)
if __name__ == '__main__':
    # Instantiate a class object and multiple student objects;
    cls_1 = Class("Class one")
    xiao_san = Student(2, "the other woman", 25, "male", 85)
    xiao_wu = Student(56, "Little dance", 21, "female", 95)
    ao_si_ka = Student(18, "Oscar", 23, "male", 86)
    rong_rong = Student(24, "Rong Rong", 24, "male", 98)
    pang_zi = Student(19, "the fat", 22, "male", 58)
    zhu_qing = Student(25, "bamboo bark", 23, "female", 100)
    mu_bai = Student(10, "Mubai", 25, "male", 97)
    # Add each student object to the designated class;
    cls_1.add_stu(xiao_san)
    cls_1.add_stu(xiao_wu)
    cls_1.add_stu(ao_si_ka)
    cls_1.add_stu(rong_rong)
    cls_1.add_stu(pang_zi)
    cls_1.add_stu(zhu_qing)
    cls_1.add_stu(mu_bai)
    # Query the information of all students in the class;
    cls_1.query_all()
    # Query the information of students with specified student number;
    cls_1.query_stu(2)
    cls_1.query_stu(58)  # Because there are no errors;
    # Query the information of failed students;
    cls_1.query_score()
    # Sort the students' information in descending order according to their grades;
    cls_1.sort()

Tags: Python

Posted on Fri, 03 Sep 2021 17:46:13 -0400 by FamousMortimer