Python object oriented Foundation

Time:2021-2-27

Definition of class

Syntax of class definition

class ClassName:
    <statement-1>
    .
    .
    .
    <statement-N>

Sample code

class Door:
    def __init__(self, number, status):
        self.number = number
        self.status = status

<!–more–>

Class instantiation

class Door:
    def __init__(self, number, status):
        self.number = number
        self.status = status

door = Door(1001, 'open')
door.number
door.status
  • Creating objects usingClass name (__ init__ Function parameter list except the first parameter)
  • When the object is created, it is actually executed__init__function
  • __init__Functions do not create objects

Function creation and initialization process

  1. Create the object first
  2. Object is passed to as a self parameter__init__function
  3. Return to self

Scope

Class variable

Sample code

In [1]: class A:
   ...: name ='a 'ා the immediate subordinate scope of a class is called a class variable
   ...:         def __init__(self, name):
   ...:              self.name  =The variables associated with an instance are called instance variables
   ...:          

In [2]: a = A('a')

In [3]: a.NAME
Out[3]: 'A'

In [4]: a.name
Out[4]: 'a'

In [5]: A.NAME
Out[5]: 'A'

In [6]: A.name
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-6-61c1cc534250> in <module>()
----> 1 A.name

AttributeError: type object 'A' has no attribute 'name'

In [7]: a2 = A('a2')

In [8]: a2.NAME
Out[8]: 'A'

In [9]: A2. Name ='a2 '?: assign value to class variable name of example A2

In [10]: a2.NAME
Out[10]: 'A2'

In [11]: a.NAME
Out[11]: 'A'

In [12]: a.name # class variable does not change
Out[12]: 'A'

In [13]: a2.xxx = 3

In [14]: a2.xxx # after assignment, A2 has more XXX attribute
Out[14]: 3

In [15]: a.name ='aa '# directly modify class variables of a class

In [16]: A.NAME
Out[16]: 'AA'

In [17]: the class variable of the corresponding instance of a.name # has also changed
Out[17]: 'AA'

In [18]: the class variable of A2. Name # A2 is overridden in the previous assignment, so changing the class variable's assignment will not affect A2
Out[18]: 'A2'

therefore

  • Class variables are visible to both classes and instances
  • All instances share class variables
  • When assigning a value to an instance’s class variable, it is equivalent to dynamically adding an attribute to the instance, covering the class variable

Property lookup order

  • __dict__: Dictionary of instance variables
  • __class__: get the class corresponding to the instance
  • Find it first__dict__Find in__class__

code

In [1]: class A:
   ...:     NAME = 'A'
   ...:     def __init__(self, name):
   ...:         self.name = name
   ...:         

In [2]: a = A('a')

In [3]: a.NAME
Out[3]: 'A'

In [4]: a.__class__.NAME
Out[4]: 'A'

In [5]: a.__dict__
Out[5]: {'name': 'a'}

In [6]: a.__ class__   # a.__ class__ Represents the class corresponding to the instance
Out[6]: __main__.A

In [7]: a.NAME = 'AA'

In [8]: a.__ dict__   #After overriding class variables__ dict__ Added a key value pair
Out[8]: {'NAME': 'AA', 'name': 'a'}

In [9]: a.__ dict__ ['name '] ='aaa' # can be modified directly__ dict__

In [10]: a.__dict__
Out[10]: {'NAME': 'AAA', 'name': 'a'}

In [11]: a.__class__.NAME
Out[11]: 'A'

In [12]: a.__class__.__dict__
Out[12]: 
mappingproxy({'NAME': 'A',
              '__dict__': <attribute '__dict__' of 'A' objects>,
              '__doc__': None,
              '__init__': <function __main__.A.__init__>,
              '__module__': '__main__',
              '__weakref__': <attribute '__weakref__' of 'A' objects>})

Class decorator

Parameter is a class, and the function that returns a class can be a class decorator.

Class decorators are usually used to add attributes to a class. If you add methods, they are all class level methods.

Code 1: add attributes to class

Function method added: define set_ The name function adds a name attribute to class F

In [1]: class F:
   ...:     pass
   ...: 

