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!
--Chapter 8 structure --Page 216 of this chapter
--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.
@property
instead of refactoring attributesBy 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