Python object-oriented basics

Class definition#

Syntax of class definition

classClassName:<statement-1>...<statement-N>

Sample code

classDoor:
 def __init__(self, number, status):
  self.number = number
  self.status = status

Class instantiation#

classDoor:
 def __init__(self, number, status):
  self.number = number
  self.status = status

door =Door(1001,'open')
door.number
door.status

Function creation and initialization process

  1. Create the object first
  2. The object is passed as the self parameter to the __init__ function
  3. Return self

Scope#

Class variable##

Sample code

In [1]:classA:...:         NAME ='A'  #The immediate subordinate scope of the class is called class variable
 ...:   def __init__(self, name):...:             self.name = name  #Variables associated with instances 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 a value to the class variable NAME of example a2

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

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

In [12]: A.NAME  #No change in class variables
Out[12]:'A'

In [13]: a2.xxx =3

In [14]: a2.xxx  #After assignment, a2 has more xxx attributes
Out[14]:3

In [15]: A.NAME ='AA'  #Modify the class variable of the class directly

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

In [17]: a.NAME  #The class variables of the corresponding instance have also changed
Out[17]:'AA'

In [18]: a2.NAME  #The class variable of a2 was overwritten in the previous assignment, so changing the class variable will not affect a2
Out[18]:'A2'

and so

Attribute search order##

Code

In [1]:classA:...:     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__Indicates 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##

The parameter is a class, and a function that returns a class can be a class decorator.

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

Code 1: Add attributes to the class

Function method increase: define set_name function to add a NAME attribute to class F

In [1]:classF:...:     pass
   ...: 

In [2]: def set_name(cls, name):  #Add attribute NAME to cls=name
 ...:  cls.NAME = name
 ...: return cls
   ...: 

In [3]: F1 =set_name(F,'F')  #Return 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 the increase is still class F
Out[10]:mappingproxy({'NAME':'F','__dict__':<attribute '__dict__'of'F' objects>,'__doc__': None,'__module__':'__main__','__weakref__':<attribute '__weakref__'of'F' objects>})

Curry the set_name function to implement a class decorator with parameters

In [2]: def set_name(name):   #Incoming parameter name
 ...:  def wrap(cls):   #The decorator is wrap
 ...:   cls.NAME = name
 ...: return cls
 ...: return wrap
   ...: 

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

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

In [5]:classG:...:     pass
   ...: 

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

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

Code 2: Add methods to the class

The class decorator get_name adds a method __get_name__ to class H

In [1]: def get_name(cls):...:     def _get_name(self):...:return cls.__name__
 ...:  cls.__get_name__ = _get_name  #Increase cls__get_name__direction_get_name
 ...: return cls
   ...: 

In [2]: @get_name
 ...: classH:...:     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##

The definitions of methods are all at the class level, but some methods are called using instances, and some are called using classes

Code

classI:
 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')

Access control#

Double underscore##

In [1]:classDoor:...:     def __init__(self, number, status):...:         self.number = number
 ...:   self.__status = status  #Start with a double underscore, and those ending with a double underscore 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):  # #Double downslide starts first, the method that does not end with double underscore is also private
 ...:   self.number = number
   ...:         

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

In [3]: door.__status  #Cannot access private properties
---------------------------------------------------------------------------
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'  #Created a new attribute for the object, and did not modify it 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'  # _Class name+Modifies private members directly by means of double-underlined attributes

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

Single underscore##

In [1]:classA:...:     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##

Introduce the property decorator

classDoor:
 def __init__(self, number):
  self.__number = number

 def get_number(self):return self.__number

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

When the number attribute is turned into a private attribute __number, it cannot be accessed directly. The __number attribute can only be accessed through the two functions get_number and set_number.

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

Use of property decorator

classDoor:
 def __init__(self, number):
  self.__number = number

 # The property decorator turns a function with only self parameter into a property, and 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. 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  #Returns 1001
door.number =1002
door.number  #Returns 1002
del door.number  #Output cannot remove number property

inherit#

Single inheritance##

classBase:
 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'classSub(Base):
 pass

sub =Sub()
sub.__dict__
# Output
{'_ Base__private_instance_var':'private__instance_var','public_instance_var':'public_instance_var'}

Method rewriting##

classBase:
 def __init__(self):
  self.__a =4

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

 @ classmethod
 def cls_print(cls):print('Base.cls_print')classSub(Base):
 def print(self):  ##When the subclass and the 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):
  # Call the print of the parent class
  super().print()
  # super(Sub, self).print()

 @ classmethod
 def cls_foo(cls):
  # cls.cls_print()
  # Base.cls_print()super().cls_print()classSubSub(Sub):
 def print(self):print('SubSub.print')

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

 def foo(self):
  # Call Base&#39;s print
  super(SubSub, self).print()
  # Proxy the method of the parent class of TYPE, and use obj to bind the first parameter to specify the direct parent of whom 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()  #When the class method, you can pass the class, you can also pass the instance self

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