In [2]: def set_ Name (CLS, name): # add attribute name = name to CLS
   ...:     cls.NAME = name
   ...:     return cls
   ...: 

In [3]: F1 = set_ Name (F, 'f') # returns f itself, and F1 points to F

In [4]: F1.NAME
Out[4]: 'F'

In [5]: f1 = F1()

In [6]: f1.NAME
Out[6]: 'F'

In [7]: F1.__dict__
Out[7]: 
mappingproxy({'NAME': 'F',
              '__dict__': <attribute '__dict__' of 'F' objects>,
              '__doc__': None,
              '__module__': '__main__',
              '__weakref__': <attribute '__weakref__' of 'F' objects>})

In [8]: f1.__dict__
Out[8]: {}

In [9]: f1.__class__
Out[9]: __main__.F

In [10]: F.__ dict__   #Essentially, class F is added
Out[10]: 
mappingproxy({'NAME': 'F',
              '__dict__': <attribute '__dict__' of 'F' objects>,
              '__doc__': None,
              '__module__': '__main__',
              '__weakref__': <attribute '__weakref__' of 'F' objects>})

Yes, set_ The name function is corized to realize the class decorator with parameters

In [2]: def set_ Name (name): # incoming parameter name
   ...: def wrap (CLS): # decorator is wrap
   ...:         cls.NAME = name
   ...:         return cls
   ...:     return wrap
   ...: 

In [3]: @set_name('G')
   ...: class G:
   ...:     pass
   ...: 

In [4]: G.NAME
Out[4]: 'G'

In [5]: class G:
   ...:     pass
   ...: 

In [6]: G = set_ Function call method of name ('g ') (g) # decorator

In [7]: G.NAME
Out[7]: 'G'

Code 2: add method to class

Class decoratorget_nameAdd a method to class H__get_name__

In [1]: def get_name(cls):
   ...:     def _get_name(self):
   ...:         return cls.__name__
   ...:     cls.__ get_ name__  = _ get_ Name # added to CLS__ get_ name__ Point to_ get_ name
   ...:     return cls
   ...: 

In [2]: @get_name
   ...: class H:
   ...:     pass
   ...: 

In [3]: h = H()

In [4]: h.__get_name__()
Out[4]: 'H'

In [5]: H.__dict__
Out[5]: 
mappingproxy({'__dict__': <attribute '__dict__' of 'H' objects>,
              '__doc__': None,
              '__get_name__': <function __main__.get_name.<locals>._get_name>,
              '__module__': '__main__',
              '__weakref__': <attribute '__weakref__' of 'H' objects>})

Class method / static method

Methods are defined at the class level, but some methods use instance calls, and some methods use classes to call

  • Class method: when a method is decorated with classmethod, the first parameter will become the class itself. Such a method is called class method
  • When a method is decorated with static method, it will not automatically pass the first parameter. Such a method is called static method

code

class I:
    Def print (self): # instance method
        print('instance method')

    @classmethod
    def class_ Print (CLS): # class method
        print(id(cls))
        print('class method')

    @staticmethod 
    def static_ Print(): # static method
        print('static method')

    def xxx_ Print (): a common method
        print('this is a function')
  • Instance methods can only be called by instances
  • Class method can be used by class and instance, and when used by instance, the first parameter passed in is class
  • Static methods can be used by classes and instances without passing in the first parameter
  • The common method in the class can only be used by the class, but not by the instance, because it does not pass in self
  • Various methods are determined according to the first parameter.

access control

Double underline

  • AllStart with double underline, end without double underlineAll members are private members
  • Strictly speaking, there are no really private members in Python
  • Python’s private members are created throughRenamingThe implementation is as follows:_ Class name + double underlined attribute
  • Do not modify private members through renaming rules unless it is really necessary and clear what the consequences will be
In [1]: class Door:
   ...:     def __init__(self, number, status):
   ...:         self.number = number
   ...:         self.__ Status = status # starting with double underscores and ending with non double underscores are private and cannot be accessed outside the class
   ...:     def open(self):
   ...:         self.__status = 'opening'
   ...:     def close(self):
   ...:         self.__status = 'closed'
   ...:     def status(self):
   ...:         return self.__status
   ...:     def __ set_ Number (self, number): ###########################
   ...:         self.number = number
   ...:         

