Skip to content

Instantly share code, notes, and snippets.

@KuRRe8
Last active June 6, 2025 17:35
Show Gist options
  • Save KuRRe8/36f63d23ef205a8e02b7b7ec009cc4e8 to your computer and use it in GitHub Desktop.
Save KuRRe8/36f63d23ef205a8e02b7b7ec009cc4e8 to your computer and use it in GitHub Desktop.
和Python使用有关的一些教程,按类别分为不同文件

Python教程

Python是一个新手友好的语言,并且现在机器学习社区深度依赖于Python,C++, Cuda C, R等语言,使得Python的热度稳居第一。本Gist提供Python相关的一些教程,可以直接在Jupyter Notebook中运行。

  1. 语言级教程,一般不涉及初级主题;
  2. 标准库教程,最常见的标准库基本用法;
  3. 第三方库教程,主要是常见的库如numpy,pytorch诸如此类,只涉及基本用法,不考虑新特性

其他内容就不往这个Gist里放了,注意Gist依旧由git进行版本控制,所以可以git clone 到本地,或者直接Google Colab\ Kaggle打开相应的ipynb文件

直接在网页浏览时,由于没有文件列表,可以按Ctrl + F来检索相应的目录,或者点击下面的超链接。

想要参与贡献的直接在评论区留言,有什么问题的也在评论区说 ^.^

目录-语言部分

目录-库部分

目录-具体业务库部分-本教程更多关注机器学习深度学习内容

目录-附录

  • sigh.md个人对于Python动态语言的看法
