Small turtle lesson 45 magic method simple customization

Posted by bwochinski on Tue, 18 Jan 2022 04:52:35 +0100

A little thought on the simple customization of magic method in lesson 45 of little turtle "zero foundation introduction to learning python" 1

The basic code is explained in the video of little turtle, so I won't waste time
The following is the link to station B. This is the old video uploaded by the little turtle boss himself. Now fishc forum should have the latest version of what you like to support
https://www.bilibili.com/video/BV1Fs411A7HZ?p=45

1. Homework after class

1.1 job 0

According to the procedure in the class, if the start time is (16:30:30, February 22, 2022) and the stop time is (15:30:30, January 23, 2025), a negative number will appear according to the calculation method of subtracting the start time from the stop time. You should make some conversion

1.1.1 topic understanding

The most important thing is to realize
start(),
shop(),
Time calculation
Output final time correctly

1.1.2 minor modification

I'm also a beginner, so I can't talk about any modules and methods. The following is just my idea analysis. Let's have a look.
When I was doing this small program, I was confused at the beginning. In the past, I knew a little about C PHP and java, so I used the idea of java to make a class, new, and then make a function call. But this is python. It's so troublesome to learn a hammer. So I watched the video and sorted out my ideas. I may feel that it was cheating, but after this program was completed, I did have a general understanding of Python programming ideas and will not be limited by the previous ideas (I won't say that this is the consequence of not doing my homework well before)

All right, let's get back to the book

After seeing the code of little turtle and understanding it, my reaction is what else can be improved
Just looking for bug s
#Problem 1: two consecutive starts () are equivalent to re timing (solved)
#Problem 2: the current digit is not enough to decrease, and a negative number may occur (solved)
#Question 3: how to be more accurate (there will be in the 1.2 code)
ps: personally, people have the greatest curiosity about new things they have just come into contact with. They are like children who have just begun to understand the world. At this time, people's thinking is the most divergent, jumping, and not easily restricted by rules. I usually go back to think about some divergent problems at this time. The problem is not terrible. Just find a way to solve it.

1.1.3 code 1

If you are here to compare the code, you can look at the code first and then the solution ideas. You can also turn to 1.1.4 to look at the ideas first

import time as myti
import math as mymath

class MyTimer():                    #Question 3 how to be more precise
    def __init__(self):
        self.unit = ["year", "month", "day", "Time", "branch", "second"]
        self.prompt = "Please start the timer first"
        self.lasted = []
        self.begin = 0
        self.end = 0
    
    def __str__(self):
        return self.prompt
        
    __repr__ = __str__
    
    #Two timer times and
    def __add__(self , other):
        prompt = "Total runs"
        result = []
        for index in range(6):
            result.append(self.lasted[index] + other.lasted[index])
            if result[index] :
                prompt += (str(result[index]) + self.unit[index])
                
        return prompt
        
    #Start timing
    def start(self):                    #Problem 1 two consecutive starts () are equivalent to retiming (solved)
        if self.begin:
            print("Timing has started, please call first stop()Stop current timer")
        else:
            self.begin = myti.localtime() 
            self.prompt = "Please call first stop()Stop current timer"
            print("Timing starts")
    
    #Stop timing
    def stop(self):
        if not self.begin:
            print( "Please call first start()Start a timer")
        else:
            self.end = myti.localtime()
            self._calc()  
            print("Timing end")

    
    
     ## #Internal method to calculate running time
    def _calc (self):
        self.lasted = []
        self.prompt = "Total runs"
        tag = 0
        temp = []
        
        for index in range(6):  #calculation
            rindex = 5 - index

            #Carry or not
            if tag:
                self.lasted.append(self.end[rindex] - self.begin[rindex] - 1)
            else  :
                self.lasted.append(self.end[rindex] - self.begin[rindex])
            tag = 0
            
            #Take positive number
            if self.lasted[index] <0 : 
                if index <= 1:
                    self.lasted[index] = 60 - self.begin[rindex] + self.end[rindex] - 1
                elif index <=2:
                    self.lasted[index] = 24 - self.begin[rindex] + self.end[rindex] - 1
                elif index <=3:
                    self.lasted[index] = 31 - self.begin[rindex] + self.end[rindex] - 1
                else:
                    self.lasted[index] = 12 - self.begin[rindex] + self.end[rindex] - 1

                tag = 1
        
        for index in range(6):  #Positive sequence output
            rindex = 5 - index
            if self.lasted[rindex] :                     #Problem 2: the current bit is not enough to decrease, and negative numbers may occur (solved)
                self.prompt += (str(self.lasted[rindex]) + self.unit[index])
        
        #Initialization for the next round of timing
        self.begin = 0
        self.end = 0
 
