...

/

A Plural Rule Iterator

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.

Press + to interact
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 = 0
return self
def __next__(self):
self.cache_index += 1
if len(self.cache) >= self.cache_index:
return self.cache[self.cache_index - 1]
if self.pattern_file.closed:
raise StopIteration
line = self.pattern_file.readline()
if not line:
self.pattern_file.close()
raise StopIteration
pattern, search, replace = line.split(None, 3)
funcs = build_match_and_apply_functions(
pattern, search, replace)
self.cache.append(funcs)
return funcs
rules = 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) calls f.__next__

Let’s take the class one bite at a time.

Press + to interact
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.

Press + to interact
import plural6
r1 = plural6.LazyRules()
r2 = plural6.LazyRules()
print (r1.rules_filename) #①
#plural6-rules.txt
print (r2.rules_filename)
#plural6-rules.txt
r2.rules_filename = 'r2-override.txt' #②
print (r2.rules_filename)
#r2-override.txt
print (r1.rules_filename)
#plural6-rules.txt
print (r2.__class__.rules_filename ) #③
#plural6-rules.txt
r2.__class__.rules_filename = 'papayawhip.txt' #④
print (r1.rules_filename)
#papayawhip.txt
print (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 ...