Display the source blob
Display the rendered blob
Raw
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Display the source blob
Display the rendered blob
Raw
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Display the source blob
Display the rendered blob
Raw
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Display the source blob
Display the rendered blob
Raw
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Display the source blob
Display the rendered blob
Raw
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Display the source blob
Display the rendered blob
Raw
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Display the source blob
Display the rendered blob
Raw
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Display the source blob
Display the rendered blob
Raw
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Python 类特殊方法 (魔术方法/Dunder 方法) 教程\n",
"\n",
"欢迎来到 Python 类特殊方法(也称为魔术方法或 Dunder 方法)的教程!这些方法以双下划线开头和结尾(例如 `__init__`, `__str__`),它们是 Python 数据模型的关键组成部分。通过实现这些方法,你可以让你的自定义对象表现得像内置类型一样,支持迭代、算术运算、属性访问控制等。\n",
"\n",
"**为什么叫 Dunder 方法?**\n",
"\"Dunder\"\"Double Underscore\" 的缩写。\n",
"\n",
"**为什么它们很重要?**\n",
"\n",
"1. **使自定义对象更 Pythonic**:让你的类与 Python 的内置函数和操作符(如 `len()`, `+`, `[]`)无缝集成。\n",
"2. **协议实现**:它们定义了 Python 中的各种协议,例如迭代器协议、序列协议、数字协议等。\n",
"3. **代码可读性**:使用标准操作符(如 `a + b` 而不是 `a.add(b)`)通常更易读。\n",
"4. **框架和库的构建**:许多库(如 ORM、数据分析库)依赖于这些方法来提供丰富的功能。\n",
"\n",
"本教程将按功能分类介绍一些最常用和最重要的魔术方法。"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 1. 基本的魔术方法"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### 1.1 `__init__(self, ...)`: 对象初始化\n",
"* **目的**:当类的实例被创建后,用于初始化该实例的属性。它不是构造函数(`__new__` 才是),而是初始化器。\n",
"* **调用时机**:在 `__new__` 创建实例后自动调用。\n",
"* `self` 是新创建的实例。"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"class Book:\n",
" def __init__(self, title, author, pages):\n",
" print(f\"Book.__init__ called for '{title}'\")\n",
" self.title = title\n",
" self.author = author\n",
" self.pages = pages\n",
"\n",
"book1 = Book(\"The Hitchhiker's Guide to the Galaxy\", \"Douglas Adams\", 224)\n",
"print(f\"Title: {book1.title}, Author: {book1.author}\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### 1.2 `__new__(cls, ...)`: 对象实例化 (构造)\n",
"* **目的**:真正的构造函数,负责创建类的实例。它在 `__init__` 之前被调用。\n",
"* **调用时机**:当调用类名创建实例时(如 `MyClass()`)。\n",
"* **返回值**:必须返回新创建的实例对象。如果 `__new__` 返回的不是 `cls` 的实例,那么该实例的 `__init__` 方法就不会被调用。\n",
"* **用途**:通常用于控制实例的创建过程,例如实现单例模式,或者创建不可变类型的子类。\n",
"* `cls` 是正在被实例化的类。"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"class PositiveInteger(int): # 继承自不可变类型 int\n",
" def __new__(cls, value):\n",
" print(f\"PositiveInteger.__new__ called with value: {value}\")\n",
" if not isinstance(value, int) or value < 0:\n",
" raise ValueError(\"PositiveInteger must be a non-negative integer\")\n",
" # 调用父类的 __new__ 来创建实例\n",
" instance = super().__new__(cls, value)\n",
" print(f\" Instance created: {instance}, type: {type(instance)}\")\n",
" return instance\n",
"\n",
" # __init__ 通常不需要为不可变类型定义,因为它们在 __new__ 中已经完全形成\n",
" # def __init__(self, value):\n",
" # print(f\"PositiveInteger.__init__ called with self: {self}, value: {value}\")\n",
" # # super().__init__() # int 的 __init__ 不需要参数\n",
"\n",
"p_int1 = PositiveInteger(10)\n",
"print(f\"Value of p_int1: {p_int1}, p_int1 + 5 = {p_int1 + 5}\")\n",
"\n",
"try:\n",
" p_int2 = PositiveInteger(-5)\n",
"except ValueError as e:\n",
" print(f\"Error: {e}\")\n",
"\n",
"# 单例模式示例 (简单版)\n",
"class Singleton:\n",
" _instance = None\n",
" def __new__(cls, *args, **kwargs):\n",
" if not cls._instance:\n",
" print(\"Singleton: Creating new instance\")\n",
" cls._instance = super().__new__(cls)\n",
" else:\n",
" print(\"Singleton: Returning existing instance\")\n",
" return cls._instance\n",
" \n",
" def __init__(self, data=None):\n",
" # __init__ 总是会被调用,即使返回的是旧实例\n",
" # 所以,初始化逻辑需要小心处理,确保只在第一次创建时执行\n",
" if not hasattr(self, 'initialized'):\n",
" print(f\"Singleton.__init__ called (first time) with data: {data}\")\n",
" self.data = data\n",
" self.initialized = True\n",
" else:\n",
" print(f\"Singleton.__init__ called (already initialized), current data: {self.data}, new data ignored: {data}\")\n",
"\n",
"s1 = Singleton(\"Data for S1\")\n",
"s2 = Singleton(\"Data for S2\") # __init__ 会被调用,但 s1 和 s2 是同一个实例\n",
"print(f\"s1 is s2: {s1 is s2}\")\n",
"print(f\"s1.data: {s1.data}\") # 'Data for S1'\n",
"print(f\"s2.data: {s2.data}\") # 'Data for S1'"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### 1.3 `__del__(self)`: 对象销毁 (析构)\n",
"* **目的**:当对象即将被垃圾回收器销毁时调用。用于执行清理操作,如关闭文件、释放资源等。\n",
"* **调用时机**:不确定,由垃圾回收器决定。不应该依赖它来执行关键的清理操作(使用 `try...finally` 或上下文管理器更好)。\n",
"* **警告**:`__del__` 的行为有时难以预测,尤其是在存在循环引用的情况下。应谨慎使用。"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"class FileHandler:\n",
" def __init__(self, filename):\n",
" print(f\"FileHandler: Opening {filename}\")\n",
" self.filename = filename\n",
" self.file = open(filename, 'w') # 示例,实际应更健壮\n",
" \n",
" def write(self, content):\n",
" self.file.write(content)\n",
" \n",
" def __del__(self):\n",
" # 尽量确保文件关闭,但这不是最佳实践\n",
" if hasattr(self, 'file') and not self.file.closed:\n",
" print(f\"FileHandler.__del__: Closing {self.filename}\")\n",
" self.file.close()\n",
"\n",
"def create_and_delete_handler():\n",
" print(\"Creating FileHandler...\")\n",
" fh = FileHandler(\"temp_del_test.txt\")\n",
" fh.write(\"Hello from __del__ test\\n\")\n",
" print(\"FileHandler created and used. Exiting function scope...\")\n",
" # 当 fh 离开作用域且没有其他引用时,它可能被垃圾回收\n",
"\n",
"create_and_delete_handler()\n",
"print(\"After function call. __del__ might have been called (or might be later).\")\n",
"\n",
"# 为了确保能看到 __del__ 的输出,可以显式删除并触发垃圾回收 (不推荐在生产代码中)\n",
"# import gc\n",
"# fh_explicit = FileHandler(\"temp_del_explicit.txt\")\n",
"# del fh_explicit\n",
"# gc.collect()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 2. 对象表示的魔术方法"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### 2.1 `__str__(self)`: 对象的可读字符串表示\n",
"* **目的**:返回一个对象的“非正式”或“用户友好”的字符串表示。\n",
"* **调用时机**:当使用 `str(obj)` 或 `print(obj)` 时。\n",
"* **返回值**:必须是一个字符串对象。"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### 2.2 `__repr__(self)`: 对象的“官方”字符串表示\n",
"* **目的**:返回一个对象的“官方”或“开发者友好”的字符串表示,理想情况下,这个字符串应该是一个有效的 Python 表达式,可以用来重新创建具有相同值的对象(`eval(repr(obj)) == obj`)。如果不可能,至少应该提供足够的信息来识别对象。\n",
"* **调用时机**:当使用 `repr(obj)`,在交互式解释器中直接输入变量名,或者当 `__str__` 未定义时 `str()` 和 `print()` 也会回退到它。\n",
"* **返回值**:必须是一个字符串对象。\n",
"* **最佳实践**:如果实现了 `__repr__` 但未实现 `__str__`,`str()` 会使用 `__repr__`。通常建议至少实现 `__repr__`。"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"class Point:\n",
" def __init__(self, x, y):\n",
" self.x = x\n",
" self.y = y\n",
"\n",
" def __str__(self):\n",
" # 用户友好的表示\n",
" return f\"Point at ({self.x}, {self.y})\"\n",
"\n",
" def __repr__(self):\n",
" # 开发者友好的表示,理想情况下可用于重建对象\n",
" return f\"Point(x={self.x!r}, y={self.y!r})\" # 使用 !r 确保内部值也是 repr 形式\n",
"\n",
"p = Point(3, 4)\n",
"print(str(p)) # 调用 p.__str__()\n",
"print(repr(p)) # 调用 p.__repr__()\n",
"print(p) # 在 print 中,优先调用 __str__\n",
"\n",
"# 在解释器中直接输入变量名,会调用 __repr__\n",
"# (在Jupyter Notebook单元格的最后一行输出,也是 __repr__)\n",
"p"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### 2.3 `__format__(self, format_spec)`: 自定义格式化\n",
"* **目的**:允许对象自定义其在格式化字符串(如 f-string 或 `str.format()`)中的表示。\n",
"* **调用时机**:当使用 `format(obj, format_spec)` 或在格式化字符串中使用格式说明符时(例如 `f\"{obj:spec}\"`)。\n",
"* `format_spec` 是格式说明符字符串。"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"from datetime import date\n",
"\n",
"class MyDate(date):\n",
" def __format__(self, format_spec):\n",
" if not format_spec:\n",
" format_spec = \"%Y-%m-%d\" # 默认格式\n",
" elif format_spec == 'mdy':\n",
" format_spec = \"%m/%d/%y\"\n",
" elif format_spec == 'dmy':\n",
" format_spec = \"%d-%m-%Y\"\n",
" # 如果不是我们支持的特殊格式,就调用父类的 __format__\n",
" # 或者可以直接使用 self.strftime(format_spec)\n",
" return self.strftime(format_spec)\n",
"\n",
"today = MyDate(2023, 10, 26)\n",
"print(f\"Default format: {today}\") # 这里的 today 会先尝试 __str__\n",
"print(f\"Default format (explicit): {today:}\")\n",
"print(f\"MDY format: {today:mdy}\")\n",
"print(f\"DMY format: {today:dmy}\")\n",
"print(f\"ISO format: {today:%Y%m%d}\") # 使用标准的 strftime 格式"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 3. 属性访问的魔术方法"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### 3.1 `__getattr__(self, name)`: 访问不存在的属性\n",
"* **目的**:当尝试访问一个实例中不存在的属性时被调用。\n",
"* **调用时机**:仅当正常的属性查找失败后(即属性不在实例的 `__dict__` 中,也不在类及其基类的 `__dict__` 中,也不是描述符)。\n",
"* **用途**:用于实现属性的动态计算、代理、延迟加载等。\n",
"* 如果找不到属性,应引发 `AttributeError`。"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### 3.2 `__getattribute__(self, name)`: 无条件属性访问\n",
"* **目的**:无条件地拦截所有属性访问(即使属性存在)。\n",
"* **调用时机**:每次访问实例属性时都会调用。\n",
"* **警告**:实现 `__getattribute__` 时要非常小心,因为它很容易导致无限递归(例如,在 `__getattribute__` 内部直接访问 `self.attr` 会再次调用 `__getattribute__`)。通常需要通过 `super().__getattribute__(name)` 或直接访问 `object.__getattribute__(self, name)` 来获取属性值。\n",
"* 如果同时定义了 `__getattr__` 和 `__getattribute__`,`__getattr__` 只有在 `__getattribute__` 引发 `AttributeError` 时才会被调用(或者 `__getattribute__` 显式调用它)。"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### 3.3 `__setattr__(self, name, value)`: 设置属性\n",
"* **目的**:当尝试给实例属性赋值时调用。\n",
"* **调用时机**:每次执行 `obj.name = value` 时。\n",
"* **警告**:类似于 `__getattribute__`,在 `__setattr__` 内部直接使用 `self.name = value` 会导致无限递归。通常使用 `super().__setattr__(name, value)` 或 `object.__setattr__(self, name, value)` 或直接操作 `self.__dict__`。"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### 3.4 `__delattr__(self, name)`: 删除属性\n",
"* **目的**:当尝试删除实例属性时调用。\n",
"* **调用时机**:每次执行 `del obj.name` 时。\n",
"* **警告**:类似地,直接在内部使用 `del self.name` 会导致无限递归。通常使用 `super().__delattr__(name)` 或 `object.__delattr__(self, name)` 或直接操作 `self.__dict__`。"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### 3.5 `__dir__(self)`: 列出属性\n",
"* **目的**:当调用 `dir(obj)` 时,返回一个包含对象属性名称的列表(字符串)。\n",
"* **用途**:自定义 `dir()` 的输出,例如包含动态生成的属性。"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"class DynamicAttributes:\n",
" def __init__(self, initial_data=None):\n",
" print(\"DynamicAttributes.__init__\")\n",
" # 使用 object.__setattr__ 避免触发我们自己的 __setattr__ (如果已定义)\n",
" object.__setattr__(self, '_data', initial_data or {})\n",
" object.__setattr__(self, '_dynamic_prefix', \"dyn_\")\n",
"\n",
" def __getattr__(self, name):\n",
" print(f\"DynamicAttributes.__getattr__ called for '{name}'\")\n",
" if name.startswith(self._dynamic_prefix):\n",
" actual_key = name[len(self._dynamic_prefix):]\n",
" if actual_key in self._data:\n",
" return self._data[actual_key]\n",
" raise AttributeError(f\"'{type(self).__name__}' object has no attribute '{name}'\")\n",
"\n",
" def __setattr__(self, name, value):\n",
" print(f\"DynamicAttributes.__setattr__ called for '{name}' = {value!r}\")\n",
" if name.startswith(self._dynamic_prefix):\n",
" actual_key = name[len(self._dynamic_prefix):]\n",
" self._data[actual_key] = value\n",
" else:\n",
" # 对于非动态属性,委托给父类 (object) 的 __setattr__\n",
" super().__setattr__(name, value)\n",
"\n",
" def __delattr__(self, name):\n",
" print(f\"DynamicAttributes.__delattr__ called for '{name}'\")\n",
" if name.startswith(self._dynamic_prefix):\n",
" actual_key = name[len(self._dynamic_prefix):]\n",
" if actual_key in self._data:\n",
" del self._data[actual_key]\n",
" return\n",
" super().__delattr__(name) # 如果不是动态属性或不存在,让父类处理或报错\n",
"\n",
" def __dir__(self):\n",
" print(\"DynamicAttributes.__dir__ called\")\n",
" # 合并常规属性和动态属性\n",
" default_dir = super().__dir__()\n",
" dynamic_attrs = [self._dynamic_prefix + key for key in self._data.keys()]\n",
" return sorted(list(set(default_dir + dynamic_attrs)))\n",
"\n",
" # __getattribute__ 示例 (谨慎使用)\n",
" # def __getattribute__(self, name):\n",
" # print(f\"DynamicAttributes.__getattribute__ for '{name}'\")\n",
" # # 必须非常小心以避免无限递归\n",
" # # 例如,总是通过 super() 访问属性\n",
" # try:\n",
" # return super().__getattribute__(name)\n",
" # except AttributeError:\n",
" # # 如果常规查找失败,可以尝试我们的动态逻辑\n",
" # if name.startswith(object.__getattribute__(self, '_dynamic_prefix')):\n",
" # _data = object.__getattribute__(self, '_data') # 安全获取 _data\n",
" # _dynamic_prefix = object.__getattribute__(self, '_dynamic_prefix')\n",
" # actual_key = name[len(_dynamic_prefix):]\n",
" # if actual_key in _data:\n",
" # return _data[actual_key]\n",
" # raise # 重新抛出原始 AttributeError\n",
"\n",
"dyn = DynamicAttributes({\"version\": \"1.0\", \"status\": \"active\"})\n",
"dyn.normal_attr = 100 # 调用 __setattr__\n",
"\n",
"print(f\"\\nAccessing normal_attr: {dyn.normal_attr}\") # 正常访问,不走 __getattr__\n",
"print(f\"Accessing dyn_version: {dyn.dyn_version}\") # 调用 __getattr__\n",
"print(f\"Accessing dyn_status: {dyn.dyn_status}\")\n",
"\n",
"dyn.dyn_user = \"admin\" # 调用 __setattr__\n",
"print(f\"Accessing dyn_user: {dyn.dyn_user}\")\n",
"\n",
"try:\n",
" print(dyn.non_existent)\n",
"except AttributeError as e:\n",
" print(f\"Error accessing non_existent: {e}\")\n",
"\n",
"print(\"\\n--- Before del --- \")\n",
"print(dir(dyn)) # 调用 __dir__\n",
"del dyn.dyn_status # 调用 __delattr__\n",
"print(\"\\n--- After del dyn_status --- \")\n",
"print(dir(dyn))\n",
"\n",
"try:\n",
" print(dyn.dyn_status)\n",
"except AttributeError as e:\n",
" print(f\"Error after deleting dyn_status: {e}\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 4. 比较的魔术方法\n",
"\n",
"这些方法用于实现对象间的比较操作符。\n",
"* `__eq__(self, other)`: `self == other`\n",
"* `__ne__(self, other)`: `self != other` (如果未实现,默认是 `not (self == other)`)\n",
"* `__lt__(self, other)`: `self < other`\n",
"* `__le__(self, other)`: `self <= other`\n",
"* `__gt__(self, other)`: `self > other`\n",
"* `__ge__(self, other)`: `self >= other`\n",
"\n",
"**注意**:\n",
"* 如果只实现 `__eq__` 而不实现 `__ne__`,Python 会自动提供 `__ne__` 的默认实现。\n",
"* 对于富比较方法 (`__lt__`, `__le__`, `__gt__`, `__ge__`),如果你实现了一个,通常也应该实现它的反面(例如,实现了 `__lt__`,也应该考虑 `__gt__`)。\n",
"* `functools.total_ordering` 装饰器:如果你实现了 `__eq__` 和至少一个富比较方法(如 `__lt__`),这个装饰器可以自动为你生成其他所有富比较方法。"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import functools\n",
"\n",
"@functools.total_ordering # 自动生成其他比较方法\n",
"class Person:\n",
" def __init__(self, name, age):\n",
" self.name = name\n",
" self.age = age\n",
"\n",
" def __eq__(self, other):\n",
" print(f\"Person.__eq__ called for {self.name} == {other.name if isinstance(other, Person) else other}\")\n",
" if not isinstance(other, Person):\n",
" return NotImplemented # 重要:让 Python 尝试 other.__eq__(self)\n",
" return self.name == other.name and self.age == other.age\n",
"\n",
" def __lt__(self, other):\n",
" print(f\"Person.__lt__ called for {self.name} < {other.name if isinstance(other, Person) else other}\")\n",
" if not isinstance(other, Person):\n",
" return NotImplemented\n",
" # 首先按年龄比较,如果年龄相同,按名字字母顺序比较\n",
" if self.age != other.age:\n",
" return self.age < other.age\n",
" return self.name < other.name\n",
" \n",
" def __repr__(self):\n",
" return f\"Person('{self.name}', {self.age})\"\n",
"\n",
"p1 = Person(\"Alice\", 30)\n",
"p2 = Person(\"Bob\", 25)\n",
"p3 = Person(\"Alice\", 30)\n",
"p4 = Person(\"Charlie\", 30)\n",
"\n",
"print(f\"p1 == p2: {p1 == p2}\") # False\n",
"print(f\"p1 == p3: {p1 == p3}\") # True\n",
"print(f\"p1 != p2: {p1 != p2}\") # True (自动生成或默认)\n",
"\n",
"print(f\"p1 < p2: {p1 < p2}\") # False (30 < 25 is False)\n",
"print(f\"p2 < p1: {p2 < p1}\") # True (25 < 30 is True)\n",
"print(f\"p1 > p2: {p1 > p2}\") # True (由 total_ordering 生成)\n",
"print(f\"p1 <= p3: {p1 <= p3}\") # True (由 total_ordering 生成)\n",
"print(f\"p1 < p4: {p1 < p4}\") # True (Alice < Charlie, age is same)\n",
"\n",
"persons = [p1, p2, p4, Person(\"David\", 25)]\n",
"print(f\"Original list: {persons}\")\n",
"persons.sort() # sort() 使用了 __lt__\n",
"print(f\"Sorted list: {persons}\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### 4.1 `__hash__(self)`: 哈希值\n",
"* **目的**:返回对象的哈希值 (一个整数)。用于将对象放入哈希表结构中,如字典的键或集合的元素。\n",
"* **规则**:\n",
" 1. 如果 `a == b`,那么必须 `hash(a) == hash(b)`。\n",
" 2. 如果对象是可变的(其影响哈希值的部分可以改变),那么它不应该实现 `__hash__`,或者 `__hash__` 应该被设置为 `None`(这是Python 3中可变类的默认行为)。\n",
" 3. 如果只实现了 `__eq__` 而没有实现 `__hash__`,类的实例默认是不可哈希的 (`__hash__` 会被隐式设为 `None`)。\n",
" 4. 如果想让一个实现了 `__eq__` 的类仍然使用其父类的 `__hash__` (例如 `object.__hash__`,它基于对象ID),可以显式设置 `__hash__ = <ParentClass>.__hash__`。\n",
"* 通常,哈希值是基于对象中不可变属性的元组来计算的,例如 `hash((self.attr1, self.attr2))`。"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"class ImmutablePoint:\n",
" def __init__(self, x, y):\n",
" self._x = x\n",
" self._y = y\n",
"\n",
" @property\n",
" def x(self):\n",
" return self._x\n",
"\n",
" @property\n",
" def y(self):\n",
" return self._y\n",
"\n",
" def __eq__(self, other):\n",
" if not isinstance(other, ImmutablePoint):\n",
" return NotImplemented\n",
" return self.x == other.x and self.y == other.y\n",
"\n",
" def __hash__(self):\n",
" # 基于不可变属性计算哈希值\n",
" print(f\"ImmutablePoint.__hash__ called for ({self.x}, {self.y})\")\n",
" return hash((self.x, self.y))\n",
"\n",
" def __repr__(self):\n",
" return f\"ImmutablePoint({self.x}, {self.y})\"\n",
"\n",
"ip1 = ImmutablePoint(1, 2)\n",
"ip2 = ImmutablePoint(1, 2)\n",
"ip3 = ImmutablePoint(3, 4)\n",
"\n",
"print(f\"ip1 == ip2: {ip1 == ip2}\") # True\n",
"print(f\"hash(ip1): {hash(ip1)}\")\n",
"print(f\"hash(ip2): {hash(ip2)}\") # 应该与 hash(ip1) 相同\n",
"print(f\"hash(ip3): {hash(ip3)}\")\n",
"\n",
"point_set = {ip1, ip2, ip3}\n",
"print(f\"Set of points: {point_set}\") # {ImmutablePoint(1, 2), ImmutablePoint(3, 4)}\n",
"\n",
"point_dict = {ip1: \"Origin related\", ip3: \"Other point\"}\n",
"print(f\"Dict with points as keys: {point_dict}\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 5. 模拟数值类型的魔术方法\n",
"\n",
"这些方法允许你的对象参与算术运算。\n",
"\n",
"### 5.1 一元运算符和函数:\n",
"* `__pos__(self)`: `+self`\n",
"* `__neg__(self)`: `-self`\n",
"* `__abs__(self)`: `abs(self)`\n",
"* `__invert__(self)`: `~self` (按位取反)\n",
"* `__round__(self, ndigits)`: `round(self, ndigits)`\n",
"* `__floor__(self)`: `math.floor(self)`\n",
"* `__ceil__(self)`: `math.ceil(self)`\n",
"* `__trunc__(self)`: `math.trunc(self)`\n",
"\n",
"### 5.2 类型转换:\n",
"* `__bool__(self)`: `bool(self)` (对象在布尔上下文中的真值,如 `if obj:`)。如果未定义,则会检查 `__len__`。\n",
"* `__int__(self)`: `int(self)`\n",
"* `__float__(self)`: `float(self)`\n",
"* `__complex__(self)`: `complex(self)`\n",
"* `__index__(self)`: 当对象被用作序列索引时(如 `my_list[obj]`),Python 内部会尝试调用它来获取一个整数索引。如果未实现,则会尝试 `__int__`。主要用于确保对象可以安全地用作索引。\n",
"\n",
"### 5.3 二元算术运算符:\n",
"对于每个二元运算符 `op` (如 `+`, `-`, `*`, `/`, `//`, `%`, `**`, `<<`, `>>`, `&`, `^`, `|`),都有三个相关的魔术方法:\n",
"1. **标准 (Normal)**: `__op__(self, other)` (例如 `__add__(self, other)` for `self + other`)\n",
"2. **反射 (Reflected)**: `__rop__(self, other)` (例如 `__radd__(self, other)` for `other + self`)。\n",
" * 当 `other + self` 时,如果 `other` 没有实现 `__add__` 或者其 `__add__` 对 `self` 的类型返回 `NotImplemented`,Python 会尝试调用 `self.__radd__(other)`。\n",
"3. **原地 (In-place)**: `__iop__(self, other)` (例如 `__iadd__(self, other)` for `self += other`)\n",
" * 如果实现了,它会直接修改 `self` 并返回 `self`。\n",
" * 如果未实现 `__iadd__`,`self += other` 会回退到 `self = self.__add__(other)`。\n",
"\n",
"**例子:**\n",
"* `__add__(self, other)`: `self + other`\n",
"* `__sub__(self, other)`: `self - other`\n",
"* `__mul__(self, other)`: `self * other`\n",
"* `__matmul__(self, other)`: `self @ other` (Python 3.5+ 矩阵乘法)\n",
"* `__truediv__(self, other)`: `self / other` (真除法)\n",
"* `__floordiv__(self, other)`: `self // other` (地板除)\n",
"* `__mod__(self, other)`: `self % other`\n",
"* `__pow__(self, other[, modulo])`: `self ** other` or `pow(self, other, modulo)`\n",
"* `__lshift__(self, other)`: `self << other`\n",
"* `__rshift__(self, other)`: `self >> other`\n",
"* `__and__(self, other)`: `self & other`\n",
"* `__xor__(self, other)`: `self ^ other`\n",
"* `__or__(self, other)`: `self | other`\n",
"\n",
"对应的反射版本是 `__radd__`, `__rsub__`, etc. 和原地版本 `__iadd__`, `__isub__`, etc."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"class Vector:\n",
" def __init__(self, x, y):\n",
" self.x = x\n",
" self.y = y\n",
"\n",
" def __repr__(self):\n",
" return f\"Vector({self.x}, {self.y})\"\n",
"\n",
" def __str__(self):\n",
" return f\"({self.x}, {self.y})\"\n",
"\n",
" def __add__(self, other):\n",
" print(\"Vector.__add__ called\")\n",
" if isinstance(other, Vector):\n",
" return Vector(self.x + other.x, self.y + other.y)\n",
" elif isinstance(other, (int, float)):\n",
" return Vector(self.x + other, self.y + other)\n",
" return NotImplemented\n",
"\n",
" def __radd__(self, other):\n",
" # other + self, 当 other 不是 Vector 或其 __add__ 返回 NotImplemented 时调用\n",
" print(\"Vector.__radd__ called\")\n",
" # 这里的逻辑通常和 __add__ 中处理标量的情况类似\n",
" return self.__add__(other) # 可以直接复用 __add__\n",
"\n",
" def __iadd__(self, other):\n",
" print(\"Vector.__iadd__ called\")\n",
" if isinstance(other, Vector):\n",
" self.x += other.x\n",
" self.y += other.y\n",
" return self # 原地操作必须返回 self\n",
" elif isinstance(other, (int, float)):\n",
" self.x += other\n",
" self.y += other\n",
" return self\n",
" return NotImplemented\n",
"\n",
" def __mul__(self, scalar):\n",
" print(\"Vector.__mul__ called\")\n",
" if isinstance(scalar, (int, float)):\n",
" return Vector(self.x * scalar, self.y * scalar)\n",
" return NotImplemented\n",
"\n",
" def __rmul__(self, scalar):\n",
" print(\"Vector.__rmul__ called\")\n",
" return self.__mul__(scalar) # 乘法通常是可交换的\n",
"\n",
" def __len__(self):\n",
" # 随便定义一个长度,比如维度\n",
" print(\"Vector.__len__ called\")\n",
" return 2\n",
"\n",
" def __bool__(self):\n",
" # 如果向量不是零向量,则为 True\n",
" print(\"Vector.__bool__ called\")\n",
" return self.x != 0 or self.y != 0\n",
"\n",
"v1 = Vector(1, 2)\n",
"v2 = Vector(3, 4)\n",
"\n",
"print(f\"v1 + v2 = {v1 + v2}\") # (4, 6)\n",
"print(f\"v1 + 10 = {v1 + 10}\") # (11, 12)\n",
"print(f\"10 + v1 = {10 + v1}\") # (11, 12) - 调用 __radd__\n",
"\n",
"v_orig_id = id(v1)\n",
"v1 += v2\n",
"print(f\"v1 after += v2: {v1}\") # (4, 6)\n",
"print(f\"id(v1) changed: {id(v1) != v_orig_id}\") # False, 因为 __iadd__ 返回 self\n",
"\n",
"v3 = Vector(2,3)\n",
"print(f\"v3 * 3 = {v3 * 3}\") # (6,9)\n",
"print(f\"3 * v3 = {3 * v3}\") # (6,9) - 调用 __rmul__\n",
"\n",
"print(f\"len(v1): {len(v1)}\")\n",
"print(f\"bool(Vector(0,0)): {bool(Vector(0,0))}\")\n",
"print(f\"bool(Vector(1,0)): {bool(Vector(1,0))}\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 6. 模拟容器类型的魔术方法 (集合协议)\n",
"\n",
"这些方法使你的对象表现得像列表、字典或集合等容器。\n",
"\n",
"* `__len__(self)`: `len(self)`。返回容器中元素的数量。如果一个类没有定义 `__bool__`,但定义了 `__len__`,那么当 `len(obj) > 0` 时,`bool(obj)` 为 `True`。\n",
"* `__getitem__(self, key)`: `self[key]`。获取键对应的项。\n",
"* `__setitem__(self, key, value)`: `self[key] = value`。设置键对应的项。\n",
"* `__delitem__(self, key)`: `del self[key]`。删除键对应的项。\n",
"* `__iter__(self)`: `iter(self)`。应返回一个迭代器对象。\n",
"* `__reversed__(self)`: `reversed(self)`。应返回一个反向迭代器对象 (可选)。\n",
"* `__contains__(self, item)`: `item in self`。检查 `item` 是否在容器中。如果未实现,Python 会迭代容器并逐个比较。\n",
"\n",
"如果实现了 `__iter__`,你的对象就是**可迭代的 (iterable)**。\n",
"如果实现了 `__getitem__` 和 `__len__`,你的对象就是**序列 (sequence)**,并且也会自动支持迭代和 `in` 操作符(尽管显式实现 `__iter__` 和 `__contains__` 可能更高效)。"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"class MyCustomList:\n",
" def __init__(self, initial_data=None):\n",
" self._data = list(initial_data) if initial_data else []\n",
"\n",
" def __len__(self):\n",
" print(\"MyCustomList.__len__ called\")\n",
" return len(self._data)\n",
"\n",
" def __getitem__(self, index):\n",
" print(f\"MyCustomList.__getitem__ called with index: {index}\")\n",
" if isinstance(index, slice):\n",
" # 处理切片\n",
" return MyCustomList(self._data[index])\n",
" return self._data[index]\n",
"\n",
" def __setitem__(self, index, value):\n",
" print(f\"MyCustomList.__setitem__ called with index: {index}, value: {value}\")\n",
" self._data[index] = value\n",
"\n",
" def __delitem__(self, index):\n",
" print(f\"MyCustomList.__delitem__ called with index: {index}\")\n",
" del self._data[index]\n",
"\n",
" def __iter__(self):\n",
" print(\"MyCustomList.__iter__ called\")\n",
" # yield from self._data # 简单的迭代器\n",
" return iter(self._data) # 或者返回内置列表的迭代器\n",
"\n",
" def __contains__(self, item):\n",
" print(f\"MyCustomList.__contains__ called for item: {item}\")\n",
" return item in self._data\n",
"\n",
" def __repr__(self):\n",
" return f\"MyCustomList({self._data!r})\"\n",
"\n",
" def append(self, item):\n",
" self._data.append(item)\n",
"\n",
"my_list = MyCustomList([1, 2, 3])\n",
"print(f\"Length: {len(my_list)}\")\n",
"print(f\"Element at index 1: {my_list[1]}\")\n",
"my_list[0] = 10\n",
"print(f\"After setting index 0: {my_list}\")\n",
"del my_list[2]\n",
"print(f\"After deleting index 2: {my_list}\")\n",
"\n",
"print(\"\\nIterating over the list:\")\n",
"for item in my_list:\n",
" print(item)\n",
"\n",
"print(f\"\\nIs 10 in my_list? {10 in my_list}\")\n",
"print(f\"Is 5 in my_list? {5 in my_list}\")\n",
"\n",
"sliced = my_list[0:1]\n",
"print(f\"Slice [0:1]: {sliced}, type: {type(sliced)}\")\n",
"my_list.append(20)\n",
"print(f\"After append: {my_list}\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 7. 可调用对象 (Callable Objects)\n",
"\n",
"### 7.1 `__call__(self, *args, **kwargs)`\n",
"* **目的**:允许类的实例像函数一样被调用。\n",
"* **调用时机**:当对实例使用函数调用语法时,如 `obj(*args, **kwargs)`。"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"class Adder:\n",
" def __init__(self, base_value):\n",
" self.base_value = base_value\n",
" print(f\"Adder initialized with base_value: {base_value}\")\n",
"\n",
" def __call__(self, x, y):\n",
" print(f\"Adder.__call__ with x={x}, y={y}, base_value={self.base_value}\")\n",
" return self.base_value + x + y\n",
"\n",
"add_5 = Adder(5)\n",
"result = add_5(10, 20) # 调用 add_5.__call__(10, 20)\n",
"print(f\"Result: {result}\")\n",
"\n",
"add_100 = Adder(100)\n",
"result2 = add_100(1, 2)\n",
"print(f\"Result2: {result2}\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 8. 上下文管理器 (Context Management Protocol)\n",
"\n",
"这两个方法使得对象可以与 `with` 语句一起使用,用于管理资源(如文件、锁、数据库连接)。\n",
"\n",
"* `__enter__(self)`: 进入 `with` 语句块之前调用。它的返回值(如果有的话)会赋给 `as` 子句中的变量。\n",
"* `__exit__(self, exc_type, exc_val, exc_tb)`: 退出 `with` 语句块时调用,无论块内是否发生异常。\n",
" * `exc_type`, `exc_val`, `exc_tb`:如果块内发生异常,它们是异常的类型、值和回溯信息;否则都是 `None`。\n",
" * 如果 `__exit__` 返回 `True`,则异常被“抑制”(即不会向外传播)。如果返回 `False`(或 `None`,这是默认行为),异常会继续传播。"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"class ManagedResource:\n",
" def __init__(self, name):\n",
" self.name = name\n",
" print(f\"ManagedResource '{self.name}': Initialized\")\n",
"\n",
" def __enter__(self):\n",
" print(f\"ManagedResource '{self.name}': __enter__ (Acquiring resource)\")\n",
" # 模拟获取资源\n",
" return self # 返回的值赋给 'as' 后面的变量\n",
"\n",
" def __exit__(self, exc_type, exc_val, exc_tb):\n",
" print(f\"ManagedResource '{self.name}': __exit__ (Releasing resource)\")\n",
" if exc_type:\n",
" print(f\" Exception occurred: Type={exc_type}, Value={exc_val}\")\n",
" # return True # 如果返回 True,异常会被抑制\n",
" print(f\" Resource '{self.name}' released.\")\n",
" return False # 默认行为,如果发生异常,则继续传播\n",
"\n",
" def use(self):\n",
" print(f\" Using resource '{self.name}'...\")\n",
"\n",
"print(\"--- Context Manager: Normal execution ---\")\n",
"with ManagedResource(\"DB Connection\") as res:\n",
" res.use()\n",
" print(\" Inside with block.\")\n",
"print(\"After with block.\")\n",
"\n",
"print(\"\\n--- Context Manager: With exception ---\")\n",
"try:\n",
" with ManagedResource(\"File Stream\") as fs:\n",
" fs.use()\n",
" raise ValueError(\"Something went wrong inside 'with'!\")\n",
" print(\" This line won't be reached.\")\n",
"except ValueError as e:\n",
" print(f\"Caught expected error: {e}\")\n",
"print(\"After with block (with exception).\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 9. 描述符协议 (Descriptor Protocol)\n",
"\n",
"描述符是实现了特定协议的类,当其作为另一个类(宿主类)的类属性时,可以自定义对该属性的访问行为。这部分内容较为高级,在元编程教程中通常会详细介绍。\n",
"\n",
"* `__get__(self, instance, owner)`: 当获取属性值时调用。\n",
"* `__set__(self, instance, value)`: 当设置属性值时调用。\n",
"* `__delete__(self, instance)`: 当删除属性时调用。\n",
"\n",
"内置的 `property`, `@staticmethod`, `@classmethod` 都是基于描述符协议实现的。"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# 简单示例,详细内容请参考元编程教程\n",
"class RevealAccess:\n",
" def __init__(self, initval=None, name='var'):\n",
" self.val = initval\n",
" self.name = name\n",
"\n",
" def __get__(self, instance, owner):\n",
" print(f\"Descriptor '{self.name}': __get__ from instance {instance} (owner {owner})\")\n",
" return self.val\n",
"\n",
" def __set__(self, instance, value):\n",
" print(f\"Descriptor '{self.name}': __set__ on instance {instance} to {value}\")\n",
" self.val = value\n",
"\n",
"class MyClassWithDescriptor:\n",
" x = RevealAccess(10, 'x') # x 是一个描述符实例\n",
"\n",
"m = MyClassWithDescriptor()\n",
"print(f\"Value of m.x: {m.x}\")\n",
"m.x = 20\n",
"print(f\"New value of m.x: {m.x}\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 10. 异步编程的魔术方法 (Python 3.5+)\n",
"\n",
"这些方法用于支持 `async/await` 语法。\n",
"\n",
"* `__await__(self)`: 必须返回一个迭代器。用于使对象成为**可等待对象 (awaitable)**。`async def` 函数返回的协程对象就实现了 `__await__`。\n",
"* `__aiter__(self)`: 必须返回一个**异步迭代器 (asynchronous iterator)** 对象。用于 `async for`。\n",
"* `__anext__(self)`: 必须返回一个可等待对象,该可等待对象在完成时产生下一个值。当没有更多项时,必须引发 `StopAsyncIteration` 异常。由异步迭代器实现。\n",
"* `__aenter__(self)`: 类似于 `__enter__`,但用于异步上下文管理器 (`async with`)。必须返回一个可等待对象。\n",
"* `__aexit__(self, exc_type, exc_val, exc_tb)`: 类似于 `__exit__`,但用于异步上下文管理器。必须返回一个可等待对象。"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import asyncio\n",
"\n",
"# 异步迭代器示例\n",
"class AsyncCounter:\n",
" def __init__(self, limit):\n",
" self.limit = limit\n",
" self.current = 0\n",
"\n",
" def __aiter__(self):\n",
" return self\n",
"\n",
" async def __anext__(self):\n",
" if self.current < self.limit:\n",
" await asyncio.sleep(0.1) # 模拟异步操作\n",
" value = self.current\n",
" self.current += 1\n",
" return value\n",
" else:\n",
" raise StopAsyncIteration\n",
"\n",
"# 异步上下文管理器示例\n",
"class AsyncManaged:\n",
" async def __aenter__(self):\n",
" print(\"AsyncManaged: Entering context...\")\n",
" await asyncio.sleep(0.1)\n",
" print(\"AsyncManaged: Resource acquired.\")\n",
" return self\n",
"\n",
" async def __aexit__(self, exc_type, exc_val, exc_tb):\n",
" print(\"AsyncManaged: Exiting context...\")\n",
" await asyncio.sleep(0.1)\n",
" if exc_type:\n",
" print(f\" AsyncManaged: Exception occurred: {exc_val}\")\n",
" print(\"AsyncManaged: Resource released.\")\n",
"\n",
" async def do_work(self):\n",
" print(\" AsyncManaged: Doing work...\")\n",
" await asyncio.sleep(0.2)\n",
"\n",
"async def main_async_demo():\n",
" print(\"--- Async Iterator Demo ---\")\n",
" async for i in AsyncCounter(3):\n",
" print(f\" Got: {i}\")\n",
"\n",
" print(\"\\n--- Async Context Manager Demo ---\")\n",
" async with AsyncManaged() as am:\n",
" await am.do_work()\n",
"\n",
"# 在Jupyter Notebook中运行asyncio代码\n",
"# asyncio.run(main_async_demo()) # 如果直接运行会报错,因为Jupyter已经有事件循环\n",
"\n",
"# 对于Jupyter, 通常可以直接 await (如果IPython版本够新)\n",
"# 或者使用 nest_asyncio\n",
"# 为了简单起见,这里不直接运行,但代码结构是正确的\n",
"print(\"Async demo functions defined. Run in an async context.\")\n",
"\n",
"# 如果你想在 notebook cell 中直接运行, 你可以这样做:\n",
"# import nest_asyncio\n",
"# nest_asyncio.apply()\n",
"# asyncio.run(main_async_demo())"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 11. 其他一些有用的魔术属性和方法\n",
"\n",
"### 魔术属性 (通常由解释器设置和使用):\n",
"* `__doc__`: 对象的文档字符串。\n",
"* `__name__`: 类、函数、方法的名称 (字符串)。模块的 `__name__` 在直接运行时是 `\"__main__\"`。\n",
"* `__qualname__` (Python 3.3+): 类或函数的限定名称,包括其所在的命名空间路径。\n",
"* `__module__`: 类或函数所在的模块名称 (字符串)。\n",
"* `__class__`: 实例所属的类 (`obj.__class__` 与 `type(obj)` 相同)。\n",
"* `__dict__`: 存储对象(或类)可写属性的字典。不是所有对象都有 `__dict__` (例如,使用 `__slots__` 的类或某些内置类型)。\n",
"* `__slots__`: 一个字符串元组,用于声明固定的实例属性集。可以节省内存并加快属性访问速度,但实例将不再有 `__dict__` (除非 `__dict__` 明确包含在 `__slots__` 中),也不能动态添加新属性。\n",
"* `__bases__`: 类的父类元组。\n",
"* `__mro__`: 方法解析顺序 (Method Resolution Order) 元组,定义了类继承和方法查找的顺序。\n",
"\n",
"### 其他魔术方法:\n",
"* `__instancecheck__(self, instance)`: `isinstance(instance, cls)`。\n",
"* `__subclasscheck__(self, subclass)`: `issubclass(subclass, cls)`。\n",
" * 这两个通常在元类中实现,用于自定义 `isinstance()` 和 `issubclass()` 的行为。\n",
"* `__getstate__(self)` 和 `__setstate__(self, state)`: 用于控制对象如何被 `pickle` 模块序列化和反序列化。\n",
"* `__copy__(self)` 和 `__deepcopy__(self, memo)`: 用于支持 `copy.copy()` 和 `copy.deepcopy()`。\n",
"* `__class_getitem__(cls, key)` (Python 3.9+): 允许类支持泛型类型订阅,例如 `MyClass[int]`。"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"class MySlotsClass:\n",
" __slots__ = ('x', 'y') # 定义固定的实例属性\n",
" # __slots__ = ('x', 'y', '__dict__') # 如果想保留 __dict__\n",
" def __init__(self, x, y):\n",
" self.x = x\n",
" self.y = y\n",
"\n",
"slot_obj = MySlotsClass(1, 2)\n",
"print(f\"slot_obj.x: {slot_obj.x}\")\n",
"# print(slot_obj.__dict__) # 会报错 AttributeError,除非 __dict__ 在 __slots__ 中\n",
"try:\n",
" slot_obj.z = 3 # 不能动态添加属性\n",
"except AttributeError as e:\n",
" print(f\"Error adding new attribute to slots object: {e}\")\n",
"\n",
"print(f\"\\nBook class doc: {Book.__doc__ if Book.__doc__ else 'No docstring'}\")\n",
"print(f\"Book class name: {Book.__name__}\")\n",
"print(f\"Book class module: {Book.__module__}\")\n",
"print(f\"book1's class: {book1.__class__}\")\n",
"print(f\"book1's __dict__: {book1.__dict__}\")\n",
"print(f\"Point class bases: {Point.__bases__}\")\n",
"print(f\"Point class MRO: {Point.__mro__}\")\n",
"\n",
"# __class_getitem__ 示例 (Python 3.9+)\n",
"class GenericContainer:\n",
" def __class_getitem__(cls, item_type):\n",
" print(f\"GenericContainer.__class_getitem__ called with item_type: {item_type}\")\n",
" # 实际上这里应该返回一个新的泛型类或别名\n",
" # 为简单起见,我们只打印\n",
" return f\"GenericContainer specialized for {item_type}\"\n",
"\n",
"if hasattr(GenericContainer, '__class_getitem__'): # 检查是否在 Python 3.9+\n",
" specialized_container = GenericContainer[int]\n",
" print(specialized_container)\n",
" specialized_container_str = GenericContainer[str]\n",
" print(specialized_container_str)\n",
"else:\n",
" print(\"__class_getitem__ not available in this Python version.\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 总结\n",
"\n",
"魔术方法是 Python 强大且富有表现力的特性,它们使得自定义类能够深度集成到语言的核心机制中。\n",
"\n",
"**关键 takeaways:**\n",
"\n",
"* **按需实现**:你不需要实现所有的魔术方法,只需要实现那些对你的类有意义的方法。\n",
"* **理解协议**:了解每个魔术方法属于哪个协议(如序列、数字、上下文管理)有助于理解其目的。\n",
"* **`NotImplemented`**:在二元操作符(如 `__add__`, `__eq__`)中,如果你的方法不知道如何处理 `other` 参数的类型,应该返回 `NotImplemented`,这样 Python 可以尝试调用 `other` 对象的反射方法(如 `other.__radd__(self)`)。\n",
"* **查阅文档**:Python 官方文档的“数据模型”章节是关于魔术方法最权威和详细的参考。\n",
"\n",
"通过熟练运用这些特殊方法,你可以编写出更优雅、更 Pythonic、功能更丰富的类。"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.10.0"
}
},
"nbformat": 4,
"nbformat_minor": 5
}
Display the source blob
Display the rendered blob
Raw
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Display the source blob
Display the rendered blob
Raw
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Display the source blob
Display the rendered blob
Raw
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Display the source blob
Display the rendered blob
Raw
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Display the source blob
Display the rendered blob
Raw
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Display the source blob
Display the rendered blob
Raw
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Display the source blob
Display the rendered blob
Raw
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Display the source blob
Display the rendered blob
Raw
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Display the source blob
Display the rendered blob
Raw
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Display the source blob
Display the rendered blob
Raw
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Display the source blob
Display the rendered blob
Raw
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Display the source blob
Display the rendered blob
Raw
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Display the source blob
Display the rendered blob
Raw
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Display the source blob
Display the rendered blob
Raw
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Display the source blob
Display the rendered blob
Raw
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Display the source blob
Display the rendered blob
Raw
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Display the source blob
Display the rendered blob
Raw
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Display the source blob
Display the rendered blob
Raw
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Display the source blob
Display the rendered blob
Raw
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