In [2]: door = Door(1001, 'closed')

In [3]: door.__ Status # unable to access private property
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-3-d55234f04e7f> in <module>()
----> 1 door.__status

AttributeError: 'Door' object has no attribute '__status'

In [4]: door.__ dict__   #Properties contained in the door object_ Door__ status
Out[4]: {'_Door__status': 'closed', 'number': 1001}

In [5]: door.__ Status ='hahaha '# creates a new attribute for the object, but it is not modified to__ status

In [6]: door.__status
Out[6]: 'hahaha'

In [7]: door.__dict__
Out[7]: {'_Door__status': 'closed', '__status': 'hahaha', 'number': 1001}

In [8]: door.status()
Out[8]: 'closed'

In [9]: door.open()

In [10]: door.status()
Out[10]: 'opening'

In [11]: door.__set_number(1002)
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-11-888a73f63746> in <module>()
----> 1 door.__set_number(1002)

AttributeError: 'Door' object has no attribute '__set_number'

In [12]: door._Door__status
Out[12]: 'opening'

In [13]: door._ Door__ status = 'hehehe'  # _ Modify private members directly by class name + double underlined attributes

In [14]: door.status()
Out[14]: 'hehehe'

Single underline

  • Single underline is a common usage to mark this member as private, but the interpreter does nothing
In [1]: class A:
   ...:     def __init__(self):
   ...:         self._a = 3
   ...:         

In [2]: a = A()

In [3]: a._a
Out[3]: 3

In [4]: a._a = 4

In [5]: a._a
Out[5]: 4

In [6]: a.__dict__
Out[6]: {'_a': 4}

Property decorator

Introducing property decorator

class Door:
    def __init__(self, number):
        self.__number = number

    def get_number(self):
        return self.__number

    def set_number(self, number):
        self.__number = number

WhennumberProperty becomes private__numberAfter that, it can’t be accessed directly, only through theget_numberandset_numberAccess to two functions__numberProperty.

If you can not only restrict parameter access, but also access class variables in a simple way like property, you can use the property decorator at this time.

  • Python built in@propertyDecorators are responsible for turning a method into a property call

Using property decorator

class Door:
    def __init__(self, number):
        self.__number = number

    #The property decorator will change a function with only the self parameter into a property. The value of the property is the return value of the method
    @property
    def number(self):
        return self.__number

    #The property setter decorator can convert a method to assign a value to it, but this method has certain requirements
    #1. The same name 2. Must receive two parameters self and value, value is the assigned value
    @number.setter
    def number(self, number):
        self.__number = number

    @number.deleter
    def number(self):
        print('cannot remove number property')

door = Door(1001)
door.number   #Return to 1001
door.number = 1002
door.number   #Back to 1002
del  door.number   #Output cannot remove number property

inherit

Single inheritance

  • In parentheses after the class name, there is an inheritance list, which is called parent class or base class or superclass
  • An obvious advantage of inheritance is that you can get the properties and methods of the parent class
class Base:
    PUBLIC_CLASS_VAR = 'PUBLIC_CLASS_VAR'
    __PRIVATE_CLASS_VAR = 'PRIVATE_CLASS_VAR'

    def __init__(self):
        self.public_instance_var = 'public_instance_var'
        self.__private_instance_var = 'private__instance_var'

    @classmethod
    def public_class_method(cls):
        return 'public_class_method'

    @classmethod
    def __private_class_method(cls):
        return 'private_class_method'

    @staticmethod
    def public_static_method():
        return 'public static method'

    @staticmethod
    def __private_static_method():
        return 'private static method'

    def public_instance_method(self):
        return 'public_instance_method'

    def __private_instance_method(self):
        return 'private_instance_method'

class Sub(Base):
    pass

sub = Sub()
sub.__dict__
#Output
{'_Base__private_instance_var': 'private__instance_var',
 'public_instance_var': 'public_instance_var'}
  • All the public can inherit
  • Nothing private can be inherited
  • What is it, inherited or something

