Syntax of class definition
classClassName:<statement-1>...<statement-N>
Sample code
classDoor:
def __init__(self, number, status):
self.number = number
self.status = status
classDoor:
def __init__(self, number, status):
self.number = number
self.status = status
door =Door(1001,'open')
door.number
door.status
class name (__init__ function parameter list except the first parameter)
__init__
function is actually executed when the object is created__ init__
function does not create objectsFunction creation and initialization process
__init__
functionSample 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
__ dict__
: dictionary of instance variables__ class__
: get the class corresponding to the instance__dict__
and look for __class__
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>})
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>})
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')
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'
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}
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.
@property
decorator is responsible for turning a method into a property callUse 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
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'}
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'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()
classA:
pass
classA(object):
pass
classA():
passs
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
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:
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:
reference
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