This is the third day of my participation in Gwen Challenge

Wechat public number search [program yuan xiaozhuang], pay attention to the halfway program yuan how to rely on Python development to support the family ~

preface

Object-oriented programming has three characteristics: encapsulation, inheritance, polymorphism, this article with you to understand and understand these three characteristics ~

Supplement – New class & classic class

In Python2. x, the new class inherits a subclass of object and subclasses of that subclass… ; A classic class is a class that does not inherit from object, and its subclasses and subclasses… .

In Python3. x, if no classes are inherited, object is inherited by default. There is no distinction between classical and modern classes.

The object class provides implementations of some commonly used built-in methods, such as __str__, which returns a string when printing an object. This will be covered in a future article

encapsulation

Encapsulation is the most important feature of object-oriented programming. Encapsulation is the integration of data and functionality together.

For properties encapsulated in an object or class, we can strictly control access to them outside the class by hiding properties and exposing interfaces.

Hidden attribute

How to hide attributes

In class body code, if you prefix an attribute name with __, you achieve the effect of hiding the attribute from the outside of the class, even though you cannot pass the object directly outside the class. The method of accessing a property that starts with a double underscore (_) does not strictly restrict external access to a property defined inside a class by knowing the class name and the property name.

This kind of hiding is not internal. This kind of deformation occurs only once when checking the syntax of the class body. Attributes defined outside the class starting with __ are not deformed

class Test() :
    __name = 'test'  The # function becomes _Test__name when accessed externally
    
    def __init__(self) :
        self.__x = 'x'
        
    def test(self) :
        self.__x = 'test'  Properties can be used normally inside the class body code
        print('test')
        
print(Test.__name)  # AttributeError: type object 'Test' has no attribute '__name'
print(Test._Test__name)  # test
print(Test.__dict__) mappingproxy(... .'_Test__name': 'test'.'_Test__test': <function Test.__test at 0x0000026E75F6E3A0>, ...})

test_obj = Test()
test_obj.test()  # Normal execution
print(test_obj.__x)  # AttributeError: 'Test' object has no attribute '__x'
test_obj.__a = 'a'  The attributes of __ defined outside the class will not be deformed
print(test_obj.__a)  # a
Copy the code

The purpose of hiding the property

Custom properties is to use, so is not the final purpose, to hide data attribute limits make it kind of external direct manipulation of data, but class should provide the corresponding interface to allow such external indirect data operation, may add on the interface of logic to external action class defined in the data to carry on the strict control. Such as:

class People:
    
    def __init__(self,name) :
        self.__name = name
        
    def tell_info(self) :
        pwd = input('Please enter code >>').strip()
        if pwd == '0':
            print(self.__name)
        else:
            print('Wrong signal')
            
    def set_name(self,new_name) :
        if type(new_name) is str:
            self.__name = new_name
            print(self.__name)
        else:
            print('Please enter a string')

p = People('python')
p.tell_info()
p.set_name(123)
Copy the code

The purpose of hiding function attributes is to expose only the interface used by the user. In this interface, other methods defined in the class may be used, but these methods can be hidden without the user directly calling them, isolating the complexity of the program.

Hidden attributes and expose interfaces is the inner and outer part of the area classification, in order to define the internal can modify the class defined in the data but not affect the external calls, outside class only needs to have an interface, as long as the name of the interface, parameter is constant, so no matter what kind of internal code change, external invocation style will not change.

Inheritance and derivation

Inheritance is a way to create a new class. The new class can be called a subclass or a derived class. The inherited class is called a parent class or a base class.

A subclass inherits all the attributes of its parent class. Python supports multiple inheritance, where a subclass can inherit one or more parent classes.

The problem of code redundancy between classes can be solved by using class inheritance.

Class inheritance syntax is as follows:

class Parent1() :
    x = 111
class Parent2() :
    pass

class Foo(Parent1) : Parent1 is the parent class
    pass

class Foo1(Parent1,Parent2) : Parent1 Parent2
    pass
Copy the code

You can view all the parent classes that a subclass inherits from through __bases__.

print(Foo.__bases__)
# (<class '__main__.Parent1'>,)
print(Foo1.__bases__)
# (<class '__main__.Parent1'>, <class '__main__.Parent2'>)
Copy the code

If you want to find the relationship between classes, you need to summarize the similarities of multiple classes, and then you can get the parent class. The inheritance between classes refers to the relationship between what is what, for example, golden retriever is a dog, and dog is an animal.