Method override

  • When a subclass and a parent class have members with the same name, the members of the subclass will override the members of the parent class with the same name
  • When the parent class contains an initialization method with parameters, the subclass must have an initialization method and call the initialization method of the parent class in the initialization method.
  • Super method: Super (type, obj) = > type: class name, obj: the first parameter passed to subsequent methods
class Base:
    def __init__(self):
        self.__a = 4

    def print(self):
        print('Base.print')

    @classmethod
    def cls_print(cls):
        print('Base.cls_print')

class Sub(Base):
    Def print (self): ### when a subclass and a parent class have members with the same name, the members of the subclass will override the members of the parent class with the same name
        print('Sub.print')

    @classmethod
    def cls_print(cls):
        print('Sub.cls_print')

    def foo(self):
        #Calling the parent class's print
        super().print()
        # super(Sub, self).print()

    @classmethod
    def cls_foo(cls):
        #cls.cls_print()
        #Base.cls_print()
        super().cls_print()

class SubSub(Sub):
    def print(self):
        print('SubSub.print')

    @classmethod
    def cls_print(cls):
        print('SubSub.cls_print')

    def foo(self):
        #Calling print of base
        super(SubSub, self).print()
        #Proxy the method of the parent class of type, and use obj to bind. The first parameter specifies whose direct parent class to call, and the second parameter specifies what to pass as the first parameter of the method when calling
        super(Sub, self).print()
        super(SubSub, SubSub).cls_ Print () # class method can pass class or instance self

    @classmethod
    def cls_foo(cls):
        # Base.cls_print()
        super(Sub, cls).cls_print()

Multi inheritance and MRO

Equivalent class definition

class A:
    pass

class A(object):
    pass

class A():
    passs

Multiple inheritance

  • When there are multiple classes in the inheritance list, it means multiple inheritance
  • Multiple inheritance will inherit all the public members in the inheritance list
class A:
    def method(self):
        print('method of A')

class B:
    def method(self):
        print('method of B')

class C(A, B):
    pass

c = C()
c. Method ()? Output method of a

MRO

Define a multiple inheritance as follows

class A:
    def method(self):
        print('method of A')

class B:
    def method(self):
        print('method of B')

class C(A, B):
    pass

class E(A):
    def method(self):
        print('method of E')

class F(E, A):
    pass

F (). Method () # output method of e

If the definition class G inherits from (a, e), as follows

Class G (a, e): # error will be reported directly when defining
    pass

---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-51-dcac33a3d00c> in <module>()
----> 1 class G(A, E):
      2     pass

TypeError: Cannot create a consistent method resolution
order (MRO) for bases E, A

Error display: cannot create a consistent method resolution order (MRO) for bases e, a

Method parsing order (MRO) does not meet the requirement of error reporting

Analysis of MRO of base class E, a

>>> A.__mro__
(__main__.A, object)
>>> E.__mro__
(__main__.E, __main__.A, object)
>>> F.__mro__
(__main__.F, __main__.E, __main__.A, object)

Therefore, MRO sequence is the sequence of inheritance

Then the MRO sequence of class G should be (g, a, e, object). Python uses the C3 algorithm to determine whether the MRO sequence of class G satisfies the requirement of multiple inheritanceTwo principles of MRO

  1. Local priority: the method defined or overridden by ourselves has priority, and it is searched from left to right according to the inheritance list
  2. Monotonicity: all subclasses should also satisfy the search order

The main function of C3 algorithm is to determine which class the attribute comes from in case of multiple inheritance, and to throw typeerror in case of no judgment

C3 algorithm

Class B (o): then the MRO sequence of B is: [b, O]
Class B (A1, A2,..., an): then the MRO sequence of B is: [b] + merge (MRO (A1), MRO (A2),..., MRO (an), [A1, A2,..., an, O])

Merge operation is the core of C3 algorithm

*Traverse list
*Look at the first element of the first list
    *It is also the first element in other lists
    *Or it doesn't exist in other lists
*If the above conditions are met, the first element is removed and merged into MRO
*If not, an exception will be thrown

