{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# 类 —— Python 的实现" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "既然已经在不碰代码的情况下,把 OOP 中的主要概念梳理清楚了,以下的行文中,那些概念就直接用英文罢,省得理解上还得再绕个弯……" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Defining Class" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Class 使用 `class` 关键字进行定义。\n", "\n", "与函数定义不同的地方在于,Class 接收参数不是在 `class Classname():` 的括号里完成 —— 那个圆括号有另外的用处。\n", "\n", "让我们先看看代码,而后再逐一解释:" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'Clay'" ] }, "execution_count": 2, "metadata": {}, "output_type": "execute_result" }, { "data": { "text/plain": [ "2019" ] }, "execution_count": 2, "metadata": {}, "output_type": "execute_result" }, { "data": { "text/plain": [ ">" ] }, "execution_count": 2, "metadata": {}, "output_type": "execute_result" }, { "name": "stdout", "output_type": "stream", "text": [ "Hi!\n" ] }, { "data": { "text/plain": [ "__main__.Golem" ] }, "execution_count": 2, "metadata": {}, "output_type": "execute_result" }, { "data": { "text/plain": [ "str" ] }, "execution_count": 2, "metadata": {}, "output_type": "execute_result" }, { "data": { "text/plain": [ "int" ] }, "execution_count": 2, "metadata": {}, "output_type": "execute_result" }, { "data": { "text/plain": [ "method" ] }, "execution_count": 2, "metadata": {}, "output_type": "execute_result" }, { "data": { "text/plain": [ "method" ] }, "execution_count": 2, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from IPython.core.interactiveshell import InteractiveShell\n", "InteractiveShell.ast_node_interactivity = \"all\"\n", "import datetime\n", "\n", "class Golem:\n", " \n", " def __init__(self, name=None):\n", " self.name = name\n", " self.built_year = datetime.date.today().year\n", " \n", " def say_hi(self):\n", " print('Hi!')\n", " \n", "g = Golem('Clay')\n", "g.name\n", "g.built_year\n", "g.say_hi\n", "g.say_hi()\n", "type(g)\n", "type(g.name)\n", "type(g.built_year)\n", "type(g.__init__)\n", "type(g.say_hi)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "以上,我们创建了一个 Class:\n", "\n", "```python\n", "class Golem:\n", " \n", " def __init__(self, name=None):\n", " self.name = name\n", " self.built_year = datetime.date.today().year\n", "```\n", "\n", "其中定义了当我们根据这个 Class 创建一个实例的时候,那个 Object 的初始化过程,即 `__init__()` 函数 —— 又由于这个函数是在 Class 中定义的,我们称它为 Class 的一个 Method。\n", "\n", "这里的 `self` 就是个变量,跟程序中其它变量的区别在于,它是一个系统默认可以识别的变量,用来指代将来用这个 Class 创建的 Instance。\n", "\n", "比如,我们创建了 Golem 这个 Class 的一个 Instance,`g = Golem('Clay')` 之后,我们写 `g.name`,那么解析器就去找 `g` 这个实例所在的 Scope 里有没有 `self.name`……\n", "\n", "注意:`self` 这个变量的定义,是在 `def __init__(self, ...)` 这一句里完成的。对于这个变量的名称取名没有强制要求,你实际上可以随便用什么名字,很多 C 程序员会习惯于将这个变量命名为 `this` —— 但根据惯例,你最好还是只用 `self` 这个变量名,省得给别人造成误会。\n", "\n", "在 Class 的代码中,如果定义了 `__init__()` 函数,那么系统就会将它当作“ Instance 在被创建后初始化的函数”。这个函数名称是强制指定的,初始化函数必须使用这个名称;注意 `init` 两端各有两个下划线 `_`。\n", "\n", "当我们用 `g = Golem('Clay')` 这一句创建了一个 Golem 的 Instance 的时候,以下一连串的事情发生了:\n", "\n", "> * `g` 从此之后就是一个根据 Golem 这个 Class 创建的 Instance,对使用者来说,它就是个 Object;\n", "> * 因为 Golem 这个 Class 的代码中有 `__init__()`,所以,当 `g` 被创建的时候,`g` 就需要被初始化……\n", "> * 在 `g` 所在的变量目录中,出现了一个叫做 `self` 的用来指代 `g` 本身的变量;\n", "> * self.name 接收了一个参数,`'Clay'`,并将其保存了下来;\n", "> * 生成了一个叫做 `self.built_year` 的变量,其中保存的是 `g` 这个 Object 被创建时的年份……\n", "\n", "对了,Golem 和 Robot 一样,都是机器人的意思;Golem 的本义来自于犹太神话,一个被赋予了生命的泥人……" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Inheritance" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "我们刚刚创建了一个 Golem Class,如果我们想用它 Inherit 一个新的 Class,比如,`Running_Golem`,一个能跑的机器人,那就像以下的代码那样做 —— 注意 `class Running_Golem` 之后的圆括号:" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [ { "data": { "text/plain": [ ">" ] }, "execution_count": 2, "metadata": {}, "output_type": "execute_result" }, { "name": "stdout", "output_type": "stream", "text": [ "Can't you see? I'm running...\n" ] }, { "data": { "text/plain": [ "'Clay'" ] }, "execution_count": 2, "metadata": {}, "output_type": "execute_result" }, { "data": { "text/plain": [ "2019" ] }, "execution_count": 2, "metadata": {}, "output_type": "execute_result" }, { "name": "stdout", "output_type": "stream", "text": [ "Hi!\n" ] } ], "source": [ "from IPython.core.interactiveshell import InteractiveShell\n", "InteractiveShell.ast_node_interactivity = \"all\"\n", "import datetime\n", "\n", "class Golem:\n", " \n", " def __init__(self, name=None):\n", " self.name = name\n", " self.built_year = datetime.date.today().year\n", " \n", " def say_hi(self):\n", " print('Hi!')\n", "\n", "class Running_Golem(Golem): # 刚刚就说,这个圆括号另有用途……\n", " \n", " def run(self):\n", " print(\"Can't you see? I'm running...\")\n", "\n", "rg = Running_Golem('Clay')\n", "\n", "rg.run\n", "rg.run()\n", "rg.name\n", "rg.built_year\n", "rg.say_hi()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "如此这般,我们根据 Golem 这个 Class 创造了一个 Subclass —— `Running_Golem`,既然它是 Golem 的 Inheritance,那么 Golem 有的 Attributes 和 Methods 它都有,并且还多了一个 Method —— `self.run`。" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Overrides" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "当我们创建一个 Inherited Class 的时候,可以重写(Overriding)Parent Class 中的 Methods。比如这样:" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "data": { "text/plain": [ ">" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" }, { "name": "stdout", "output_type": "stream", "text": [ "Can't you see? I'm running...\n" ] }, { "data": { "text/plain": [ "'Clay'" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" }, { "data": { "text/plain": [ "2019" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" }, { "name": "stdout", "output_type": "stream", "text": [ "Hey! Nice day, Huh?\n" ] } ], "source": [ "from IPython.core.interactiveshell import InteractiveShell\n", "InteractiveShell.ast_node_interactivity = \"all\"\n", "import datetime\n", "\n", "class Golem:\n", " \n", " def __init__(self, name=None):\n", " self.name = name\n", " self.built_year = datetime.date.today().year\n", " \n", " def say_hi(self):\n", " print('Hi!')\n", "\n", "class runningGolem(Golem):\n", " \n", " def run(self):\n", " print(\"Can't you see? I'm running...\")\n", " \n", " def say_hi(self): # 不再使用 Parent Class 中的定义,而是新的……\n", " print('Hey! Nice day, Huh?')\n", "\n", "rg = runningGolem('Clay')\n", "rg.run\n", "rg.run()\n", "rg.name\n", "rg.built_year\n", "rg.say_hi()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Inspecting A Class" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "当我们作为用户想了解一个 Class 的 Interface,即,它的 Attributes 和 Methods 的时候,常用的有三种方式:\n", "\n", "```python\n", "1. help(object)\n", "2. dir(object)\n", "3. object.__dict__\n", "```" ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "scrolled": true }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Help on runningGolem in module __main__ object:\n", "\n", "class runningGolem(Golem)\n", " | runningGolem(name=None)\n", " | \n", " | Method resolution order:\n", " | runningGolem\n", " | Golem\n", " | builtins.object\n", " | \n", " | Methods defined here:\n", " | \n", " | run(self)\n", " | \n", " | say_hi(self)\n", " | \n", " | ----------------------------------------------------------------------\n", " | Methods inherited from Golem:\n", " | \n", " | __init__(self, name=None)\n", " | Initialize self. See help(type(self)) for accurate signature.\n", " | \n", " | ----------------------------------------------------------------------\n", " | Data descriptors inherited from Golem:\n", " | \n", " | __dict__\n", " | dictionary for instance variables (if defined)\n", " | \n", " | __weakref__\n", " | list of weak references to the object (if defined)\n", "\n" ] }, { "data": { "text/plain": [ "['__class__',\n", " '__delattr__',\n", " '__dict__',\n", " '__dir__',\n", " '__doc__',\n", " '__eq__',\n", " '__format__',\n", " '__ge__',\n", " '__getattribute__',\n", " '__gt__',\n", " '__hash__',\n", " '__init__',\n", " '__init_subclass__',\n", " '__le__',\n", " '__lt__',\n", " '__module__',\n", " '__ne__',\n", " '__new__',\n", " '__reduce__',\n", " '__reduce_ex__',\n", " '__repr__',\n", " '__setattr__',\n", " '__sizeof__',\n", " '__str__',\n", " '__subclasshook__',\n", " '__weakref__',\n", " 'built_year',\n", " 'name',\n", " 'run',\n", " 'say_hi']" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" }, { "data": { "text/plain": [ "{'name': 'Clay', 'built_year': 2019}" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" }, { "data": { "text/plain": [ "True" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from IPython.core.interactiveshell import InteractiveShell\n", "InteractiveShell.ast_node_interactivity = \"all\"\n", "import datetime\n", "\n", "class Golem:\n", " \n", " def __init__(self, name=None):\n", " self.name = name\n", " self.built_year = datetime.date.today().year\n", " \n", " def say_hi(self):\n", " print('Hi!')\n", "\n", "class runningGolem(Golem):\n", " \n", " def run(self):\n", " print('Can\\'t you see? I\\'m running...')\n", " \n", " def say_hi(self): # 不再使用 Parent Class 中的定义,而是新的……\n", " print('Hey! Nice day, Huh?')\n", "\n", "rg = runningGolem('Clay')\n", "help(rg)\n", "dir(rg)\n", "rg.__dict__\n", "hasattr(rg, 'built_year')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Scope" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "每个变量都属于某一个 **Scope**(变量的作用域),在同一个 Scope 中,变量可以被引用被操作…… 这么说非常抽象,难以理解 —— 只能通过例子说明。\n", "\n", "我们先给 Golem 这个 Class 增加一点功能 —— 我们需要随时知道究竟有多少个 Golem 处于活跃状态…… 也因此顺带给 Golem 加上一个 Method:`cease()` —— 哈!机器人么,想关掉它,说关掉它,就能关掉它;\n", "\n", "另外,我们还要给机器人设置个使用年限,比如 10 年;\n", "\n", "…… 而外部会每隔一段时间,用 `Golem.is_active()` 去检查所有的机器人,所以,不需要外部额外操作,到了年头,它应该能关掉自己。—— 当然,又由于以下代码是简化书写的,核心目的是为了讲解 Scope,所以并没有专门写模拟 10 年后某些机器人自动关闭的情形……\n", "\n", "在运行以下代码之前,需要先介绍三个 Python 的内建函数:\n", "\n", "> * `hasattr(object, attr)` 查询这个 `object` 中有没有这个 `attr`,返回布尔值\n", "> * `getattr(object, attr)` 获取这个 `object` 中这个 `attr` 的值\n", "> * `setattr(object, attr, value)` 将这个 `object` 中的 `attr` 值设置为 `value`\n", "\n", "现在的你,应该一眼望过去,就已经能掌握这三个内建函数的用法 —— 还记得之前的你吗?眼睁睁看着,那些字母放在那里对你来说没任何意义…… 这才多久啊!" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "True" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" }, { "data": { "text/plain": [ "True" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" }, { "data": { "text/plain": [ "False" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" }, { "data": { "text/plain": [ "False" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" }, { "data": { "text/plain": [ "False" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" }, { "data": { "text/plain": [ "1" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" }, { "data": { "text/plain": [ "10" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" }, { "data": { "text/plain": [ "11" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" }, { "data": { "text/plain": [ "10" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" }, { "data": { "text/plain": [ "10" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" }, { "data": { "text/plain": [ "True" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from IPython.core.interactiveshell import InteractiveShell\n", "InteractiveShell.ast_node_interactivity = \"all\"\n", "import datetime\n", "\n", "class Golem:\n", " population = 0\n", " __life_span = 10\n", " \n", " def __init__(self, name=None):\n", " self.name = name\n", " self.built_year = datetime.date.today().year\n", " self.__active = True\n", " Golem.population += 1 # 执行一遍之后,试试把这句改成 population += 1\n", " \n", " def say_hi(self):\n", " print('Hi!')\n", " \n", " def cease(self):\n", " self.__active = False\n", " Golem.population -= 1\n", " \n", " def is_active(self):\n", " if datetime.date.today().year - self.built_year >= Golem.__life_span:\n", " self.cease()\n", " return self.__active\n", "\n", "g = Golem()\n", "hasattr(Golem, 'population') # True\n", "hasattr(g, 'population') # True\n", "hasattr(Golem, '__life_span') # False\n", "hasattr(g, '__life_span') # False\n", "hasattr(g, '__active') # False\n", "Golem.population # 1\n", "setattr(Golem, 'population', 10) \n", "Golem.population # 10\n", "x = Golem()\n", "Golem.population # 11\n", "x.cease()\n", "Golem.population # 10\n", "getattr(g, 'population') # 10\n", "g.is_active()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "如果你试过把第 13 行的 `Golem.population += 1` 改成 `population += 1`,你会被如下信息提醒:\n", "\n", "```python\n", " 12 self.__active = True\n", "---> 13 population += 1\n", "UnboundLocalError: local variable 'population' referenced before assignment\n", "```\n", "—— 本地变量 `population` 尚未赋值,就已经提前被引用…… 为什么会这样呢?因为在你所创建 `g` 之后,马上执行的是 `__init()__` 这个初始化函数,而 `population` 是在这个函数之外定义的……\n", "\n", "如果你足够细心,你会发现这个版本中,有些变量前面有两个下划线 `__`,比如,`__life_span` 和 `self.__active`。这是 Python 的定义,变量名前面加上一个以上下划线(Underscore)`_` 的话,那么该变量是 “私有变量”(Private Variables),不能被外部引用。而按照 Python 的惯例,我们会使用两个下划线起始,去命名私有变量,如:`__life_span`。你可以回去试试,把所有的 `__life_span` 改成 `_life_span`(即,变量名开头只有一个 `_`,那么,`hasattr(Golem, '_life_span')` 和 `hasattr(g, '_life_span')` 的返回值就都变成了 `True`。\n", "\n", "看看下面的图示,理解起来更为直观一些,其中每个方框代表一个 Scope:\n", "\n", "![](images/class-variables-scope.png)\n", "\n", "整个代码启动之后,总计有 4 个 Scopes 如图所示:\n", "\n", "> * ① `class Golem` 之外;\n", "> * ② `class Golem` 之内;\n", "> * ③ `__init__(self, name=None)` 之内;\n", "> * ④ `cease(self)` 之内;\n", "\n", "在 Scope ① 中,可以引用 `Golem.population`,在生成一个 Golem 的实例 `g` 之后,也可以引用 `g.population`;但 `Golem.__life_span` 和 `g.__active` 在 Scope ① 是不存在的;\n", "\n", "在 Scope ② 中,存在两个变量,`population` 和 `__life_span`;而 `__life_span` 是 Private(私有变量,因为它的变量名中前两个字符是下划线 `__`;于是,在 Scope ② 中,不存在 `Golem.__life_span` —— `hasattr(Golem, '__life_span')` 的值为 `False`;\n", "\n", "在 Scope ③ 中和 Scope ④ 中,由于都给它们传递了 `self` 这个参数,于是,在这两个 Scope 里,都可以引用 `self.xxx`,比如 `self.population`,比如 `self.__life_span`;\n", "\n", "在 Scope ③ 中,`population` 是不存在的,如果需要引用这个值,可以用 `Golem.population`,也可以用 `self.population`。同样的道理,在 Scope ③ 中 `__life_span` 也不存在,如果想用这个值,可以用 `Golem.__life_span` 或者 `self.__life_span`;\n", "\n", "Scope ④ 与 Scope ③ 平行存在。所以在这里,`population` 和 `__life_span` 也同样并不存在。\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**补充**\n", "\n", "在本例子中,在 `__init__(self, name=None)` 函数中 `self.population` 和 `Golem.population` 都可以使用,但使用效果是不一样的:\n", "> * `self.population` 总是去读取 `Golem` 类中 `population` 的初始值,即使后面通过 `setattr(Golem, 'population', 10)` 更改 `population` 的值后,`self.population` 的值仍为 `0`,但 `Golem.population` 值则为 `10`,你可以自己动手尝试一下。" ] }, { "cell_type": "markdown", "metadata": { "toc-hr-collapsed": true }, "source": [ "## Encapsulation" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "到目前为止,Golem 这个 Class 看起来不错,但有个问题,它里面的数据,外面是可以随便改的 —— 虽然,我们已经通过给变量 life_span 前面加上两个下划线,变成 `__life_span`,使其成为私有变量,外部不能触达(你不能引用 `Golem.__life_span`),可 Golem.population 就不一样,外面随时可以引用,还可以随时修改它,只需要写上一句:\n", "\n", "```python\n", "Golem.population = 1000000\n", "```\n", "\n", "我们干脆把 `population` 这个变量也改成私有的罢:`__population`,而后需要从外界查看这个变量的话,就在 Class 里面写个函数,返回那个值好了:" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "data": { "text/plain": [ ">" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" }, { "data": { "text/plain": [ "1" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from IPython.core.interactiveshell import InteractiveShell\n", "InteractiveShell.ast_node_interactivity = \"all\"\n", "import datetime\n", "\n", "class Golem:\n", " __population = 0\n", " __life_span = 10\n", " \n", " def __init__(self, name=None):\n", " self.name = name\n", " self.built_year = datetime.date.today().year\n", " self.__active = True\n", " Golem.__population += 1\n", " \n", " def say_hi(self):\n", " print('Hi!')\n", " \n", " def cease(self):\n", " self.__active = False\n", " Golem.__population -= 1\n", " \n", " def is_active(self):\n", " if datetime.date.today().year - self.built_year >= Golem.__life_span:\n", " self.cease\n", " return self.__active\n", " \n", " def population(self):\n", " return Golem.__population\n", "\n", "g = Golem('Clay')\n", "g.population\n", "g.population()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "如果,你希望外部能够像获得 Class 的属性那样,直接写 `g.population`,而不是必须加上一个括号 `g.population()` 传递参数(实际上传递了一个隐含的 `self` 参数),那么可以在 `def population(self):` 之前的一行加上一句 `@property`:\n", "\n", "```python\n", "class Golem:\n", " __population = 0\n", " ...\n", " \n", " @property\n", " def population(self):\n", " return Golem.__population\n", "```\n", "\n", "如此这般之后,你就可以用 `g.population` 了:" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "1" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from IPython.core.interactiveshell import InteractiveShell\n", "InteractiveShell.ast_node_interactivity = \"all\"\n", "import datetime\n", "\n", "class Golem:\n", " __population = 0\n", " __life_span = 10\n", " \n", " def __init__(self, name=None):\n", " self.name = name\n", " self.built_year = datetime.date.today().year\n", " self.__active = True\n", " Golem.__population += 1\n", " \n", " def say_hi(self):\n", " print('Hi!')\n", " \n", " def cease(self):\n", " self.__active = False\n", " Golem.__population -= 1\n", " \n", " def is_active(self):\n", " if datetime.date.today().year - self.built_year >= Golem.__life_span:\n", " self.cease\n", " return self.__active\n", " \n", " @property\n", " def population(self):\n", " return Golem.__population\n", "\n", "g = Golem('Clay')\n", "g.population\n", "# g.population = 100" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "如此这般之后,不仅你可以直接引用 `g.population`,并且,在外部不能再直接给 `g.population` 赋值了,否则会报错:\n", "\n", "```python\n", "---------------------------------------------------------------------------\n", "AttributeError Traceback (most recent call last)\n", " in \n", " 26 g = Golem('Clay')\n", " 27 g.population\n", "---> 28 g.population = 100\n", "\n", "AttributeError: can't set attribute\n", "```\n", "\n", "到此为止,Encapsulation 就做得不错了。\n", "\n", "如果你非得希望从外部可以设置这个值,那么,你就得再写个函数,并且在函数之前加上一句:\n", "```python\n", " ...\n", " \n", " @property\n", " def population(self):\n", " return Golem.__population\n", " \n", " @population.setter\n", " def population(self, value):\n", " Golem.__population = value\n", "\n", "```\n", "\n", "这样之后,`.population` 这个 Attribute 就可以从外部被设定其值了(虽然在当前的例子中显得没必要让外部设定 `__population` 这个值…… 以下仅仅是为了举例):" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "1" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" }, { "data": { "text/plain": [ "101" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" }, { "data": { "text/plain": [ "101" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" }, { "name": "stdout", "output_type": "stream", "text": [ "Help on class Golem in module __main__:\n", "\n", "class Golem(builtins.object)\n", " | Golem(name=None)\n", " | \n", " | Methods defined here:\n", " | \n", " | __init__(self, name=None)\n", " | Initialize self. See help(type(self)) for accurate signature.\n", " | \n", " | cease(self)\n", " | \n", " | is_active(self)\n", " | \n", " | say_hi(self)\n", " | \n", " | ----------------------------------------------------------------------\n", " | Data descriptors defined here:\n", " | \n", " | __dict__\n", " | dictionary for instance variables (if defined)\n", " | \n", " | __weakref__\n", " | list of weak references to the object (if defined)\n", " | \n", " | population\n", "\n" ] }, { "data": { "text/plain": [ "mappingproxy({'__module__': '__main__',\n", " '_Golem__population': 101,\n", " '_Golem__life_span': 10,\n", " '__init__': ,\n", " 'say_hi': ,\n", " 'cease': ,\n", " 'is_active': ,\n", " 'population': ,\n", " '__dict__': ,\n", " '__weakref__': ,\n", " '__doc__': None})" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" }, { "data": { "text/plain": [ "{'name': 'Clay', 'built_year': 2019, '_Golem__active': True}" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" }, { "data": { "text/plain": [ "True" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" }, { "data": { "text/plain": [ "" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" }, { "data": { "text/plain": [ "10000" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from IPython.core.interactiveshell import InteractiveShell\n", "InteractiveShell.ast_node_interactivity = \"all\"\n", "import datetime\n", "\n", "class Golem:\n", " __population = 0\n", " __life_span = 10\n", " \n", " def __init__(self, name=None):\n", " self.name = name\n", " self.built_year = datetime.date.today().year\n", " self.__active = True\n", " Golem.__population += 1\n", " \n", " def say_hi(self):\n", " print('Hi!')\n", " \n", " def cease(self):\n", " self.__active = False\n", " Golem.__population -= 1\n", " \n", " def is_active(self):\n", " if datetime.date.today().year - self.built_year >= Golem.__life_span:\n", " self.cease\n", " return self.__active\n", " \n", " @property\n", " def population(self):\n", " return Golem.__population\n", " \n", " @population.setter\n", " def population(self, value):\n", " Golem.__population = value\n", "\n", "g = Golem('Clay')\n", "g.population\n", "g.population = 100\n", "ga = Golem('New')\n", "g.population\n", "ga.population\n", "help(Golem)\n", "Golem.__dict__\n", "g.__dict__\n", "hasattr(Golem, 'population')\n", "getattr(Golem, 'population')\n", "setattr(Golem, 'population', 10000)\n", "g.population # 所以,在很多的情况下,不把数据封装在 Class 内部的话,后面会有很多麻烦。" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Next Page" ] } ], "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.9.7" }, "toc-autonumbering": true }, "nbformat": 4, "nbformat_minor": 4 }