Subclasses can inherit all the attributes of the parent class. For example, we still use the takeout system in the above article as an example. In this program, there are at least three classes: merchant class, takeout boy class and customer class.

# merchant class
class Merchants() :
    
    platform = 'Python Takeaway Platform'
    
    def __init__(self, name, addr, tel) :
        self.name = name  # Merchant name
        self.addr = addr  # business address
        self.tel = tel  # Merchant contact number
        
    def reveive_order(self) :
        print(f'{self.name}Received order ')
        
# Take-out guy
class Rider() :
    
    platform = 'Python Takeaway Platform'
    
    def __init__(self, name, addr, tel) :
        self.name = name  # Takeout guy's name
        self.addr = addr  # Delivery address
        self.tel = tel  # Takeout guy on the phone
        
    def distribution(self) :
        print(f'{self.name}In delivery ')
        
# the customer class
class Customer() :
    
    platform = 'Python Takeaway Platform'
    
    def __init__(self, name, addr, tel) :
        self.name = name  # Customer name
        self.addr = addr  # Shipping address
        self.tel = tel  # Customer phone
        
    def accept_delivery(self) :
        print(f'{self.name}Have received takeout ')
Copy the code

There is duplicate code between the above three classes, which all belong to the Python delivery platform, and all require unique name, ADDR, and tel. Therefore, the following inheritance relationship can be obtained to achieve code reuse:

class Personnel_information() :
    platform = 'Python Takeaway Platform'
    
    def __init__(self, name, addr, tel) :
        self.name = name  
        self.addr = addr 
        self.tel = tel 
        
# merchant class
class Merchants(Personnel_information) :
        
    def reveive_order(self) :
        print(f'{self.name}Received order ')
        
# Take-out guy
class Rider(Personnel_information) :
    
    def distribution(self) :
        print(f'{self.name}In delivery ')
    
# the customer class
class Customer(Personnel_information) :
    
    def accept_delivery(self) :
        print(f'{self.name}Have received takeout ')
Copy the code

The __init__ method is not defined in the merchant, takeout, and customer classes, but is found in their parent class, because subclasses inherit all the attributes and methods of the parent class, so they can still be instantiated normally:

mer = Merchants('sichuan'.'Picturesque Area'.'11111')
rider = Rider('small farmhouse'.'Mountain and water Area'.'2222')
customer = Customer('xu'.'Health Zone'.'33333')
Copy the code

Attributes to find

Single inheritance property lookup

The attributes of an object can be viewed through the object.__dict__. After the inheritance relationship, the object looks for attributes from its own __dict__ first. Note that the attribute lookup must be based on the definition phase.

class Test() :
    
    def t1(self) :
        print('from Test t1')
        
class Foo(Test) :
    
    def f1(self) :
        print('Foo f1')
        
f = Foo()
f.t1
Run result
from Test t1
Copy the code

If a parent class does not want subclasses to override its methods, it can make them private by starting with a double underscore.

class Test() :
    
    def __t1(self) :
        print('from Test t1')
    
    def t2(self) :
        self.__t1() 
        print('from Test t2')
        
class Foo(Test) :
    
    def __t1(self) :
        print('Foo t1')
        
f = Foo()
f.t2()
Run result
from Test t1
from Test t2
Copy the code

Multiple inheritance property lookup

Python object-oriented support multiple inheritance, benefit is a subclass can inherit multiple attributes of the parent class, at the same time maximize the reuse of code, but multiple inheritance also has many shortcomings, multiple inheritance has violated the inheritance relationship between expression of what is what, but if a class inherits multiple parent at the same time, the readability of the code is very poor, you will become diamond inherited problems may occur.

Diamond inheritance is when different subclasses have the same non-Object parent, and different subclasses are the parent of another subclass. If class A has A method that B or C overrides, which version of the method does class D inherit?

To understand the above problem, you need to know the rules by which class lookup properties are searched. In Python, for each class defined, a method is computed to resolve the order list (MRO), which is a simple sequential list of all base classes.

>>> D.mro() The new class has mrO methods built in to view the contents of linear lists. The classic class does not have mrO methods built in
[<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>]
Copy the code

Python looks for the base class from left to right on the MRO list until it finds the first class that matches the attribute.

If there is a rhombus inheritance problem, there is a non-rhombus inheritance, as follows:

Whether rhombus inheritance or non-rhombus inheritance, just remember:

If it is an object instantiated by a class, the attribute lookup will be retrieved from the object’s own attributes; if it is not, it will be retrieved from the object’s attributesClass. Mro ()Go down in the prescribed order; If a property lookup is initiated by a class, it is based on the currentClass. Mro ()I’m going to do it in the right order.

Mixins mechanism

In fact, multiple inheritance improves the coupling degree of the program, but the idea of object-oriented programming is to decouple, the purpose of decouple is to improve the readability and extensibility of the code, so should we use multiple inheritance?

Python provides multiple inheritance for use, but there are two things to avoid when using multiple inheritance:

1. Inheritance structure should not be too complex;

2. The mixins mechanism is recommended to satisfy the what-is-what relationship in the context of multiple inheritance.

Mixins mechanism is to improve the readability of multi-inheritance code as much as possible in the context of multi-inheritance. In simple terms, mixins mechanism refers to subclasses mixing functions of different classes, and these classes adopt a unified naming convention (such as the mixin suffix), which identifies these classes as just for mixing functions. Mixins are not used to identify subclass affiliations, so the Mixins mechanism is still multi-inheritance in nature, but also obeys what is what, as shown in the following code:

Define the parent class - the vehicle class
class Vechicle:
    print('I'm a vehicle')

# Define fly functionality -Mixin indicates that this class is only a function
class FlyableMixin() :
    def __init__(self,fly) :
        self.fly = fly


class CivilAircraft(FlyableMixin, Vechicle) :  # Civil aircraft
    pass

class Helicopter(FlyableMixin, Vechicle) :  # Helicopter
    pass

class Bus(Vechicle) :
    pass

# Civil aircraft object
c = CivilAircraft('I can fly')
print(c.__dict__)
# Helicopter object
h = Helicopter('fly')
print(h.__dict__)
# Car object
b = Bus()
print(b.__dict__)
Copy the code

Cars, boats, helicopters, civil aviation planes are traffic tools, but only have the function of the flying plane, if add the function of the fly to the traffic tools in the parent class is unreasonable, so can “fly” the function definition, in addition to the unified naming conventions (for example a Mixin suffix), the suffix would explicitly told to read the code, This class was added as a feature to the subclass, not as a parent class. When the bus class mixes flying functionality, bus is a flying vehicle.

But there are a few things to note when using mixins:

1. Must represent a function, not a class of goods

2. Python names mixin classes with mixin, able, ible suffixes

3. Write multiple Mixin classes if you have multiple capabilities

4. The more Minx classes you define, the less readable the subclass code will be

The derived

A subclass inherits all the attributes of the parent class after inheriting the parent class. A subclass can directly inherit the attributes of the parent class, derive its own attributes, or rewrite the attributes of the parent class. A subclass can reuse the attributes of the parent class in the following four ways.

class Personnel_information() :
    platform = 'Python Takeaway Platform'
    
    def __init__(self, name, addr, tel) :
        self.name = name  
        self.addr = addr 
        self.tel = tel 
        
# merchant class
class Merchants(Personnel_information) :
        
    def reveive_order(self) :
        print(f'{self.name}Received order ')
        
# Take-out guy
class Rider(Personnel_information) :
    
    def distribution(self) :
        print(f'{self.name}In delivery ')
    
# the customer class
class Customer(Personnel_information) :
    
    def accept_delivery(self) :
        print(f'{self.name}Have received takeout ')
        print(f'{self.name}Have received takeout ')
Copy the code

Inherit the parent class attributes directly

As in the code above, subclasses directly inherit all methods and attributes from their parent class.

Overrides the parent class attribute

In addition to the basic name, ADDR, tel attributes, the merchant class has the food information attribute, but the parent class __init__ method is not defined in this attribute, so you can override the __init__ method in the subclass to override the parent class __init__ method.

class Personnel_information() :
    platform = 'Python Takeaway Platform'
    
    def __init__(self, name, addr, tel) :
        self.name = name  
        self.addr = addr 
        self.tel = tel 
        
# merchant class
class Merchants(Personnel_information) :
    
    def __init__(self, name, addr, tel, food_info) :
        self.name = name  
        self.addr = addr 
        self.tel = tel 
    	self.food_info = food_info
        
    def reveive_order(self) :
        print(f'{self.name}Received order ')
Copy the code

To the parent classAsk formethods

Overriding the attributes of the parent class is fine, but there is duplicate code in the __init__ method of the child class, so the child class can ask the parent class for that method and add its own attributes to that method. This approach does not rely on inheritance and can request methods from any class.

class Personnel_information() :
    platform = 'Python Takeaway Platform'
    
    def __init__(self, name, addr, tel) :
        self.name = name  
        self.addr = addr 
        self.tel = tel 
        
# merchant class
class Merchants(Personnel_information) :
    
    def __init__(self, name, addr, tel, food_info) :
        Personnel_information.__init__(self, name, addr, tel)  Request method from class
    	self.food_info = food_info  # Customize your own unique attributes
        
    def reveive_order(self) :
        print(f'{self.name}Received order ')
Copy the code

Super invokes properties in the parent class

This approach relies heavily on inheritance. Calling super yields a special object that looks for properties in the parent class of the current class, referring to the MRO of the class that initiated the property lookup.

class Personnel_information() :
    platform = 'Python Takeaway Platform'
    
    def __init__(self, name, addr, tel) :
        self.name = name  
        self.addr = addr 
        self.tel = tel 
        
# merchant class
class Merchants(Personnel_information) :
    
    def __init__(self, name, addr, tel, food_info) :
		super().__init__(name, addr, tel)  The binding method is called, and self is passed in automatically
        # super(Merchants, self).__init__(name, addr, tel) # is equivalent to the previous line of code, but must be in python2, where python3 can be abbreviated
        self.food_info = food_info 
        
    def reveive_order(self) :
        print(f'{self.name}Received order ')
Copy the code

polymorphism

Polymorphism is when something has multiple forms, like animals have multiple forms, like cats, dogs, sheep.

# 🐱 🐕 belong to the same group of animals
class Animal:
    pass
class cat(Animal) :
    pass
class Dog(Animal) :
    pass
Copy the code

Polymorphism refers to the direct use of objects regardless of the specific type of object. Specifically, it refers to the unity of methods used by objects. For example, animals all have the function of making sounds.

class Animal:
    
    def talk(self) :
        print('The sounds that animals make... ')
        
class Cat(Animal) :
    
    def talk(self) :
        super().talk()
        print('meow meow')
        
class Dog(Animal) :
    
    def talk(self) :
        super().talk()
            print('wang wang')
            
# create object
cat = Cat()
dog = Dog()
# call method
cat.talk()
dog.talk()

Run resultThe sounds that animals make... Meow, meow, meow animal sounds... Wang wangCopy the code

The essence of polymorphism is that different classes define the same method name, and objects can be used in one way regardless of the class. Python provides the concept of abstract classes to impose certain method names on subclasses.

An abstract class

A few things to note when using abstract classes:

1. Specify the metaclass attribute in the parent class to set the class as an abstract class. Abstract classes themselves are only used to constrain subclasses and cannot be instantiated.

2. All subclasses that inherit from an abstract class must comply with the standards specified by the abstract class. If a subclass does not comply with the standards specified by the abstract class, TypeError will be raised and cannot be instantiated.

import abc  To use abstract classes, you must import the ABC package

# specify that the metaclass attribute sets the class to an abstract class. Abstract classes themselves are used only to constrain subclasses and cannot be instantiated
class Animal(metaclass=abc.ABCMeta) :
    
    @abc.abstractmethod The decorator subclass must define a method called talk
    def talk(self) : There is no need to implement concrete functionality in abstract methods
        pass

class Cat(Animal) : All subclasses that inherit from Animal must follow the Animal standard
    def talk(self) :
        pass

cat=Cat() If a subclass does not have a method named talk, TypeError will be raised and cannot be instantiated
Copy the code

The duck type

But Python is more of a duck type – if it looks like a duck, quacks like a duck, and walks like a duck, then it’s a duck.

Instead of using abstract classes and inheritance to dictate which class something belongs to, the duck type decouples the program to some extent. We just need to create objects that look and behave the same when defining the class. We can also use objects regardless of their type.

# Like what is what, with what function

class Cpu:
    def read(self) :
        print('cpu read')

    def write(self) :
        print('cpu write')

class Memory:
    def read(self) :
        print('mem read')

    def write(self) :
        print('mem write')
        
obj1=Cpu()
obj2=Mem()

obj1.read()
obj1.write()

obj2.read()
obj2.write()
Copy the code