{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 类和实例\n",
    "\n",
    "面向对象最重要的概念就是类（Class）和实例（Instance），必须牢记类是抽象的模板，比如Student类，而实例是根据类创建出来的一个个具体的“对象”，每个对象都拥有相同的方法，但各自的数据可能不同。\n",
    "\n",
    "仍以Student类为例，在Python中，定义类是通过**class关键字**：\n",
    "\n",
    "class Student(object):\n",
    "    pass\n",
    "\n",
    "class后面紧接着是类名，即Student，类名通常是大写开头的单词，紧接着是(object)，表示该类是从哪个类继承下来的，继承的概念我们后面再讲，通常，如果没有合适的继承类，就使用object类，这是所有类最终都会继承的类。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 定义一个空类\n",
    "定义好了**Student类**，就可以根据Student类创建出**Student的实例**，创建实例是通过类名+()实现的："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Student(object):\n",
    "    pass"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Student:\n",
    "    pass"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "class后面紧接着是类名，即Student，类名通常是大写开头的单词，紧接着是**(object)**，表示该类是**从哪个类继承**下来的，继承的概念我们后面再讲，通常，如果没有合适的继承类，就使用object类，这是所有类最终都会继承的类。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 生成类的实例\n",
    "定义好了Student类，就可以根据Student类创建出Student的实例，创建实例是通过类名+()实现的："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<__main__.Student at 0x24e0eb11e48>"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "bart = Student()\n",
    "bart"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "__main__.Student"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "Student"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "可以看到，变量**bart**指向的就是一个**Student的实例**，后面的0x10a67a590是内存地址，每个object的地址都不一样，而Student本身则是一个类。\n",
    "\n",
    "### 自由地给一个**实例变量动态绑定属性**\n",
    "\n",
    "比如，给实例**bart绑定一个name**属性：\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'Bart Simpson'"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "bart.name = 'Bart Simpson'\n",
    "bart.name"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 构造函数__init__()\n",
    "**类可以起到模板**的作用，因此，可以在创建实例的时候，把一些我们认为必须绑定的属性强制填写进去。<br/>\n",
    "通过定义一个特殊的__init__方法，在创建实例的时候，就把name，score等属性绑上去："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Student(object):\n",
    "    #注意：特殊方法“__init__”前后分别有两个下划线！！！ \n",
    "    def __init__(self, name, score):\n",
    "        self.name = name\n",
    "        self.score = score"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "注意到**__init__方法的第一个参数永远是self**，表示创建的实例本身，因此，在__init__方法内部，就可以把各种属性绑定到self，因为self就指向创建的实例本身。\n",
    "\n",
    "有了__init__方法，在创建实例的时候，就不能传入空的参数了，必须传入与__init__方法匹配的参数，但self不需要传，Python解释器自己会把实例变量传进去："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'Bart Simpson'"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "bart = Student('Bart Simpson', 59)\n",
    "bart.name"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "59"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "bart.score"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "和普通的函数相比，在**类中定义的函数只有一点不同，就是第一个参数永远是实例变量self**，并且，调用时，不用传递该参数。除此之外，类的方法和普通函数没有什么区别，\n",
    "所以，你仍然可以用默认参数、可变参数、关键字参数和命名关键字参数。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### **数据封装**\n",
    "\n",
    "面向对象编程的一个重要特点就是数据封装。在上面的Student类中，每个实例就拥有**各自的name和score**这些数据。\n",
    "\n",
    "可以**通过函数**来访问这些数据，比如打印一个学生的成绩："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Bart Simpson: 59\n"
     ]
    }
   ],
   "source": [
    "def print_score(std):\n",
    "     print('%s: %s' % (std.name, std.score))\n",
    "\n",
    "print_score(bart)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "但是，既然**Student实例本身就拥有这些数据**，要访问这些数据，就没有必要从外面的函数去访问，可以**直接在Student类的内部**定义访问数据的函数，这样，就把“数据”给封装起来了。<br/>\n",
    "这些封装数据的函数是和Student类本身是关联起来的，我们称之为类的方法："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Student(object):\n",
    "\n",
    "    def __init__(self, name, score):\n",
    "        self.name = name\n",
    "        self.score = score\n",
    "\n",
    "    def print_score(self):\n",
    "        print('%s: %s' % (self.name, self.score))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "要定义一个方法，除了第一个参数是self外，其他和普通函数一样。要调用一个方法，只需要在实例变量上直接调用，除了self不用传递，其他参数正常传入："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Bart Simpson: 59\n"
     ]
    }
   ],
   "source": [
    "bart = Student('Bart Simpson', 59)\n",
    "bart.print_score()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "这样一来，我们从外部看Student类，就只需要知道，创建实例需要给出name和score，而如何打印，都是在Student类的内部定义的，\n",
    "这些数据和逻辑被“封装”起来了，调用很容易，但却不用知道内部实现的细节。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "封装的另一个好处是可以给**Student类增加新的方法**，比如get_grade："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Student(object):\n",
    "\n",
    "    def get_grade(self):\n",
    "        if self.score >= 90:\n",
    "            return 'A'\n",
    "        elif self.score >= 60:\n",
    "            return 'B'\n",
    "        else:\n",
    "            return 'C'"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "同样的，get_grade方法可以直接在实例变量上调用，不需要知道内部实现细节："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Student(object):\n",
    "    def __init__(self, name, score):\n",
    "        self.name = name\n",
    "        self.score = score\n",
    "\n",
    "    def get_grade(self):\n",
    "        if self.score >= 90:\n",
    "            return 'A'\n",
    "        elif self.score >= 60:\n",
    "            return 'B'\n",
    "        else:\n",
    "            return 'C'"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Lisa A\n",
      "Bart C\n"
     ]
    }
   ],
   "source": [
    "lisa = Student('Lisa', 99)\n",
    "bart = Student('Bart', 59)\n",
    "print(lisa.name, lisa.get_grade())\n",
    "print(bart.name, bart.get_grade())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": true
   },
   "source": [
    "# 访问限制,私有属性和公有属性\n",
    "\n",
    "\n",
    "在Class内部，可以有属性和方法，而外部代码可以通过直接调用实例变量的方法来操作数据，这样，就隐藏了内部的复杂逻辑。\n",
    "\n",
    "### 实例属性\n",
    "Student类的定义来看，外部代码还是可以自由地修改一个实例的name、score属性："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Student(object):\n",
    "    def __init__(self, name, score):\n",
    "        self.name = name\n",
    "        self.score = score\n",
    "\n",
    "    def get_grade(self):\n",
    "        if self.score >= 90:\n",
    "            return 'A'\n",
    "        elif self.score >= 60:\n",
    "            return 'B'\n",
    "        else:\n",
    "            return 'C'"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "59"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "bart = Student('Bart Simpson', 59)\n",
    "bart.score"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "99"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "bart.score = 99\n",
    "bart.score"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 私有属性\n",
    "如果要让内部属性不被外部访问，可以把属性的名称前加上**两个下划线__**，在Python中，实例的变量名如果以__开头，就变成了一个私有变量（private），\n",
    "只有内部可以访问，外部不能访问，所以，我们把Student类改一改："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Student(object):\n",
    "\n",
    "    def __init__(self, name, score):\n",
    "        self.__name = name\n",
    "        self.__score = score\n",
    "\n",
    "    def print_score(self):\n",
    "        print('%s: %s' % (self.__name, self.__score))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "改完后，对于外部代码来说，没什么变动，但是已经无法从外部访问实例变量.__name和实例变量.__score了："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [
    {
     "ename": "AttributeError",
     "evalue": "'Student' object has no attribute '__score'",
     "output_type": "error",
     "traceback": [
      "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[1;31mAttributeError\u001b[0m                            Traceback (most recent call last)",
      "\u001b[1;32m<ipython-input-21-3fcac74db37e>\u001b[0m in \u001b[0;36m<module>\u001b[1;34m\u001b[0m\n\u001b[0;32m      1\u001b[0m \u001b[0mbart\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mStudent\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;34m'Bart Simpson'\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;36m59\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m      2\u001b[0m \u001b[1;31m# bart.__name = 'BS'  # 动态绑定了__name\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m----> 3\u001b[1;33m \u001b[0mbart\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0m__score\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[1;31mAttributeError\u001b[0m: 'Student' object has no attribute '__score'"
     ]
    }
   ],
   "source": [
    "bart = Student('Bart Simpson', 59)\n",
    "# bart.__name = 'BS'  # 动态绑定了__name\n",
    "bart.__score"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "这样就确保了外部代码不能随意修改对象内部的状态，这样通过访问限制的保护，代码更加健壮。\n",
    "\n",
    "但是如果外部代码要**获取name和score**怎么办？可以给Student类增加get_name和get_score这样的方法："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Student(object):\n",
    "    def __init__(self, name, score):\n",
    "        self.__name = name\n",
    "        self.__score = score\n",
    "    def print_score(self):\n",
    "        print('%s: %s' % (self.__name, self.__score))\n",
    "        \n",
    "    def get_name(self):\n",
    "        return self.__name\n",
    "    def get_score(self):\n",
    "        return self.__score"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "如果又要允许**外部代码修改score**怎么办？可以再给Student类增加set_score方法："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Student(object):\n",
    "    def __init__(self, name, score):\n",
    "        self.__name = name\n",
    "        self.__score = score\n",
    "    def print_score(self):\n",
    "        print('%s: %s' % (self.__name, self.__score))\n",
    "        \n",
    "    def get_name(self):\n",
    "        return self.__name\n",
    "    def get_score(self):\n",
    "        return self.__score\n",
    "    \n",
    "    def set_score(self, score):\n",
    "        self.__score = score"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "你也许会问，原先那种直接通过bart.score = 99也可以修改啊，为什么要定义一个方法大费周折？<br/>\n",
    "因为在方法中，可以对参数做检查，避免传入无效的参数："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Student(object):\n",
    "    def __init__(self, name, score):\n",
    "        self.__name = name\n",
    "        self.__score = score\n",
    "    def print_score(self):\n",
    "        print('%s: %s' % (self.__name, self.__score))\n",
    "        \n",
    "    def get_name(self):\n",
    "        return self.__name\n",
    "    def get_score(self):\n",
    "        return self.__score\n",
    "    \n",
    "    def set_score(self, score):\n",
    "        if 0 <= score <= 100:\n",
    "            self.__score = score\n",
    "        else:\n",
    "            raise ValueError('bad score')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**需要注意:<br/>**\n",
    "在Python中，变量名类似__xxx__的，也就是以**双下划线开头，并且以双下划线结尾的，是特殊变量**，特殊变量是可以直接访问的，不是private变量，所以，不能用__name__、__score__这样的变量名。\n",
    "\n",
    "有些时候，你会看到以**一个下划线开头的实例变量名**，比如_name，这样的实例变量外部是可以访问的，但是，按照约定俗成的规定，当你看到这样的变量时，意思就是，“虽然我可以被访问，但是，请把我视为私有变量，不要随意访问”。\n",
    "\n",
    "双下划线开头的实例变量是不是一定不能从外部访问呢？其实也不是。不能直接访问__name是因为Python解释器对外把__name变量改成了_Student__name，所以，**仍然可以通过_Student__name来访问__name变量**："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'Bart Simpson'"
      ]
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "bart = Student('Bart Simpson', 59)\n",
    "bart._Student__name"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "59"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "bart._Student__score"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "但是强烈建议你不要这么干，因为不同版本的Python解释器可能会把__name改成不同的变量名。\n",
    "\n",
    "总的来说就是，Python本身没有任何机制阻止你干坏事，一切全靠自觉。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'Bart Simpson'"
      ]
     },
     "execution_count": 13,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "bart = Student('Bart Simpson', 59)\n",
    "bart.get_name()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'New Name'"
      ]
     },
     "execution_count": 26,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "bart.__name = 'New Name' # 设置__name变量！\n",
    "bart.__name"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "表面上看，外部代码“成功”地设置了__name变量，但实际上这个__name变量和class内部的__name变量不是一个变量！内部的__name变量已经被Python解释器自动改成了_Student__name，而外部代码给bart新增了一个__name变量。不信试试："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'Bart Simpson'"
      ]
     },
     "execution_count": 27,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "bart.get_name() # get_name()内部返回self.__name"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 类属性、实例属性、类方法、实例方法以及静态方法"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 1、类属性就是类对象所拥有的属性，它被所有类对象的实例对象所共有，在内存中只存在一个副本"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "jack\n",
      "jack\n"
     ]
    }
   ],
   "source": [
    "class People:\n",
    "    name = 'jack'  #公有的类属性\n",
    "    __age = 12     #私有的类属性\n",
    "p = People()\n",
    " \n",
    "print (p.name)             #正确\n",
    "print (People.name)        #正确\n",
    "# print (p.__age)            #错误，不能在类外通过实例对象访问私有的类属性\n",
    "# print (People.__age)       #错误，不能在类外通过类对象访问私有的类属性"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "jack\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "'tom'"
      ]
     },
     "execution_count": 23,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "p2 = People()\n",
    "print(p2.name)\n",
    "p2.name = 'tom'\n",
    "p2.name"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'jack'"
      ]
     },
     "execution_count": 24,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "p.name"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 2、实例属性是不需要在类中显示定义的"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "jack\n",
      "12\n",
      "jack\n"
     ]
    }
   ],
   "source": [
    "class people:\n",
    "    name = 'jack'\n",
    " \n",
    "p = people()\n",
    "\n",
    "# 动态绑定age属性\n",
    "p.age =12  \n",
    "print (p.name)    #正确\n",
    "print( p.age)     #正确\n",
    " \n",
    "print (people.name)    #正确\n",
    "# print (people.age)     #错误"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "jack\n",
      "jack\n",
      "13\n",
      "12\n"
     ]
    }
   ],
   "source": [
    "class People:\n",
    "    name = 'jack'  #公有的类属性\n",
    "    __age = 12     #私有的类属性\n",
    "    @classmethod\n",
    "    def getAge(cls):\n",
    "        print(cls.__age)\n",
    "    \n",
    "p = People()\n",
    " \n",
    "print (p.name)             #正确\n",
    "print (People.name)        #正确\n",
    "p.__age = 13\n",
    "print(p.__age)\n",
    "People.getAge()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**如果需要在类外修改类属性，必须通过类对象去引用然后进行修改。**<br/>\n",
    "**如果通过实例对象去引用，会产生一个同名的实例属性**，这种方式修改的是实例属性，不会影响到类属性，并且之后如果通过实例对象去引用该名称的属性，实例属性会强制屏蔽掉类属性，即引用的是实例属性，除非删除了该实例属性"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "china\n",
      "china\n"
     ]
    }
   ],
   "source": [
    "class People:\n",
    "    country = 'china'\n",
    " \n",
    "print (People.country)  # 类属性\n",
    "p =People()\n",
    "print (p.country)  # 通过实例调用类属性"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "japan\n",
      "china\n"
     ]
    }
   ],
   "source": [
    "p.country = 'japan'  # 在实例上绑定属性\n",
    "print (p.country)      #实例属性会屏蔽掉同名的类属性\n",
    "print (People.country)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "china\n"
     ]
    }
   ],
   "source": [
    "del p.country    #删除实例属性\n",
    "print (p.country)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 类方法、实例方法和静态方法"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 类方法：类方法的第一个参数是类对象cls，那么通过cls引用的必定是类对象的属性和方法；\n",
    "是类对象所拥有的方法，需要用修饰器\"@classmethod\"来标识其为类方法，对于类方法，第一个参数必须是类对象，一般以\"cls\"作为第一个参数（当然可以用其他名称的变量作为其第一个参数，但是大部分人都习惯以'cls'作为第一个参数的名字，就最好用'cls'了），能够通过实例对象和类对象去访问。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 33,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "china\n",
      "china\n",
      "fn1\n"
     ]
    }
   ],
   "source": [
    "class People:\n",
    "    country = 'china'\n",
    "        \n",
    "    #类方法，用classmethod来进行修饰，可以用实例引用，也可通过类引用\n",
    "    @classmethod\n",
    "    def getCountry(cls):\n",
    "        return cls.country\n",
    "    \n",
    "    #实例方法，只能用实例引用，不能用类名引用\n",
    "    def fn1(self):\n",
    "        print('fn1')\n",
    " \n",
    "p = People()\n",
    "print(p.getCountry())    #可以用过实例对象引用\n",
    "print(People.getCountry() )   #可以通过类对象引用\n",
    "\n",
    "p.fn1()       #正确，可以用过实例对象引用\n",
    "# People.fn1()  # 错误，不能通过类对象引用实例方法"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**类方法另一用途**就是可以对类属性进行修改："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "class People:\n",
    "    country = 'china'\n",
    "        \n",
    "    #类方法，用classmethod来进行修饰\n",
    "    @classmethod\n",
    "    def getCountry(cls):\n",
    "        return cls.country\n",
    "        \n",
    "    @classmethod\n",
    "    def setCountry(cls,country):\n",
    "        cls.country = country\n",
    "        \n",
    " \n",
    "p = People()\n",
    "print(p.getCountry())    #可以用过实例对象引用\n",
    "print(People.getCountry())    #可以通过类对象引用\n",
    " \n",
    "p.setCountry('japan')   \n",
    " \n",
    "print(p.getCountry() )  \n",
    "print(People.getCountry())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 实例方法： 在类外,实例方法只能通过实例对象去调用，不能通过其他方式去调用。\n",
    "#### 实例方法的第一个参数是实例对象self，那么通过self引用的可能是类属性、也有可能是实例属性（这个需要具体分析），不过在存在相同名称的类属性和实例属性的情况下，实例属性优先级更高。<br/>\n",
    " 在类中最常定义的成员方法，它**至少有一个参数**并且必须以**实例对象作为其第一个参数，一般以名为'self'的变量**作为第一个参数（当然可以以其他名称的变量作为第一个参数）。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "class People:\n",
    "    country = 'china'\n",
    "    \n",
    "    #实例方法\n",
    "    def getCountry(self):\n",
    "        return self.country\n",
    "        \n",
    "p = People()\n",
    "print(p.getCountry())         #正确，可以用过实例对象引用\n",
    "# print People.getCountry()    #错误，不能通过类对象引用实例方法"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 静态方法：\n",
    "需要通过**修饰器\"@staticmethod\"来进行修饰**，静态方法不需要多定义参数。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 35,
   "metadata": {},
   "outputs": [],
   "source": [
    "class People:\n",
    "    country = 'china'\n",
    "    \n",
    "    # 实例方法\n",
    "    def instance_methon(self):\n",
    "        print('instance methon')\n",
    "    \n",
    "    @staticmethod\n",
    "    #静态方法,不需要参数,\n",
    "    def getCountry():\n",
    "        return People.country\n",
    "    \n",
    "    # 相同名称的实例属性与类属性\n",
    "    def fn1(self):\n",
    "        print('fn1 实例方法')\n",
    "    @classmethod  #相当于执行了语句：fn1 = classmethod(fn1)\n",
    "    def fn1(cls):\n",
    "        print('fn1 类方法')\n",
    "    @staticmethod\n",
    "    def fn1():\n",
    "        print('fn1 静态方法')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 36,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "china\n",
      "china\n",
      "jap\n",
      "china\n"
     ]
    }
   ],
   "source": [
    "p = People()\n",
    "print(p.getCountry())  # 可以通过实例调用\n",
    "print(People.getCountry())  # 可以通过类调用\n",
    "p.country = 'jap'\n",
    "print(p.country)\n",
    "print(People.country)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 37,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "fn1 静态方法\n"
     ]
    }
   ],
   "source": [
    "p.fn1()  # 会执行实例方法还是类方法呢"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 38,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "fn1 静态方法\n"
     ]
    }
   ],
   "source": [
    "People.fn1()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "metadata": {},
   "outputs": [],
   "source": [
    "class CLanguage:\n",
    "    #类构造方法，也属于实例方法\n",
    "    def __init__(self):\n",
    "        self.name = \"C语言中文网\"\n",
    "        self.add = \"http://c.biancheng.net\"\n",
    "    # 下面定义了一个say实例方法\n",
    "    def say(self):\n",
    "        print(\"正在调用 say() 实例方法\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "正在调用 say() 实例方法\n"
     ]
    }
   ],
   "source": [
    "clang = CLanguage()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "正在调用 say() 实例方法\n"
     ]
    }
   ],
   "source": [
    "CLanguage.say(clang)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**类的实例**可以访问“实例方法、类方法和静态方法”，使用**类**可以访问“类方法和静态方法”。一般情况下，如果要修改实例成员的值，直接使用实例方法；如果要修改类成员的值，直接使用类方法；如果是**辅助功能**，如打印菜单，则可以考虑使用静态方法。\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## python中的多态, 由于python本身就是动态绑定，天然具有多态性质"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Animal(object):\n",
    "    def run(self):\n",
    "        print('Animal is running...')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Dog(Animal):\n",
    "     def run(self):\n",
    "        print('Dog is running...')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Dog is running...\n"
     ]
    }
   ],
   "source": [
    "dog = Dog()\n",
    "dog.run()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Dog is running...\n"
     ]
    }
   ],
   "source": [
    "def Duotai(Animal):\n",
    "    Animal.run()\n",
    "Duotai(dog)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Animal is running...\n"
     ]
    }
   ],
   "source": [
    "ani = Animal()\n",
    "ani.run()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Animal:\n",
    "    def run(self):\n",
    "        raise AttributeError('子类必须实现这个方法')\n",
    "        \n",
    "class People(Animal):\n",
    "    def run(self):\n",
    "        print('人正在走')\n",
    "class Pig(Animal):\n",
    "    def run(self):\n",
    "        print('pig is walking')\n",
    "class Dog(Animal):\n",
    "    def run(self):\n",
    "        print('dog is running')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [],
   "source": [
    "peo1=People()\n",
    "pig1=Pig()\n",
    "d1=Dog()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "人正在走\n",
      "pig is walking\n",
      "dog is running\n"
     ]
    }
   ],
   "source": [
    "peo1.run()\n",
    "pig1.run()\n",
    "d1.run()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import abc\n",
    "class Animal(metaclass=abc.ABCMeta): #同一类事物:动物\n",
    "    @abc.abstractmethod\n",
    "    def talk(self):\n",
    "        pass"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "class People(Animal): #动物的形态之一:人\n",
    "    def talk(self):\n",
    "        print('say hello')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Dog(Animal): #动物的形态之二:狗\n",
    "    def talk(self):\n",
    "        print('say wangwang')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Pig(Animal): #动物的形态之三:猪\n",
    "    def talk(self):\n",
    "        print('say aoao')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "文件有多种形态：文件、文本文件、可执行文件"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import abc\n",
    "class File(metaclass=abc.ABCMeta): #同一类事物:文件\n",
    "    @abc.abstractmethod\n",
    "    def click(self):\n",
    "        pass"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Text(File): #文件的形态之一:文本文件\n",
    "    def click(self):\n",
    "        print('open file')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "class ExeFile(File): #文件的形态之二:可执行文件\n",
    "    def click(self):\n",
    "        print('execute file')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "二 多态性\n",
    "\n",
    "（1）什么是多态性（注意：**多态与多态性是两种概念**）\n",
    "\n",
    "**多态性**是指具有不同功能的函数可以使用相同的函数名，这样就可以用一个函数名调用不同内容的函数。在面向对象方法中一般是这样表述多态性：向不同的对象发送同一条消息，不同的对象在接收时会产生不同的行为（即方法）。也就是说，每个对象可以用自己的方式去响应共同的消息。所谓消息，就是调用函数，不同的行为就是指不同的实现，即执行不同的函数。\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [],
   "source": [
    "#多态性：一种调用方式，不同的执行效果（多态性）\n",
    "def func(obj):\n",
    "    obj.run()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "人正在走\n",
      "pig is walking\n",
      "dog is running\n"
     ]
    }
   ],
   "source": [
    "func(peo1)\n",
    "func(pig1)\n",
    "func(d1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "人正在走\n",
      "pig is walking\n"
     ]
    }
   ],
   "source": [
    "peo1.run()\n",
    "pig1.run()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 多态性依赖于：继承\n",
    "##多态性：定义统一的接口，\n",
    "def func(obj): #obj这个参数没有类型限制，可以传入不同类型的值\n",
    "    obj.run() #调用的逻辑都一样，执行的结果却不一样"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "人正在走\n",
      "pig is walking\n",
      "dog is running\n"
     ]
    }
   ],
   "source": [
    "func(peo1)\n",
    "func(pig1)\n",
    "func(d1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def func(animal): #参数animal就是对态性的体现\n",
    "    animal.talk()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "people1=People() #产生一个人的对象\n",
    "pig1=Pig() #产生一个猪的对象\n",
    "dog1=Dog() #产生一个狗的对象\n",
    "func(people1) "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "func(pig1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "func(dog1)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "综上可以说，**多态性是一个接口（函数func）**,**多种实现（如f.click()）**"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "二、为什么要用多态性（多态性的好处）\n",
    "\n",
    "其实大家从上面多态性的例子可以看出，我们并没有增加上面新的知识，也就是说Python本身就是支持多态性的，这么做的好处是什么呢？\n",
    "（1）增加了程序的灵活性\n",
    "\t　　以不变应万变，不论对象千变万化，使用者都是同一种形式去调用，如func(animal)\n",
    "（2）增加了程序额可扩展性\n",
    "\t　　通过继承animal类创建了一个新的类，使用者无需更改自己的代码，还是用func(animal)去调用\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 继承\n",
    "\n",
    "继承是面向对象编程的一个重要的方式，因为通过继承，子类就可以扩展父类的功能。\n",
    "\n",
    "1. 在继承关系中，子类会自动继承父类中定义的方法，但如果父类中的方法功能不能满足需求，就可以在子类中重写父类的方法。即子类中的方法会覆盖父类中同名的方法，这也称为重载。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "我是一个学生\n",
      "我是一个人类\n"
     ]
    }
   ],
   "source": [
    "#定义一个父类\n",
    "class Person:\n",
    "    def speak(self):    #定义方法用于输出\n",
    "        print ('我是一个人类')\n",
    "#定义一个子类\n",
    "class Stu(Person):\n",
    "    def speak(self):    #定义方法用于输出\n",
    "        print ('我是一个学生')\n",
    "        super().speak()\n",
    "student = Stu()         #创建学生对象\n",
    "student.speak()         #调用同名方法\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "2. 如果需要在子类中调用父类的方法，可以使用内置函数super()或通过“父类名.方法名()”的方式来实现。\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "姓名:Jack，性别:Male，成绩: 90\n"
     ]
    }
   ],
   "source": [
    "#定义父类\n",
    "class Person():\n",
    "    def __init__(self, name, sex):\n",
    "        self.name = name\n",
    "        self.sex = sex\n",
    "        \n",
    "#定义子类\n",
    "class Stu(Person):\n",
    "    def __init__(self, name, sex, score):\n",
    "        super().__init__(name, sex)   #调用父类中的__init__方法\n",
    "        self.score = score\n",
    "        \n",
    "#创建对象实例\n",
    "student = Stu('Jack','Male',90)\n",
    "print(\"姓名:%s，性别:%s，成绩: %s\"%(student.name,student.sex,student.score))\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 多重继承\n",
    "\n",
    "继承是面向对象编程的一个重要的方式，因为通过继承，子类就可以扩展父类的功能。"
   ]
  },
  {
   "cell_type": "raw",
   "metadata": {},
   "source": [
    "Animal类层次的设计，假设我们要实现以下4种动物：\n",
    "\n",
    "Dog - 狗狗；\n",
    "Bat - 蝙蝠；\n",
    "Parrot - 鹦鹉；\n",
    "Ostrich - 鸵鸟。"
   ]
  },
  {
   "cell_type": "raw",
   "metadata": {},
   "source": [
    "如果按照哺乳动物和鸟类归类，我们可以设计出这样的类的层次：\n",
    "\n",
    "                ┌───────────────┐\n",
    "                │    Animal     │\n",
    "                └───────────────┘\n",
    "                        │\n",
    "           ┌────────────┴────────────┐\n",
    "           │                         │\n",
    "           ▼                         ▼\n",
    "    ┌─────────────┐           ┌─────────────┐\n",
    "    │   Mammal    │           │    Bird     │\n",
    "    └─────────────┘           └─────────────┘\n",
    "           │                         │\n",
    "     ┌─────┴──────┐            ┌─────┴──────┐\n",
    "     │            │            │            │\n",
    "     ▼            ▼            ▼            ▼\n",
    "┌─────────┐  ┌─────────┐  ┌─────────┐  ┌─────────┐\n",
    "│   Dog   │  │       Bat   │  │           Parrot  │  │ Ostrich │\n",
    "└─────────┘  └─────────┘  └─────────┘  └─────────┘\n",
    "但是如果按照“能跑”和“能飞”来归类，我们就应该设计出这样的类的层次：\n",
    "\n",
    "                ┌───────────────┐\n",
    "                │    Animal     │\n",
    "                └───────────────┘\n",
    "                        │\n",
    "           ┌────────────┴────────────┐\n",
    "           │                         │\n",
    "           ▼                         ▼\n",
    "    ┌─────────────┐           ┌─────────────┐\n",
    "    │  Runnable   │           │   Flyable   │\n",
    "    └─────────────┘           └─────────────┘\n",
    "           │                         │\n",
    "     ┌─────┴──────┐            ┌─────┴──────┐\n",
    "     │            │            │            │\n",
    "     ▼            ▼            ▼            ▼\n",
    "┌─────────┐  ┌─────────┐  ┌─────────┐  ┌─────────┐\n",
    "│   Dog   │         │ Ostrich │     │ Parrot  │  │        Bat   │\n",
    "└─────────┘  └─────────┘  └─────────┘  └─────────┘\n",
    "如果要把上面的两种分类都包含进来，我们就得设计更多的层次：\n",
    "\n",
    "哺乳类：能跑的哺乳类，能飞的哺乳类；\n",
    "鸟类：能跑的鸟类，能飞的鸟类。\n",
    "这么一来，类的层次就复杂了：\n",
    "\n",
    "                ┌───────────────┐\n",
    "                │    Animal     │\n",
    "                └───────────────┘\n",
    "                        │\n",
    "           ┌────────────┴────────────┐\n",
    "           │                         │\n",
    "           ▼                         ▼\n",
    "    ┌─────────────┐           ┌─────────────┐\n",
    "    │   Mammal    │           │    Bird     │\n",
    "    └─────────────┘           └─────────────┘\n",
    "           │                         │\n",
    "     ┌─────┴──────┐            ┌─────┴──────┐\n",
    "     │            │            │            │\n",
    "     ▼            ▼            ▼            ▼\n",
    "┌─────────┐  ┌─────────┐  ┌─────────┐  ┌─────────┐\n",
    "│  MRun   │        │  MFly   │           │  BRun   │  │  BFly   │\n",
    "└─────────┘  └─────────┘  └─────────┘  └─────────┘\n",
    "     │            │            │            │\n",
    "     │            │            │            │\n",
    "     ▼            ▼            ▼            ▼\n",
    "┌─────────┐  ┌─────────┐  ┌─────────┐  ┌─────────┐\n",
    "│   Dog   │  │           Bat   │  │         Ostrich │  │ Parrot  │\n",
    "└─────────┘  └─────────┘  └─────────┘  └─────────┘\n",
    "如果要再增加“宠物类”和“非宠物类”，这么搞下去，类的数量会呈指数增长，很明显这样设计是不行的。\n",
    "\n",
    "正确的做法是采用多重继承。首先，主要的类层次仍按照哺乳类和鸟类设计："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Animal(object):\n",
    "    pass\n",
    "\n",
    "# 大类:\n",
    "class Mammal(Animal):\n",
    "    pass\n",
    "\n",
    "class Bird(Animal):\n",
    "    pass\n",
    "\n",
    "# 各种动物:\n",
    "class Dog(Mammal):\n",
    "    pass\n",
    "\n",
    "class Bat(Mammal):\n",
    "    pass\n",
    "\n",
    "class Parrot(Bird):\n",
    "    pass\n",
    "\n",
    "class Ostrich(Bird):\n",
    "    pass"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "现在，我们要给动物再加上Runnable和Flyable的功能，<br/>\n",
    "**定义好Runnable和Flyable的类,在java里面是接口：**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Runnable(object):\n",
    "    def run(self):\n",
    "        print('Running...')\n",
    "\n",
    "class Flyable(object):\n",
    "    def fly(self):\n",
    "        print('Flying...')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "对于需要Runnable功能的动物，就多继承一个Runnable，例如Dog："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Running...\n"
     ]
    }
   ],
   "source": [
    "class Dog(Mammal, Runnable):\n",
    "    pass\n",
    "dog1 = Dog()\n",
    "dog1.run()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "通过**type()函数**创建类"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [],
   "source": [
    "def fn1(self, name='dog'): # 单独定义成员函数\n",
    "    print('Hello, %s.' % name)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 使用type（）创建class，显示名称Dog_1, 继承(Mammal,Runnable)，使用字典传递函数，并命名为hello\n",
    "Dog1 = type('Dog_1', (Mammal,Runnable), dict(hello=fn1)) \n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "<class '__main__.Dog_1'>\n",
      "Running...\n",
      "Hello, dog.\n"
     ]
    }
   ],
   "source": [
    "d1 = Dog1()\n",
    "print(Dog1)\n",
    "d1.run()\n",
    "d1.hello()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 使用元类\n",
    "\n",
    "\n",
    "**type()**\n",
    "\n",
    "动态语言和静态语言最大的不同，就是**函数和类的定义，不是编译时定义的，而是运行时动态创建的**。\n",
    "\n",
    "比方说我们要定义一个Hello的class，就写一个hello.py模块："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 48,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Hello(object):\n",
    "    def hello(self, name='world'):\n",
    "        print('Hello, %s.' % name)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "当Python解释器载入hello模块时，就会依次执行该模块的所有语句，执行结果就是动态创建出一个Hello的class对象，测试如下："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 49,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Hello, world.\n"
     ]
    }
   ],
   "source": [
    "h = Hello()\n",
    "h.hello()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 50,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "<class 'type'>\n"
     ]
    }
   ],
   "source": [
    "print(type(Hello))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 51,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "<class '__main__.Hello'>\n"
     ]
    }
   ],
   "source": [
    "print(type(h))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "type()函数可以查看一个类型或变量的类型，Hello是一个class，它的类型就是type，而h是一个实例，它的类型就是class Hello。\n",
    "\n",
    "我们说class的定义是运行时动态创建的，而创建class的方法就是使用type()函数。\n",
    "\n",
    "**type()函数既可以返回一个对象的类型，又可以创建出新的类型**，比如，我们可以**通过type()函数创建出Hello类，**而无需通过class Hello(object)...的定义："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def fn(self, name='world'): # 先定义函数\n",
    "    print('Hello, %s.' % name)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 使用type（）创建Hello class, 并把函数送入类中"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "ello = type('Hello', (object,), dict(hello=fn))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "h = Hello()\n",
    "h.hello()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(type(Hello))\n",
    "print(type(h))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "要创建一个class对象，type()函数依次传入3个参数：\n",
    "\n",
    "class的名称；\n",
    "继承的父类集合，注意Python支持多重继承，如果只有一个父类，别忘了tuple的单元素写法；\n",
    "class的方法名称与函数绑定，这里我们把函数fn绑定到方法名hello上。\n",
    "通过type()函数创建的类和直接写class是完全一样的，因为Python解释器遇到class定义时，仅仅是扫描一下class定义的语法，然后调用type()函数创建出class。\n",
    "\n",
    "正常情况下，我们都用class Xxx...来定义类，但是，type()函数也允许我们动态创建出类来，也就是说，动态语言本身支持运行期动态创建类，这和静态语言有非常大的不同，要在静态语言运行期创建类，必须构造源代码字符串再调用编译器，或者借助一些工具生成字节码实现，本质上都是动态编译，会非常复杂。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# metaclass\n",
    "\n",
    "除了使用**$type()$**动态创建类以外，要控制类的创建行为，还可以使用metaclass。\n",
    "\n",
    "metaclass，直译为元类，简单的解释就是：\n",
    "\n",
    "当我们定义了类以后，就可以根据这个类创建出实例，所以：先定义类，然后创建实例。\n",
    "\n",
    "但是如果我们想创建出类呢？那就必须根据metaclass创建出类，所以：先定义metaclass，然后创建类。\n",
    "\n",
    "连接起来就是：先定义metaclass，就可以创建类，最后创建实例。\n",
    "\n",
    "所以，metaclass允许你创建类或者修改类。换句话说，你可以把类看成是metaclass创建出来的“实例”。\n",
    "\n",
    "metaclass是Python面向对象里最难理解，也是最难使用的魔术代码。正常情况下，你不会碰到需要使用metaclass的情况，所以，以下内容看不懂也没关系，因为基本上你不会用到。\n",
    "\n",
    "我们先看一个简单的例子，这个metaclass可以给我们自定义的MyList增加一个add方法：\n",
    "\n",
    "定义ListMetaclass，按照默认习惯，metaclass的类名总是以Metaclass结尾，以便清楚地表示这是一个metaclass："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# metaclass是类的模板，所以必须从`type`类型派生：\n",
    "class ListMetaclass(type):\n",
    "    def __new__(cls, name, bases, attrs):\n",
    "        attrs['add'] = lambda self, value: self.append(value)\n",
    "        return type.__new__(cls, name, bases, attrs)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 有了ListMetaclass，我们在定义类的时候还要指示使用ListMetaclass来定制类，传入关键字参数metaclass：\n",
    "class MyList(list, metaclass=ListMetaclass):\n",
    "    pass"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "当我们传入关键字参数metaclass时，魔术就生效了，它指示Python解释器在创建MyList时，要通过ListMetaclass.__new__()来创建，在此，我们可以修改类的定义，比如，加上新的方法，然后，返回修改后的定义。"
   ]
  },
  {
   "cell_type": "raw",
   "metadata": {},
   "source": [
    "__new__()方法接收到的参数依次是：\n",
    "1、当前准备创建的类的对象；\n",
    "2、类的名字；\n",
    "3、类继承的父类集合；\n",
    "4、类的方法集合。\n",
    "测试一下MyList是否可以调用add()方法："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "L = MyList()\n",
    "L.add(1)\n",
    "L"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 而普通的list没有add()方法：\n",
    "L2 = list()\n",
    "L2.add(1)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "动态修改有什么意义？直接在MyList定义中写上add()方法不是更简单吗？正常情况下，确实应该直接写，通过metaclass修改纯属变态。\n",
    "\n",
    "但是，总会遇到需要通过metaclass修改类定义的。ORM就是一个典型的例子。\n",
    "\n",
    "ORM全称“Object Relational Mapping”，即对象-关系映射，就是把关系数据库的一行映射为一个对象，也就是一个类对应一个表，这样，写代码更简单，不用直接操作SQL语句。\n",
    "\n",
    "要编写一个ORM框架，所有的类都只能动态定义，因为只有使用者才能根据表的结构定义出对应的类来。\n",
    "\n",
    "让我们来尝试编写一个ORM框架。\n",
    "\n",
    "编写底层模块的第一步，就是先把调用接口写出来。比如，使用者如果使用这个ORM框架，想定义一个User类来操作对应的数据库表User，我们期待他写出这样的代码："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "class User(Model):\n",
    "    # 定义类的属性到列的映射：\n",
    "    id = IntegerField('id')\n",
    "    name = StringField('username')\n",
    "    email = StringField('email')\n",
    "    password = StringField('password')\n",
    "\n",
    "# 创建一个实例：\n",
    "u = User(id=12345, name='Michael', email='test@orm.org', password='my-pwd')\n",
    "# 保存到数据库：\n",
    "u.save()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "其中，父类Model和属性类型StringField、IntegerField是由ORM框架提供的，剩下的魔术方法比如save()全部由metaclass自动完成。虽然metaclass的编写会比较复杂，但ORM的使用者用起来却异常简单。\n",
    "\n",
    "现在，我们就按上面的接口来实现该ORM。\n",
    "\n",
    "首先来定义Field类，它负责保存数据库表的字段名和字段类型："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Field(object):\n",
    "    def __init__(self, name, column_type):\n",
    "        self.name = name\n",
    "        self.column_type = column_type\n",
    "    def __str__(self):\n",
    "        return '<%s:%s>' % (self.__class__.__name__, self.name)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    " 在Field的基础上，进一步定义各种类型的Field，比如StringField，IntegerField等等："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "class StringField(Field):\n",
    "    def __init__(self, name):\n",
    "        super(StringField, self).__init__(name, 'varchar(100)')\n",
    "        \n",
    "class IntegerField(Field):\n",
    "    def __init__(self, name):\n",
    "        super(IntegerField, self).__init__(name, 'bigint')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "下一步，就是编写最复杂的ModelMetaclass了："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "class ModelMetaclass(type):\n",
    "\n",
    "    def __new__(cls, name, bases, attrs):\n",
    "        if name=='Model':\n",
    "            return type.__new__(cls, name, bases, attrs)\n",
    "        print('Found model: %s' % name)\n",
    "        mappings = dict()\n",
    "        for k, v in attrs.items():\n",
    "            if isinstance(v, Field):\n",
    "                print('Found mapping: %s ==> %s' % (k, v))\n",
    "                mappings[k] = v\n",
    "        for k in mappings.keys():\n",
    "            attrs.pop(k)\n",
    "        attrs['__mappings__'] = mappings # 保存属性和列的映射关系\n",
    "        attrs['__table__'] = name # 假设表名和类名一致\n",
    "        return type.__new__(cls, name, bases, attrs)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "以及基类Model："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Model(dict, metaclass=ModelMetaclass):\n",
    "\n",
    "    def __init__(self, **kw):\n",
    "        super(Model, self).__init__(**kw)\n",
    "\n",
    "    def __getattr__(self, key):\n",
    "        try:\n",
    "            return self[key]\n",
    "        except KeyError:\n",
    "            raise AttributeError(r\"'Model' object has no attribute '%s'\" % key)\n",
    "\n",
    "    def __setattr__(self, key, value):\n",
    "        self[key] = value\n",
    "\n",
    "    def save(self):\n",
    "        fields = []\n",
    "        params = []\n",
    "        args = []\n",
    "        for k, v in self.__mappings__.items():\n",
    "            fields.append(v.name)\n",
    "            params.append('?')\n",
    "            args.append(getattr(self, k, None))\n",
    "        sql = 'insert into %s (%s) values (%s)' % (self.__table__, ','.join(fields), ','.join(params))\n",
    "        print('SQL: %s' % sql)\n",
    "        print('ARGS: %s' % str(args))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "当用户定义一个class User(Model)时，Python解释器首先在当前类User的定义中查找metaclass，如果没有找到，就继续在父类Model中查找metaclass，找到了，就使用Model中定义的metaclass的ModelMetaclass来创建User类，也就是说，metaclass可以隐式地继承到子类，但子类自己却感觉不到。\n",
    "\n",
    "在ModelMetaclass中，一共做了几件事情：\n",
    "\n",
    "排除掉对Model类的修改；\n",
    "\n",
    "在当前类（比如User）中查找定义的类的所有属性，如果找到一个Field属性，就把它保存到一个__mappings__的dict中，同时从类属性中删除该Field属性，否则，容易造成运行时错误（实例的属性会遮盖类的同名属性）；\n",
    "\n",
    "把表名保存到__table__中，这里简化为表名默认为类名。\n",
    "\n",
    "在Model类中，就可以定义各种操作数据库的方法，比如save()，delete()，find()，update等等。\n",
    "\n",
    "我们实现了save()方法，把一个实例保存到数据库中。因为有表名，属性到字段的映射和属性值的集合，就可以构造出INSERT语句。\n",
    "\n",
    "编写代码试试："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "u = User(id=12345, name='Michael', email='test@orm.org', password='my-pwd')\n",
    "u.save()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "class MyClass:\n",
    "    \"\"\"一个简单的类实例\"\"\"\n",
    "    i = 12345\n",
    "    def f(self):\n",
    "        return 'hello world'\n",
    " \n",
    "# 实例化类\n",
    "x = MyClass()\n",
    "y= MyClass()\n",
    "# 访问类的属性和方法\n",
    "print(\"MyClass 类的属性 i 为：\", x.i)\n",
    "print(\"MyClass 类的方法 f 输出为：\", x.f())\n",
    "y.i=222\n",
    "print(MyClass.i)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "class People:\n",
    "    #定义基本属性\n",
    "    name = ''\n",
    "    age = 0\n",
    "    #定义私有属性,私有属性在类外部无法直接进行访问\n",
    "    __weight = 0\n",
    "    #定义构造方法\n",
    "    def __init__(self,n,a,w):\n",
    "        self.name = n\n",
    "        self.age = a\n",
    "        self.__weight = w\n",
    "    def speak(self):\n",
    "        print(\"%s 说: 我 %d 岁。\" %(self.name,self.age))\n",
    " \n",
    "# 实例化类\n",
    "p = People('runoob',10,30)\n",
    "p.speak()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "p.age"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "People.age"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Parent:        # 定义父类\n",
    "   def myMethod(self):\n",
    "      print ('调用父类方法')\n",
    " \n",
    "class Child(Parent): # 定义子类\n",
    "   def myMethod(self):\n",
    "      print ('调用子类方法')\n",
    " \n",
    "c = Child()          # 子类实例\n",
    "c.myMethod()         # 子类调用重写方法\n",
    "super(Child,c).myMethod() #用子类对象调用父类已被覆盖的方法\n",
    "# Parent.myMehod()\n",
    "print('...')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Python面向对象中super用法与MRO机制"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 为什么要用super\n",
    "### 在子类中引用父类的属性，我们可以通过“父类名.属性名”的方式来调用，代码如下："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "A.fun\n",
      "B.fun\n"
     ]
    }
   ],
   "source": [
    "class A:\n",
    "    def fun(self):\n",
    "        print('A.fun')\n",
    "\n",
    "class B(A):\n",
    "    def fun(self):\n",
    "        A.fun(self)\n",
    "        print('B.fun')\n",
    "        \n",
    "B().fun()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "在子类B中调用了父类A的方法，这时候如果我们改变了A类的类名也只需要在B类中修改一下就好了，<br/>\n",
    "但是如果有几十上百个类继承了A类呢？\n",
    "\n",
    "一旦A类类名改了，我们就要分别到那几十上百个子类中修改，不但要改继承时用到的A类名，<br/>\n",
    "调用A类方法时用到的A类名也要改，繁琐的很，用super就好多了"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "A.fun\n",
      "B.fun\n"
     ]
    }
   ],
   "source": [
    "class A:\n",
    "    def fun(self):\n",
    "        print('A.fun')\n",
    "\n",
    "class B(A):\n",
    "    def fun(self):\n",
    "        super().fun()\n",
    "        print('B.fun')\n",
    "        \n",
    "B().fun()\n",
    "\n",
    "#  这时候，就算A类类名改了，也只需要在子类声明继承关系时修改就好了，简单得大多。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 解决多继承带来的重复调用（菱形继承）、查找顺序（MRO）问题\n",
    "\n",
    "假如有A、B、C、D这4个类，继承关系如下，我们要在各子类方法中显式调用父类的方法"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "A.fun\n",
      "B.fun\n",
      "A.fun\n",
      "C.fun\n",
      "D.fun\n"
     ]
    }
   ],
   "source": [
    "class A:\n",
    "    def fun(self):\n",
    "        print('A.fun')\n",
    "\n",
    "class B(A):\n",
    "    def fun(self):\n",
    "        A.fun(self)\n",
    "        print('B.fun')\n",
    "\n",
    "class C(A):\n",
    "    def fun(self):\n",
    "        A.fun(self)\n",
    "        print('C.fun')\n",
    "\n",
    "class D(B , C):\n",
    "    def fun(self):\n",
    "        B.fun(self)\n",
    "        C.fun(self)\n",
    "        print('D.fun')\n",
    "\n",
    "D().fun()  # A类被实例化了两次。这就是多继承带来的重复调用（菱形继承）的问题。\n",
    "           #使用super可以很好的解决这一问题："
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "使用super可以很好的解决这一问题"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "A.fun\n",
      "C.fun\n",
      "B.fun\n",
      "D.fun\n"
     ]
    }
   ],
   "source": [
    "class A:\n",
    "    def fun(self):\n",
    "        print('A.fun')\n",
    "\n",
    "class B(A):\n",
    "    def fun(self):\n",
    "        super(B , self).fun()\n",
    "        print('B.fun')\n",
    "\n",
    "class C(A):\n",
    "    def fun(self):\n",
    "        super(C , self).fun()\n",
    "        print('C.fun')\n",
    "\n",
    "class D(B , C):\n",
    "    def fun(self):\n",
    "        super(D , self).fun()\n",
    "        print('D.fun')\n",
    "\n",
    "D().fun()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**那么，为什么输出顺序是A->C->B->D而不是A->B->C->D呢？**\n",
    "\n",
    "这就涉及到Python继承中的MRO（Method Resolution Order）：方法解析顺序\n",
    "\n",
    "## super与mro机制\n",
    "\n",
    "每个类声明之后，Python都会自动为创建一个名为“__mro__”的内置属性，这个属性就是Python的MRO机制生成的，<br/>\n",
    "该属性是一个tuple，定义的是该类的方法解析顺序（继承顺序），<br/>\n",
    "当用super调用父类的方法时，会按照__mro__属性中的元素顺序去挨个查找方法。\n",
    "\n",
    "我们可以通过“类名.__mro__”或“类名.mro()”这2种方法来查看上面代码中D类的__mro__属性值："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)\n",
      "[<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>]\n"
     ]
    }
   ],
   "source": [
    "# 查看继承链\n",
    "print(D.__mro__)\n",
    "print(D.mro())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 怎么用super\n",
    "\n",
    "### **super是一个类（不是方法）实例化之后得到的是一个代理对象，而不是得到了父类**\n",
    "\n",
    "使用这个代理对象来调用父类或者兄弟类的方法。使用格式如下：\n",
    "\n",
    "将这个格式展开来就有一下几种传参方式：\n",
    "\n",
    " * super() <br/>\n",
    " * super(type , obj)  <br/>\n",
    " * super(type_1 , type_2)\n",
    " \n",
    "### super(type , obj)\n",
    "\n",
    "super(type , obj)，这个方式要传入两个常数，第一个参数type必须是一个类名，第二个参数是一个该类的实例化对象，不过可以不是直接的实例化对象，该类的子类的实例化对象也行。\n",
    "\n",
    "在上文中已经说到，super会按照__mro__属性中的顺序去查找方法，\n",
    "\n",
    "super(type , obj)两个参数中**type作用是定义在__mro__数组中的那个位置开始找**，**obj定义的是用哪个类的__mro__元素**。\n",
    "\n",
    "用代码来说明，将图2的代码各个类中添加一个fun方法，继承关系不变，代码如下："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(<class '__main__.F'>, <class '__main__.D'>, <class '__main__.A'>, <class '__main__.E'>, <class '__main__.B'>, <class '__main__.C'>, <class 'object'>)\n"
     ]
    }
   ],
   "source": [
    "class A(object):\n",
    "    def fun(self):\n",
    "        print('A.fun')\n",
    "\n",
    "class B(object):\n",
    "    def fun(self):\n",
    "        print('B.fun')\n",
    "\n",
    "class C(object):\n",
    "    def fun(self):\n",
    "        print('C.fun')\n",
    "\n",
    "class D(A,B):\n",
    "    def fun(self):\n",
    "        print('D.fun')\n",
    "\n",
    "class E(B, C):\n",
    "    def fun(self):\n",
    "        print('E.fun')\n",
    "\n",
    "class F(D, E):\n",
    "    def fun(self):\n",
    "        print('F.fun')\n",
    "\n",
    "print(F.__mro__)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "B.fun\n"
     ]
    }
   ],
   "source": [
    "# 输出结果：B.fun\n",
    "super(E , F()).fun() # 从E开始找，使用F的mro\n",
    "\n",
    "# 输出结果：A.fun\n",
    "# super(D , F()).fun() \n",
    "\n",
    "# 输出结果：D.fun\n",
    "# super(F , F()).fun() "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**再让type保持不变，obj尝试不同的实例：**\n",
    "(super是父类的代理)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "C.fun\n"
     ]
    }
   ],
   "source": [
    "super(B , F()).fun() # 输出结果：C.fun ：从B开始，使用F的mro\n",
    "# super(B , E()).fun() # 输出结果：C.fun ：从B开始，使用F的mro"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "B.fun\n"
     ]
    }
   ],
   "source": [
    "super(E , E()).fun()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(<class '__main__.B'>, <class 'object'>)\n"
     ]
    },
    {
     "ename": "AttributeError",
     "evalue": "'super' object has no attribute 'fun'",
     "output_type": "error",
     "traceback": [
      "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[1;31mAttributeError\u001b[0m                            Traceback (most recent call last)",
      "\u001b[1;32m<ipython-input-19-5443e84e1b3e>\u001b[0m in \u001b[0;36m<module>\u001b[1;34m\u001b[0m\n\u001b[0;32m      1\u001b[0m \u001b[0mprint\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mB\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0m__mro__\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m----> 2\u001b[1;33m \u001b[0msuper\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mB\u001b[0m \u001b[1;33m,\u001b[0m \u001b[0mB\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mfun\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m \u001b[1;31m# 这是错误的，会报错 ：从B开始，使用F的mro\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m      3\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m      4\u001b[0m \u001b[1;31m## B的父类是没有fun函数的\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n",
      "\u001b[1;31mAttributeError\u001b[0m: 'super' object has no attribute 'fun'"
     ]
    }
   ],
   "source": [
    "print(B.__mro__)\n",
    "super(B , B()).fun() # 这是错误的，会报错 ：从B开始，使用F的mro\n",
    "\n",
    "## B的父类是没有fun函数的"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### super()\n",
    "　　super()事实上是懒人版的super(type , obj)，这种方式只能用在类体内部，Python会自动把两个参数填充上，type指代当前类，obj指导当前类的实例对象，**相当于super(__class__ , self)**。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)\n",
      "A.fun\n",
      "C.fun\n",
      "B.fun\n",
      "D.fun\n"
     ]
    }
   ],
   "source": [
    "class A:\n",
    "    def fun(self):\n",
    "        print('A.fun')\n",
    "\n",
    "class B(A):\n",
    "    def fun(self):\n",
    "        super(B , self).fun()  # (2)self依旧是D的实例,从B开始，那么找到的是C\n",
    "        print('B.fun')\n",
    "\n",
    "class C(A):\n",
    "    def fun(self):\n",
    "        super(C , self).fun()  # (3)self依旧是D的实例,从C开始，那么找到的是A\n",
    "        print('C.fun')\n",
    "\n",
    "class D(B , C):\n",
    "    def fun(self):  # self 是D的实例\n",
    "        super(D , self).fun()  #(1)使用self指向的D实例的mro,从D开始，那么找到的是B\n",
    "        print('D.fun')\n",
    "print(D.__mro__)\n",
    "\n",
    "D().fun()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## super(type_1 , type_2)\n",
    "\n",
    "　　当super传入的两个参数都是类名是，type_2必须是type_1的子类。功能上与super(type , obj)有什么不同呢？"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 38,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(<class '__main__.F'>, <class '__main__.D'>, <class '__main__.A'>, <class '__main__.E'>, <class '__main__.B'>, <class '__main__.C'>, <class 'object'>)\n"
     ]
    }
   ],
   "source": [
    "class A(object):\n",
    "    def fun(self):\n",
    "        print('A.fun')\n",
    "\n",
    "class B(object):\n",
    "    def fun(self):\n",
    "        print('B.fun')\n",
    "\n",
    "class C(object):\n",
    "    def fun(self):\n",
    "        print('C.fun')\n",
    "\n",
    "class D(A,B):\n",
    "    def fun(self):\n",
    "        print('D.fun')\n",
    "\n",
    "class E(B, C):\n",
    "    def fun(self):\n",
    "        print('E.fun')\n",
    "\n",
    "class F(D, E):\n",
    "    def fun(self):\n",
    "        print('F.fun')\n",
    "\n",
    "print(F.__mro__)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 39,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "<super: <class 'F'>, <F object>>\n",
      "D.fun\n"
     ]
    }
   ],
   "source": [
    "print(super(F , F())) # 输出：<super: <class 'F'>, <F object>> ,找到的父类的代理对象，D是父类\n",
    "super(F , F()).fun()  # 用代理对象调用D的fun()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "super(F , F())和super(F , F)输出结果是一样的，那super(type_1 , type_2)与super(type , obj)一样吗？"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 40,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "<super: <class 'F'>, <F object>>\n"
     ]
    },
    {
     "ename": "TypeError",
     "evalue": "fun() missing 1 required positional argument: 'self'",
     "output_type": "error",
     "traceback": [
      "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[1;31mTypeError\u001b[0m                                 Traceback (most recent call last)",
      "\u001b[1;32m<ipython-input-40-86a3494da0d9>\u001b[0m in \u001b[0;36m<module>\u001b[1;34m\u001b[0m\n\u001b[0;32m      1\u001b[0m \u001b[0mprint\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0msuper\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mF\u001b[0m \u001b[1;33m,\u001b[0m \u001b[0mF\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m)\u001b[0m \u001b[1;31m#输出结果为：<super: <class 'F'>, <F object>>\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m----> 2\u001b[1;33m \u001b[0mprint\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0msuper\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mF\u001b[0m \u001b[1;33m,\u001b[0m \u001b[0mF\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mfun\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m)\u001b[0m \u001b[1;31m# 报错：TypeError: fun() missing 1 required positional argument: 'self'\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[1;31mTypeError\u001b[0m: fun() missing 1 required positional argument: 'self'"
     ]
    }
   ],
   "source": [
    "print(super(F , F)) #输出结果为：<super: <class 'F'>, <F object>>\n",
    "print(super(F , F).fun()) # 报错：TypeError: fun() missing 1 required positional argument: 'self'"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**修改**：当super传入的两个传输都是类时，得到的就是一个指向继承顺序下的类的代理，并未绑定实例，要调用D类的fun方法，还需传入实例："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 41,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "D.fun\n",
      "None\n"
     ]
    }
   ],
   "source": [
    "print(super(F , F).fun(F()))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python [conda env:py36] *",
   "language": "python",
   "name": "conda-env-py36-py"
  },
  "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.6.10"
  },
  "toc": {
   "base_numbering": 1,
   "nav_menu": {},
   "number_sections": true,
   "sideBar": true,
   "skip_h1_title": false,
   "title_cell": "Table of Contents",
   "title_sidebar": "Contents",
   "toc_cell": false,
   "toc_position": {
    "height": "calc(100% - 180px)",
    "left": "10px",
    "top": "150px",
    "width": "227px"
   },
   "toc_section_display": true,
   "toc_window_display": true
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
