Detailed parsing in Python

Source: Internet
Author: User
Tags class definition in python advantage

This article mainly introduces the advanced application of the __INIT__ () method in Python, including the more complex usage in mapping and elif sequences, and the need for friends can refer to the following

Using the factory function to __init__ ()

We can build a complete deck of poker with a factory function. This would be much better than enumerating all 52 cards, and in Python we have the following two common factory methods:

Defines a function that creates an object of the desired class.

Defines a class that has methods for creating objects. This is a complete factory design pattern, as described in the design pattern book. In languages such as Java, the factory class hierarchy is necessary because the language does not support independent functions.

In Python, classes are not required. It is only when the relevant factories are very complex that they show an advantage. The advantage of Python is that we never force the use of class hierarchies when a simple function can do better.

Although this is a book about object-oriented programming, the function is really a good thing. This is common in Python and is the most authentic.

If necessary, we can always rewrite a function as the appropriate callable object. We can refactor a callable object into our factory class hierarchy. We will learn the callable object in the fifth chapter, "Using callable objects and contexts."

Generally, the advantage of class definition is to implement code reuse through inheritance. The function of a factory class is to wrap the structure of some target class hierarchies and complex objects. If we have a factory class, when we extend the target class hierarchy, we can add subclasses to the factory class. This provides us with polymorphic factory classes; Different factory class definitions have the same method signature and can be used interchangeably.

Such horizontal polymorphism is useful for statically compiled languages such as Java or C + +. The compiler can resolve the details of the class and method generation code.

If you choose a factory definition that cannot reuse any code, the class hierarchy in Python will not help. We can simply use functions that have the same signature.

Here are the factory functions for our various card subclasses:

?

