· Python · use a generator and iterator to implement your own xrange

Source: Internet
Author: User
· Python · use a generator and iterator to implement your own xrange Disclaimer: This article is published by love butterfly at http://blog.csdn.net/lanphaday. all rights are available. Welcome to download this article. The statement should be reprinted. Thank you. Those who have used Python must be familiar with the following two lines: Code : >>> For I in xrange (0, 10, 1 ): Print I The above two lines of code print the 10 numbers 0-9 in a loop. Do you also want to implement functions (classes) such as xrange that can be used in for statements? Come with me! First, let's introduce the yield Statement of Python. The yield statement is generated and generated. Its syntax is: Yield Expression about yield statements. The official manual said this: the yield statement is only used to define generator functions and can only appear in generator functions. The full reason for using yield statements in function definition is to implement a generator function instead of a common function. When a generator function is called, it returns an iterator that is regarded as a generator, more commonly called a generator. The function body of the generator function will be repeatedly called by the next method of the generator until an exception occurs. When the yield statement is executed, the status of the generator is frozen and the value of the expression is returned to next () called "freeze", we can understand that the function is saved here and switched out (if you know the process management of the operating system, it should be easy to understand this sentence ). Well, it's too obscure. Check an instance. >>> Def simple_xrange (Num ): While (Num ): Yield num Num-= 1 >>> L = List (simple_xrange (8) >>> print L [8, 7, 6, 5, 4, 3, 2, 1] In the above example, we implemented a simple xrange to generate an inverted number series. However, we still cannot see how simple_xrange is executed. Now let's take a look at the experiment below: >>> it = simple_xrange (8) >>> it. next () 8 >>> it. next () 7 >>> it. next () 6…… >>> It. Next () 1 >>> it. Next () traceback (most recent call last): file "<pyshell #48>", line 1, in-toplevel- It. Next () Stopiteration now we can see from the above experiment the execution process of simple_xrange: 1, When it = simple_xrange (8) is executed, simple_xrange returns a generator, that is, it becomes a generator. 2, When it. Next () is executed, the simple_xrange function body is executed. When the yield num statement is executed, simple_xrange is "Frozen" and then num is returned, namely 83, Execute it again. next (), simple_xrange "unfreeze", execute num-= 1, because it is a loop, so execute while (Num) Again, then execute yield num, simple_xrange is "Frozen ", returns num, that is, 74, Call it again and again until the while (Num) of simple_xrange is invalid and jumps out of the loop. When the return result is returned, the next () function throws a stopiteration exception, and the execution of the generator function is complete. Comparing the above 1234 entries with the above statement of Python manual, they echo each other, so that we can understand the implementation mechanism of xrange, so that we can use yield statements to write our own xrange. After yield is understood, it is much easier to understand another method to implement xrange. This method is to define its own iterator. For the iterator, the python manual statement is as follows: Python supports an iterator concept that surpasses the container, so that user-defined classes support iteration. The iterator object must support the _ ITER _ () and next () methods, where _ ITER _ () returns the iterator itself, next () returns the next element of the series. Well, let's go through the example: >>> class simple_xrange: Def _ init _ (self, num ): Self. _ num = num Def _ ITER _ (Self ): Return self Def next (Self ): If self. _ num <= 0: Raise stopiteration TMP = self. _ num Self. _ num-= 1 Return TMP >>> L = List (simple_xrange (8) >>> L [8, 7, 6, 5, 4, 3, 2, 1] Haha, read it. Source code It seems that this statement is more concise and understandable than the yield statement. Maybe this is why the yield statement must support the iterator type! With yield knowledge, it is very easy to understand the source code. After so long, is it necessary to implement your own xrange? Yes, of course. xrange only produces a series. If you want to extend the series, the code you write will be ugly. For example, in practical work, I used a card game. I used a list to represent the cards to be played (I used 0 ~ 53 indicates a deck, where 0 indicates the smallest card-block 3). For example, [0, 0, 3] indicates two pairs of cards numbered 0, 3, respectively, it is a bomb consisting of two blocks 3 and two spades 3 (this game uses two decks, so there can be two identical card IDs ). Later, the game rule was modified. The new game rule stipulated that the large Joker (card ID is 53) could be changed to any card. For example, [,] is also a bomb. At this time, I wrote the following code to determine whether a list is a correct card: # When there is a big joker in cards, I call this function to determine whether it is a valid card Type Def is_valid_pattern_with_big_joker (CARDS ): _ Cards = cards [:] # Because the cards needs to be changed, the cards copy is used in the function. Be_replace_card = 53 # big joker Will be replaced # Enumerate all possibilities For I in xrange (53,-1,-1 ): _ Cards. Remove (be_replace_card) _ Cards. append (I) Be_replace_card = I If is_valid_pattern (_ Cards )# If there is another big joker, is_valid_pattern will recursively call this function Return true Return false Let's see how complicated the for loop body is. Is there a way to save the remove, append, and intermediate variables? Yes! Use the iterator. Class replacedbigjokercards: Def _ init _ (self, cards ): Self. _ Cards = cards [:] Self. _ be_replaced_card = 53 Self. _ candidate = 52 Def _ ITER _ (Self ): Return self Def next (Self ): If self. _ candidate <0: Raise stopiteration Self. _ Cards. Remove (self. _ be_replace_card) self. _ Cards. append (self. _ candidate) self. _ be_replace_card = self. _ candidate Self. _ candidate-= 1 Return self. _ Cards Def is_valid_pattern_with_big_joker (CARDS ): For _ Cards in replacedbigjokercards (CARDS ): If is_valid_pattern (_ Cards) Return true Return false Now, we can hide the details of changing the major joker card. is_valid_pattern_with_big_joker has become more elegant. Another important point is that in the game, in addition to the card type, there are multiple functions such as smart prompts that can be reused in replacebigjokercards, using such a custom iterator, it is much better than remove/append that is scattered across the code.

 

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.