From 82bd4f68847b337c0605058086aad886ab867a6a Mon Sep 17 00:00:00 2001 From: haocoder Date: Wed, 8 Oct 2025 22:41:06 +0800 Subject: [PATCH 1/3] data model and seq --- 01-data-model/data-model.ipynb | 611 +++++++++++++++++- 02-array-seq/array-seq.ipynb | 1096 +++++++++++++++++++++----------- 2 files changed, 1316 insertions(+), 391 deletions(-) diff --git a/01-data-model/data-model.ipynb b/01-data-model/data-model.ipynb index 5b30397..ec24edf 100644 --- a/01-data-model/data-model.ipynb +++ b/01-data-model/data-model.ipynb @@ -12,6 +12,21 @@ "* [Emulating Numeric Types](#Emulating-Numeric-Types)" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Python data model first glance\n", + "- if you learned another object-oriented language before Python, you may\n", + "find it strange to use len(collection) instead of collection.len(). This apparent\n", + "oddity is the tip of an iceberg that, when properly understood, is the key to every‐\n", + "thing we call Pythonic. **The iceberg is called the Python Data Model, and it is the API\n", + "that we use to make our own objects play well with the most idiomatic language features**\n", + "- You can think of the data model as a description of Python as a framework. It formalizes the interfaces of the building blocks of the language itself, such as sequences, functions, iterators, coroutines, classes, context managers, and so on.\n", + "- when we leverage the Python Data Model to build new classes. The Python interpreter invokes special methods to perform basic object operations, often triggered by special syntax. The special method is *dunder methods*, for example the obj[key] is supported by the \\_\\_getitem\\_\\_ special method.\n", + "- 因为python数据模型的存在,使得用户可以很自然的像使用内置类型一样使用用户自定义类型,比如通过对用户自定义类型应用python内置函数(abs(), bool(), str()等)和内置操作符(+, -, *, []等),会由python解释器自动调用类型对应的dunder methods(首尾双下划线特殊方法)" + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -32,10 +47,13 @@ "metadata": {}, "outputs": [], "source": [ + "import sys\n", + "import inspect\n", "import collections\n", "\n", "Card = collections.namedtuple('Card', ['rank', 'suit'])\n", "\n", + "\n", "class FrenchDeck:\n", " ranks = [str(n) for n in range(2, 11)] + list('JQKA')\n", " suits = 'spades diamonds clubs hearts'.split()\n", @@ -43,10 +61,14 @@ " def __init__(self):\n", " self._cards = [Card(rank, suit) for suit in self.suits for rank in self.ranks]\n", "\n", + " # delegate the len() method\n", " def __len__(self):\n", " return len(self._cards)\n", "\n", + " # Delegate the [] operator\n", " def __getitem__(self, position):\n", + " sys.stdout.write(f\"call {inspect.currentframe().f_code.co_name} with position {position}\\n\")\n", + " sys.stdout.flush() \n", " return self._cards[position]" ] }, @@ -97,6 +119,13 @@ "execution_count": 4, "metadata": {}, "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "call __getitem__ with position 0\n" + ] + }, { "data": { "text/plain": [ @@ -117,6 +146,13 @@ "execution_count": 5, "metadata": {}, "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "call __getitem__ with position -1\n" + ] + }, { "data": { "text/plain": [ @@ -137,10 +173,17 @@ "execution_count": 6, "metadata": {}, "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "call __getitem__ with position 34\n" + ] + }, { "data": { "text/plain": [ - "Card(rank='6', suit='diamonds')" + "Card(rank='10', suit='clubs')" ] }, "execution_count": 6, @@ -150,16 +193,36 @@ ], "source": [ "# NBVAL_IGNORE_OUTPUT\n", + "# 利用random.choice随机选择序列元素\n", "from random import choice\n", "\n", "choice(deck)" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Two advantages of using special methods to leverage the Python Data Model\n", + "- Users of your classes don’t have to memorize arbitrary method names for stan‐\n", + "dard operations. (“How to get the number of items? Is it .size(), .length(), or\n", + "what?”)\n", + "- It’s easier to benefit from the rich Python standard library and avoid reinventing\n", + "the wheel, like the random.choice function." + ] + }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "call __getitem__ with position slice(None, 3, None)\n" + ] + }, { "data": { "text/plain": [ @@ -182,6 +245,42 @@ "execution_count": 8, "metadata": {}, "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "call __getitem__ with position slice(None, 9, 3)\n" + ] + }, + { + "data": { + "text/plain": [ + "[Card(rank='2', suit='spades'),\n", + " Card(rank='5', suit='spades'),\n", + " Card(rank='8', suit='spades')]" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "deck[:9:3]" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "call __getitem__ with position slice(12, None, 13)\n" + ] + }, { "data": { "text/plain": [ @@ -191,7 +290,7 @@ " Card(rank='A', suit='hearts')]" ] }, - "execution_count": 8, + "execution_count": 9, "metadata": {}, "output_type": "execute_result" } @@ -200,176 +299,479 @@ "deck[12::13]" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "### 1. Python迭代协议(Iteration Protocol)\n", + "\n", + "Python的for循环基于**迭代协议**,它需要对象实现以下方法之一:\n", + "\n", + "#### 方法1:`__iter__()` 方法(推荐)\n", + "```python\n", + "def __iter__(self):\n", + " return iter(self._cards) # 返回一个迭代器\n", + "```\n", + "\n", + "#### 方法2:`__getitem__()` 方法(fallback机制)\n", + "当对象没有实现 `__iter__()` 时,Python会使用 `__getitem__()` 作为fallback。\n", + "\n", + "### 2. 你的FrenchDeck类的行为\n", + "\n", + "```python\n", + "class FrenchDeck:\n", + " def __getitem__(self, position):\n", + " return self._cards[position]\n", + "```\n", + "\n", + "由于 `FrenchDeck` 只实现了 `__getitem__()` 而没有实现 `__iter__()`,Python解释器会:\n", + "\n", + "1. **从索引0开始**:调用 `deck[0]`\n", + "2. **依次递增索引**:调用 `deck[1]`, `deck[2]`, `deck[3]`...\n", + "3. **直到IndexError**:当索引超出范围时,Python捕获 `IndexError` 异常,结束迭代\n", + "\n", + "### 3. 内部执行流程\n", + "\n", + "```python\n", + "# 当执行 for card in deck: 时,Python内部相当于:\n", + "i = 0\n", + "while True:\n", + " try:\n", + " card = deck[i] # 调用 __getitem__(i)\n", + " # 执行循环体\n", + " print(card)\n", + " i += 1\n", + " except IndexError:\n", + " break # 结束迭代\n", + "```\n", + "\n", + "### 4. 为什么使用 `__getitem__` 作为fallback?\n", + "\n", + "- **向后兼容**:早期Python版本主要依赖 `__getitem__`\n", + "- **简单实现**:对于序列类型,`__getitem__` 已经足够\n", + "- **自动支持切片**:`deck[1:5]` 也能正常工作\n", + "\n", + "### 5. 更高效的实现方式\n", + "\n", + "如果要优化性能,可以实现 `__iter__()` 方法:\n", + "\n", + "```python\n", + "class FrenchDeck:\n", + " def __iter__(self):\n", + " return iter(self._cards) # 直接返回list的迭代器\n", + " \n", + " def __getitem__(self, position):\n", + " return self._cards[position]\n", + "```\n", + "\n", + "这样 `for card in deck:` 就不会调用 `__getitem__`,而是直接使用list的高效迭代器。\n", + "\n", + "### 6. 总结\n", + "\n", + "`for card in deck:` 的内部原理是:\n", + "1. Python首先尝试调用 `__iter__()` 方法\n", + "2. 如果没有 `__iter__()`,则使用 `__getitem__()` 作为fallback\n", + "3. 从索引0开始,依次调用 `__getitem__(0)`, `__getitem__(1)`...\n", + "4. 直到捕获 `IndexError` 异常,结束迭代\n", + "\n", + "这就是为什么你的notebook中能看到 `__getitem__` 被反复调用的原因!" + ] + }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 10, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ + "call __getitem__ with position 0\n", "Card(rank='2', suit='spades')\n", + "call __getitem__ with position 1\n", "Card(rank='3', suit='spades')\n", + "call __getitem__ with position 2\n", "Card(rank='4', suit='spades')\n", + "call __getitem__ with position 3\n", "Card(rank='5', suit='spades')\n", + "call __getitem__ with position 4\n", "Card(rank='6', suit='spades')\n", + "call __getitem__ with position 5\n", "Card(rank='7', suit='spades')\n", + "call __getitem__ with position 6\n", "Card(rank='8', suit='spades')\n", + "call __getitem__ with position 7\n", "Card(rank='9', suit='spades')\n", + "call __getitem__ with position 8\n", "Card(rank='10', suit='spades')\n", + "call __getitem__ with position 9\n", "Card(rank='J', suit='spades')\n", + "call __getitem__ with position 10\n", "Card(rank='Q', suit='spades')\n", + "call __getitem__ with position 11\n", "Card(rank='K', suit='spades')\n", + "call __getitem__ with position 12\n", "Card(rank='A', suit='spades')\n", + "call __getitem__ with position 13\n", "Card(rank='2', suit='diamonds')\n", + "call __getitem__ with position 14\n", "Card(rank='3', suit='diamonds')\n", + "call __getitem__ with position 15\n", "Card(rank='4', suit='diamonds')\n", + "call __getitem__ with position 16\n", "Card(rank='5', suit='diamonds')\n", + "call __getitem__ with position 17\n", "Card(rank='6', suit='diamonds')\n", + "call __getitem__ with position 18\n", "Card(rank='7', suit='diamonds')\n", + "call __getitem__ with position 19\n", "Card(rank='8', suit='diamonds')\n", + "call __getitem__ with position 20\n", "Card(rank='9', suit='diamonds')\n", + "call __getitem__ with position 21\n", "Card(rank='10', suit='diamonds')\n", + "call __getitem__ with position 22\n", "Card(rank='J', suit='diamonds')\n", + "call __getitem__ with position 23\n", "Card(rank='Q', suit='diamonds')\n", + "call __getitem__ with position 24\n", "Card(rank='K', suit='diamonds')\n", + "call __getitem__ with position 25\n", "Card(rank='A', suit='diamonds')\n", + "call __getitem__ with position 26\n", "Card(rank='2', suit='clubs')\n", + "call __getitem__ with position 27\n", "Card(rank='3', suit='clubs')\n", + "call __getitem__ with position 28\n", "Card(rank='4', suit='clubs')\n", + "call __getitem__ with position 29\n", "Card(rank='5', suit='clubs')\n", + "call __getitem__ with position 30\n", "Card(rank='6', suit='clubs')\n", + "call __getitem__ with position 31\n", "Card(rank='7', suit='clubs')\n", + "call __getitem__ with position 32\n", "Card(rank='8', suit='clubs')\n", + "call __getitem__ with position 33\n", "Card(rank='9', suit='clubs')\n", + "call __getitem__ with position 34\n", "Card(rank='10', suit='clubs')\n", + "call __getitem__ with position 35\n", "Card(rank='J', suit='clubs')\n", + "call __getitem__ with position 36\n", "Card(rank='Q', suit='clubs')\n", + "call __getitem__ with position 37\n", "Card(rank='K', suit='clubs')\n", + "call __getitem__ with position 38\n", "Card(rank='A', suit='clubs')\n", + "call __getitem__ with position 39\n", "Card(rank='2', suit='hearts')\n", + "call __getitem__ with position 40\n", "Card(rank='3', suit='hearts')\n", + "call __getitem__ with position 41\n", "Card(rank='4', suit='hearts')\n", + "call __getitem__ with position 42\n", "Card(rank='5', suit='hearts')\n", + "call __getitem__ with position 43\n", "Card(rank='6', suit='hearts')\n", + "call __getitem__ with position 44\n", "Card(rank='7', suit='hearts')\n", + "call __getitem__ with position 45\n", "Card(rank='8', suit='hearts')\n", + "call __getitem__ with position 46\n", "Card(rank='9', suit='hearts')\n", + "call __getitem__ with position 47\n", "Card(rank='10', suit='hearts')\n", + "call __getitem__ with position 48\n", "Card(rank='J', suit='hearts')\n", + "call __getitem__ with position 49\n", "Card(rank='Q', suit='hearts')\n", + "call __getitem__ with position 50\n", "Card(rank='K', suit='hearts')\n", - "Card(rank='A', suit='hearts')\n" + "call __getitem__ with position 51\n", + "Card(rank='A', suit='hearts')\n", + "call __getitem__ with position 52\n" ] } ], "source": [ - "for card in deck:\n", + "for card in deck: # by implementing the __getitem__ special method, our deck is also iterable\n", " print(card)" ] }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 11, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ + "call __getitem__ with position 51\n", "Card(rank='A', suit='hearts')\n", + "call __getitem__ with position 50\n", "Card(rank='K', suit='hearts')\n", + "call __getitem__ with position 49\n", "Card(rank='Q', suit='hearts')\n", + "call __getitem__ with position 48\n", "Card(rank='J', suit='hearts')\n", + "call __getitem__ with position 47\n", "Card(rank='10', suit='hearts')\n", + "call __getitem__ with position 46\n", "Card(rank='9', suit='hearts')\n", + "call __getitem__ with position 45\n", "Card(rank='8', suit='hearts')\n", + "call __getitem__ with position 44\n", "Card(rank='7', suit='hearts')\n", + "call __getitem__ with position 43\n", "Card(rank='6', suit='hearts')\n", + "call __getitem__ with position 42\n", "Card(rank='5', suit='hearts')\n", + "call __getitem__ with position 41\n", "Card(rank='4', suit='hearts')\n", + "call __getitem__ with position 40\n", "Card(rank='3', suit='hearts')\n", + "call __getitem__ with position 39\n", "Card(rank='2', suit='hearts')\n", + "call __getitem__ with position 38\n", "Card(rank='A', suit='clubs')\n", + "call __getitem__ with position 37\n", "Card(rank='K', suit='clubs')\n", + "call __getitem__ with position 36\n", "Card(rank='Q', suit='clubs')\n", + "call __getitem__ with position 35\n", "Card(rank='J', suit='clubs')\n", + "call __getitem__ with position 34\n", "Card(rank='10', suit='clubs')\n", + "call __getitem__ with position 33\n", "Card(rank='9', suit='clubs')\n", + "call __getitem__ with position 32\n", "Card(rank='8', suit='clubs')\n", + "call __getitem__ with position 31\n", "Card(rank='7', suit='clubs')\n", + "call __getitem__ with position 30\n", "Card(rank='6', suit='clubs')\n", + "call __getitem__ with position 29\n", "Card(rank='5', suit='clubs')\n", + "call __getitem__ with position 28\n", "Card(rank='4', suit='clubs')\n", + "call __getitem__ with position 27\n", "Card(rank='3', suit='clubs')\n", + "call __getitem__ with position 26\n", "Card(rank='2', suit='clubs')\n", + "call __getitem__ with position 25\n", "Card(rank='A', suit='diamonds')\n", + "call __getitem__ with position 24\n", "Card(rank='K', suit='diamonds')\n", + "call __getitem__ with position 23\n", "Card(rank='Q', suit='diamonds')\n", + "call __getitem__ with position 22\n", "Card(rank='J', suit='diamonds')\n", + "call __getitem__ with position 21\n", "Card(rank='10', suit='diamonds')\n", + "call __getitem__ with position 20\n", "Card(rank='9', suit='diamonds')\n", + "call __getitem__ with position 19\n", "Card(rank='8', suit='diamonds')\n", + "call __getitem__ with position 18\n", "Card(rank='7', suit='diamonds')\n", + "call __getitem__ with position 17\n", "Card(rank='6', suit='diamonds')\n", + "call __getitem__ with position 16\n", "Card(rank='5', suit='diamonds')\n", + "call __getitem__ with position 15\n", "Card(rank='4', suit='diamonds')\n", + "call __getitem__ with position 14\n", "Card(rank='3', suit='diamonds')\n", + "call __getitem__ with position 13\n", "Card(rank='2', suit='diamonds')\n", + "call __getitem__ with position 12\n", "Card(rank='A', suit='spades')\n", + "call __getitem__ with position 11\n", "Card(rank='K', suit='spades')\n", + "call __getitem__ with position 10\n", "Card(rank='Q', suit='spades')\n", + "call __getitem__ with position 9\n", "Card(rank='J', suit='spades')\n", + "call __getitem__ with position 8\n", "Card(rank='10', suit='spades')\n", + "call __getitem__ with position 7\n", "Card(rank='9', suit='spades')\n", + "call __getitem__ with position 6\n", "Card(rank='8', suit='spades')\n", + "call __getitem__ with position 5\n", "Card(rank='7', suit='spades')\n", + "call __getitem__ with position 4\n", "Card(rank='6', suit='spades')\n", + "call __getitem__ with position 3\n", "Card(rank='5', suit='spades')\n", + "call __getitem__ with position 2\n", "Card(rank='4', suit='spades')\n", + "call __getitem__ with position 1\n", "Card(rank='3', suit='spades')\n", + "call __getitem__ with position 0\n", "Card(rank='2', suit='spades')\n" ] } ], "source": [ - "for card in reversed(deck):\n", + "for card in reversed(deck): # iterate over the deck in reverse\n", " print(card)" ] }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 12, "metadata": {}, "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "call __getitem__ with position 0\n", + "call __getitem__ with position 1\n", + "call __getitem__ with position 2\n", + "call __getitem__ with position 3\n", + "call __getitem__ with position 4\n", + "call __getitem__ with position 5\n", + "call __getitem__ with position 6\n", + "call __getitem__ with position 7\n", + "call __getitem__ with position 8\n", + "call __getitem__ with position 9\n", + "call __getitem__ with position 10\n", + "call __getitem__ with position 11\n", + "call __getitem__ with position 12\n", + "call __getitem__ with position 13\n", + "call __getitem__ with position 14\n", + "call __getitem__ with position 15\n", + "call __getitem__ with position 16\n", + "call __getitem__ with position 17\n", + "call __getitem__ with position 18\n", + "call __getitem__ with position 19\n", + "call __getitem__ with position 20\n", + "call __getitem__ with position 21\n", + "call __getitem__ with position 22\n", + "call __getitem__ with position 23\n", + "call __getitem__ with position 24\n", + "call __getitem__ with position 25\n", + "call __getitem__ with position 26\n", + "call __getitem__ with position 27\n", + "call __getitem__ with position 28\n", + "call __getitem__ with position 29\n", + "call __getitem__ with position 30\n", + "call __getitem__ with position 31\n", + "call __getitem__ with position 32\n", + "call __getitem__ with position 33\n", + "call __getitem__ with position 34\n", + "call __getitem__ with position 35\n", + "call __getitem__ with position 36\n", + "call __getitem__ with position 37\n", + "call __getitem__ with position 38\n", + "call __getitem__ with position 39\n", + "call __getitem__ with position 40\n", + "call __getitem__ with position 41\n", + "call __getitem__ with position 42\n", + "call __getitem__ with position 43\n", + "call __getitem__ with position 44\n", + "call __getitem__ with position 45\n", + "call __getitem__ with position 46\n", + "call __getitem__ with position 47\n", + "call __getitem__ with position 48\n", + "call __getitem__ with position 49\n" + ] + }, { "data": { "text/plain": [ "True" ] }, - "execution_count": 11, + "execution_count": 12, "metadata": {}, "output_type": "execute_result" } ], "source": [ + "# 这行代码的作用是判断Card('Q', 'hearts')这张牌是否在deck(牌组)中。\n", + "# 其内部原理是:当使用`in`运算符时,Python会自动调用deck对象的__contains__方法(如果有实现),\n", + "# 否则会退而调用__getitem__方法,从索引0开始依次取出元素,与Card('Q', 'hearts')进行比较(通过Card的__eq__方法),\n", + "# 直到找到相等的元素或遍历结束。因此,这里实际上是依次遍历deck中的每一张牌,判断是否有一张等于Card('Q', 'hearts')。\n", "Card('Q', 'hearts') in deck" ] }, { "cell_type": "code", - "execution_count": 12, + "execution_count": 13, "metadata": {}, "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "call __getitem__ with position 0\n", + "call __getitem__ with position 1\n", + "call __getitem__ with position 2\n", + "call __getitem__ with position 3\n", + "call __getitem__ with position 4\n", + "call __getitem__ with position 5\n", + "call __getitem__ with position 6\n", + "call __getitem__ with position 7\n", + "call __getitem__ with position 8\n", + "call __getitem__ with position 9\n", + "call __getitem__ with position 10\n", + "call __getitem__ with position 11\n", + "call __getitem__ with position 12\n", + "call __getitem__ with position 13\n", + "call __getitem__ with position 14\n", + "call __getitem__ with position 15\n", + "call __getitem__ with position 16\n", + "call __getitem__ with position 17\n", + "call __getitem__ with position 18\n", + "call __getitem__ with position 19\n", + "call __getitem__ with position 20\n", + "call __getitem__ with position 21\n", + "call __getitem__ with position 22\n", + "call __getitem__ with position 23\n", + "call __getitem__ with position 24\n", + "call __getitem__ with position 25\n", + "call __getitem__ with position 26\n", + "call __getitem__ with position 27\n", + "call __getitem__ with position 28\n", + "call __getitem__ with position 29\n", + "call __getitem__ with position 30\n", + "call __getitem__ with position 31\n", + "call __getitem__ with position 32\n", + "call __getitem__ with position 33\n", + "call __getitem__ with position 34\n", + "call __getitem__ with position 35\n", + "call __getitem__ with position 36\n", + "call __getitem__ with position 37\n", + "call __getitem__ with position 38\n", + "call __getitem__ with position 39\n", + "call __getitem__ with position 40\n", + "call __getitem__ with position 41\n", + "call __getitem__ with position 42\n", + "call __getitem__ with position 43\n", + "call __getitem__ with position 44\n", + "call __getitem__ with position 45\n", + "call __getitem__ with position 46\n", + "call __getitem__ with position 47\n", + "call __getitem__ with position 48\n", + "call __getitem__ with position 49\n", + "call __getitem__ with position 50\n", + "call __getitem__ with position 51\n", + "call __getitem__ with position 52\n" + ] + }, { "data": { "text/plain": [ "False" ] }, - "execution_count": 12, + "execution_count": 13, "metadata": {}, "output_type": "execute_result" } @@ -380,13 +782,66 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": 14, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ + "call __getitem__ with position 0\n", + "call __getitem__ with position 1\n", + "call __getitem__ with position 2\n", + "call __getitem__ with position 3\n", + "call __getitem__ with position 4\n", + "call __getitem__ with position 5\n", + "call __getitem__ with position 6\n", + "call __getitem__ with position 7\n", + "call __getitem__ with position 8\n", + "call __getitem__ with position 9\n", + "call __getitem__ with position 10\n", + "call __getitem__ with position 11\n", + "call __getitem__ with position 12\n", + "call __getitem__ with position 13\n", + "call __getitem__ with position 14\n", + "call __getitem__ with position 15\n", + "call __getitem__ with position 16\n", + "call __getitem__ with position 17\n", + "call __getitem__ with position 18\n", + "call __getitem__ with position 19\n", + "call __getitem__ with position 20\n", + "call __getitem__ with position 21\n", + "call __getitem__ with position 22\n", + "call __getitem__ with position 23\n", + "call __getitem__ with position 24\n", + "call __getitem__ with position 25\n", + "call __getitem__ with position 26\n", + "call __getitem__ with position 27\n", + "call __getitem__ with position 28\n", + "call __getitem__ with position 29\n", + "call __getitem__ with position 30\n", + "call __getitem__ with position 31\n", + "call __getitem__ with position 32\n", + "call __getitem__ with position 33\n", + "call __getitem__ with position 34\n", + "call __getitem__ with position 35\n", + "call __getitem__ with position 36\n", + "call __getitem__ with position 37\n", + "call __getitem__ with position 38\n", + "call __getitem__ with position 39\n", + "call __getitem__ with position 40\n", + "call __getitem__ with position 41\n", + "call __getitem__ with position 42\n", + "call __getitem__ with position 43\n", + "call __getitem__ with position 44\n", + "call __getitem__ with position 45\n", + "call __getitem__ with position 46\n", + "call __getitem__ with position 47\n", + "call __getitem__ with position 48\n", + "call __getitem__ with position 49\n", + "call __getitem__ with position 50\n", + "call __getitem__ with position 51\n", + "call __getitem__ with position 52\n", "Card(rank='2', suit='clubs')\n", "Card(rank='2', suit='diamonds')\n", "Card(rank='2', suit='hearts')\n", @@ -453,6 +908,46 @@ " print(card)" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Although FrenchDeck implicitly inherits from the object class, most of its function‐\n", + "ality is not inherited, but comes from leveraging the data model and composition. By\n", + "implementing the special methods \\_\\_len\\_\\_ and \\_\\_getitem\\_\\_, our FrenchDeck\n", + "behaves like a standard Python sequence, allowing it to benefit from core language\n", + "features (e.g., iteration and slicing) and from the standard library, as shown by the\n", + "examples using random.choice, reversed, and sorted. Thanks to composition, the\n", + "\\_\\_len\\_\\_ and \\_\\_getitem\\_\\_ implementations can delegate all the work to a list\n", + "object, self._cards." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## How Special Methods are used\n", + "1. special methods is that they are meant to be called by\n", + "the Python interpreter, and not by you. You don’t write my_object.\\_\\_len\\_\\_(). You\n", + "write len(my_object) and, if my_object is an instance of a user-defined class, then\n", + "Python calls the \\_\\_len\\_\\_ method you implemented\n", + "2. But the interpreter takes a shortcut when dealing for built-in types like list, str,\n", + "bytearray, or extensions like the NumPy arrays. Python variable-sized collections\n", + "written in C include a struct2 called PyVarObject, which has an ob_size field holding\n", + "the number of items in the collection. So, if my_object is an instance of one of those\n", + "built-ins, then len(my_object) retrieves the value of the ob_size field, and this is\n", + "much faster than calling a method.\n", + "3. Normally, your code should not have many direct calls to special methods. Unless\n", + "you are doing a lot of metaprogramming, you should be implementing special meth‐\n", + "ods more often than invoking them explicitly. The only special method that is fre‐\n", + "quently called by user code directly is \\_\\_init\\_\\_ to invoke the initializer of the\n", + "superclass in your own \\_\\_init\\_\\_ implementation.\n", + "4. If you need to invoke a special method, it is usually better to call the related built-in\n", + "function (e.g., len, iter, str, etc.). These built-ins call the corresponding special\n", + "method, but often provide other services and—for built-in types—are faster than\n", + "method calls" + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -469,48 +964,69 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import math\n", "\n", "class Vector:\n", + " \"\"\"\n", + " two-dimensional vector\n", + " \"\"\"\n", "\n", " def __init__(self, x=0, y=0):\n", " self.x = x\n", " self.y = y\n", "\n", + " # delegate built-inn repr()\n", " def __repr__(self):\n", + " print(f\"call {inspect.currentframe().f_code.co_name}\")\n", " return f'Vector({self.x!r}, {self.y!r})'\n", "\n", + " # delegate built-in abs()\n", " def __abs__(self):\n", + " print(f\"call {inspect.currentframe().f_code.co_name}\")\n", " return math.hypot(self.x, self.y)\n", "\n", + " # delegate built-in bool()\n", " def __bool__(self):\n", + " print(f\"call {inspect.currentframe().f_code.co_name}\")\n", " return bool(abs(self))\n", "\n", + " # delegate operator +\n", " def __add__(self, other):\n", + " print(f\"call {inspect.currentframe().f_code.co_name}\")\n", " x = self.x + other.x\n", " y = self.y + other.y\n", " return Vector(x, y)\n", "\n", + " # delegate operator *\n", " def __mul__(self, scalar):\n", + " print(f\"call {inspect.currentframe().f_code.co_name}\")\n", " return Vector(self.x * scalar, self.y * scalar)" ] }, { "cell_type": "code", - "execution_count": 15, + "execution_count": 16, "metadata": {}, "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "call __add__\n", + "call __repr__\n" + ] + }, { "data": { "text/plain": [ "Vector(4, 5)" ] }, - "execution_count": 15, + "execution_count": 16, "metadata": {}, "output_type": "execute_result" } @@ -523,16 +1039,23 @@ }, { "cell_type": "code", - "execution_count": 16, + "execution_count": 17, "metadata": {}, "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "call __abs__\n" + ] + }, { "data": { "text/plain": [ "5.0" ] }, - "execution_count": 16, + "execution_count": 17, "metadata": {}, "output_type": "execute_result" } @@ -544,16 +1067,24 @@ }, { "cell_type": "code", - "execution_count": 17, + "execution_count": 18, "metadata": {}, "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "call __mul__\n", + "call __repr__\n" + ] + }, { "data": { "text/plain": [ "Vector(9, 12)" ] }, - "execution_count": 17, + "execution_count": 18, "metadata": {}, "output_type": "execute_result" } @@ -564,16 +1095,24 @@ }, { "cell_type": "code", - "execution_count": 18, + "execution_count": 19, "metadata": {}, "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "call __mul__\n", + "call __abs__\n" + ] + }, { "data": { "text/plain": [ "15.0" ] }, - "execution_count": 18, + "execution_count": 19, "metadata": {}, "output_type": "execute_result" } @@ -581,6 +1120,34 @@ "source": [ "abs(v * 3)" ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## String representation\n", + "- The interactive console and debugger call repr on the results of the expressions evaluated,\n", + " as does the %r placeholder in classic formatting with the % operator, and the !r \n", + " conversion field in the new format string syntax used in f-strings the str.format method\n", + "- In contrast, \\_\\_str\\_\\_ is called by the str() built-in and implicitly used by the print\n", + "function. It should return a string suitable for display to end users.\n", + "Sometimes same string returned by \\_\\_repr\\_\\_ is user-friendly, and you don’t need to\n", + "code \\_\\_str\\_\\_ because the implementation inherited from the object class calls\n", + "\\_\\_repr\\_\\_ as a fallback. " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Boolean value of a custom type\n", + "- bool(): To determine whether a value x is truthy or falsy, Python applies bool(x), which\n", + "returns either True or False\n", + "- user-defined classes: By default, instances of user-defined classes are considered truthy, unless either\n", + "**\\_\\_bool\\_\\_** or **\\_\\_len\\_\\_** is implemented. Basically, bool(x) calls x.\\_\\_bool\\_\\_() and uses\n", + "the result. If **\\_\\_bool\\_\\_** is not implemented, Python tries to invoke x.\\_\\_len\\_\\_(), and\n", + "if that returns zero, bool returns False. Otherwise bool returns True" + ] } ], "metadata": { @@ -599,7 +1166,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.0" + "version": "3.11.9" } }, "nbformat": 4, diff --git a/02-array-seq/array-seq.ipynb b/02-array-seq/array-seq.ipynb index afdbf6b..3c82a7d 100644 --- a/02-array-seq/array-seq.ipynb +++ b/02-array-seq/array-seq.ipynb @@ -23,6 +23,49 @@ "* [Soapbox](#Soapbox)" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Pythonic\n", + "- ABC introduced many ideas we now consider “Pythonic”: generic operations on different types\n", + "of sequences, built-in tuple and mapping types, structure by indentation, strong\n", + "typing without variable declarations, and more\n", + "- Python inherited from ABC the uniform handling of sequences. Strings, lists, byte\n", + "sequences, arrays, XML elements, and database results share a rich set of common\n", + "operations, including iteration, slicing, sorting, and concatenation\n", + "- Understanding the variety of sequences available in Python saves us from reinventing\n", + "the wheel, and their common interface inspires us to create APIs that properly sup‐\n", + "port and leverage existing and future sequence type" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Overview of built-in Sequences" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The standard library offers a rich selection of sequence types implemented in C:\n", + "- Container sequences: \n", + " - Can hold items of different types, including nested containers. Some examples: list, tuple, and collections.deque.\n", + " - A container sequence holds references to the objects it contains, which may be of any type, each item is a separate Python object, possibly holding references to other Python objects\n", + "- Flat sequences: \n", + " - Hold items of one simple type. Some examples: str, bytes, and array.array\n", + " - a flat sequence stores the value of its contents in its own memory space, not as distinct Python objects\n", + " - flat sequences are more compact, but they are limited to holding primitive machine values like bytes, integers, and floats.\n", + "\n", + "Another way of grouping sequence types is by mutability:\n", + "- Mutable sequences\n", + " - For example, list, bytearray, array.array, and collections.deque.\n", + "- Immutable sequences\n", + " - For example, tuple, str, and bytes" + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -44,7 +87,9 @@ "outputs": [ { "data": { - "text/plain": "[36, 162, 163, 165, 8364, 164]" + "text/plain": [ + "[36, 162, 163, 165, 8364, 164]" + ] }, "execution_count": 1, "metadata": {}, @@ -75,7 +120,9 @@ "outputs": [ { "data": { - "text/plain": "[36, 162, 163, 165, 8364, 164]" + "text/plain": [ + "[36, 162, 163, 165, 8364, 164]" + ] }, "execution_count": 2, "metadata": {}, @@ -99,14 +146,16 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 8, "metadata": {}, "outputs": [ { "data": { - "text/plain": "'ABC'" + "text/plain": [ + "'ABC'" + ] }, - "execution_count": 3, + "execution_count": 8, "metadata": {}, "output_type": "execute_result" } @@ -119,14 +168,16 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 9, "metadata": {}, "outputs": [ { "data": { - "text/plain": "[65, 66, 67]" + "text/plain": [ + "[65, 66, 67]" + ] }, - "execution_count": 4, + "execution_count": 9, "metadata": {}, "output_type": "execute_result" } @@ -135,29 +186,72 @@ "codes" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Local Scope Within Comprehensions and Generator Expressions\n", + "- In Python 3, list comprehensions, generator expressions, and their siblings set and\n", + "dict comprehensions, have a local scope to hold the variables assigned in the for\n", + "clause.\n", + "- However, variables assigned with the **“Walrus operator” :=** remain accessible after\n", + "those comprehensions or expressions return—unlike local variables in a function. The scope of the target of := as the enclosing function, unless there is a global or nonlocal declaration for that target." + ] + }, { "cell_type": "code", - "execution_count": 5, + "execution_count": null, + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + }, + "outputs": [ + { + "ename": "NameError", + "evalue": "name 'c' is not defined", + "output_type": "error", + "traceback": [ + "\u001b[31m---------------------------------------------------------------------------\u001b[39m", + "\u001b[31mNameError\u001b[39m Traceback (most recent call last)", + "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[11]\u001b[39m\u001b[32m, line 2\u001b[39m\n\u001b[32m 1\u001b[39m codes = [last := \u001b[38;5;28mord\u001b[39m(c) \u001b[38;5;28;01mfor\u001b[39;00m c \u001b[38;5;129;01min\u001b[39;00m x]\n\u001b[32m----> \u001b[39m\u001b[32m2\u001b[39m \u001b[43mc\u001b[49m\n", + "\u001b[31mNameError\u001b[39m: name 'c' is not defined" + ] + } + ], + "source": [ + "codes = [last := ord(c) for c in x]\n", + "c # c has local scope within listcomp" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, "outputs": [ { "data": { - "text/plain": "67" + "text/plain": [ + "67" + ] }, - "execution_count": 5, + "execution_count": 13, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "codes = [last := ord(c) for c in x]\n", - "last" - ], - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - } + "last # last is assigned by \"Walrus operator :=\", it has no local scope" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Listcomps Versus map and filter\n", + "- Listcomps do everything the map and filter functions do, without the contortions of the functionally challenged Python lambda" + ] }, { "cell_type": "markdown", @@ -168,14 +262,16 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 14, "metadata": {}, "outputs": [ { "data": { - "text/plain": "[162, 163, 165, 8364, 164]" + "text/plain": [ + "[162, 163, 165, 8364, 164]" + ] }, - "execution_count": 6, + "execution_count": 14, "metadata": {}, "output_type": "execute_result" } @@ -188,14 +284,16 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 15, "metadata": {}, "outputs": [ { "data": { - "text/plain": "[162, 163, 165, 8364, 164]" + "text/plain": [ + "[162, 163, 165, 8364, 164]" + ] }, - "execution_count": 7, + "execution_count": 15, "metadata": {}, "output_type": "execute_result" } @@ -214,14 +312,21 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 20, "metadata": {}, "outputs": [ { "data": { - "text/plain": "[('black', 'S'),\n ('black', 'M'),\n ('black', 'L'),\n ('white', 'S'),\n ('white', 'M'),\n ('white', 'L')]" + "text/plain": [ + "[('black', 'S'),\n", + " ('black', 'M'),\n", + " ('black', 'L'),\n", + " ('white', 'S'),\n", + " ('white', 'M'),\n", + " ('white', 'L')]" + ] }, - "execution_count": 8, + "execution_count": 20, "metadata": {}, "output_type": "execute_result" } @@ -235,7 +340,7 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 19, "metadata": {}, "outputs": [ { @@ -259,14 +364,21 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 18, "metadata": {}, "outputs": [ { "data": { - "text/plain": "[('black', 'S'),\n ('black', 'M'),\n ('black', 'L'),\n ('white', 'S'),\n ('white', 'M'),\n ('white', 'L')]" + "text/plain": [ + "[('black', 'S'),\n", + " ('black', 'M'),\n", + " ('black', 'L'),\n", + " ('white', 'S'),\n", + " ('white', 'M'),\n", + " ('white', 'L')]" + ] }, - "execution_count": 10, + "execution_count": 18, "metadata": {}, "output_type": "execute_result" } @@ -277,6 +389,19 @@ "tshirts" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Generator Expressions\n", + "- To initialize tuples, arrays, and other types of sequences, you could also start from a\n", + "listcomp, but a genexp (generator expression) saves memory because it yields items\n", + "one by one using the iterator protocol instead of building a whole list just to feed\n", + "another constructor.\n", + "- Genexps use the same syntax as listcomps, but are enclosed in parentheses rather\n", + "than brackets" + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -291,7 +416,9 @@ "outputs": [ { "data": { - "text/plain": "(36, 162, 163, 165, 8364, 164)" + "text/plain": [ + "(36, 162, 163, 165, 8364, 164)" + ] }, "execution_count": 11, "metadata": {}, @@ -310,7 +437,9 @@ "outputs": [ { "data": { - "text/plain": "array('I', [36, 162, 163, 165, 8364, 164])" + "text/plain": [ + "array('I', [36, 162, 163, 165, 8364, 164])" + ] }, "execution_count": 12, "metadata": {}, @@ -358,32 +487,53 @@ }, { "cell_type": "markdown", - "source": [ - "## Tuples Are Not Just Immutable Lists" - ], "metadata": { "collapsed": false, "pycharm": { "name": "#%% md\n" } }, - "execution_count": 73 + "source": [ + "## Tuples Are Not Just Immutable Lists" + ] }, { "cell_type": "markdown", + "metadata": {}, "source": [ - "#### Example 2-7. Tuples used as records" - ], + "tuple has double duty:\n", + "- as immutable lists\n", + "- as records with no field names" + ] + }, + { + "cell_type": "markdown", "metadata": { "collapsed": false, "pycharm": { "name": "#%% md\n" } - } + }, + "source": [ + "#### Example 2-7. Tuples used as records" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "When using a tuple as a collection of fields, the number of items is usually fixed and their order is always important" + ] }, { "cell_type": "code", - "execution_count": 14, + "execution_count": null, + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + }, "outputs": [ { "name": "stdout", @@ -397,22 +547,23 @@ ], "source": [ "lax_coordinates = (33.9425, -118.408056)\n", - "city, year, pop, chg, area = ('Tokyo', 2003, 32_450, 0.66, 8014)\n", + "city, year, pop, chg, area = ('Tokyo', 2003, 32_450, 0.66, 8014) # tuple unpacking\n", "traveler_ids = [('USA', '31195855'), ('BRA', 'CE342567'), ('ESP', 'XDA205856')]\n", "\n", - "for passport in sorted(traveler_ids):\n", - " print('%s/%s' % passport)" - ], + "for passport in sorted(traveler_ids): # passport is bound to each tuple.\n", + " # the % operator assigned each item in the passport tuple to the corresponding slot in the format string is also tuple unpacking\n", + " print('%s/%s' % passport) # The % formatting operator understands tuples and treats each item as a separate field" + ] + }, + { + "cell_type": "code", + "execution_count": null, "metadata": { "collapsed": false, "pycharm": { "name": "#%%\n" } - } - }, - { - "cell_type": "code", - "execution_count": 15, + }, "outputs": [ { "name": "stdout", @@ -425,37 +576,56 @@ } ], "source": [ + "# The for loop knows how to retrieve the items of a tuple separately—this is called “tuple unpacking.”\n", "for country, _ in traveler_ids:\n", " print(country)" - ], + ] + }, + { + "cell_type": "markdown", "metadata": { "collapsed": false, "pycharm": { - "name": "#%%\n" + "name": "#%% md\n" } - } + }, + "source": [ + "### Tuples as Immutable Lists" + ] }, { "cell_type": "markdown", + "metadata": {}, "source": [ - "### Tuples as Immutable Lists" - ], + "The Python interpreter and standard library make extensive use of tuples as immutable lists, and so should you. This brings two key benefits:\n", + "- Clarity:\n", + " - When you see a tuple in code, you know its length will never change\n", + "- Performance\n", + " - A tuple uses less memory than a list of the same length, and it allows Python to do some optimizations\n", + "\n", + "References immutable\n", + "- However, be aware that the immutability of a tuple only applies to the references\n", + "contained in it. References in a tuple cannot be deleted or replaced. But if one of\n", + "those references points to a mutable object, and that object is changed, then the value of the tuple changes" + ] + }, + { + "cell_type": "code", + "execution_count": 23, "metadata": { "collapsed": false, "pycharm": { - "name": "#%% md\n" + "name": "#%%\n" } - } - }, - { - "cell_type": "code", - "execution_count": 16, + }, "outputs": [ { "data": { - "text/plain": "True" + "text/plain": [ + "True" + ] }, - "execution_count": 16, + "execution_count": 23, "metadata": {}, "output_type": "execute_result" } @@ -464,45 +634,49 @@ "a = (10, 'alpha', [1, 2])\n", "b = (10, 'alpha', [1, 2])\n", "a == b" - ], + ] + }, + { + "cell_type": "code", + "execution_count": null, "metadata": { "collapsed": false, "pycharm": { "name": "#%%\n" } - } - }, - { - "cell_type": "code", - "execution_count": 17, + }, "outputs": [ { "data": { - "text/plain": "False" + "text/plain": [ + "False" + ] }, - "execution_count": 17, + "execution_count": 25, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "b[-1].append(99)\n", + "b[-1].append(99) # [1,2] is list object , can be modified\n", "a == b" - ], + ] + }, + { + "cell_type": "code", + "execution_count": 18, "metadata": { "collapsed": false, "pycharm": { "name": "#%%\n" } - } - }, - { - "cell_type": "code", - "execution_count": 18, + }, "outputs": [ { "data": { - "text/plain": "(10, 'alpha', [1, 2, 99])" + "text/plain": [ + "(10, 'alpha', [1, 2, 99])" + ] }, "execution_count": 18, "metadata": {}, @@ -511,28 +685,41 @@ ], "source": [ "b" - ], + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### What is Hashable\n", + "- Tuples with mutable items can be a source of bugs.\n", + "- An object is only hashable if its value cannot ever change. An unhashable tuple cannot be inserted as a dict key, or a set element" + ] + }, + { + "cell_type": "code", + "execution_count": 28, "metadata": { "collapsed": false, "pycharm": { "name": "#%%\n" } - } - }, - { - "cell_type": "code", - "execution_count": 19, + }, "outputs": [ { "data": { - "text/plain": "True" + "text/plain": [ + "True" + ] }, - "execution_count": 19, + "execution_count": 28, "metadata": {}, "output_type": "execute_result" } ], "source": [ + "# If you want to determine explicitly if a tuple (or any object) has a fixed value, you can\n", + "# use the hash built-in to create a fixed function like this:\n", "def fixed(o):\n", " try:\n", " hash(o)\n", @@ -544,81 +731,99 @@ "tf = (10, 'alpha', (1, 2)) # Contains no mutable items\n", "tm = (10, 'alpha', [1, 2]) # Contains a mutable item (list)\n", "fixed(tf)" - ], + ] + }, + { + "cell_type": "code", + "execution_count": 27, "metadata": { "collapsed": false, "pycharm": { "name": "#%%\n" } - } - }, - { - "cell_type": "code", - "execution_count": 20, + }, "outputs": [ { "data": { - "text/plain": "False" + "text/plain": [ + "False" + ] }, - "execution_count": 20, + "execution_count": 27, "metadata": {}, "output_type": "execute_result" } ], "source": [ "fixed(tm)" - ], + ] + }, + { + "cell_type": "markdown", "metadata": { "collapsed": false, "pycharm": { - "name": "#%%\n" + "name": "#%% md\n" } - } + }, + "source": [ + "## Unpacking sequences and iterables" + ] }, { "cell_type": "markdown", + "metadata": {}, "source": [ - "## Unpacking sequences and iterables" - ], + "- Unpacking is important because it avoids unnecessary and error-prone use of\n", + "indexes to extract elements from sequences. \n", + "- Also, unpacking works with any iterable object as the data source ——— including iterators, which don’t support index notation\n", + "([])." + ] + }, + { + "cell_type": "code", + "execution_count": null, "metadata": { "collapsed": false, "pycharm": { - "name": "#%% md\n" + "name": "#%%\n" } - } - }, - { - "cell_type": "code", - "execution_count": 21, + }, "outputs": [ { "data": { - "text/plain": "33.9425" + "text/plain": [ + "33.9425" + ] }, - "execution_count": 21, + "execution_count": 29, "metadata": {}, "output_type": "execute_result" } ], "source": [ + "# the most visible form of unpacking is parallel assignment; that is, assigning items\n", + "# from an iterable to a tuple of variables, as you can see in this example\n", "lax_coordinates = (33.9425, -118.408056)\n", "latitude, longitude = lax_coordinates # unpacking\n", "latitude" - ], + ] + }, + { + "cell_type": "code", + "execution_count": 22, "metadata": { "collapsed": false, "pycharm": { "name": "#%%\n" } - } - }, - { - "cell_type": "code", - "execution_count": 22, + }, "outputs": [ { "data": { - "text/plain": "-118.408056" + "text/plain": [ + "-118.408056" + ] }, "execution_count": 22, "metadata": {}, @@ -627,21 +832,23 @@ ], "source": [ "longitude" - ], + ] + }, + { + "cell_type": "code", + "execution_count": 23, "metadata": { "collapsed": false, "pycharm": { "name": "#%%\n" } - } - }, - { - "cell_type": "code", - "execution_count": 23, + }, "outputs": [ { "data": { - "text/plain": "(2, 4)" + "text/plain": [ + "(2, 4)" + ] }, "execution_count": 23, "metadata": {}, @@ -650,21 +857,23 @@ ], "source": [ "divmod(20, 8)" - ], + ] + }, + { + "cell_type": "code", + "execution_count": 24, "metadata": { "collapsed": false, "pycharm": { "name": "#%%\n" } - } - }, - { - "cell_type": "code", - "execution_count": 24, + }, "outputs": [ { "data": { - "text/plain": "(2, 4)" + "text/plain": [ + "(2, 4)" + ] }, "execution_count": 24, "metadata": {}, @@ -674,21 +883,23 @@ "source": [ "t = (20, 8)\n", "divmod(*t)" - ], + ] + }, + { + "cell_type": "code", + "execution_count": 25, "metadata": { "collapsed": false, "pycharm": { "name": "#%%\n" } - } - }, - { - "cell_type": "code", - "execution_count": 25, + }, "outputs": [ { "data": { - "text/plain": "(2, 4)" + "text/plain": [ + "(2, 4)" + ] }, "execution_count": 25, "metadata": {}, @@ -698,21 +909,23 @@ "source": [ "quotient, remainder = divmod(*t)\n", "quotient, remainder" - ], + ] + }, + { + "cell_type": "code", + "execution_count": 26, "metadata": { "collapsed": false, "pycharm": { "name": "#%%\n" } - } - }, - { - "cell_type": "code", - "execution_count": 26, + }, "outputs": [ { "data": { - "text/plain": "'id_rsa.pub'" + "text/plain": [ + "'id_rsa.pub'" + ] }, "execution_count": 26, "metadata": {}, @@ -724,33 +937,35 @@ "\n", "_, filename = os.path.split('/home/luciano/.ssh/id_rsa.pub')\n", "filename" - ], - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - } + ] }, { "cell_type": "markdown", - "source": [ - "### Using * to grab excess items" - ], "metadata": { "collapsed": false, "pycharm": { "name": "#%% md\n" } - } + }, + "source": [ + "### Using * to grab excess items" + ] }, { "cell_type": "code", "execution_count": 27, + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + }, "outputs": [ { "data": { - "text/plain": "(0, 1, [2, 3, 4])" + "text/plain": [ + "(0, 1, [2, 3, 4])" + ] }, "execution_count": 27, "metadata": {}, @@ -760,21 +975,23 @@ "source": [ "a, b, *rest = range(5)\n", "a, b, rest" - ], + ] + }, + { + "cell_type": "code", + "execution_count": 28, "metadata": { "collapsed": false, "pycharm": { "name": "#%%\n" } - } - }, - { - "cell_type": "code", - "execution_count": 28, + }, "outputs": [ { "data": { - "text/plain": "(0, 1, [2])" + "text/plain": [ + "(0, 1, [2])" + ] }, "execution_count": 28, "metadata": {}, @@ -784,21 +1001,23 @@ "source": [ "a, b, *rest = range(3)\n", "a, b, rest" - ], + ] + }, + { + "cell_type": "code", + "execution_count": 29, "metadata": { "collapsed": false, "pycharm": { "name": "#%%\n" } - } - }, - { - "cell_type": "code", - "execution_count": 29, + }, "outputs": [ { "data": { - "text/plain": "(0, 1, [])" + "text/plain": [ + "(0, 1, [])" + ] }, "execution_count": 29, "metadata": {}, @@ -808,21 +1027,23 @@ "source": [ "a, b, *rest = range(2)\n", "a, b, rest" - ], + ] + }, + { + "cell_type": "code", + "execution_count": 30, "metadata": { "collapsed": false, "pycharm": { "name": "#%%\n" } - } - }, - { - "cell_type": "code", - "execution_count": 30, + }, "outputs": [ { "data": { - "text/plain": "(0, [1, 2], 3, 4)" + "text/plain": [ + "(0, [1, 2], 3, 4)" + ] }, "execution_count": 30, "metadata": {}, @@ -832,21 +1053,23 @@ "source": [ "a, *body, c, d = range(5)\n", "a, body, c, d" - ], + ] + }, + { + "cell_type": "code", + "execution_count": 31, "metadata": { "collapsed": false, "pycharm": { "name": "#%%\n" } - } - }, - { - "cell_type": "code", - "execution_count": 31, + }, "outputs": [ { "data": { - "text/plain": "([0, 1], 2, 3, 4)" + "text/plain": [ + "([0, 1], 2, 3, 4)" + ] }, "execution_count": 31, "metadata": {}, @@ -856,33 +1079,35 @@ "source": [ "*head, b, c, d = range(5)\n", "head, b, c, d" - ], - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - } + ] }, { "cell_type": "markdown", - "source": [ - "### Unpacking with * in function calls and sequence literals" - ], "metadata": { "collapsed": false, "pycharm": { "name": "#%% md\n" } - } + }, + "source": [ + "### Unpacking with * in function calls and sequence literals" + ] }, { "cell_type": "code", "execution_count": 32, + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + }, "outputs": [ { "data": { - "text/plain": "(1, 2, 3, 4, (5, 6))" + "text/plain": [ + "(1, 2, 3, 4, (5, 6))" + ] }, "execution_count": 32, "metadata": {}, @@ -895,21 +1120,23 @@ "\n", "\n", "fun(*[1, 2], 3, *range(4, 7))" - ], + ] + }, + { + "cell_type": "code", + "execution_count": 33, "metadata": { "collapsed": false, "pycharm": { "name": "#%%\n" } - } - }, - { - "cell_type": "code", - "execution_count": 33, + }, "outputs": [ { "data": { - "text/plain": "(0, 1, 2, 3, 4)" + "text/plain": [ + "(0, 1, 2, 3, 4)" + ] }, "execution_count": 33, "metadata": {}, @@ -918,21 +1145,23 @@ ], "source": [ "*range(4), 4" - ], + ] + }, + { + "cell_type": "code", + "execution_count": 34, "metadata": { "collapsed": false, "pycharm": { "name": "#%%\n" } - } - }, - { - "cell_type": "code", - "execution_count": 34, + }, "outputs": [ { "data": { - "text/plain": "[0, 1, 2, 3, 4]" + "text/plain": [ + "[0, 1, 2, 3, 4]" + ] }, "execution_count": 34, "metadata": {}, @@ -941,21 +1170,23 @@ ], "source": [ "[*range(4), 4]" - ], + ] + }, + { + "cell_type": "code", + "execution_count": 35, "metadata": { "collapsed": false, "pycharm": { "name": "#%%\n" } - } - }, - { - "cell_type": "code", - "execution_count": 35, + }, "outputs": [ { "data": { - "text/plain": "{0, 1, 2, 3, 4, 5, 6, 7}" + "text/plain": [ + "{0, 1, 2, 3, 4, 5, 6, 7}" + ] }, "execution_count": 35, "metadata": {}, @@ -964,45 +1195,45 @@ ], "source": [ "{*range(4), 4, *(5, 6, 7)}" - ], + ] + }, + { + "cell_type": "markdown", "metadata": { "collapsed": false, "pycharm": { - "name": "#%%\n" + "name": "#%% md\n" } - } - }, - { - "cell_type": "markdown", + }, "source": [ "### Nested unpacking\n", "#### Example 2-8. Unpacking nested tuples to access the longitude\n", "\n", "[02-array-seq/metro_lat_lon.py](02-array-seq/metro_lat_lon.py)" - ], - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%% md\n" - } - } + ] }, { "cell_type": "markdown", - "source": [ - "## Pattern Matching with Sequences\n", - "#### Example 2-9. Method from an imaginary Robot class" - ], "metadata": { "collapsed": false, "pycharm": { "name": "#%% md\n" } - } + }, + "source": [ + "## Pattern Matching with Sequences\n", + "#### Example 2-9. Method from an imaginary Robot class" + ] }, { "cell_type": "code", "execution_count": 36, + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + }, "outputs": [], "source": [ "# def handle_command(self, message):\n", @@ -1017,30 +1248,30 @@ "# self.leds[ident].set_color(ident, red, green, blue)\n", "# case _:\n", "# raise InvalidCommand(message)" - ], - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - } + ] }, { "cell_type": "markdown", - "source": [ - "#### Example 2-10. Destructuring nested tuples—requires Python ≥ 3.10.\n", - "[02-array-seq/match_lat_lon.py](02-array-seq/match_lat_lon.py)" - ], "metadata": { "collapsed": false, "pycharm": { "name": "#%% md\n" } - } + }, + "source": [ + "#### Example 2-10. Destructuring nested tuples—requires Python ≥ 3.10.\n", + "[02-array-seq/match_lat_lon.py](02-array-seq/match_lat_lon.py)" + ] }, { "cell_type": "code", "execution_count": 37, + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + }, "outputs": [ { "name": "stdout", @@ -1069,57 +1300,51 @@ " case [name, _, _, (lat, lon)] if lon <= 0:\n", " print(f'{name:15} | {lat:9.4f} | {lon:9.4f}')\n", "main()" - ], - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - } + ] }, { "cell_type": "markdown", - "source": [], "metadata": { "collapsed": false, "pycharm": { "name": "#%% md\n" } - } + }, + "source": [] }, { "cell_type": "markdown", - "source": [ - "### Pattern Matching Sequences in an Interpreter\n", - "#### Example 2-11. Matching patterns without match/case.\n", - "[02-array-seq/lispy/py3.9/lis.py](02-array-seq/lispy/py3.9/lis.py)" - ], "metadata": { "collapsed": false, "pycharm": { "name": "#%% md\n" } - } + }, + "source": [ + "### Pattern Matching Sequences in an Interpreter\n", + "#### Example 2-11. Matching patterns without match/case.\n", + "[02-array-seq/lispy/py3.9/lis.py](02-array-seq/lispy/py3.9/lis.py)" + ] }, { "cell_type": "markdown", - "source": [ - "#### Example 2-12. Pattern matching with match/case—requires Python ≥ 3.10.\n", - "[02-array-seq/lispy/py3.10/lis.py](02-array-seq/lispy/py3.10/lis.py)" - ], "metadata": { "collapsed": false, "pycharm": { "name": "#%% md\n" } - } + }, + "source": [ + "#### Example 2-12. Pattern matching with match/case—requires Python ≥ 3.10.\n", + "[02-array-seq/lispy/py3.10/lis.py](02-array-seq/lispy/py3.10/lis.py)" + ] }, { "cell_type": "markdown", - "source": [], "metadata": { "collapsed": false - } + }, + "source": [] }, { "cell_type": "markdown", @@ -1142,7 +1367,9 @@ "outputs": [ { "data": { - "text/plain": "[10, 20]" + "text/plain": [ + "[10, 20]" + ] }, "execution_count": 38, "metadata": {}, @@ -1162,7 +1389,9 @@ "outputs": [ { "data": { - "text/plain": "[30, 40, 50, 60]" + "text/plain": [ + "[30, 40, 50, 60]" + ] }, "execution_count": 39, "metadata": {}, @@ -1180,7 +1409,9 @@ "outputs": [ { "data": { - "text/plain": "[10, 20, 30]" + "text/plain": [ + "[10, 20, 30]" + ] }, "execution_count": 40, "metadata": {}, @@ -1198,7 +1429,9 @@ "outputs": [ { "data": { - "text/plain": "[40, 50, 60]" + "text/plain": [ + "[40, 50, 60]" + ] }, "execution_count": 41, "metadata": {}, @@ -1223,7 +1456,9 @@ "outputs": [ { "data": { - "text/plain": "'bye'" + "text/plain": [ + "'bye'" + ] }, "execution_count": 42, "metadata": {}, @@ -1242,7 +1477,9 @@ "outputs": [ { "data": { - "text/plain": "'elcycib'" + "text/plain": [ + "'elcycib'" + ] }, "execution_count": 43, "metadata": {}, @@ -1260,7 +1497,9 @@ "outputs": [ { "data": { - "text/plain": "'eccb'" + "text/plain": [ + "'eccb'" + ] }, "execution_count": 44, "metadata": {}, @@ -1330,7 +1569,9 @@ "outputs": [ { "data": { - "text/plain": "[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]" + "text/plain": [ + "[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]" + ] }, "execution_count": 46, "metadata": {}, @@ -1349,7 +1590,9 @@ "outputs": [ { "data": { - "text/plain": "[0, 1, 20, 30, 5, 6, 7, 8, 9]" + "text/plain": [ + "[0, 1, 20, 30, 5, 6, 7, 8, 9]" + ] }, "execution_count": 47, "metadata": {}, @@ -1368,7 +1611,9 @@ "outputs": [ { "data": { - "text/plain": "[0, 1, 20, 30, 5, 8, 9]" + "text/plain": [ + "[0, 1, 20, 30, 5, 8, 9]" + ] }, "execution_count": 48, "metadata": {}, @@ -1387,7 +1632,9 @@ "outputs": [ { "data": { - "text/plain": "[0, 1, 20, 11, 5, 22, 9]" + "text/plain": [ + "[0, 1, 20, 11, 5, 22, 9]" + ] }, "execution_count": 49, "metadata": {}, @@ -1433,7 +1680,9 @@ "outputs": [ { "data": { - "text/plain": "[0, 1, 100, 22, 9]" + "text/plain": [ + "[0, 1, 100, 22, 9]" + ] }, "execution_count": 51, "metadata": {}, @@ -1459,7 +1708,9 @@ "outputs": [ { "data": { - "text/plain": "[1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3]" + "text/plain": [ + "[1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3]" + ] }, "execution_count": 52, "metadata": {}, @@ -1478,7 +1729,9 @@ "outputs": [ { "data": { - "text/plain": "'abcdabcdabcdabcdabcd'" + "text/plain": [ + "'abcdabcdabcdabcdabcd'" + ] }, "execution_count": 53, "metadata": {}, @@ -1510,7 +1763,9 @@ "outputs": [ { "data": { - "text/plain": "[['_', '_', '_'], ['_', '_', '_'], ['_', '_', '_']]" + "text/plain": [ + "[['_', '_', '_'], ['_', '_', '_'], ['_', '_', '_']]" + ] }, "execution_count": 54, "metadata": {}, @@ -1529,7 +1784,9 @@ "outputs": [ { "data": { - "text/plain": "[['_', '_', '_'], ['_', '_', 'X'], ['_', '_', '_']]" + "text/plain": [ + "[['_', '_', '_'], ['_', '_', 'X'], ['_', '_', '_']]" + ] }, "execution_count": 55, "metadata": {}, @@ -1555,7 +1812,9 @@ "outputs": [ { "data": { - "text/plain": "[['_', '_', '_'], ['_', '_', '_'], ['_', '_', '_']]" + "text/plain": [ + "[['_', '_', '_'], ['_', '_', '_'], ['_', '_', '_']]" + ] }, "execution_count": 56, "metadata": {}, @@ -1574,7 +1833,9 @@ "outputs": [ { "data": { - "text/plain": "[['_', '_', 'O'], ['_', '_', 'O'], ['_', '_', 'O']]" + "text/plain": [ + "[['_', '_', 'O'], ['_', '_', 'O'], ['_', '_', 'O']]" + ] }, "execution_count": 57, "metadata": {}, @@ -1600,7 +1861,9 @@ "outputs": [ { "data": { - "text/plain": "[['_', '_', '_'], ['_', '_', '_'], ['_', '_', '_']]" + "text/plain": [ + "[['_', '_', '_'], ['_', '_', '_'], ['_', '_', '_']]" + ] }, "execution_count": 58, "metadata": {}, @@ -1622,7 +1885,9 @@ "outputs": [ { "data": { - "text/plain": "[['_', '_', '_'], ['_', '_', '_'], ['X', '_', '_']]" + "text/plain": [ + "[['_', '_', '_'], ['_', '_', '_'], ['X', '_', '_']]" + ] }, "execution_count": 59, "metadata": {}, @@ -1658,7 +1923,9 @@ "outputs": [ { "data": { - "text/plain": "140694277263808" + "text/plain": [ + "140694277263808" + ] }, "execution_count": 61, "metadata": {}, @@ -1677,7 +1944,9 @@ "outputs": [ { "data": { - "text/plain": "[1, 2, 3, 1, 2, 3]" + "text/plain": [ + "[1, 2, 3, 1, 2, 3]" + ] }, "execution_count": 62, "metadata": {}, @@ -1696,7 +1965,9 @@ "outputs": [ { "data": { - "text/plain": "True" + "text/plain": [ + "True" + ] }, "execution_count": 63, "metadata": {}, @@ -1724,7 +1995,9 @@ "outputs": [ { "data": { - "text/plain": "140694329335488" + "text/plain": [ + "140694329335488" + ] }, "execution_count": 65, "metadata": {}, @@ -1743,7 +2016,9 @@ "outputs": [ { "data": { - "text/plain": "False" + "text/plain": [ + "False" + ] }, "execution_count": 66, "metadata": {}, @@ -1786,15 +2061,15 @@ }, { "cell_type": "markdown", - "source": [ - "#### Example 2-17. The unexpected result: item t2 is changed and an exception is raised" - ], "metadata": { "collapsed": false, "pycharm": { "name": "#%% md\n" } - } + }, + "source": [ + "#### Example 2-17. The unexpected result: item t2 is changed and an exception is raised" + ] }, { "cell_type": "code", @@ -1803,7 +2078,9 @@ "outputs": [ { "data": { - "text/plain": "(1, 2, [30, 40, 50, 60])" + "text/plain": [ + "(1, 2, [30, 40, 50, 60])" + ] }, "execution_count": 68, "metadata": {}, @@ -1863,7 +2140,9 @@ "outputs": [ { "data": { - "text/plain": "['apple', 'banana', 'grape', 'raspberry']" + "text/plain": [ + "['apple', 'banana', 'grape', 'raspberry']" + ] }, "execution_count": 70, "metadata": {}, @@ -1882,7 +2161,9 @@ "outputs": [ { "data": { - "text/plain": "['grape', 'raspberry', 'apple', 'banana']" + "text/plain": [ + "['grape', 'raspberry', 'apple', 'banana']" + ] }, "execution_count": 71, "metadata": {}, @@ -1900,7 +2181,9 @@ "outputs": [ { "data": { - "text/plain": "['raspberry', 'grape', 'banana', 'apple']" + "text/plain": [ + "['raspberry', 'grape', 'banana', 'apple']" + ] }, "execution_count": 72, "metadata": {}, @@ -1918,7 +2201,9 @@ "outputs": [ { "data": { - "text/plain": "['grape', 'apple', 'banana', 'raspberry']" + "text/plain": [ + "['grape', 'apple', 'banana', 'raspberry']" + ] }, "execution_count": 73, "metadata": {}, @@ -1936,7 +2221,9 @@ "outputs": [ { "data": { - "text/plain": "['raspberry', 'banana', 'grape', 'apple']" + "text/plain": [ + "['raspberry', 'banana', 'grape', 'apple']" + ] }, "execution_count": 74, "metadata": {}, @@ -1954,7 +2241,9 @@ "outputs": [ { "data": { - "text/plain": "['grape', 'raspberry', 'apple', 'banana']" + "text/plain": [ + "['grape', 'raspberry', 'apple', 'banana']" + ] }, "execution_count": 75, "metadata": {}, @@ -1972,7 +2261,9 @@ "outputs": [ { "data": { - "text/plain": "['apple', 'banana', 'grape', 'raspberry']" + "text/plain": [ + "['apple', 'banana', 'grape', 'raspberry']" + ] }, "execution_count": 76, "metadata": {}, @@ -2012,7 +2303,9 @@ "outputs": [ { "data": { - "text/plain": "0.8190492979077034" + "text/plain": [ + "0.8190492979077034" + ] }, "execution_count": 77, "metadata": {}, @@ -2045,7 +2338,9 @@ "outputs": [ { "data": { - "text/plain": "0.8190492979077034" + "text/plain": [ + "0.8190492979077034" + ] }, "execution_count": 79, "metadata": {}, @@ -2068,7 +2363,9 @@ "outputs": [ { "data": { - "text/plain": "True" + "text/plain": [ + "True" + ] }, "execution_count": 80, "metadata": {}, @@ -2088,23 +2385,31 @@ }, { "cell_type": "markdown", - "source": [ - "#### Example 2-20. Handling 6 bytes memory of as 1×6, 2×3, and 3×2 views" - ], "metadata": { "collapsed": false, "pycharm": { "name": "#%% md\n" } - } + }, + "source": [ + "#### Example 2-20. Handling 6 bytes memory of as 1×6, 2×3, and 3×2 views" + ] }, { "cell_type": "code", "execution_count": 81, + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + }, "outputs": [ { "data": { - "text/plain": "[0, 1, 2, 3, 4, 5]" + "text/plain": [ + "[0, 1, 2, 3, 4, 5]" + ] }, "execution_count": 81, "metadata": {}, @@ -2115,21 +2420,23 @@ "octets = array('B', range(6))\n", "m1 = memoryview(octets)\n", "m1.tolist()" - ], + ] + }, + { + "cell_type": "code", + "execution_count": 82, "metadata": { "collapsed": false, "pycharm": { "name": "#%%\n" } - } - }, - { - "cell_type": "code", - "execution_count": 82, + }, "outputs": [ { "data": { - "text/plain": "[[0, 1, 2], [3, 4, 5]]" + "text/plain": [ + "[[0, 1, 2], [3, 4, 5]]" + ] }, "execution_count": 82, "metadata": {}, @@ -2139,21 +2446,23 @@ "source": [ "m2 = m1.cast('B', [2, 3])\n", "m2.tolist()" - ], + ] + }, + { + "cell_type": "code", + "execution_count": 83, "metadata": { "collapsed": false, "pycharm": { "name": "#%%\n" } - } - }, - { - "cell_type": "code", - "execution_count": 83, + }, "outputs": [ { "data": { - "text/plain": "[[0, 1], [2, 3], [4, 5]]" + "text/plain": [ + "[[0, 1], [2, 3], [4, 5]]" + ] }, "execution_count": 83, "metadata": {}, @@ -2163,21 +2472,23 @@ "source": [ "m3 = m1.cast('B', [3, 2])\n", "m3.tolist()" - ], + ] + }, + { + "cell_type": "code", + "execution_count": 84, "metadata": { "collapsed": false, "pycharm": { "name": "#%%\n" } - } - }, - { - "cell_type": "code", - "execution_count": 84, + }, "outputs": [ { "data": { - "text/plain": "array('B', [0, 1, 2, 33, 22, 5])" + "text/plain": [ + "array('B', [0, 1, 2, 33, 22, 5])" + ] }, "execution_count": 84, "metadata": {}, @@ -2188,13 +2499,7 @@ "m2[1,1] = 22\n", "m3[1,1] = 33\n", "octets" - ], - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - } + ] }, { "cell_type": "markdown", @@ -2210,7 +2515,9 @@ "outputs": [ { "data": { - "text/plain": "5" + "text/plain": [ + "5" + ] }, "execution_count": 85, "metadata": {}, @@ -2230,7 +2537,9 @@ "outputs": [ { "data": { - "text/plain": "-2" + "text/plain": [ + "-2" + ] }, "execution_count": 86, "metadata": {}, @@ -2248,7 +2557,9 @@ "outputs": [ { "data": { - "text/plain": "[254, 255, 255, 255, 0, 0, 1, 0, 2, 0]" + "text/plain": [ + "[254, 255, 255, 255, 0, 0, 1, 0, 2, 0]" + ] }, "execution_count": 87, "metadata": {}, @@ -2267,7 +2578,9 @@ "outputs": [ { "data": { - "text/plain": "array('h', [-2, -1, 1024, 1, 2])" + "text/plain": [ + "array('h', [-2, -1, 1024, 1, 2])" + ] }, "execution_count": 88, "metadata": {}, @@ -2300,7 +2613,9 @@ "outputs": [ { "data": { - "text/plain": "array([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11])" + "text/plain": [ + "array([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11])" + ] }, "execution_count": 89, "metadata": {}, @@ -2321,7 +2636,9 @@ "outputs": [ { "data": { - "text/plain": "numpy.ndarray" + "text/plain": [ + "numpy.ndarray" + ] }, "execution_count": 90, "metadata": {}, @@ -2339,7 +2656,9 @@ "outputs": [ { "data": { - "text/plain": "(12,)" + "text/plain": [ + "(12,)" + ] }, "execution_count": 91, "metadata": {}, @@ -2357,7 +2676,11 @@ "outputs": [ { "data": { - "text/plain": "array([[ 0, 1, 2, 3],\n [ 4, 5, 6, 7],\n [ 8, 9, 10, 11]])" + "text/plain": [ + "array([[ 0, 1, 2, 3],\n", + " [ 4, 5, 6, 7],\n", + " [ 8, 9, 10, 11]])" + ] }, "execution_count": 92, "metadata": {}, @@ -2376,7 +2699,9 @@ "outputs": [ { "data": { - "text/plain": "array([ 8, 9, 10, 11])" + "text/plain": [ + "array([ 8, 9, 10, 11])" + ] }, "execution_count": 93, "metadata": {}, @@ -2394,7 +2719,9 @@ "outputs": [ { "data": { - "text/plain": "9" + "text/plain": [ + "9" + ] }, "execution_count": 94, "metadata": {}, @@ -2412,7 +2739,9 @@ "outputs": [ { "data": { - "text/plain": "array([1, 5, 9])" + "text/plain": [ + "array([1, 5, 9])" + ] }, "execution_count": 95, "metadata": {}, @@ -2430,7 +2759,12 @@ "outputs": [ { "data": { - "text/plain": "array([[ 0, 4, 8],\n [ 1, 5, 9],\n [ 2, 6, 10],\n [ 3, 7, 11]])" + "text/plain": [ + "array([[ 0, 4, 8],\n", + " [ 1, 5, 9],\n", + " [ 2, 6, 10],\n", + " [ 3, 7, 11]])" + ] }, "execution_count": 96, "metadata": {}, @@ -2475,7 +2809,9 @@ "outputs": [ { "data": { - "text/plain": "array([0.06078257, 0.61741189, 0.84349987])" + "text/plain": [ + "array([0.06078257, 0.61741189, 0.84349987])" + ] }, "execution_count": 99, "metadata": {}, @@ -2493,7 +2829,9 @@ "outputs": [ { "data": { - "text/plain": "array([0.03039128, 0.30870594, 0.42174994])" + "text/plain": [ + "array([0.03039128, 0.30870594, 0.42174994])" + ] }, "execution_count": 100, "metadata": {}, @@ -2512,7 +2850,9 @@ "outputs": [ { "data": { - "text/plain": "True" + "text/plain": [ + "True" + ] }, "execution_count": 101, "metadata": {}, @@ -2545,7 +2885,9 @@ "outputs": [ { "data": { - "text/plain": "memmap([0.06078257, 0.61741189, 0.84349987])" + "text/plain": [ + "memmap([0.06078257, 0.61741189, 0.84349987])" + ] }, "execution_count": 103, "metadata": {}, @@ -2577,7 +2919,9 @@ "outputs": [ { "data": { - "text/plain": "deque([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])" + "text/plain": [ + "deque([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])" + ] }, "execution_count": 104, "metadata": {}, @@ -2598,7 +2942,9 @@ "outputs": [ { "data": { - "text/plain": "deque([7, 8, 9, 0, 1, 2, 3, 4, 5, 6])" + "text/plain": [ + "deque([7, 8, 9, 0, 1, 2, 3, 4, 5, 6])" + ] }, "execution_count": 105, "metadata": {}, @@ -2617,7 +2963,9 @@ "outputs": [ { "data": { - "text/plain": "deque([1, 2, 3, 4, 5, 6, 7, 8, 9, 0])" + "text/plain": [ + "deque([1, 2, 3, 4, 5, 6, 7, 8, 9, 0])" + ] }, "execution_count": 106, "metadata": {}, @@ -2636,7 +2984,9 @@ "outputs": [ { "data": { - "text/plain": "deque([-1, 1, 2, 3, 4, 5, 6, 7, 8, 9])" + "text/plain": [ + "deque([-1, 1, 2, 3, 4, 5, 6, 7, 8, 9])" + ] }, "execution_count": 107, "metadata": {}, @@ -2655,7 +3005,9 @@ "outputs": [ { "data": { - "text/plain": "deque([3, 4, 5, 6, 7, 8, 9, 11, 22, 33])" + "text/plain": [ + "deque([3, 4, 5, 6, 7, 8, 9, 11, 22, 33])" + ] }, "execution_count": 108, "metadata": {}, @@ -2674,7 +3026,9 @@ "outputs": [ { "data": { - "text/plain": "deque([40, 30, 20, 10, 3, 4, 5, 6, 7, 8])" + "text/plain": [ + "deque([40, 30, 20, 10, 3, 4, 5, 6, 7, 8])" + ] }, "execution_count": 109, "metadata": {}, @@ -2743,7 +3097,9 @@ "outputs": [ { "data": { - "text/plain": "[0, '1', 5, 6, '9', 14, 19, '23', 28, '28']" + "text/plain": [ + "[0, '1', 5, 6, '9', 14, 19, '23', 28, '28']" + ] }, "execution_count": 112, "metadata": {}, @@ -2763,7 +3119,9 @@ "outputs": [ { "data": { - "text/plain": "[0, '1', 14, 19, '23', 28, '28', 5, 6, '9']" + "text/plain": [ + "[0, '1', 14, 19, '23', 28, '28', 5, 6, '9']" + ] }, "execution_count": 113, "metadata": {}, @@ -2798,9 +3156,9 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.0" + "version": "3.11.9" } }, "nbformat": 4, "nbformat_minor": 2 -} \ No newline at end of file +} From 41fa928541ad7363029fbe5669c28c0f933218b2 Mon Sep 17 00:00:00 2001 From: haocoder Date: Thu, 9 Oct 2025 23:30:33 +0800 Subject: [PATCH 2/3] Slices operations --- 02-array-seq/array-seq.ipynb | 222 +++++++++++++++++++++++--------- 02-array-seq/lispy/py3.9/lis.py | 4 +- 2 files changed, 165 insertions(+), 61 deletions(-) diff --git a/02-array-seq/array-seq.ipynb b/02-array-seq/array-seq.ipynb index 3c82a7d..7e6e01f 100644 --- a/02-array-seq/array-seq.ipynb +++ b/02-array-seq/array-seq.ipynb @@ -780,9 +780,16 @@ "([])." ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### parallel assignment" + ] + }, { "cell_type": "code", - "execution_count": null, + "execution_count": 34, "metadata": { "collapsed": false, "pycharm": { @@ -796,7 +803,7 @@ "33.9425" ] }, - "execution_count": 29, + "execution_count": 34, "metadata": {}, "output_type": "execute_result" } @@ -811,7 +818,7 @@ }, { "cell_type": "code", - "execution_count": 22, + "execution_count": 35, "metadata": { "collapsed": false, "pycharm": { @@ -825,7 +832,7 @@ "-118.408056" ] }, - "execution_count": 22, + "execution_count": 35, "metadata": {}, "output_type": "execute_result" } @@ -834,9 +841,46 @@ "longitude" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### swapping values" + ] + }, { "cell_type": "code", - "execution_count": 23, + "execution_count": 36, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(-118.408056, 33.9425)" + ] + }, + "execution_count": 36, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# An elegant application of unpacking is swapping the values of variables without using\n", + "# a temporary variable\n", + "latitude, longitude = longitude, latitude\n", + "latitude, longitude" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Another example of unpacking is prefixing an argument with * when calling a function" + ] + }, + { + "cell_type": "code", + "execution_count": 37, "metadata": { "collapsed": false, "pycharm": { @@ -850,7 +894,7 @@ "(2, 4)" ] }, - "execution_count": 23, + "execution_count": 37, "metadata": {}, "output_type": "execute_result" } @@ -861,7 +905,7 @@ }, { "cell_type": "code", - "execution_count": 24, + "execution_count": 38, "metadata": { "collapsed": false, "pycharm": { @@ -875,7 +919,7 @@ "(2, 4)" ] }, - "execution_count": 24, + "execution_count": 38, "metadata": {}, "output_type": "execute_result" } @@ -887,7 +931,7 @@ }, { "cell_type": "code", - "execution_count": 25, + "execution_count": 39, "metadata": { "collapsed": false, "pycharm": { @@ -901,7 +945,7 @@ "(2, 4)" ] }, - "execution_count": 25, + "execution_count": 39, "metadata": {}, "output_type": "execute_result" } @@ -951,9 +995,17 @@ "### Using * to grab excess items" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Defining function parameters with *args to grab arbitrary excess arguments is a\n", + "classic Python feature" + ] + }, { "cell_type": "code", - "execution_count": 27, + "execution_count": 41, "metadata": { "collapsed": false, "pycharm": { @@ -967,19 +1019,19 @@ "(0, 1, [2, 3, 4])" ] }, - "execution_count": 27, + "execution_count": 41, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "a, b, *rest = range(5)\n", + "a, b, *rest = range(5) # parallel assignment and * grab\n", "a, b, rest" ] }, { "cell_type": "code", - "execution_count": 28, + "execution_count": 42, "metadata": { "collapsed": false, "pycharm": { @@ -993,7 +1045,7 @@ "(0, 1, [2])" ] }, - "execution_count": 28, + "execution_count": 42, "metadata": {}, "output_type": "execute_result" } @@ -1005,7 +1057,7 @@ }, { "cell_type": "code", - "execution_count": 29, + "execution_count": 43, "metadata": { "collapsed": false, "pycharm": { @@ -1019,7 +1071,7 @@ "(0, 1, [])" ] }, - "execution_count": 29, + "execution_count": 43, "metadata": {}, "output_type": "execute_result" } @@ -1031,7 +1083,7 @@ }, { "cell_type": "code", - "execution_count": 30, + "execution_count": 44, "metadata": { "collapsed": false, "pycharm": { @@ -1045,7 +1097,7 @@ "(0, [1, 2], 3, 4)" ] }, - "execution_count": 30, + "execution_count": 44, "metadata": {}, "output_type": "execute_result" } @@ -1057,7 +1109,7 @@ }, { "cell_type": "code", - "execution_count": 31, + "execution_count": 45, "metadata": { "collapsed": false, "pycharm": { @@ -1071,7 +1123,7 @@ "([0, 1], 2, 3, 4)" ] }, - "execution_count": 31, + "execution_count": 45, "metadata": {}, "output_type": "execute_result" } @@ -1122,6 +1174,13 @@ "fun(*[1, 2], 3, *range(4, 7))" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The * can also be used when defining list, tuple, or set literals" + ] + }, { "cell_type": "code", "execution_count": 33, @@ -1227,7 +1286,7 @@ }, { "cell_type": "code", - "execution_count": 36, + "execution_count": 46, "metadata": { "collapsed": false, "pycharm": { @@ -1265,7 +1324,7 @@ }, { "cell_type": "code", - "execution_count": 37, + "execution_count": null, "metadata": { "collapsed": false, "pycharm": { @@ -1296,7 +1355,9 @@ "def main():\n", " print(f'{\"\":15} | {\"latitude\":>9} | {\"longitude\":>9}')\n", " for record in metro_areas:\n", - " match record:\n", + " match record: # subject of this match is record —— i.e., each of the tuples in metro_areas\n", + " # case clause has two parts: a pattern and a optional guard with the if keyword\n", + " # this pattern matches a sequence with four items and the last item must be a two-item sequence\n", " case [name, _, _, (lat, lon)] if lon <= 0:\n", " print(f'{name:15} | {lat:9.4f} | {lon:9.4f}')\n", "main()" @@ -1310,7 +1371,12 @@ "name": "#%% md\n" } }, - "source": [] + "source": [ + "In general, a sequence pattern matches the subject if:\n", + "1. The subject is a sequence and;\n", + "2. The subject and the pattern have the same number of items and;\n", + "3. Each corresponding item matches, including nested items." + ] }, { "cell_type": "markdown", @@ -1353,6 +1419,13 @@ "## Slicing" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "list, str, tuple,and all sequence types in Python support slicing operations" + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -1360,9 +1433,24 @@ "### Why Slices and Range Exclude the Last Item" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The Pythonic convention of excluding the last item in slices and ranges works well\n", + " with the zero-based indexing used in Python, C, and many other languages. Some\n", + " convenient features of the convention are:\n", + " - It’s easy to see the length of a slice or range when only the stop position is given:\n", + " range(3) and my_list[:3] both produce three items.\n", + " - It’s easy to compute the length of a slice or range when start and stop are given:\n", + " just subtract stop - start.\n", + " - It’s easy to split a sequence in two parts at any index x, without overlapping: sim\n", + "ply get my_list[:x] and my_list[x:]. " + ] + }, { "cell_type": "code", - "execution_count": 38, + "execution_count": 1, "metadata": {}, "outputs": [ { @@ -1371,7 +1459,7 @@ "[10, 20]" ] }, - "execution_count": 38, + "execution_count": 1, "metadata": {}, "output_type": "execute_result" } @@ -1384,7 +1472,7 @@ }, { "cell_type": "code", - "execution_count": 39, + "execution_count": 2, "metadata": {}, "outputs": [ { @@ -1393,7 +1481,7 @@ "[30, 40, 50, 60]" ] }, - "execution_count": 39, + "execution_count": 2, "metadata": {}, "output_type": "execute_result" } @@ -1404,7 +1492,7 @@ }, { "cell_type": "code", - "execution_count": 40, + "execution_count": 3, "metadata": {}, "outputs": [ { @@ -1413,7 +1501,7 @@ "[10, 20, 30]" ] }, - "execution_count": 40, + "execution_count": 3, "metadata": {}, "output_type": "execute_result" } @@ -1424,7 +1512,7 @@ }, { "cell_type": "code", - "execution_count": 41, + "execution_count": 4, "metadata": {}, "outputs": [ { @@ -1433,7 +1521,7 @@ "[40, 50, 60]" ] }, - "execution_count": 41, + "execution_count": 4, "metadata": {}, "output_type": "execute_result" } @@ -1451,7 +1539,7 @@ }, { "cell_type": "code", - "execution_count": 42, + "execution_count": 15, "metadata": {}, "outputs": [ { @@ -1460,7 +1548,7 @@ "'bye'" ] }, - "execution_count": 42, + "execution_count": 15, "metadata": {}, "output_type": "execute_result" } @@ -1472,7 +1560,7 @@ }, { "cell_type": "code", - "execution_count": 43, + "execution_count": null, "metadata": {}, "outputs": [ { @@ -1481,18 +1569,18 @@ "'elcycib'" ] }, - "execution_count": 43, + "execution_count": 16, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "s[::-1]" + "s[::-1] # stride is negative, returing items in reverse" ] }, { "cell_type": "code", - "execution_count": 44, + "execution_count": 7, "metadata": {}, "outputs": [ { @@ -1501,7 +1589,7 @@ "'eccb'" ] }, - "execution_count": 44, + "execution_count": 7, "metadata": {}, "output_type": "execute_result" } @@ -1510,6 +1598,14 @@ "s[::-2]" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "- The notation a:b:c is only valid within [] when used as the indexing or subscript operator, and it produces a slice object: `slice(a, b, c)`\n", + "- when evaluate the expression `seq[start:stop:step]`, Python calls `seq.__getitem__(slice(start, stop, step))`" + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -1519,17 +1615,17 @@ }, { "cell_type": "code", - "execution_count": 45, + "execution_count": null, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - " $17.50 imoroni PiBrella \n", - " $4.95 mm Tactile Switch x20 \n", - " $28.00 anavise Jr. - PV-201 \n", - " $34.95 iTFT Mini Kit 320x240 \n", + " $17.50 Pimoroni PiBrella \n", + " $4.95 6mm Tactile Switch x20 \n", + " $28.00 Panavise Jr. - PV-201 \n", + " $34.95 PiTFT Mini Kit 320x240 \n", " \n" ] } @@ -1543,8 +1639,8 @@ "1601 PiTFT Mini Kit 320x240 $34.95 1 $34.95\n", "\"\"\"\n", "\n", - "SKU = slice(0, 6)\n", - "DESCRIPTION = slice(6, 40)\n", + "SKU = slice(0, 5)\n", + "DESCRIPTION = slice(5, 40)\n", "UNIT_PRICE = slice(40, 52)\n", "QUANTITY = slice(52, 55)\n", "ITEM_TOTAL = slice(55, None)\n", @@ -1552,7 +1648,15 @@ "line_items = invoice.split('\\n')[2:]\n", "\n", "for item in line_items:\n", - " print(item[UNIT_PRICE], item[DESCRIPTION])" + " print(item)\n", + " print(item[UNIT_PRICE], item[DESCRIPTION]) # 用slice object表示序列的切片操作" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Mul" ] }, { @@ -1564,7 +1668,7 @@ }, { "cell_type": "code", - "execution_count": 46, + "execution_count": 9, "metadata": {}, "outputs": [ { @@ -1573,7 +1677,7 @@ "[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]" ] }, - "execution_count": 46, + "execution_count": 9, "metadata": {}, "output_type": "execute_result" } @@ -1585,7 +1689,7 @@ }, { "cell_type": "code", - "execution_count": 47, + "execution_count": 10, "metadata": {}, "outputs": [ { @@ -1594,7 +1698,7 @@ "[0, 1, 20, 30, 5, 6, 7, 8, 9]" ] }, - "execution_count": 47, + "execution_count": 10, "metadata": {}, "output_type": "execute_result" } @@ -1606,7 +1710,7 @@ }, { "cell_type": "code", - "execution_count": 48, + "execution_count": 11, "metadata": {}, "outputs": [ { @@ -1615,7 +1719,7 @@ "[0, 1, 20, 30, 5, 8, 9]" ] }, - "execution_count": 48, + "execution_count": 11, "metadata": {}, "output_type": "execute_result" } @@ -1627,7 +1731,7 @@ }, { "cell_type": "code", - "execution_count": 49, + "execution_count": 12, "metadata": {}, "outputs": [ { @@ -1636,7 +1740,7 @@ "[0, 1, 20, 11, 5, 22, 9]" ] }, - "execution_count": 49, + "execution_count": 12, "metadata": {}, "output_type": "execute_result" } @@ -1655,7 +1759,7 @@ }, { "cell_type": "code", - "execution_count": 50, + "execution_count": 13, "metadata": {}, "outputs": [ { @@ -1675,7 +1779,7 @@ }, { "cell_type": "code", - "execution_count": 51, + "execution_count": 14, "metadata": {}, "outputs": [ { @@ -1684,7 +1788,7 @@ "[0, 1, 100, 22, 9]" ] }, - "execution_count": 51, + "execution_count": 14, "metadata": {}, "output_type": "execute_result" } diff --git a/02-array-seq/lispy/py3.9/lis.py b/02-array-seq/lispy/py3.9/lis.py index dd80096..a3b976b 100644 --- a/02-array-seq/lispy/py3.9/lis.py +++ b/02-array-seq/lispy/py3.9/lis.py @@ -146,8 +146,8 @@ def evaluate(exp: Expression, env: Environment) -> Any: elif exp[0] == 'quote': # (quote exp) (_, x) = exp return x - elif exp[0] == 'if': # (if test conseq alt) - (_, test, consequence, alternative) = exp + elif exp[0] == 'if': # (if test conseq alt) # first check + (_, test, consequence, alternative) = exp # then unpacking list if evaluate(test, env): return evaluate(consequence, env) else: From 8639c83e34517acd2243bb106a7c1ce31a13c9b5 Mon Sep 17 00:00:00 2001 From: haocoder Date: Sun, 19 Oct 2025 23:05:39 +0800 Subject: [PATCH 3/3] +/*/+=/*+ operators --- 02-array-seq/array-seq.ipynb | 490 +++++++++++++++++++++++++++++------ 1 file changed, 414 insertions(+), 76 deletions(-) diff --git a/02-array-seq/array-seq.ipynb b/02-array-seq/array-seq.ipynb index 7e6e01f..d1b363a 100644 --- a/02-array-seq/array-seq.ipynb +++ b/02-array-seq/array-seq.ipynb @@ -1539,7 +1539,7 @@ }, { "cell_type": "code", - "execution_count": 15, + "execution_count": 1, "metadata": {}, "outputs": [ { @@ -1548,7 +1548,7 @@ "'bye'" ] }, - "execution_count": 15, + "execution_count": 1, "metadata": {}, "output_type": "execute_result" } @@ -1615,17 +1615,22 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 19, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ + "1909 Pimoroni PiBrella $17.50 3 $52.50\n", " $17.50 Pimoroni PiBrella \n", + "1489 6mm Tactile Switch x20 $4.95 2 $9.90\n", " $4.95 6mm Tactile Switch x20 \n", + "1510 Panavise Jr. - PV-201 $28.00 1 $28.00\n", " $28.00 Panavise Jr. - PV-201 \n", + "1601 PiTFT Mini Kit 320x240 $34.95 1 $34.95\n", " $34.95 PiTFT Mini Kit 320x240 \n", + "\n", " \n" ] } @@ -1656,7 +1661,38 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### Mul" + "### Multidimensional Slicing and Ellipsis(...)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "- Slicing includes additional features such as multidimensional slices and ellipsis(...) notation.\n", + "- The [] operator can also take multiple indexes or slices separated by commas" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "- numpy.ndarray:\n", + " - `a[i, j]`, two indexes\n", + " - `a[m:n, k:l]`, two-dimensional slice " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Ellipsis\n", + "- The ellipsis—written with three full stops (...) and not … (Unicode U+2026)—is recognized as a token by the Python parser. It is an alias to the Ellipsis object, the single instance of the ellipsis class.\n", + "- passed ... as an argument to functions:\n", + " - `f(a, ..., z)`\n", + "- as part of a slice:\n", + " - `a[i:...]`\n", + "- Numpy uses ... as a shorcut when slicing multi-dimensional arrays\n", + " - x is four-dimensional array, `x[i, ...]` is a shortcut for `x[i, :, :, :,]`" ] }, { @@ -1666,6 +1702,14 @@ "### Assigning to Slices" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "- above-ammentioned slices is used to extract information from sequences, but Slices can also be used to change mutable sequences\n", + "- Mutable sequences can be grafted, excised, and otherwise modified in place using slice notation on the lefthand side of an assignment statement or as the target of a del statement." + ] + }, { "cell_type": "code", "execution_count": 9, @@ -1759,7 +1803,7 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": 20, "metadata": {}, "outputs": [ { @@ -1779,16 +1823,16 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": 21, "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "[0, 1, 100, 22, 9]" + "[0, 1, 100]" ] }, - "execution_count": 14, + "execution_count": 21, "metadata": {}, "output_type": "execute_result" } @@ -1805,9 +1849,16 @@ "## Using + and * with Sequences" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "- Both + and * always create a new object, and never change their operands" + ] + }, { "cell_type": "code", - "execution_count": 52, + "execution_count": 8, "metadata": {}, "outputs": [ { @@ -1816,19 +1867,39 @@ "[1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3]" ] }, - "execution_count": 52, + "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "l = [1, 2, 3]\n", - "l * 5" + "l * 5 # concatenate multiple copies of the same sequence with * operators, new sequence of the same type is created but l is not modified" ] }, { "cell_type": "code", - "execution_count": 53, + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[1, 2, 3]" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "l # l was not modified" + ] + }, + { + "cell_type": "code", + "execution_count": 7, "metadata": {}, "outputs": [ { @@ -1837,7 +1908,7 @@ "'abcdabcdabcdabcdabcd'" ] }, - "execution_count": 53, + "execution_count": 7, "metadata": {}, "output_type": "execute_result" } @@ -1911,7 +1982,7 @@ }, { "cell_type": "code", - "execution_count": 56, + "execution_count": null, "metadata": {}, "outputs": [ { @@ -1926,7 +1997,7 @@ } ], "source": [ - "weird_board = [['_'] * 3] * 3\n", + "weird_board = [['_'] * 3] * 3 # The outer list is made of three references to the same inner list\n", "weird_board" ] }, @@ -1951,6 +2022,13 @@ "weird_board" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "- the above result reveals that all rows of weird_board are aliases referring to the same object" + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -1960,7 +2038,7 @@ }, { "cell_type": "code", - "execution_count": 58, + "execution_count": null, "metadata": {}, "outputs": [ { @@ -1969,22 +2047,23 @@ "[['_', '_', '_'], ['_', '_', '_'], ['_', '_', '_']]" ] }, - "execution_count": 58, + "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ + "# list comprehension from example 2-14 is equivalent to this code\n", "board = []\n", "for i in range(3):\n", - " row = ['_'] * 3\n", + " row = ['_'] * 3 # each iteration builds a new row and append it to board\n", " board.append(row)\n", "board" ] }, { "cell_type": "code", - "execution_count": 59, + "execution_count": 10, "metadata": {}, "outputs": [ { @@ -1993,7 +2072,7 @@ "[['_', '_', '_'], ['_', '_', '_'], ['X', '_', '_']]" ] }, - "execution_count": 59, + "execution_count": 10, "metadata": {}, "output_type": "execute_result" } @@ -2003,6 +2082,40 @@ "board" ] }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# example 2-15 is same as this code\n", + "row = ['_'] * 3\n", + "board = []\n", + "for i in range(3):\n", + " board.append(row) # the same row is appended three times to board" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[['x', '_', '_'], ['x', '_', '_'], ['x', '_', '_']]" + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "board[2][0] = 'x'\n", + "board" + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -2010,9 +2123,19 @@ "## Augmented Assignment with Sequences" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "- The augmented assignment operators += and *= behave quite differently, depending on the first operand.\n", + "- The special method that makes += work is \\_\\_iadd\\_\\_ (for “in-place addition”).\n", + "- However, if \\_\\_iadd\\_\\_ is not implemented, Python falls back to calling \\_\\_add\\_\\_. \n", + "- Operator *=, which is implemented via \\_\\_imul\\_\\_" + ] + }, { "cell_type": "code", - "execution_count": 60, + "execution_count": 19, "metadata": {}, "outputs": [], "source": [ @@ -2022,16 +2145,16 @@ }, { "cell_type": "code", - "execution_count": 61, + "execution_count": 16, "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "140694277263808" + "2316354397824" ] }, - "execution_count": 61, + "execution_count": 16, "metadata": {}, "output_type": "execute_result" } @@ -2043,7 +2166,7 @@ }, { "cell_type": "code", - "execution_count": 62, + "execution_count": 20, "metadata": {}, "outputs": [ { @@ -2052,7 +2175,7 @@ "[1, 2, 3, 1, 2, 3]" ] }, - "execution_count": 62, + "execution_count": 20, "metadata": {}, "output_type": "execute_result" } @@ -2064,7 +2187,7 @@ }, { "cell_type": "code", - "execution_count": 63, + "execution_count": 21, "metadata": {}, "outputs": [ { @@ -2073,7 +2196,7 @@ "True" ] }, - "execution_count": 63, + "execution_count": 21, "metadata": {}, "output_type": "execute_result" } @@ -2084,26 +2207,26 @@ }, { "cell_type": "code", - "execution_count": 64, + "execution_count": 25, "metadata": {}, "outputs": [], "source": [ - "t = (1, 2, 3)\n", + "t = (1, 2, 3) # t is immutable sequeces\n", "idt = id(t)" ] }, { "cell_type": "code", - "execution_count": 65, + "execution_count": 26, "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "140694329335488" + "2316354442816" ] }, - "execution_count": 65, + "execution_count": 26, "metadata": {}, "output_type": "execute_result" } @@ -2115,7 +2238,7 @@ }, { "cell_type": "code", - "execution_count": 66, + "execution_count": 27, "metadata": {}, "outputs": [ { @@ -2124,16 +2247,117 @@ "False" ] }, - "execution_count": 66, + "execution_count": 27, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "t *= 2\n", + "t *= 2 # preduce new tuple and t reference it\n", "id(t) == idt # new tuple" ] }, + { + "cell_type": "code", + "execution_count": 28, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(1, 2, 3, 1, 2, 3)" + ] + }, + "execution_count": 28, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "t" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "- Repeated concatenation of immutable sequences is inefficient, because instead of just appending new items, the interpreter has to copy the whole target sequence to create a new one with the new items concatenated.\n", + "- str is an exception to this description. Because string building with += in loops is so common in real codeba\n", + "ses, CPython is optimized for this use case. Instances of str are allocated in memory with extra room, so that\n", + " concatenation does not require copying the whole string every time" + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "metadata": {}, + "outputs": [], + "source": [ + "s = 'abc'\n", + "idt = id(s)" + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "140710833721696" + ] + }, + "execution_count": 30, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "idt" + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "False" + ] + }, + "execution_count": 31, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "s *= 2\n", + "idt == id(s)" + ] + }, + { + "cell_type": "code", + "execution_count": 32, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'abcabc'" + ] + }, + "execution_count": 32, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "s" + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -2144,7 +2368,7 @@ }, { "cell_type": "code", - "execution_count": 67, + "execution_count": 33, "metadata": {}, "outputs": [ { @@ -2177,7 +2401,7 @@ }, { "cell_type": "code", - "execution_count": 68, + "execution_count": 34, "metadata": {}, "outputs": [ { @@ -2186,7 +2410,7 @@ "(1, 2, [30, 40, 50, 60])" ] }, - "execution_count": 68, + "execution_count": 34, "metadata": {}, "output_type": "execute_result" } @@ -2195,6 +2419,24 @@ "t" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "- The result of example 2-16 is both raise exception and t[2] was be changed\n", + "- test by https://pythontutor.com/" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Lessons from example 2-16\n", + "- Avoid putting mutable items in tuples.\n", + "- Augmented assignment is not an atomic operation—we just saw it throwing an exception after doing part of its job.\n", + "- Inspecting Python bytecode is not too difficult, and can be helpful to see what is going on under the hood." + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -2204,23 +2446,27 @@ }, { "cell_type": "code", - "execution_count": 69, + "execution_count": 35, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - " 1 0 LOAD_NAME 0 (s)\n", - " 2 LOAD_NAME 1 (a)\n", - " 4 DUP_TOP_TWO\n", - " 6 BINARY_SUBSCR\n", - " 8 LOAD_NAME 2 (b)\n", - " 10 INPLACE_ADD\n", - " 12 ROT_THREE\n", - " 14 STORE_SUBSCR\n", - " 16 LOAD_CONST 0 (None)\n", - " 18 RETURN_VALUE\n" + " 0 0 RESUME 0\n", + "\n", + " 1 2 LOAD_NAME 0 (s)\n", + " 4 LOAD_NAME 1 (a)\n", + " 6 COPY 2\n", + " 8 COPY 2\n", + " 10 BINARY_SUBSCR\n", + " 20 LOAD_NAME 2 (b)\n", + " 22 BINARY_OP 13 (+=)\n", + " 26 SWAP 3\n", + " 28 SWAP 2\n", + " 30 STORE_SUBSCR\n", + " 34 LOAD_CONST 0 (None)\n", + " 36 RETURN_VALUE\n" ] } ], @@ -2237,30 +2483,62 @@ "## list.sort and the sorted Built-In Function" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "- The list.sort method sorts a list in place—that is, without making a copy. It returns None to remind us that it changes the receiver(Receiver is the target of a method call, the object bound to self in the method body) and does not create a new list.\n", + "- This is an important Python API convention: **functions or methods that change an object in place should return None to make it clear to the caller that the receiver was changed, and no new object was created**.\n", + "- The convention of returning None to signal in-place changes has a drawback: we cannot cascade calls to those methods. In contrast, methods that return new objects (e.g., all str methods) can be cascaded in the fluent interface style.\n", + "- In contrast, the built-in function sorted creates a new list and returns it. It accepts any iterable object as an argument, including immutable sequences and generators. Regardless of the type of iterable given to sorted, it always returns a\n", + " newly created list." + ] + }, { "cell_type": "code", - "execution_count": 70, + "execution_count": 41, "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "['apple', 'banana', 'grape', 'raspberry']" + "2316354507072" ] }, - "execution_count": 70, + "execution_count": 41, "metadata": {}, "output_type": "execute_result" } ], "source": [ "fruits = ['grape', 'raspberry', 'apple', 'banana']\n", - "sorted(fruits)" + "idt = id(fruits)\n", + "idt" ] }, { "cell_type": "code", - "execution_count": 71, + "execution_count": null, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['apple', 'banana', 'grape', 'raspberry']" + ] + }, + "execution_count": 42, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "sorted(fruits) # This produces a new list of strings sorted alphabetically" + ] + }, + { + "cell_type": "code", + "execution_count": 39, "metadata": {}, "outputs": [ { @@ -2269,18 +2547,18 @@ "['grape', 'raspberry', 'apple', 'banana']" ] }, - "execution_count": 71, + "execution_count": 39, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "fruits" + "fruits # not changed" ] }, { "cell_type": "code", - "execution_count": 72, + "execution_count": 44, "metadata": {}, "outputs": [ { @@ -2289,47 +2567,53 @@ "['raspberry', 'grape', 'banana', 'apple']" ] }, - "execution_count": 72, + "execution_count": 44, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "sorted(fruits, reverse=True)" + "# reverse is true the items are returned in descending order (i.e., by reversing the comparison of the items). The default is False\n", + "sorted(fruits, reverse=True) " ] }, { "cell_type": "code", - "execution_count": 73, + "execution_count": 53, "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "['grape', 'apple', 'banana', 'raspberry']" + "['apple', 'grape', 'banana', 'raspberry']" ] }, - "execution_count": 73, + "execution_count": 53, "metadata": {}, "output_type": "execute_result" } ], "source": [ + "# key is a one-argument function that will be applied to each item to produce its sorting key\n", + "# key=len, will sort the strings by character length\n", + "# key=min() or max() with sort by min or max of each item\n", + "# key=str.lower for a list of strings will perform a case-insensitive sort\n", + "# thee default sort key is the identity function(i.e., the items themselves are compared)\n", "sorted(fruits, key=len)" ] }, { "cell_type": "code", - "execution_count": 74, + "execution_count": 54, "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "['raspberry', 'banana', 'grape', 'apple']" + "['raspberry', 'banana', 'apple', 'grape']" ] }, - "execution_count": 74, + "execution_count": 54, "metadata": {}, "output_type": "execute_result" } @@ -2340,7 +2624,7 @@ }, { "cell_type": "code", - "execution_count": 75, + "execution_count": 47, "metadata": {}, "outputs": [ { @@ -2349,7 +2633,7 @@ "['grape', 'raspberry', 'apple', 'banana']" ] }, - "execution_count": 75, + "execution_count": 47, "metadata": {}, "output_type": "execute_result" } @@ -2360,7 +2644,7 @@ }, { "cell_type": "code", - "execution_count": 76, + "execution_count": 55, "metadata": {}, "outputs": [ { @@ -2369,16 +2653,45 @@ "['apple', 'banana', 'grape', 'raspberry']" ] }, - "execution_count": 76, + "execution_count": 55, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "fruits.sort()\n", + "fruits.sort() # in-place sort and return None\n", "fruits" ] }, + { + "cell_type": "code", + "execution_count": 50, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "2316354507072" + ] + }, + "execution_count": 50, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "id(fruits)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "- bisect module\n", + "- https://www.fluentpython.com/\n", + "- https://www.fluentpython.com/extra/ordered-sequences-with-bisect/" + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -2386,6 +2699,17 @@ "## When a List Is Not the Answer" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### better than list\n", + "- An array saves a lot of memory when you need to handle millions of floating-point values\n", + "- On the other hand, if you are constantly adding and removing items from opposite ends of a list, it’s good to know that a\n", + " deque (double-ended queue) is a more efficient FIFO14 data structure.\n", + "- If your code frequently checks whether an item is present in a collection (e.g., item in my_collection), consider using a set for my_collection, especially if it holds a large number of items. Sets are optimized for fast membership checking. They are also iterable, but they are not sequences because the ordering of set items is unspecified. " + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -2393,6 +2717,18 @@ "### Arrays" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "- If a list only contains numbers, an array.array is a more efficient replacement.\n", + " Arrays support all mutable sequence operations (including .pop, .insert,\n", + " and .extend), as well as additional methods for fast loading and saving, such\n", + " as .frombytes and .tofile\n", + " - When creating an array, you provide a typecode, a letter to determine the underlying C\n", + " type used to store each item in the array. " + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -2402,7 +2738,7 @@ }, { "cell_type": "code", - "execution_count": 77, + "execution_count": null, "metadata": {}, "outputs": [ { @@ -2411,7 +2747,7 @@ "0.8190492979077034" ] }, - "execution_count": 77, + "execution_count": 56, "metadata": {}, "output_type": "execute_result" } @@ -2421,23 +2757,25 @@ "from random import random, seed\n", "seed(10) # Use seed to make the output consistent\n", "\n", + "# Create an array of double-precision floats (typecode 'd') from any iterable object —in this case, a generator expression.\n", "floats = array('d', (random() for i in range(10 ** 7)))\n", "floats[-1]" ] }, { "cell_type": "code", - "execution_count": 78, + "execution_count": 58, "metadata": {}, "outputs": [], "source": [ + "# save the array to binary file\n", "with open('floats.bin', 'wb') as fp:\n", " floats.tofile(fp)" ] }, { "cell_type": "code", - "execution_count": 79, + "execution_count": null, "metadata": {}, "outputs": [ { @@ -2452,7 +2790,7 @@ } ], "source": [ - "floats2 = array('d')\n", + "floats2 = array('d') # Create an empty array of doubles\n", "\n", "with open('floats.bin', 'rb') as fp:\n", " floats2.fromfile(fp, 10 ** 7)\n",