''' 
#No sleep() needs to be entered manually in the test
t1 = MyTimer()
t1.start()
t1.stop()
t1
t2 = MyTimer()
t2.start()
t2.stop()
#print(t1 + t2)
'''

1.1.4 thinking and expansion

For the problem, execute start() one or two times in succession,

The second time will cover the first time. This is a small bug (not really). I think what if it is really graphical in the future and users click by mistake?

A: it's very simple. Just confirm it once
The method is the same as that of stop(), so I won't repeat it

Problem 2: the current digit is not enough to decrease, and a negative number may occur

Small turtle reference answer:
(subtraction thinking) it is a way of circular borrowing, which feels a little cumbersome,
The code given by the reference answer is that each time the current bit is calculated, the while() must be executed to implement "borrow 1". This is a bit expensive in time, and a lot of consideration should be given to writing code
Here is the reference answer_ cacl()
ps: I do the after-school questions first and then look at the answers, so my ideas are somewhat different. I suggest you do it yourself first

	#Reference answer of small turtle
	#https://www.jianshu.com/p/c78d54ad1fce
    # Internal method to calculate running time
    def _calc(self):
        self.lasted = []
        self.prompt = "Total runs"
        for index in range(6):
            temp = self.end[index] - self.begin[index]
            # If the low position is not enough, you need to borrow from the high position
            if temp < 0:
                # Test whether the high position can be borrowed. If not, borrow from the high position
                i = 1
                while self.lasted[index-i] < 1:
                    self.lasted[index-i] += self.borrow[index-i] - 1
                    self.lasted[index-i-1] -= 1
                    i += 1
                self.lasted.append(self.borrow[index] + temp)
                self.lasted[index-1] -= 1
            else:
                self.lasted.append(temp)

        # Since the high position will be borrowed at any time, the printing should be placed last
        for index in range(6):
            if self.lasted[index]:
                self.prompt += str(self.lasted[index]) + self.unit[index]

        # Initializing variables for the next round of calculations
        self.begin = 0
        self.end = 0
        print(self.prompt)

Solution 2 My thinking (additive thinking)
Each binary is different, which makes the time conversion look complex, but it has been devastated by binary and octal calculation for so long, we can easily think of: in fact, they are all the same
(addition thought) through my little circuit knowledge from the composition principle, isn't adder a good way.
An addend, an addend, a carry identifier
The adder does not need to know the carry of the current bit, which will eventually have much impact. As long as there is carry, I can modify the tag (like 99999 + 1)
.
So I made a "subtracter" in reverse. Whenever the current bit needs to "borrow 1", I modify the tag, no matter how the "bit" is calculated.
As for the reason that can be realized, it is actually very simple. For example, the addition (subtraction) of hexadecimal units is up to 9 + 9 = 18, that is, the carry is up to 1. Then make a flag bit tag, and add (subtract) one more 1 when calculating the next "bit". As for the base, it doesn't matter
.
It's just an understanding. Real computers only have adders (plus some additional circuits to realize + - * / + + or and)
Here is the code:

 ## #Internal method to calculate running time
    def _calc (self):
        self.lasted = []
        self.prompt = "Total runs"
        tag = 0
        temp = []
        
        for index in range(6):  #calculation
            rindex = 5 - index #Low to high calculation

            #Borrow or not
            if tag:
                self.lasted.append(self.end[rindex] - self.begin[rindex] - 1)
            else  :
                self.lasted.append(self.end[rindex] - self.begin[rindex])
            tag = 0
            
            #Take a positive number of seconds, minutes, days and months (there is no year because the year cannot be a negative number, so the if statement cannot be entered naturally)
            if self.lasted[index] <0 : 
                if index <= 1:
                    self.lasted[index] = 60 - self.begin[rindex] + self.end[rindex] - 1
                elif index <=2:
                    self.lasted[index] = 24 - self.begin[rindex] + self.end[rindex] - 1
                elif index <=3:
                    self.lasted[index] = 31 - self.begin[rindex] + self.end[rindex] - 1
                else:
                    self.lasted[index] = 12 - self.begin[rindex] + self.end[rindex] - 1

                tag = 1		#As long as you come in, if that's a loan
        
        for index in range(6):  #Positive sequence output
            rindex = 5 - index
            if self.lasted[rindex] :                     #Problem 2: the current bit is not enough to decrease, and negative numbers may occur (solved)
                self.prompt += (str(self.lasted[rindex]) + self.unit[index])
        
        #Initialization for the next round of timing
        self.begin = 0
        self.end = 0