对动态语言Python的一些感慨

众所周知Python是完全动态的语言,体现在

  1. 类型动态绑定
  2. 运行时检查
  3. 对象结构内容可动态修改(而不仅仅是值)
  4. 反射
  5. 一切皆对象(instance, class, method)
  6. 可动态执行代码(eval, exec)
  7. 鸭子类型支持

动态语言的约束更少,对使用者来说更易于入门,但相应的也会有代价就是运行时开销很大,和底层汇编执行逻辑完全解耦不知道代码到底是怎么执行的。

而且还有几点是我认为较为严重的缺陷。下面进行梳理。

破坏了OOP的语义

较为流行的编程语言大多支持OOP编程范式。即继承和多态。同样,Python在执行简单任务时候可以纯命令式(Imperative Programming),也可以使用复杂的面向对象OOP。

但是,其动态特性破环了OOP的结构:

  1. 类型模糊:任何类型实例,都可以在运行时添加或者删除属性或者方法(相比之下静态语言只能在运行时修改它们的值)。经此修改的实例,按理说不再属于原来的类型,毕竟和原类型已经有了明显的区别。但是该实例的内建__class__属性依旧会指向原类型,这会给类型的认知造成困惑。符合一个class不应该只是名义上符合,而是内容上也应该符合。
  2. 破坏继承:体现在以下两个方面
    1. 大部分实践没有虚接口继承。abc模块提供了虚接口的基类ABC,经典的做法是让自己的抽象类继承自ABC,然后具体类继承自自己的抽象类,然后去实现抽象方法。但PEP提案认为Pythonic的做法是用typing.Protocol来取代ABC,具体类完全不继承任何虚类,只要实现相应的方法,那么就可以被静态检查器认为是符合Protocol的。
    2. 不需要继承自具体父类。和上一条一样,即使一个类没有任何父类(除了object类),它依旧可以生成同名的方法,以实现和父类方法相同的调用接口。这样在语义逻辑上,类的定义完全看不出和其他类有何种关系。完全可以是一种松散的组织结构,任何两个类之间都没继承关系。
  3. 破坏多态:任何一个入参出参,天然不限制类型。这使得要求父类型的参数处,传入子类型显得没有意义,依旧是因为任何类型都能动态修改满足要求。