1 2 3 4 5 6 7 8 9 10 11 Def card (rank, suit): if rank = = 1:return Acecard (' A ', suit) elif 2 <= rank < 11:return numbercard (str (rank), suit Elif <= Rank < 14:name = {one: ' J ', ' Q ', ' K '}[rank] return Facecard (name, suit) else:raise exceptio N ("Rank Out of Range")

This function constructs the card class through the rank and suit objects of numeric types. We can now build the cards very simply. We have encapsulated the construction problem into a single factory function that allows the application to build without knowing exactly how the class hierarchy and the polymorphic design work.

Here's an example of how to build a deck of cards from this factory function:

?

1 deck = [Card (rank, suit) for rank in range (1,14) for suit in (Club, Diamond, Heart, Spade)]

It enumerates all the card values and colors to create a complete 52 card.

1. Faulty factory design and ambiguous else clause

Note The IF statement structure inside the card () function. We do not use the "all-inclusive" else clause to do any processing; we just throw the exception. Using the ELSE clause "all-encompassing" will lead to a small debate.

On the one hand, the condition that is subordinate to the ELSE clause cannot be self-evident, as it may conceal subtle design errors. On the other hand, some else clauses are indeed obvious.

It is important to avoid the ambiguous else clause.

Consider the variants of the following factory function definitions:

?

1 2 3 4 5 6 7 8 def card2 (rank, suit): if rank = = 1:return Acecard (' A ', suit) elif 2 <= rank < 11:return numbercard (str (rank), Sui T) Else:name = {one: ' J ', ' Q ', #: ' K '}[rank] return Facecard (name, suit)

Here's what happens when we try to create a whole deck of cards:

?

1 Deck2 = [Card2 (rank, suit) for rank in range (for suit in (Club, Diamond, Heart, Spade)]

Does it work? If the if condition is more complicated?

Some programmers can understand this if statement when they scan. Others will find it difficult to determine whether all the cases are being executed correctly.

For advanced Python programming, we should not leave it to the reader to deduce whether the condition applies to the ELSE clause. For the rookie conditions should be obvious, at least should be shown.

When to use an "all-inclusive" else

Try to use less. Use it only when the conditions are obvious. When in doubt, explicitly and throws an exception.

Avoid ambiguous else clauses.

2. Simple and consistent use of elif sequences

Our factory function card () is a mixture of two common factory design patterns:

If-elif sequence

Mapping

For simplicity's sake, it's best to focus on one of these technologies rather than two.

We can always use maps to replace elif conditions. (yes, always.) But the opposite is not true; changing elif conditions for mapping will be challenging. )

Here is a card factory with no mapping:

?

1 2 3 4 5 6 7 8 9 10 11 12-13 def card3 (rank, suit): if rank = = 1:return Acecard (' A ', suit) elif 2 <= rank < 11:return numbercard (str (rank), Sui T) elif rank = = 11:return Facecard (' J ', suit) elif rank = = 12:return Facecard (' Q ', suit) elif rank = = 13:return Facecar D (' K ', suit) else:raise Exception ("Rank Out of Range")

We rewrote the card () factory function. The mapping has been converted to an additional ELIF clause. One advantage of this function is that it is more consistent than previous versions.

3. Simple use of mappings and class objects

In some examples, we can use mappings to replace a series of elif conditions. It is possible to find that the conditions are too complex, and it may be wise to use a series of elif conditions to express it. For simple examples, mappings can be done better and more readable in any case.

Because class is the best object, we can easily map the rank parameter to a class that has already been constructed.

The following is a card factory that uses only the mappings:

?

1 2 3 def card4 (rank, suit): class_= {1:acecard, 11:facecard, 12:facecard, 13:facecard}.get (rank, Numbercard) return Class_ ( Rank, suit)

We have mapped the rank object into the class. We then pass the rank value and the suit value to the class to create the final card instance.

We'd better use the Defaultdict class. In any case, a trivial static mapping will not be simpler than this. Looks like the following code fragment:

Defaultdict (Lambda:numbercard, {1:acecard, 11:facecard, 12:facecard, 12:facecard})

Note: The Defaultdict class must be a function of 0 parameters by default. We have used lambda to create the necessary functions to encapsulate constants. This function, however, has some drawbacks. For the previous version we were missing 1 to a and 13 to K conversions. When we try to add these features, there is always a problem.

We need to modify the mappings to provide a card subclass that can be the same as the rank object of the string version. What else can we do with the mapping of these two parts? There are four common solutions:

You can do two parallel mappings. We do not recommend this, but we will emphasize the importance of showing undesirable places.

You can map a two-tuple. This will also have some drawbacks.

can be mapped to the partial () function. The partial () function is an attribute of the Functools module.

Consider modifying our class definitions, which are easier to map. You can see the __init__ () in the subclass definition in the next section.

Let's take a look at every concrete example.

3.1. Two parallel mappings

The following are key codes for two parallel mapping solutions:

?

1 2 3 class_= {1:acecard, 11:facecard, 12:facecard, 13:facecard}.get (rank, Numbercard) rank_str= {1: ' A ', one: ' J ', ' Q ', 13: ' K '}.get (rank, str (rank)) return Class_ (RANK_STR, suit)

It's not advisable. It involves repeating mapping keys 1, 11, 12, and 13 sequences. Repetition is bad because the parallel structure remains this way after the software is updated.

Do not use parallel structures

A parallel structure must be substituted with a tuple or some other appropriate collection.

3.2. Values mapped to tuples

The following are key codes for the two-tuple mapping:

?

1 2 3 4 5 6 7 Class_, rank_str= {1: (Acecard, ' A '), one: (Facecard, ' J '), (Facecard, ' Q '), (Facecard, ' K '),}.get (rank, (Numbercard, STR (rank)) return Class_ (RANK_STR, suit)

It's pretty good. There is no need for too much code to classify special cases in cards. When we need to change the card class hierarchy to add extra card subclasses, we will see how it is modified or expanded.

It is really strange to map rank values to a class object, and only one of the two parameters required for class initialization. It seems more reasonable to map a card value to a simple class or a function object that does not provide some confusing parameters (but not all).

3.3. Partial function solution

We can create a partial () function rather than one of the two-tuple functions and parameters. This is a function that has provided some (but not all) parameters. We'll use the partial () function from the Functools library to create a partial class with the rank argument.

The following is a mapping rank to partial () function that can be used for object creation:

?

1 2 3 4 5 6 7 8 From Functools Import partial part_class= {1:partial (Acecard, ' A '), 11:partial (Facecard, ' J '), 12:partial (Facecard, ' Q '), 13:partial (Facecard, ' K '),}.get (rank, partial (Numbercard, str (rank)) return Part_class (suit)

The map links the rank object to the partial () function and assigns it to Part_class. This partial () function can be applied to the suit object to create the final object. The partial () function is a common functional programming technique. It is used in a particular case where we have a function to override the object method.

Overall, however, the partial () function does not help most object-oriented programming. Rather than creating the partial () function, we can simply update the methods of the class to accept the parameters of the different combinations. The partial () function is similar to creating a coherent interface for object construction.

3.4. Consistent factory class interface

In some cases, the classes we design define the order for the use of the method, and the order of the methods is much like the partial () function.

In an object notation we may have x.a (). B (). We can think of it as X (A, B). The X.A () function is a class of partial () functions that wait for B (). We can think of it as X (a) (b).

The idea here is that Python provides us with two choices to manage state. We can both update the object and create a stateful (to some extent) partial () function. Because of this equivalence, we can rewrite the partial () function into a coherent factory object. We make the rank object a coherent way to return self. Setting the suit object will actually create the card instance.

Here is a coherent card factory class that has two method functions that must be used in a particular order:

?

1 2 3 4 5 6 7 8 9 10 11 Class Cardfactory:def rank (self, rank): Self.class_, self.rank_str= {1: (Acecard, ' A '), one: (Facecard, ' J '), (Facecar D, ' Q '), (Facecard, ' K '),}.get (rank, (Numbercard, str (rank)) return to self def suit (self, suit): Return Self.class_ (self . Rank_str, suit)

The rank () method updates the state of the constructor, and the suit () method actually creates the final card object.

This factory class can be used as follows:

?

1 2 Card8 = Cardfactory () deck8 = [Card8.rank (r+1). Suit (s) for R in range (Club, Diamond, Heart, Spade)]

First, we create a factory instance, and then we use that instance to create the card instance. This does not materially alter how __INIT__ () itself works in the card class hierarchy. However, it does change the way our client applications create objects.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.