Effective Python Learning Memorandum Day 15 [15/100]

Introduction

The other day I learned about 100 Days Of Code, which was popular on Twitter for a while. The purpose of this article is to keep a record and output how much I, as a beginner, can grow through 100 days of study. I think there are many mistakes and difficult to read. I would appreciate it if you could point out!

Teaching materials to be learned this time

--Chapter 8 structure --Page 216 of this chapter

Today's progress

--Progress: Pages 91-95 --Chapter 4: Metaclasses and Attributes ――I will write down what I often forget or didn't know about what I learned today.

Think of @property instead of refactoring attributes

By using @property, you can change what was a simple numeric attribute so that you can perform the necessary calculations on the spot without changing the caller at all.

Now, to compare the normal implementation with using @property, let's look at an example of a water allocation class from a leak bucket.

from datetime import timedelta, datetime

class Bucket(object):
    def __init__(self, period):
        '''
        Parameters
        ----------
        period: int
Set the time until water leaks from the bucket
        '''

        self.period_delta = timedelta(seconds=period)  #Set the time until water leaks from the bucket
        self.reset_time = datetime.now()               #Reset the current time_Assign to time
        self.quota = 0

    def __repr__(self):                                # __repr__Is a special method that returns the output result as a string
        return 'Bucket(quota=%d)' % self.quota

def fill(bucket, amount):
    '''
Put a certain amount of water in the bucket
Treat the if block if the ghost water is leaking

    Parameters
    ----------
    bucket: Bucket 
    amount: int   
    '''

    now = datetime.now()
    if now - bucket.reset_time > bucket.period_delta: #The time from initialization to calling fill is the period_Process this block if it is greater than the time set in delta
        bucket.quota = 0                              
        bucket.reset_time = now                       #Reset the current time_Assign to time
    bucket.quota += amount                            #Add amount to quota

def deduct(bucket, amount):
    '''
If it is not spilled from the bucket and the amount of water is more than the amount, spit out the amount of water.

    Parameters
    ----------
    bucket: Bucket 
    amount: int

    Returns
    -------
    True or False
    '''
    now = datetime.now()
    if now - bucket.reset_time > bucket.period_delta:
        return False
    if bucket.quota - amount < 0:
        return False
    bucket.quota -= amount
    return True

bucket = Bucket(60)    #60 seconds to leak from the bucket
fill(bucket, 100)      #Add 100 water to the bucket
print(bucket)
# Bucket(quota=100)

Use deduct to spit out the required water.

if deduct(bucket, 99):
    print('Had 99 quota')
else:
    print('Not enough for 99 quota')
print(bucket)

Output result

Had 99 quota
Bucket(quota=1)  #Amount of water in the remaining bucket

If you try to spit out 3 more water here

if deduct(bucket, 3):
    print('Had 3 quota')
else:
    print('Not enough for 3 quota')
print(bucket)

Output result

Not enough for 3 quota
Bucket(quota=1)

The problem with this implementation is that if you try to drain more water than the bucket, you will get stuck and you will not see the bucket situation at all. So improve the class to record the quota and the amount of water consumed in variables.

class Bucket(object):
    def __init__(self, period):
        self.period_delta = timedelta(seconds=period)
        self.reset_time = datetime.now()
        self.max_quota = 0                            #Record maximum amount
        self.quota_consumed = 0                       #Record the amount of water discharged

    def __repr__(self):
        return ('Bucket(max_quota=%d, quota_consumed=%d)' % (self.max_quota, self.quota_consumed))

    # @Calculate the new attributes you added using the property method
    @property
    def quota(self):
        return self.max_quota - self.quota_consumed
    
    #Write the process so that it matches the interface of the class used in fill and deduct
    @quota.setter
    def quota(self, amount):
        delta = self.max_quota - amount
        if amount == 0:
            #Reset quota due to new period
            self.quota_consumed = 0
            self.max_quota = 0
        elif delta < 0:
            #Add an appropriate amount for a new period
            assert self.quota_consumed == 0
            self.max_quota = amount
        else:
            #Consumed in appropriate amount within the period
            assert self.max_quota >= self.quota_consumed
            self.quota_consumed += delta

Run the same code as before.

bucket = Bucket(60)
fill(bucket, 100)
print(bucket)


if deduct(bucket, 99):
    print('Had 99 quota')
else:
    print('Not enough for 99 quota')
print(bucket)


if deduct(bucket, 3):
    print('Had 3 quota')
else:
    print('Not enough for 3 quota')
print(bucket)

Output result

Bucket(quota=100)
Had 99 quota
Bucket(quota=1)
Not enough for 3 quota
Bucket(quota=1)
Bucket(max_quota=100, quota_consumed=0)
Had 99 quota
Bucket(max_quota=100, quota_consumed=99)
Not enough for 3 quota
Bucket(max_quota=100, quota_consumed=99)

By using @property, you can see the internal situation.

If you're messing with a class, why not just add methods that correspond to fill and deduct normally? I thought, but in reality, it seems that in many cases, @property is used to gradually improve the model. If you overuse @property and your code becomes hard to read, it seems like it's time to consider refactoring the class and all callers. I understand that it is more efficient to refactor all at once while improving sequentially with @property rather than improving the class.

Recommended Posts

Effective Python Learning Memorandum Day 15 [15/100]
Effective Python Learning Memorandum Day 6 [6/100]
Effective Python Learning Memorandum Day 12 [12/100]
Effective Python Learning Memorandum Day 9 [9/100]
Effective Python Learning Memorandum Day 8 [8/100]
Effective Python Learning Memorandum Day 14 [14/100]
Effective Python Learning Memorandum Day 1 [1/100]
Effective Python Learning Memorandum Day 13 [13/100]
Effective Python Learning Memorandum Day 3 [3/100]
Effective Python Learning Memorandum Day 5 [5/100]
Effective Python Learning Memorandum Day 4 [4/100]
Effective Python Learning Memorandum Day 7 [7/100]
Effective Python Learning Memorandum Day 2 [2/100]
Python learning day 4
Python memorandum
Python Memorandum 2
Python memorandum
python learning
python memorandum
python memorandum
Python day 1
Python memorandum
python memorandum
Python memorandum
Python basics memorandum
[Python] Learning Note 1
Python learning notes
Python pathlib memorandum
Python memorandum (algorithm)
python learning output
Deep Learning Memorandum
Python learning site
Python Deep Learning
Python learning (supplement)
Deep learning × Python
Python memorandum [links]
python learning notes
Python study day 1
Machine learning starting with Python Personal memorandum Part2
Machine learning starting with Python Personal memorandum Part1
Python memorandum numbering variables
Python class (Python learning memo ⑦)
Learning Python with ChemTHEATER 03
"Object-oriented" learning with python
Python module (Python learning memo ④)
Reinforcement learning 1 Python installation
Learning Python with ChemTHEATER 05-1
Python: Deep Learning Practices
Python ~ Grammar speed learning ~
python memorandum (sequential update)
Python: Unsupervised Learning: Basics
Learning record 4 (8th day)
Learning record 9 (13th day)
[1day1lang AdventCalender] day4 Python
Learning record 3 (7th day)
Learning record 5 (9th day)
Learning record 6 (10th day)
Python memorandum (personal bookmark)
Programming learning record day 2
Learning record 8 (12th day)
Learning record 1 (4th day)