From a6658ba65b3c409202105a5d4ddca052dad091d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jure=20=C5=A0orn?= Date: Sun, 7 Jul 2019 14:35:13 +0200 Subject: [PATCH] Iterable duck types --- README.md | 149 +++++++++++++++++++++++++++++------------------------ index.html | 138 ++++++++++++++++++++++++++----------------------- parse.js | 2 +- 3 files changed, 155 insertions(+), 134 deletions(-) diff --git a/README.md b/README.md index 8230b3b..aa2a877 100644 --- a/README.md +++ b/README.md @@ -1082,7 +1082,7 @@ class MyHashable: ``` ### Sortable -* **With 'total_ordering' decorator you only need to provide one of lt(), gt(), le() or ge() special methods.** +* **With 'total_ordering' decorator you only need to provide eq() and one of lt(), gt(), le() or ge() special methods.** ```python from functools import total_ordering @@ -1100,73 +1100,6 @@ class MySortable: return NotImplemented ``` -### Iterable -* **Only required method for iterable is iter(), that should return an iterator of its contents.** -```python -class MyIterable: - def __init__(self, a): - self.a = a - def __iter__(self): - for el in self.a: - yield el -``` - -### Collection -* **Every collection is also an iterable.** -* **This cheatsheet actually means `''` when it uses `''`.** -* **I chose not to use the name iterable because it sounds scarier and more vague than collection.** -```python -class MyCollection: - def __init__(self, a): - self.a = a - def __iter__(self): - for el in self.a: - yield el - def __len__(self): - return len(self.a) - def __contains__(self, el): - return el in self.a -``` - -### Sequence -* **Every sequence is also an iterable.** -* **That is because iter() is automatically generated if getitem() is defined.** -```python -class MySequence: - def __init__(self, a): - self.a = a - def __len__(self): - return len(self.a) - def __getitem__(self, i): - return self.a[i] -``` - -### Collections.abc.Sequence -* **A much richer interface than the basic sequence.** -* **Extending it generates contains(), iter(), reversed(), index(), and count().** -* **It is not a duck type, so `'issubclass(MySequence, collections.abc.Sequence)'` would return 'False' even if it had all the methods defined.** -```python -class MyAbcSequence(collections.abc.Sequence): - def __init__(self, a): - self.a = a - def __len__(self): - return len(self.a) - def __getitem__(self, i): - return self.a[i] -``` - -#### Table of the methods that each (duck) type provides: -```text -+------------+----------+------------+----------+--------------+ -| | Iterable | Collection | Sequence | abc.Sequence | -+------------+----------+------------+----------+--------------+ -| iter() | yes | yes | yes | yes | -| len() | | yes | yes | yes | -| getitem() | | | yes | yes | -| contains() | | yes | | yes | -+------------+----------+------------+----------+--------------+ -``` - ### Iterator ```python class Counter: @@ -1235,6 +1168,86 @@ con = sqlite3.connect(''); with con: con.execute('') ``` +Iterable Duck Types +------------------- + +### Iterable +* **Only required method for iterable is iter(), that should return an iterator of its contents.** +* **Contains() is automatically generated whenever iter() is present.** +```python +class MyIterable: + def __init__(self, a): + self.a = a + def __iter__(self): + for el in self.a: + yield el +``` + +```python +>>> a = MyIterable([1, 2, 3]) +>>> iter(a) + +>>> 1 in a +True +``` + +### Collection +* **Every collection is also an iterable.** +* **This cheatsheet actually means `''` when it uses `''`.** +* **I chose not to use the name iterable because it sounds scarier and more vague than collection.** +```python +class MyCollection: + def __init__(self, a): + self.a = a + def __contains__(self, el): + return el in self.a + def __len__(self): + return len(self.a) + def __iter__(self): + for el in self.a: + yield el +``` + +### Sequence +* **Every sequence is also a collection.** +* **Iter() and reversed() are automatically generated whenever getitem() is present.** +```python +class MySequence: + def __init__(self, a): + self.a = a + def __getitem__(self, i): + return self.a[i] + def __len__(self): + return len(self.a) +``` + +### Collections.abc.Sequence +* **It's a richer interface than the basic sequence.** +* **Extending it generates contains(), iter(), reversed(), index(), and count().** +* **Unlike `'abc.Iterable'` and `'abc.Collection'`, it is not a duck type. That's why `'issubclass(MySequence, collections.abc.Sequence)'` would return 'False' even if it had all the methods defined.** +```python +class MyAbcSequence(collections.abc.Sequence): + def __init__(self, a): + self.a = a + def __getitem__(self, i): + return self.a[i] + def __len__(self): + return len(self.a) +``` + +#### Table of the methods that each (duck) type provides: +```text ++------------+----------+------------+----------+--------------+ +| | Iterable | Collection | Sequence | abc.Sequence | ++------------+----------+------------+----------+--------------+ +| iter() | yes | yes | yes | yes | +| len() | | yes | yes | yes | +| getitem() | | | yes | yes | +| contains() | | yes | | yes | ++------------+----------+------------+----------+--------------+ +``` + + Enum ---- ```python diff --git a/index.html b/index.html index 36fd517..ed776a2 100644 --- a/index.html +++ b/index.html @@ -1016,7 +1016,7 @@ Z = dataclasses.make_dataclass('Z', [

Sortable

    -
  • With 'total_ordering' decorator you only need to provide one of lt(), gt(), le() or ge() special methods.
  • +
  • With 'total_ordering' decorator you only need to provide eq() and one of lt(), gt(), le() or ge() special methods.
from functools import total_ordering
 
@@ -1033,9 +1033,62 @@ Z = dataclasses.make_dataclass('Z', [return self.a < other.a
         return NotImplemented
 
+

Iterator

+
class Counter:
+    def __init__(self):
+        self.i = 0
+    def __next__(self):
+        self.i += 1
+        return self.i
+    def __iter__(self):
+        return self
+
+
>>> counter = Counter()
+>>> next(counter), next(counter), next(counter)
+(1, 2, 3)
+
+

Callable

+
class Counter:
+    def __init__(self):
+        self.i = 0
+    def __call__(self):
+        self.i += 1
+        return self.i
+
+
>>> counter = Counter()
+>>> counter(), counter(), counter()
+(1, 2, 3)
+
+

Context Manager

+
class MyOpen():
+    def __init__(self, filename):
+        self.filename = filename
+    def __enter__(self):
+        self.file = open(self.filename)
+        return self.file
+    def __exit__(self, *args):
+        self.file.close()
+
+
>>> with open('test.txt', 'w') as file:
+...     file.write('Hello World!')
+>>> with MyOpen('test.txt') as file:
+...     print(file.read())
+Hello World!
+
+

Context managers:

+
with open('<path>', ...) as file: ...
+with wave.open('<path>', ...) as wave_file: ...
+with memoryview(<bytes/bytearray/array>) as view: ...
+
+

Reusable context managers:

+
lock = threading.RLock(); with lock: ...
+con  = sqlite3.connect('<path>'); with con: con.execute('<insert_query>')
+
+

#Iterable Duck Types

Iterable

  • Only required method for iterable is iter(), that should return an iterator of its contents.
  • +
  • Contains() is automatically generated whenever iter() is present.
class MyIterable:
     def __init__(self, a):
@@ -1044,6 +1097,12 @@ Z = dataclasses.make_dataclass('Z', [for el in self.a:
             yield el
 
+
>>> a = MyIterable([1, 2, 3])
+>>> iter(a)
+<generator object MyIterable.__iter__ at 0x1026c18b8>
+>>> 1 in a
+True
+

Collection

  • Every collection is also an iterable.
  • @@ -1053,40 +1112,40 @@ Z = dataclasses.make_dataclass('Z', [class MyCollection: def __init__(self, a): self.a = a + def __contains__(self, el): + return el in self.a + def __len__(self): + return len(self.a) def __iter__(self): for el in self.a: yield el - def __len__(self): - return len(self.a) - def __contains__(self, el): - return el in self.a

    Sequence

      -
    • Every sequence is also an iterable.
    • -
    • That is because iter() is automatically generated if getitem() is defined.
    • +
    • Every sequence is also a collection.
    • +
    • Iter() and reversed() are automatically generated whenever getitem() is present.
    class MySequence:
         def __init__(self, a):
             self.a = a
    -    def __len__(self):
    -        return len(self.a)
         def __getitem__(self, i):
             return self.a[i]
    +    def __len__(self):
    +        return len(self.a)
     

    Collections.abc.Sequence

      -
    • A much richer interface than the basic sequence.
    • +
    • It's a richer interface than the basic sequence.
    • Extending it generates contains(), iter(), reversed(), index(), and count().
    • -
    • It is not a duck type, so 'issubclass(MySequence, collections.abc.Sequence)' would return 'False' even if it had all the methods defined.
    • +
    • Unlike 'abc.Iterable' and 'abc.Collection', it is not a duck type. That's why 'issubclass(MySequence, collections.abc.Sequence)' would return 'False' even if it had all the methods defined.
    class MyAbcSequence(collections.abc.Sequence):
         def __init__(self, a):
             self.a = a
    -    def __len__(self):
    -        return len(self.a)
         def __getitem__(self, i):
             return self.a[i]
    +    def __len__(self):
    +        return len(self.a)
     

    Table of the methods that each (duck) type provides:

    ┏━━━━━━━━━━━━┯━━━━━━━━━━┯━━━━━━━━━━━━┯━━━━━━━━━━┯━━━━━━━━━━━━━━┓
    @@ -1094,61 +1153,10 @@ Z = dataclasses.make_dataclass('Z', [
    -

    Iterator

    -
    class Counter:
    -    def __init__(self):
    -        self.i = 0
    -    def __next__(self):
    -        self.i += 1
    -        return self.i
    -    def __iter__(self):
    -        return self
    -
    -
    >>> counter = Counter()
    ->>> next(counter), next(counter), next(counter)
    -(1, 2, 3)
    -
    -

    Callable

    -
    class Counter:
    -    def __init__(self):
    -        self.i = 0
    -    def __call__(self):
    -        self.i += 1
    -        return self.i
    -
    -
    >>> counter = Counter()
    ->>> counter(), counter(), counter()
    -(1, 2, 3)
    -
    -

    Context Manager

    -
    class MyOpen():
    -    def __init__(self, filename):
    -        self.filename = filename
    -    def __enter__(self):
    -        self.file = open(self.filename)
    -        return self.file
    -    def __exit__(self, *args):
    -        self.file.close()
    -
    -
    >>> with open('test.txt', 'w') as file:
    -...     file.write('Hello World!')
    ->>> with MyOpen('test.txt') as file:
    -...     print(file.read())
    -Hello World!
    -
    -

    Context managers:

    -
    with open('<path>', ...) as file: ...
    -with wave.open('<path>', ...) as wave_file: ...
    -with memoryview(<bytes/bytearray/array>) as view: ...
    -
    -

    Reusable context managers:

    -
    lock = threading.RLock(); with lock: ...
    -con  = sqlite3.connect('<path>'); with con: con.execute('<insert_query>')
    -

    #Enum

    from enum import Enum, auto
     
    diff --git a/parse.js b/parse.js
    index dcba0af..6498c55 100755
    --- a/parse.js
    +++ b/parse.js
    @@ -184,7 +184,7 @@ const DIAGRAM_7_B =
       '┠────────────┼──────────┼────────────┼──────────┼──────────────┨\n' +
       '┃ iter()     │    ✓     │     ✓      │    ✓     │      ✓       ┃\n' +
       '┃ len()      │          │     ✓      │    ✓     │      ✓       ┃\n' +
    -  '┃ getitem()  │          │            │    ✓     │      ✓       ┃\n' +
    +  '┃ getitem()  │          │     +      │    ✓     │      ✓       ┃\n' +
       '┃ contains() │          │     ✓      │          │      ✓       ┃\n' +
       '┗━━━━━━━━━━━━┷━━━━━━━━━━┷━━━━━━━━━━━━┷━━━━━━━━━━┷━━━━━━━━━━━━━━┛\n';