A Plural Rule Iterator
We'll cover the following...
Now it’s time for the finale. Let’s rewrite the plural rules generator as an iterator.
class LazyRules:rules_filename = 'plural6-rules.txt'def __init__(self):self.pattern_file = open(self.rules_filename, encoding='utf-8')self.cache = []def __iter__(self):self.cache_index = 0return selfdef __next__(self):self.cache_index += 1if len(self.cache) >= self.cache_index:return self.cache[self.cache_index - 1]if self.pattern_file.closed:raise StopIterationline = self.pattern_file.readline()if not line:self.pattern_file.close()raise StopIterationpattern, search, replace = line.split(None, 3)funcs = build_match_and_apply_functions(pattern, search, replace)self.cache.append(funcs)return funcsrules = LazyRules()
So this is a class that implements __iter__()
and __next__()
, so it can be used as an iterator. Then, you instantiate the class and assign it to rules
. This happens just once, on import.
iter(f) calls
f.__iter__
. next(f) callsf.__next__
Let’s take the class one bite at a time.
class LazyRules:rules_filename = 'plural6-rules.txt'def __init__(self):self.pattern_file = open(self.rules_filename, encoding='utf-8') #①self.cache = [] #②
① When we instantiate the LazyRules
class, open the pattern file but don’t read anything from it. (That comes later.)
② After opening the patterns file, initialize the cache. You’ll use this cache later (in the __next__()
method) as you read lines from the pattern file.
Before we continue, let’s take a closer look at rules_filename
. It’s not defined within the __iter__()
method. In fact, it’s not defined within any method. It’s defined at the class level. It’s a class variable, and although you can access it just like an instance variable (self.rules_filename
), it is shared across all instances of the LazyRules
class.
import plural6r1 = plural6.LazyRules()r2 = plural6.LazyRules()print (r1.rules_filename) #①#plural6-rules.txtprint (r2.rules_filename)#plural6-rules.txtr2.rules_filename = 'r2-override.txt' #②print (r2.rules_filename)#r2-override.txtprint (r1.rules_filename)#plural6-rules.txtprint (r2.__class__.rules_filename ) #③#plural6-rules.txtr2.__class__.rules_filename = 'papayawhip.txt' #④print (r1.rules_filename)#papayawhip.txtprint (r2.rules_filename) #⑤#r2-overridetxt
① Each instance of the class inherits the rules_filename
attribute with the value defined by the class.
② Changing the attribute’s value in one instance does not affect other instances…
③ …nor does it change the class attribute. You can access the class attribute (as opposed to an individual instance’s attribute) by using the special __class__
attribute to access the class itself.
④ If you change the class attribute, all instances that are still inheriting that value (like r1
here) will be affected.
⑤ Instances that have overridden that attribute (like r2
here) will not be affected.
And now ...