<jupyter_start><jupyter_text>Python Digs Sequences<jupyter_code>class Foo:
    def __getitem__(self, pos):
        return range(0, 30, 10)[pos]
f = Foo()
f[1]
for i in f: print(i)
20 in f
15 in f<jupyter_output><empty_output><jupyter_text>Monkey-Patching to Implement a Protocol at Runtime<jupyter_code>from random import shuffle
l = list(range(10))
shuffle(l)
l
import collections

Card = collections.namedtuple('Card', ['rank', 'suit'])

class FrenchDeck:
    ranks = [str(n) for n in range(2, 11)] + list('JQKA')
    suits = 'spades diamonds clubs hearts'.split()
    
    def __init__(self):
        self._cards = [Card(rank, suit) for suit in self.suits
                                        for rank in self.ranks]
        
    def __len__(self):
        return len(self._cards)
    
    def __getitem__(self, position):
        return self._cards[position]
deck = FrenchDeck()
shuffle(deck)
def set_card(deck, position, card):
    deck._cards[position] = card
FrenchDeck.__setitem__ = set_card
shuffle(deck)
print(deck[:5])<jupyter_output>[Card(rank='Q', suit='clubs'), Card(rank='A', suit='hearts'), Card(rank='3', suit='diamonds'), Card(rank='J', suit='diamonds'), Card(rank='7', suit='hearts')]<jupyter_text><NAME>'s Waterfowl<jupyter_code>class Struggle:
    def __len__(self): return 23
    
from collections import abc
isinstance(Struggle(), abc.Sized)
try:
    field_names = field_names.replace(',',' ').split()
except AttributeError:
    pass
field_names = tuple(field_names)<jupyter_output><empty_output><jupyter_text>Subclassing an ABC<jupyter_code>import collections

Card = collections.namedtuple('Card', ['rank', 'suit'])

class FrenchDeck2(collections.MutableSequence):
    ranks = [str(n) for n in range(2, 11)] + list('JQKA')
    suits = 'spades diamonds clubs hearts'.split()
    
    def __init__(self):
        self._cards = [Card(rank, suit) for suit in self.suits
                                        for rank in self.ranks]
        
    def __len__(self):
        return len(self._cards)
    
    def __getitem__(self, position):
        return self._cards[position]
    
    def __setitem__(self, position, value):
        self._cards[position] = value
        
    def __delitem__(self, position):
        del self._cards[position]
        
    def insert(self, position, value):
        self._cards.insert(position, value)<jupyter_output><empty_output><jupyter_text>Defining and Using an ABC<jupyter_code>import abc
import abc

class Tombola(abc.ABC):
    
    @abc.abstractmethod
    def load(self, iterable):
        """Add items from an iterable"""
        
    @abc.abstractmethod
    def pick(self):
        """Remove item at random, returning it
        
        This method should raise 'LookupError' when the instance is empty.
        """
        
    def loaded(self):
        """Return 'True' if there's at least 1 item, 'False' otherwise."""
        return bool(self.inspect())
    
    def inspect(self):
        """Return a sorted tuple with the items currently inside."""
        items = []
        while True:
            try:
                items.append(self.pick())
            except LookupError:
                break
        self.load(items)
        return tuple(sorted(items))
class Fake(Tombola):
    def pick(self):
        return 13
Fake
f = Fake()<jupyter_output><empty_output><jupyter_text>Subclassing the Tombola ABC<jupyter_code>import random

class BingoCage(Tombola):
    
    def __init__(self, items):
        self._randomizer = random.SystemRandom()
        self._items = []
        self.load(items)
        
    def load(self, items):
        self._items.extend(items)
        self._randomizer.shuffle(self._items)
        
    def pick(self):
        try:
            return self._items.pop()
        except IndexError:
            raise LookupError('pick from empty BingoCage')
            
    def __call__(self):
        self.pick()

import random

class LotteryBlower(Tombola):
    
    def __init__(self, iterable):
        self._balls = list(iterable)
        
    def load(self, iterable):
        self._balls.extend(iterable)
        
    def pick(self):
        try:
            position = random.randrange(len(self._balls))
        except ValueError:
            raise LookupError('pick from empty LotteryBlower')
        return self._balls.pop(position)
    
    def loaded(self):
        return bool(self._balls)
    
    def inspect(self):
        return tuple(sorted(self._balls))<jupyter_output><empty_output><jupyter_text>A Virtual Subclass of Tombola<jupyter_code>from random import randrange

@Tombola.register
class TomboList(list):
    
    def pick(self):
        if self:
            position = randrange(len(self))
            return self.pop(position)
        else:
            raise LookupError('pop from empty TomboList')
            
    load = list.extend
    
    def loaded(self):
        return bool(self)
    
    def inspect(self):
        return tuple(sorted(self))
issubclass(TomboList, Tombola)
t = TomboList(range(100))
isinstance(t, Tombola)
TomboList.__mro__<jupyter_output><empty_output><jupyter_text>Geese Can Behave as Ducks<jupyter_code>class Struggle:
    def __len__(self): return 23
from collections import abc
isinstance(Struggle(), abc.Sized)
issubclass(Struggle, abc.Sized)<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.11246714232069095, 'fraction_numerical': 0.0060082613593691325, 'mean_word_length': 2.8104434907010014, 'pattern_counts': {'":': 0, '<': 33, '<?xml version=': 0, '>': 33, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '8', 'dir': 'jupyter-structured-clean-dedup', 'id': '346763', 'license': 'MIT', 'n_tokens_mistral': 1704, 'n_tokens_neox': 1663, 'n_words': 393, 'path': '11. Interfaces - From Protocols to ABCs.ipynb', 'repo_name': 'stephenl6705/fluentPy', 'size': '5333'}