A little discussion on solution 2:
”Time complexity and space complexity:
My method is to judge two if statements. In the second case, there are many cases, so the judgment should be more complex. However, if is still simpler than while. When cpu executes, the spatial complexity of while is better, and the time complexity of if is generally better (now the time complexity is generally considered in the judgment of code, and the memory and hard disk constraints should also be considered in special cases).
This is based on a large amount of data, but modern programming software will have its own compilation method, and there are not many while loop layers, and the program is not complex. When it really runs, the two methods are similar
.
”Algorithm problem“
After I finished writing this program, I suddenly found that I had done similar topics before and forgot whether it was HDOJ or Niuke (hey, no advertising fee -# -). Think carefully about the way of solution 2. It is really easy to understand. If you take part in the competition, are you willing to write a scheme that can be remembered and slightly excellent, or a scheme that takes a lot of brains. (this is just a discussion of the code. Personally, I still admire turtle dada. Some other videos on the Internet really feel like reading textbooks.)
.
”Education“
(it has nothing to do with the code. Students who don't want to see it skip it)
Some time ago, I read my nephew's first grade textbook. It's very interesting to share with you the new understanding of subtraction. The subtraction I learned in primary school is the column formula, which is not enough to "borrow 1" before subtraction. But they teach "division method". (it also gives me some inspiration when writing the code of solution 2)
For example: "72-37" they will split into 22-17 and 50-20, so the calculation becomes two numbers that can see the result at a glance, so there is no need to meditate on borrowing 1, and there are 6 left in the previous one (it's a shame for those who calculate fast to skip this paragraph.).
Is this method good? Within 100, well, I feel very good. You know, this is the topic of grade one. How old are those children? It takes time to play every day and teach advanced abacus mental arithmetic.
But what about three digits, four, five, six digits? 5678131-41545 still split? I didn't get his second grade textbook, and I don't know if there would be a better way. It feels like a simplified version of "youth class" teaching, which is decentralized. Method is a good method, but if you teach back to the "column vertical" method later, will it be a little superfluous?
.
"Medicine can't stop"
In fact, it can be seen that the code will make mistakes when calculating the number of days to months. As the little turtle said, if we don't correct it in time, we will go farther and farther on the wrong road
So there is no end to learning

1.2 operation 1

1.2.1 correct output of time 2

For Mao, a month must be 31 days? I don't know, maybe 30 days or 29 days? (our answer to the previous question is to assume 31 days a month). Yes, if we want to get the number of days of the month correctly, we also need to consider whether it is a leap year and the maximum number of days of the month, so it's too troublesome... If we don't get there in time, we will go further and further on the wrong road
.
Source of original code
Link: https://www.jianshu.com/p/c78d54ad1fce
Source: Jianshu
The copyright belongs to the author. For commercial reprint, please contact the author for authorization, and for non-commercial reprint, please indicate the source.
I made a little change

How to solve the problem of hexadecimal conversion? The method given here with reference to the answer is very violent. It is to directly change it to seconds and no longer calculate time and days.

However, I feel that "12464862.02 seconds in this round of timing" is not in line with my reading habits (too lazy to calculate by myself)
All slightly modified, added__ The StoD() method is changed to day, hour, minute

1.2.2 code 2

Or code first

import time as myti

class MyTimer():                    #Question 3 how to be more precise (solved)
    def __init__(self):
        self.unit = [ "day", "Time", "branch", "second","millisecond"]
        self.prompt = "Please start the timer first"
        self.lasted = []
        self.pre_lasted = 0         
        self.begin = 0
        self.end = 0
        self.default_timer = myti.perf_counter  #perf_counter() returns the precise time of the timer (the running time of the system)
                                                #process_time() returns the total execution time of the current process CPU
    
    def __str__(self):
        return self.prompt
        
    __repr__ = __str__
    
    #Two timer times and
    def __add__(self , other):
        self.prompt = "They work together" 
        result = self.lasted + other.lasted
        tuple2 = self._StoD(result)
        for index in range(5):  #Positive sequence output
            if tuple2[index] :                   
                self.prompt += (str(tuple2[index]) + self.unit[index])
        
        return self.prompt
       

        
    #Start timing
    def start(self):                    #Problem 1 two consecutive starts () are equivalent to retiming (solved)
        if self.begin:
            print("Timing has started, please call first stop()Stop current timer")
        else:
            self.begin = self.default_timer() 
            self.prompt = "Please call first stop()Stop current timer"
            print("Timing starts")
    
    #Stop timing
    def stop(self):
        if not self.begin:
            print( "Please call first start()Start a timer")
        else:
            self.end = self.default_timer()
            print("Timing end")
            self._calc()  
           

    
    
     ## #Internal method to calculate running time
    def _calc(self):
        self.prompt = "Total runs"
        self.lasted = self.end - self.begin
        tuple1 = self._StoD(self.lasted)            #Problem 4: it is inconvenient to read only seconds (solved)
        for index in range(5):  #Positive sequence output
            if tuple1[index] :                   
                self.prompt += (str(tuple1[index]) + self.unit[index])
        
        print(self.prompt)
  
        #Initialization for the next round of timing
        self.begin = 0
        self.end = 0
    
    # Change the internal method from seconds to days
    def _StoD(self,pre_lasted):
        
        str_prelasted = str(pre_lasted)            #Convert to string
        str_lasted = str_prelasted.partition(".") #segmentation
        str_lasted_inte = int(str_lasted[0])
        str_lasted_decimal = (str_lasted[2][0:2])

        
        m , s = divmod(str_lasted_inte , 60)    #divmod(div, mod)
        h , m = divmod(m , 60)
        d , h = divmod(h , 24)
        
        return (d,h,m,s,str_lasted_decimal)
        
        
        
    def set_timer(self, timer):
        if timer == "process_time":
            self.default_timer = myti.process_time
        elif timer == "perf_counter":
            self.default_timer = myti.perf_counter
        else :
            print("Invalid input")
            
            
            
            
#test
t1 = MyTimer()
t1.set_timer('perf_counter')
t1.start()
myti.sleep(1.1)
t1.stop()
t2 = MyTimer()
t2.set_timer('perf_counter')
t2.start()
myti.sleep(2.0)
t2.stop()
print(t1 + t2)

1.2.3 changing seconds to days (date output)

Question 4 change reading habits from seconds to days
Here, I just convert the floating-point number into a string, do a division, and output a time tuple, which is also a review of the previous knowledge

    # Change the internal method from seconds to days
    def _StoD(self,pre_lasted):
        
        str_prelasted = str(pre_lasted)            #Convert to string
        str_lasted = str_prelasted.partition(".") #segmentation
        str_lasted_inte = int(str_lasted[0])		#Integer part
        str_lasted_decimal = (str_lasted[2][0:2])	#First two decimal places

        
        m , s = divmod(str_lasted_inte , 60)    #divmod(div, mod)
        h , m = divmod(m , 60)
        d , h = divmod(h , 24)
        
        return (d,h,m,s,str_lasted_decimal)
Expansion: is this perfect?
No, in the actual operation, you can find that the number of digits after the decimal point may be incorrect
 for example t1 Run 1.01s 
     t2 Run 2.01s
     t1+t2 Run 3.01s
     Reason 1 self.lasted Is a floating-point number that is rounded when added
     	  2 python The module that carries out the operation after the decimal point has its own operation mode, which is prone to the problem that the carry is not in line with the reality( C++Floating point operations have similar problems)
    	(py There may be detailed explanations in the community. Students in need can go and have a look.)
    	
	How?
		Method 1: self.lasted + other.lasted Change them to two decimal places before adding them
		Method 2: take out the two digits after the decimal point, change it into integer, and then splice it to the decimal point after operation
					Pseudo code
					int_decimal = int(str_lasted[2][0:2]) + the other one
					str_decimal = str(int_decimal)
					str_lasted = str_lasted_inte + '.' +  str_decimal 
					#There are other parts that need to be changed, which will not be repeated here



reference material

  1. https://www.bilibili.com/video/BV1Fs411A7HZ?p=45 ↩︎

  2. https://www.jianshu.com/p/c78d54ad1fce ↩︎

Topics: Python