Analysis of class F MRO with C3 algorithm

mro(F) -> [F] + merge(mro(E), mro(A), [E, A, O])
    -> [F] + merge([E, A, O], [A, O], [E, A, O])
    -> [F, E] + merge([A, O], [A, O], [A, O])
    -> [F, E, A] + merge([O], [O], [O])
    -> [F, E, A, O]

Merge operation is successful, MRO parsing is correct, and the final MRO is [F, e, a, O]

Analysis of class G MRO with C3 algorithm

mro(G) -> [G] + merge(mro(A), mro(E), [A, E, O])
    -> [G] + merge([A, O], [E, A, O], [A, E, O])
    -> raise TypeError:

The first element of the first list is a, which exists in the second list but is not the first element. If the condition of merge is not satisfied, an exception will be thrown directly.

conclusion

  1. More inheritance should be avoided
  2. Multiple inheritance will cause great pressure on the mental burden of the program

Mixin class

reference resources

  1. Liao Xuefeng multiple inheritance and mixin
  2. What is Zhihu mixin?
  3. Python Cookbook – extending class functions with mixins

In programming, mixin refers to a class that provides additional functions for the class inherited from it, but it is not used alone. In programming languages with multiple inheritance capabilities, mixin can add additional functions or methods to classes.

Therefore, the purpose of mixin pattern is to add multiple functions to a class. In this way, when designing a class, we give priority to combining multiple mixin functions through multiple inheritance rather than designing multi-level complex inheritance relationship.

In Python 3.5.2 source code socketserver.py You can see the definitions of the following four classes in lines 639 to 643 of

class ForkingUDPServer(ForkingMixIn, UDPServer): pass
class ForkingTCPServer(ForkingMixIn, TCPServer): pass

class ThreadingUDPServer(ThreadingMixIn, UDPServer): pass
class ThreadingTCPServer(ThreadingMixIn, TCPServer): pass
  • Baseserver: the base class of the server class
  • Udpserver: UDP server class, inherited from baseserver
  • Tcpserver: TCP server class, inherited from baseserver
  • ForkingMixIn:Mix-in class to handle each request in a new process.
  • ThreadingMixIn:Mix-in class to handle each request in a new thread.

Python comes with itTCPServerandUDPServerIn order to serve multiple users at the same time, we must use multi process or multi thread modelForkingMixInandThreadingMixInProvide. Through the combination, we can get the above four classes.

The relationship between these classes is as follows:

Python object oriented Foundation

As you can see, in the process of layer by layer inheritance from baseserver, the forkingmixin class and threadingmixin class are mixed.

This technique of multiple inheritance is called mixin.

If we don’t use mixin technology, but use hierarchical complex single inheritance implementation, the number of classes will grow exponentially.

Please refer to the following table for the inheritance hierarchy relationship designed without mixin Technology:Liao Xuefeng multiple inheritance and mixinThe design idea of the animal class in.

Mixin summary

Mixin is actually a way of combining. In general, composition is better than inheritance

The limitation of mixin class

  • Mixin classes should not have initialization methods
  • Mixin classes usually don’t work independently
  • The ancestor of a mixin class should also be a mixin class

In general, the mixin class is always at the top of the inheritance list


Remember to praise me!

We have carefully arranged video courses and e-books from entry-level, advanced and practical in all directions of computer. We can always find the learning materials you need according to the reasonable classification of the catalogue. What are you waiting for? Pay attention to download it!!!

Python object oriented Foundation

If you don’t forget, there will be an echo. Please give me a compliment. Thank you very much.

I am a bright brother in the workplace, YY Senior Software Engineer, with four years of working experience. I refuse to be a leading slash programmer.

Listen to me, more progress, a shuttle of procedural life

If you are lucky enough to help you, please give me a “like” to pay attention to it. If you can comment on it and give me encouragement, I will be very grateful.

List of articles of workplace bright brother:More articles

Python object oriented Foundation

All my articles and answers have cooperation with the copyright protection platform. The copyright belongs to brother Liang in the workplace. Without authorization, reprint must be investigated!