The __init__ () method is of significant significance for a total of two reasons. The first reason is that initializing in the object life cycle is the most important step, and each object must be properly initialized to function correctly. The second reason is that the __init__ () parameter value can have many forms.
Because there are many ways to provide parameter values for __init__ (), there are a number of use cases for object creation, and we can look at a few of them. We want to make it as clear as possible, so we need to define an initialization to correctly describe the problem area.
Before we touch the __init__ () method, in any case, we need to look at the hierarchy of object classes implicitly in Python in a cursory and simple way.
In this chapter, we look at the initialization of different forms of simple objects (for example, playing cards). After that, we can also look at more complex objects, like hands objects containing collections and players that contain policies and states.
Implied super-class--object
Each Python class implies a superclass: object. It is a very simple class definition and hardly anything to do. We can create an instance of object, but we can't do too much with it, because many special methods are prone to throw exceptions.
When we customize a class, object is a superclass. Here is an example of a class definition that simply inherits the object with the new name:
Class X: Pass
Here are some of the interactions with custom classes:
>>> x.__class__
>>>
x.__class__.__base__
We can see that the class is an object of the type class, and its base class is object.
As you can see in each method, we also look at the default behavior inherited from Object. In some cases, the behavior of a super-class special method is what we want. In other cases, we need to override this particular method.
Init () method for base class objects
The basis of an object's life cycle is creation, initialization, and destruction. We defer the creation and destruction of advanced special methods to the later chapters, which are currently only focused on initialization.
For all classes of superclass object, there is a default __init__ () implementation that contains pass, and we do not need to implement __init__ (). If you do not implement it, the instance variable is not created after the object is created. In some cases, this default behavior is acceptable.
We always add an attribute to the object, which is a subclass of the base class object. Consider the following classes, which require two instance variables but do not initialize them:
Class Rectangle: def area (self): return self.length * self.width
The rectangle class has a method that uses two properties to return a value. These properties are not initialized. This is the legal Python code. It can effectively avoid setting properties, although it feels a bit strange, but effective.
The following is the interaction of the rectangle class:
>>> r = Rectangle () >>> r.length, r.width = 8>>> R.area () 104
Obviously this is legal, but it is also a source of confusion, so it is the reason we need to avoid.
In any case, this design gives you a lot of flexibility so that sometimes we don't have to set all the properties in the __init__ () method. So far we have gone smoothly. An optional attribute is actually a subclass, but there is no real formal declaration as a subclass. We create polymorphism in a way that may cause confusion and the coiling caused by improper use of the IF statement. Although uninitialized properties may be useful, they can be a precursor to bad design.
The recommendations in the Zen of Python:
"Explicit is better than implicit." "
A __init__ () method should make an instance variable explicit.
Poor polymorphism.
Flexibility and stupidity are among the first thoughts.
When we feel the need to write like this, we are moving from the edge of agility to stupidity:
If ' x ' in self.__dict__:
Or:
Try: self.xexcept attributeerror:
It's time to reconsider the API and add a common method or property. Refactoring is more sensible than adding an if statement.
Implementing Init () in a superclass
We initialize the object by implementing the __init__ () method. When an object is created, Python first creates an empty object and then calls the __init__ () method for that new object. This method function is typically used to create an instance variable of an object and perform any other one-time processing.
The following is the hierarchical structure defined by the card class sample. We will define the card superclass and three subclasses, and these three subclasses are variants of the card. Two instance variables are set directly by the parameter values, and two variables are computed by the initialization method:
Class Card: def __init__ (self, Rank, suit): self.suit = suit Self.rank = Rank Self.hard, self.soft = self . _points () class Numbercard (Card): def _points (self): return int (self.rank), int (Self.rank) class Acecard ( Card): def _points (self): return 1, 11class Facecard (card): def _points (self): return 10, 10
In this example, we extract the __init__ () method to the superclass so that general initialization in the card superclass can be applied to three subclasses Numbercard, Acecard, and Facecard.
This is a common polymorphic design. Each subclass provides a unique implementation of the _points () method. All subclasses have the same signature: they have the same methods and properties. The objects of these three subclasses can be used interchangeably in an application.
If we use simple characters for suits, we can create a card instance as follows:
cards = [Acecard (' A ', '? '), Numbercard (' 2 ', '? '), Numbercard (' 3 ', '? '),]
We cite some of the cards ' class, card values and suits in the list. In the long run, we need smarter factory functions to create card instances, and it's boring and error-prone to enumerate 52 cards in this way. Before we touch the factory function, we look at some other issues.
To create an explicit constant using init ()
You can define color classes for your cards. In 21 points, the color does not matter, simple string can be.
We use the color constructor as an example of creating a constant object. In many cases, a small subset of the objects in our application can be defined by a constant collection. A small subset of static objects may be part of implementing a policy pattern or a state pattern.
In some cases, we will have a pool of constant objects created in the initialization or configuration file, or we can create constant objects based on command-line arguments. We will get the details of the initialization design and launch design in the 16th chapter, "Copying by command".
Python does not have a simple formal mechanism to define an immutable object, and we will look at the relevant techniques for ensuring immutability in the third chapter, property access, method properties, and descriptors. In this example, it makes sense that the suit is immutable.
The following class, which we will use to create four explicit constants:
Class Suit: def __init__ (self, Name, symbol): self.name= name self.symbol= symbol
The following constants are created from this class:
Club, Diamond, heart, Spade = Suit (' Club ', '? '), Suit (' Diamond ', '? '), Suit (' Heart ', '? '), Suit (' Spade ', '? ')
Now we can create cards with the code snippet shown below:
cards = [Acecard (' A ', Spade), Numbercard (' 2 ', Spade), Numbercard (' 3 ', Spade),]
This small example, this approach is not a significant improvement for the color code of a single feature. In more complex cases, some policy or state objects are created in this way. Reuse from a small, static constant object can make a policy or state design pattern more efficient.
We must admit that in Python these objects are not technically immutable and are mutable. Additional coding makes these objects truly immutable and may have some benefits.
Non-trivial invariance
Immutability is attractive but it is easy to bring trouble. Sometimes the mythical "malicious programmer" is adjusted in their application by modifying the constant value. It is very foolish to consider this in terms of design. These fabulous, malicious programmers do not stop doing this because there is no better way to encode in Python more succinctly and simply. A malicious programmer who accesses the source code and modifies it simply wants to write it as easily as possible to modify a constant.
It is best not to struggle for a long time when defining the classes of immutable objects. In chapter III, property Access, method properties, and descriptors, we will demonstrate how to achieve immutability by providing appropriate diagnostic information in a bug-based program.