Multiple inheritance and MRO

Equivalent class definition###

classA:
 pass

classA(object):
 pass

classA():
 passs

Multiple inheritance###

classA:
 def method(self):print('method of A')classB:
 def method(self):print('method of B')classC(A, B):
 pass

c =C()
c.method()  #Output method of A

MRO

Define a multiple inheritance, as follows

classA:
 def method(self):print('method of A')classB:
 def method(self):print('method of B')classC(A, B):
 pass

classE(A):
 def method(self):print('method of E')classF(E, A):
 pass

F().method()  #Output method of E

If the definition class G inherits from (A, E), as follows

classG(A, E):  #It will report an error directly when defining
 pass

---------------------------------------------------------------------------
TypeError                                 Traceback(most recent call last)<ipython-input-51-dcac33a3d00c>in<module>()---->1classG(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 Resolution Order (MRO) does not satisfy the error report

Analyze the 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, the mro sequence is the order of inheritance

Then the mro sequence of the G class should be (G, A, E, object), and Python uses the C3 algorithm to determine whether the two principles of mro are met when multiple inheritance:

  1. Local priority: Self-defined or rewritten methods are preferred, and search from left to right according to the inheritance list
  2. Monotonicity: All sub-categories must also satisfy the search order

The main function of the C3 algorithm is to determine which class the attribute comes from in multiple inheritance, and throw TypeError when it cannot be determined.

C3 algorithm

classB(O): Then the mro sequence of B is:[B, O]classB(A1, A2,..., An): Then the mro sequence of B is:[B]+merge(mro(A1),mro(A2),...,mro(An),[A1, A2,..., An, O])

The merge operation is the core of the C3 algorithm. The merge steps are as follows:

* Traverse the list
* Look at the first element of the first list
 * It is also the first element in other lists
 * Or it does not exist in other lists
* If the above conditions are met, the first element is removed and merged into mro
* If not satisfied, throw an exception

C3 algorithm analyzes the mro of class F

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]

The merge operation is successful, mro is parsed correctly, and finally mro is [F, E, A, O]

C3 algorithm analyzes the mro of class G

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 met, an exception is thrown directly.

in conclusion:

  1. Should try to avoid multiple inheritance
  2. Multiple inheritance will put a lot of pressure on the mental burden of the program

Mixin class##

reference

  1. Liao Xuefeng-Multiple Inheritance and MixIn
  2. What is the concept of Zhihu-Mixin?
  3. Python Cookbook-Using Mixins to extend class functions

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

Therefore, the purpose of the 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 a multi-level complex inheritance relationship.

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

classForkingUDPServer(ForkingMixIn, UDPServer): pass
classForkingTCPServer(ForkingMixIn, TCPServer): pass

classThreadingUDPServer(ThreadingMixIn, UDPServer): pass
classThreadingTCPServer(ThreadingMixIn, TCPServer): pass

Python comes with two types of network services: TCPServer and UDPServer. To serve multiple users at the same time, you must use a multi-process or multi-thread model. These two models are provided by ForkingMixIn and ThreadingMixIn. Through the combination, the above four categories can be obtained.

The relationship between these categories is as follows:

It can be seen that in the process of layer-by-layer inheritance from BaseServer, the ForkingMixIn class and the ThreadingMixIn class are mixed (MixIn).

This technique of multiple inheritance is called MixIn.

If the MixIn technology is not used, but a single inheritance implementation with complex levels is used, the number of classes will increase exponentially.

For specific inheritance hierarchical relationships designed without MixIn technology, please refer to the design ideas of the Animal class in Liao Xuefeng-Multiple Inheritance and MixIn.

MixIn summary

MixIn is actually a way of combination. Generally speaking, composition is better than inheritance

Limitations of the Mixin class

Normally, the Mixin class is always the first in the inheritance list

Recommended Posts

Python object-oriented basics
Python basics
Python basics 2
Python basics
Python basics 3
Python basics 4
Python basics 5
Python object-oriented example
Python object-oriented magic method
Python custom function basics
Basics of Python syntax
Python multi-process and multi-thread basics
Analysis of Python object-oriented programming
What is object-oriented in python
How to understand python object-oriented programming
Python multithreading
Python CookBook
Python FAQ
Python3 dictionary
Python3 module
Python descriptor
Python exec
Python3 tuple
Two days of learning the basics of Python
Python decorator
Python IO
Python multithreading
Python toolchain
Python3 list
Python multitasking-coroutine
Python overview
python introduction
Python analytic
07. Python3 functions
Python multitasking-threads
Python functions
python operator
Python entry-3
Centos 7.5 python3.6
Python string
python queue Queue