破坏了设计模式

经典的模式诸如工厂模式,抽象工厂,访问者模式,都严重依赖于继承和多态的性质。但是在python的设计中,其动态能力使得设计模式形同虚设。 大家常见的库中使用设计模式的有transformers库,其中的from_pretrained系列则是工厂模式,通过字符串名称确定了具体的构造器得到具体的子类。而工厂构造器的输出类型是一个所有模型的基类。

安全性问题

Python在代码层面一般不直接管理指针,所以指针越界,野指针,悬空指针等问题一般不存在。而gc机制也能自动处理垃圾回收使得编码过程不必关注这类安全性问题。但与之相对的,Python也有自己的安全性问题。以往非托管形式的代码的攻击难度较大,注入代码想要稳定执行需要避免破坏原来的结构导致程序直接崩溃(段错误)。 Python却可以直接注入任何代码修改原本的逻辑,并且由于不是在code段固定的内容,攻击时候也无需有额外考虑。运行时可以手动修改globals() locals()内容,亦有一定风险。 另一个危险则是类型不匹配导致的代码执行问题,因为只有在运行时才确定类型,无法提前做出保证,可能会产生类型错误的异常,造成程序崩溃。

总结

我出身于C++。但是近年来一直在用python编程。而且python的市场占有率已经多年第一,且遥遥领先。这和其灵活性分不开关系。对于一个面向大众的编程语言,这样的特性是必要的。即使以上说了诸多python的不严谨之处,但是对于程序员依旧可以选择严谨的面向对象写法。所以,程序的优劣不在于语言怎么样,而在于程序员本身。程序员有责任写出易于维护,清晰,规范的代码~

Display the source blob
Display the rendered blob
Raw
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Display the source blob
Display the rendered blob
Raw
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Display the source blob
Display the rendered blob
Raw
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
@KuRRe8
Copy link
Author

KuRRe8 commented May 8, 2025

返回顶部

有见解,有问题,或者单纯想盖楼灌水,都可以在这里发表!

因为文档比较多,有时候渲染不出来ipynb是浏览器性能的问题,刷新即可

或者git clone到本地来阅读

ChatGPT Image May 9, 2025, 04_45_04 AM

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment