{
 "cells": [
  {
   "cell_type": "raw",
   "metadata": {},
   "source": [
    "Python 面向对象编程（一）\n",
    "　　虽然Python是解释性语言，但是它是面向对象的，能够进行对象编程。下面就来了解一下如何在Python中进行对象编程。\n",
    "一.如何定义一个类\n",
    "　　在进行python面向对象编程之前，先来了解几个术语：类，类对象，实例对象，属性，函数和方法。\n",
    "　　类是对现实世界中一些事物的封装，定义一个类可以采用下面的方式来定义："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "class people:\n",
    "    name = 'jack'       #定义了一个属性\n",
    "    #定义了一个方法\n",
    "    def printName(self):\n",
    "        print(self.name)"
   ]
  },
  {
   "cell_type": "raw",
   "metadata": {},
   "source": [
    "二.属性\n",
    "　　在类中我们可以定义一些属性，比如："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "class people:\n",
    "    name = 'jack'\n",
    "    age = 12\n",
    "\n",
    "p = people()\n",
    "print (p.name,p.age)"
   ]
  },
  {
   "cell_type": "raw",
   "metadata": {},
   "source": [
    "　定义了一个people类，里面定义了name和age属性，默认值分别为'jack'和12。在定义了类之后，就可以用来产生实例化对象了，这句p = people( )实例化了一个对象p，然后就可以通过p来读取属性了。这里的name和age都是公有的，可以直接在类外通过对象名访问，如果想定义成私有的，则需在前面加2个下划线 ' __'。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "class people:\n",
    "    __name = 'jack'\n",
    "    __age = 12\n",
    "\n",
    "p = people()\n",
    "print(p.__name,p.__age)"
   ]
  },
  {
   "cell_type": "raw",
   "metadata": {},
   "source": [
    "三.方法\n",
    "　　在类中可以根据需要定义一些方法，定义方法采用def关键字，在类中定义的方法至少会有一个参数，，一般以名为'self'的变量作为该参数（用其他名称也可以），而且需要作为第一个参数。下面看个例子："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "class people:\n",
    "    __name = 'jack'\n",
    "    __age = 12\n",
    "\n",
    "    def getName(self):\n",
    "        return self.__name\n",
    "    def getAge(self):\n",
    "        return self.__age\n",
    "\n",
    "p = people()\n",
    "print(p.getName(),p.getAge())"
   ]
  },
  {
   "cell_type": "raw",
   "metadata": {},
   "source": [
    "四.类中内置的方法\n",
    "　　在Python中有一些内置的方法，这些方法命名都有比较特殊的地方（其方法名以2个下划线开始然后以2个下划线结束）。类中最常用的就是构造方法和析构方法。\n",
    "　　构造方法__init__(self,....)在生成对象时调用，可以用来进行一些初始化操作，不需要显示去调用，系统会默认去执行。构造方法支持重载，如果用户自己没有重新定义构造方法，系统就自动执行默认的构造方法。\n",
    "　　析构方法__del__(self)在释放对象时调用，支持重载，可以在里面进行一些释放资源的操作，不需要显示调用。\n",
    "　　还有其他的一些内置方法：\n",
    "　　比如 __cmp__( ), __len( )__等，具体的用法可以参考这篇博文：\n",
    "　　http://www.cnblogs.com/simayixin/archive/2011/05/04/2036295.html"
   ]
  },
  {
   "cell_type": "raw",
   "metadata": {},
   "source": [
    "五.类属性、实例属性、类方法、实例方法以及静态方法\n",
    "　　在了解了类基本的东西之后，下面看一下python中这几个概念的区别。\n",
    "　　先来谈一下类属性和实例属性\n",
    "　　在前面的例子中我们接触到的就是类属性，顾名思义，类属性就是类对象所拥有的属性，它被所有类对象的实例对象所共有，在内存中只存在一个副本，这个和C++中类的静态成员变量有点类似。对于公有的类属性，在类外可以通过类对象和实例对象访问。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "class People:\n",
    "    name = 'jack'  #公有的类属性\n",
    "    __age = 12     #私有的类属性\n",
    "\n",
    "p = People()\n",
    "\n",
    "print (p.name)             #正确\n",
    "print (People.name )       #正确\n",
    "print (p.__age)            #错误，不能在类外通过实例对象访问私有的类属性\n",
    "print (People.__age)       #错误，不能在类外通过类对象访问私有的类属性"
   ]
  },
  {
   "cell_type": "raw",
   "metadata": {},
   "source": [
    "实例属性是不需要在类中显示定义的，比如："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "class People:\n",
    "    name = 'jack'\n",
    "\n",
    "p = People()\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": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "class People:\n",
    "    name = 'jack'\n",
    "    \n",
    "    #__init__()是内置的构造方法，在实例化对象时自动调用\n",
    "    def __init__(self,age):\n",
    "        self.age = age\n",
    "\n",
    "p = People(12)\n",
    "print(p.name)     #正确\n",
    "print(p.age)      #正确\n",
    "\n",
    "print(People.name)     #正确\n",
    "# print(People.age)      #错误"
   ]
  },
  {
   "cell_type": "raw",
   "metadata": {},
   "source": [
    "如果需要在类外修改类属性，必须通过类对象去引用然后进行修改。如果通过实例对象去引用，会产生一个同名的实例属性，这种方式修改的是实例属性，不会影响到类属性，并且之后如果通过实例对象去引用该名称的属性，实例属性会强制屏蔽掉类属性，即引用的是实例属性，除非删除了该实例属性。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "class People:\n",
    "    country = 'china'\n",
    "    \n",
    "\n",
    "print(People.country) \n",
    "p = People()\n",
    "print(p.country) \n",
    "p.country = 'japan' \n",
    "print(p.country)       #实例属性会屏蔽掉同名的类属性\n",
    "print(People.country) \n",
    "del p.country    #删除实例属性\n",
    "print(p.country) "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "raw",
   "metadata": {},
   "source": [
    "下面来看一下类方法、实例方法和静态方法的区别。\n",
    "　　类方法：是类对象所拥有的方法，需要用修饰器\"@classmethod\"来标识其为类方法，对于类方法，第一个参数必须是类对象，一般以\"cls\"作为第一个参数（当然可以用其他名称的变量作为其第一个参数，但是大部分人都习惯以'cls'作为第一个参数的名字，就最好用'cls'了），能够通过实例对象和类对象去访问。"
   ]
  },
  {
   "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",
    "print(People.getCountry())     #可以通过类对象引用\n",
    "p = People()\n",
    "print(p.getCountry())     #可以用过实例对象引用"
   ]
  },
  {
   "cell_type": "raw",
   "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": "raw",
   "metadata": {},
   "source": [
    "结果显示在用类方法对类属性修改之后，通过类对象和实例对象访问都发生了改变。"
   ]
  },
  {
   "cell_type": "raw",
   "metadata": {},
   "source": [
    "实例方法：在类中最常定义的成员方法，它至少有一个参数并且必须以实例对象作为其第一个参数，一般以名为'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",
    "\n",
    "p = People()\n",
    "print(p.getCountry())          #正确，可以用过实例对象引用\n",
    "# print(People.getCountry())     #错误，不能通过类对象引用实例方法"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "静态方法：需要通过修饰器\"@staticmethod\"来进行修饰，静态方法不需要多定义参数。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "class People:\n",
    "    country = 'china'\n",
    "    \n",
    "    @staticmethod\n",
    "    #静态方法\n",
    "    def getCountry():\n",
    "        return People.country"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(People.getCountry()) "
   ]
  },
  {
   "cell_type": "raw",
   "metadata": {},
   "source": [
    "　从类方法和实例方法以及静态方法的定义形式就可以看出来，类方法的第一个参数是类对象cls，那么通过cls引用的必定是类对象的属性和方法；而实例方法的第一个参数是实例对象self，那么通过self引用的可能是类属性、也有可能是实例属性（这个需要具体分析），不过在存在相同名称的类属性和实例属性的情况下，实例属性优先级更高。静态方法中不需要额外定义参数，因此在静态方法中引用类属性的话，必须通过类对象来引用。"
   ]
  },
  {
   "cell_type": "raw",
   "metadata": {},
   "source": [
    "实例方法 \n",
    "    定义：第一个参数必须是实例对象，该参数名一般约定为“self”，通过它来传递实例的属性和方法（也可以传类的属性和方法）；\n",
    "    调用：只能由实例对象调用。\n",
    "类方法   使用装饰器@classmethod。\n",
    "    定义：使用装饰器@classmethod。第一个参数必须是当前类对象，该参数名一般约定为“cls”，通过它来传递类的属性和方法（不能传实例的属性和方法）；\n",
    "    调用：实例对象和类对象都可以调用。\n",
    "静态方法  使用装饰器@staticmethod。 \n",
    "    定义：使用装饰器@staticmethod。参数随意，没有“self”和“cls”参数，但是方法体中不能使用类或实例的任何属性和方法；\n",
    "    调用：实例对象和类对象都可以调用。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "静态方法是类中的函数，不需要实例。静态方法主要是用来存放逻辑性的代码，逻辑上属于类，但是和类本身没有关系，也就是说在静态方法中，不会涉及到类中的属性和方法的操作。可以理解为，静态方法是个独立的、单纯的函数，它仅仅托管于某个类的名称空间中，便于使用和维护。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import time\n",
    "\n",
    "class TimeTest(object):\n",
    "    def __init__(self, hour, minute, second):\n",
    "        self.hour = hour\n",
    "        self.minute = minute\n",
    "        self.second = second\n",
    "\n",
    "    @staticmethod\n",
    "    def showTime():\n",
    "        return time.strftime(\"%H:%M:%S\", time.localtime())\n",
    "\n",
    "\n",
    "print(TimeTest.showTime())\n",
    "t = TimeTest(2, 10, 10)\n",
    "nowTime = t.showTime()\n",
    "print(nowTime)"
   ]
  },
  {
   "cell_type": "raw",
   "metadata": {},
   "source": [
    "使用了静态方法（函数），然而方法体中并没使用（也不能使用）类或实例的属性（或方法）。若要获得当前时间的字符串时，并不一定需要实例化对象，此时对于静态方法而言，所在类更像是一种名称空间。\n",
    "其实，我们也可以在类外面写一个同样的函数来做这些事，但是这样做就打乱了逻辑关系，也会导致以后代码维护困难。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "在Python中类的继承定义基本形式如下："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "class UniversityMember:\n",
    "\n",
    "    def __init__(self,name,age):\n",
    "        self.name = name\n",
    "        self.age = age\n",
    "\n",
    "    def getName(self):\n",
    "        return self.name\n",
    "\n",
    "    def getAge(self):\n",
    "        return self.age\n",
    "\n",
    "class Student(UniversityMember):\n",
    "\n",
    "    def __init__(self,name,age,sno,mark):\n",
    "        UniversityMember.__init__(self,name,age)     #注意要显示调用父类构造方法，并传递参数self\n",
    "        # 这其实是和继承没啥关系的写法，如果父类名字改了，在子类中也要改，更优雅的写法是用super()\n",
    "        super().__init__(self,name,age)\n",
    "        self.sno = sno\n",
    "        self.mark = mark\n",
    "\n",
    "    def getSno(self):\n",
    "        return self.sno\n",
    "\n",
    "    def getMark(self):\n",
    "        return self.mark\n",
    "\n",
    "\n",
    "\n",
    "class Teacher(UniversityMember):\n",
    "\n",
    "    def __init__(self,name,age,tno,salary):\n",
    "        UniversityMember.__init__(self,name,age)\n",
    "        self.tno = tno\n",
    "        self.salary = salary\n",
    "\n",
    "    def getTno(self):\n",
    "        return self.tno\n",
    "\n",
    "    def getSalary(self):\n",
    "        return self.salary"
   ]
  },
  {
   "cell_type": "raw",
   "metadata": {},
   "source": [
    "在大学中的每个成员都有姓名和年龄，而学生有学号和分数这2个属性，老师有教工号和工资这2个属性，从上面的代码中可以看到：\n",
    "　　1）在Python中，如果父类和子类都重新定义了构造方法__init( )__，在进行子类实例化的时候，子类的构造方法不会自动调用父类的构造方法，必须在子类中显示调用。\n",
    "　　2）如果需要在子类中调用父类的方法，需要以   父类名.方法  这种方式调用，以这种方式调用的时候，注意要传递self参数过去。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 面向对象程序设计"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "面向对象的程序设计，核心是对象，对象就是特征（变量）与技能（函数）的结合体。\n",
    "　　优点：解决了程序扩展性差的问题\n",
    "　　缺点：可控性差，无法预测最终结果\n",
    "　　主要应用场景是需求经常变化的软件，即与用户交互比较频繁的软件\n",
    "需要注意的是：面向对象的程序设计并不能解决全部问题，只是用来解决扩展性。当然，现在的的互联网软件，扩展性是最重要的"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#定义一个中国人的类\n",
    "class Chinese:\n",
    "    #共同的特征\n",
    "    country='China'\n",
    "  \n",
    "    #共同的技能\n",
    "    def talk(self):\n",
    "        print('is talking Chinese')\n",
    "    def eat(self):\n",
    "        print('is eating Chinese food')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#实例化的方式产生一个对象\n",
    "p1=Chinese()\n",
    "p2=Chinese()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#定义一个中国人的类\n",
    "class Chinese:\n",
    "    #共同的特征\n",
    "    country='China'\n",
    "    #初始化\n",
    "    def __init__(self,name,age):\n",
    "        self.name=name #每个对象都有自己的名字\n",
    "        self.age=age #每个对象都有自己的年龄\n",
    "    #共同的技能\n",
    "    def talk(self):\n",
    "        print('is talking Chinese')\n",
    "    def eat(self):\n",
    "        print('is eating Chinese food')\n",
    "\n",
    "#实例化的方式产生一个对象\n",
    "p1=Chinese('zhang',18)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "p1=Chinese('zhang',18)\n",
    "print(Chinese.__dict__)\n",
    "print()\n",
    "print(p1.__dict__)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "p1=Chinese('zhang',18)\n",
    "p2=Chinese('li',19)\n",
    "print(Chinese.talk)\n",
    "print(p1.talk) \n",
    "print(p2.talk) "
   ]
  },
  {
   "cell_type": "raw",
   "metadata": {},
   "source": [
    "可以看到，他们的内存地址都不一样。而且注意bound method，是绑定方法.\n",
    "对象本身只有数据属性，但是Python的class机制将类的函数也绑定到对象上，称为对象的方法，或者叫绑定方法。绑定方法唯一绑定一个对象，同一个类的方法绑定到不同的对象上，属于不同的方法。"
   ]
  },
  {
   "cell_type": "raw",
   "metadata": {},
   "source": [
    "我们可以验证一下：\n",
    "当用到这个函数时：类调用的是函数属性，既然是函数，就是函数名加括号，有参数传参数\n",
    "而对象用到这个函数时，对象没有函数属性，他是绑定方法，绑定方法怎么用呢，也是直接加括号，但不同的是，绑定方法会默认把对象自己作为第一个参数"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Chinese:\n",
    "    country='China'\n",
    "    \n",
    "    def __init__(self,name,age):\n",
    "        self.name=name \n",
    "        self.age=age \n",
    "    def talk(self):\n",
    "        print('%s is talking Chinese'%self.name)\n",
    "    def eat(self):\n",
    "        print('is eating Chinese food')\n",
    " \n",
    "p1=Chinese('zhang',18)\n",
    "p2=Chinese('li',19)\n",
    "Chinese.talk(p1) #zhang is talking Chinese\n",
    "p1.talk()  #zhang is talking Chinese"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "class People:\n",
    "    pass\n",
    "class Animals:\n",
    "    pass\n",
    "class Student(People,Animals):\n",
    "    pass\n",
    " \n",
    "print(Student.__bases__)#(<class '__main__.People'>, <class '__main__.Animals'>)\n",
    "print(People.__bases__)#(<class 'object'>,)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "可以看到，在People父类中，默认也继承了一个object类，这就是新式类和经典类的区别：\n",
    "凡是继承了object类的类及其子类，都称为新式类，没有继承object类的类，称为经典类。\n",
    "在Python 3中，默认就是新式类，而在Python2.X中，默认都是是经典类"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "class People:\n",
    "    def __init__(self,name,age):\n",
    "        self.name=name\n",
    "        self.age=age\n",
    "    def walk(self):\n",
    "        print('%s is walkig'%self.name)\n",
    "        \n",
    "class Teacher(People):\n",
    "    def __init__(self,name,age,level):\n",
    "        People.__init__(self,name,age)\n",
    "        self.level=level\n",
    "\n",
    "t1=Teacher('zhang',18,10)\n",
    "print(t1.level) #10\n",
    "print(t1.name) #zhang  子类可以用父类定义的属性\n",
    "t1.walk() #zhang is walking 子类无需定义就可以用父类的方法\n",
    "print(issubclass(Teacher,People)) #True查看Teacher类是不是People类的子类"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "组合\n",
    "\n",
    "继承是解决什么‘是'什么的问题，那还有一种场景就是什么有什么，比如老师有生日，学生也有生日，生日有年月日这些属性，如果每个类都写的话，又是重复代码。但是又不能让学生和老师继承生日类。这时就用到了组合。组合就是解决什么‘有'什么的问题。看例子"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Date:\n",
    "    def __init__(self,year,mon,day):\n",
    "        self.year=year\n",
    "        self.mon=mon\n",
    "        self.day=day\n",
    "    def tell_birth(self):\n",
    "        print('出生于%s年%s月%s日'%(self.year,self.mon,self.day))\n",
    "class Teacher:\n",
    "    def __init__(self,name,age,*args):\n",
    "        self.name=name\n",
    "        self.age=age\n",
    "        self.birth=Date(*args)\n",
    "\n",
    "t=Teacher('egon',19,2010,10,10)\n",
    "print(t.birth)  #<__main__.Date object at 0x0000017E559380F0>\n",
    "t.birth.tell_birth() #出生于2010年10月10日"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 抽象类与接口\n",
    "继承有两种用途：\n",
    "\n",
    "1.代码重用，子类继承父类的方法\n",
    "\n",
    "2.声明某个子类兼容于某父类，定义一个接口类Interface，接口类中定义了一些接口名（就是函数名）且并未实现接口的功能，子类继承接口类，并且实现接口中的功能\n",
    "\n",
    "需要注意的是，Python中并没有接口的关键字，我们只能是模仿接口的功能,比如在 Python中，一切皆文件嘛，那程序是文件，硬件是文件，文本文档也是文件，我们知道什么叫文件呢，就是能读能写，那程序，文本文档这些，都应该有读和写的功能，我们来模拟一下"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Interface:\n",
    "    def read(self):\n",
    "         pass\n",
    "    def write(self):\n",
    "         pass\n",
    "        \n",
    "class Txt(Interface):\n",
    "    def read(self):\n",
    "         print('文本文档的读取方式')\n",
    "    def write(self):\n",
    "         print('文本文档的写入方式')\n",
    "            \n",
    "class Sata(Interface):\n",
    "    def read(self):\n",
    "         print('硬盘文件的读取方式')\n",
    "    def write(self):\n",
    "         print('硬盘文件的写入方式')\n",
    "class process(Interface):\n",
    "    def read(self):\n",
    "         print('进程数据的读取方式')\n",
    "    def write(self):\n",
    "         print('进程数据的写入方式')"
   ]
  },
  {
   "cell_type": "raw",
   "metadata": {},
   "source": [
    "这么做的意义就是：我们不需要知道子类有什么具体的方法，既然他们继承了文件类，那他们就是文件，那他们就有读和写这两个功能"
   ]
  },
  {
   "cell_type": "raw",
   "metadata": {},
   "source": [
    "父类限制了子类子类必须有read和write这两个方法，而且名字也必须一样（当然现在只是我们主观上的限制，一会我们说完抽象类，就可以从代码级别上限制了），这样就实现了统一，模拟了接口的概念，这就是归一化设计。在归一化设计中，只要是基于一个接口设计的类，那么所有的这些类实例化出来的对象，在用法上是一样的\n",
    "我们再来说一下抽象类："
   ]
  },
  {
   "cell_type": "raw",
   "metadata": {},
   "source": [
    "Python中的抽象类需要导入一个模块来实现。抽象类只能被继承，不能被实现。抽象类的写法："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import abc\n",
    "class File(metaclass=abc.ABCMeta):\n",
    "    @abc.abstractmethod\n",
    "    def read(self):\n",
    "        pass\n",
    "    @abc.abstractmethod\n",
    "    def write(self):\n",
    "        pass\n",
    "#父类使用了抽象类，那子类就必须继承父类的方法，而且名字也必须一样\n",
    "#这样就实现了代码级别的限制\n",
    " \n",
    "class Txt(File):\n",
    "    def read(self):\n",
    "        print('文本文档的读取方式')\n",
    "    def write(self):\n",
    "        print('文本文档的写入方式')"
   ]
  },
  {
   "cell_type": "raw",
   "metadata": {},
   "source": [
    "继承原理：\n",
    "当我们定义一个类后，Python就会根据上面的继承规律解析出一个继承顺序的列表（MRO列表），可以通过mro()查看，但是这个方法只有在新式类中才有，经典类没有super()方法\n",
    "我们之前用继承是怎么用的来着，"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Parent(object):\n",
    "    def __init__(self,name,age):\n",
    "        self.name=name\n",
    "        self.age=age\n",
    " \n",
    "class Child(Parent):\n",
    "    def __init__(self,name,age,salary):\n",
    "        Parent.__init__(self,name,age,salary)\n",
    "        self.salary=salary"
   ]
  },
  {
   "cell_type": "raw",
   "metadata": {},
   "source": [
    "这其实是和继承没啥关系的写法，如果父类名字改了，在子类中也要改，更优雅的写法是用super()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Parent(object):\n",
    "    def __init__(self,name,age):\n",
    "        self.name=name\n",
    "        self.age=age\n",
    " \n",
    "class Child(Parent):\n",
    "        def __init__(self,name,age,salary):\n",
    "            super().__init__(name,age)\n",
    "            self.salary=salary"
   ]
  },
  {
   "cell_type": "raw",
   "metadata": {},
   "source": [
    "注意：super()方法只适用于新式类\n",
    "如果是多继承的关系，就用到mro列表，如果就是要继承多个父类的方法，那就还是用以前指名道姓的方法引用"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 封装\n",
    "1.为什么要封装？\n",
    "封装就是要把数据属性和方法的具体实现细节隐藏起来，只提供一个接口。封装可以不用关心对象是如何构建的，其实在面向对象中，封装其实是最考验水平的\n",
    "\n",
    "2.封装包括数据的封装和函数的封装，数据的封装是为了保护隐私，函数的封装是为了隔离复杂度\n",
    "\n",
    "3.数据的封装就是在属性前面加一个__"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "class People:\n",
    "    def __init__(self,name,age,salary):\n",
    "        self.name=name\n",
    "        self.age=age\n",
    "        self.__salary=salary\n",
    "        \n",
    "p=People('zhang',19,10000)\n",
    "print(p.name)#zhang\n",
    "print(p.age)#\n",
    "print(p.__salary)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#报错了，让我们打开对象的名称空间，看看发生了什么?\n",
    "print(p.__dict__)#"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 原来python把__salary变形成了_People__salary,\n",
    "print(p._People__salary)"
   ]
  },
  {
   "cell_type": "raw",
   "metadata": {},
   "source": [
    "Python中并没有绝对的隐藏，这些变形操作，只在类的定义阶段或者对象定义（实例化阶段）阶段发生，\n",
    "虽然在外部无法直接访问加了__的属性，但是在类内部可以访问到，\n",
    "可以这么理解，在定义阶段，只要遇到__开头的，Python解释器自动识别为_类名__属性，所以在类内部是可以访问到的"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "class A:\n",
    "    def foo(self):\n",
    "        print('from A foo')\n",
    "        self.bar()\n",
    "    def bar(self):\n",
    "        print('from A bar')\n",
    "class B(A):\n",
    "    def bar(self):\n",
    "        print('from B bar')\n",
    "        \n",
    "b=B()\n",
    "b.foo() #调用从A继承的foo函数"
   ]
  },
  {
   "cell_type": "raw",
   "metadata": {},
   "source": [
    "如果就是想调用父类的bar()函数呢？该怎么做"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "class A:\n",
    "    def foo(self):\n",
    "        print('from A foo')\n",
    "        self.__bar() #调用A中的方法\n",
    "    def __bar(self):\n",
    "        print('from A bar')\n",
    "        \n",
    "class B(A):\n",
    "    def __bar(self):\n",
    "        print('from B bar')\n",
    "        \n",
    "b=B()\n",
    "b.foo() #from A foo"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**封装的应用**\n",
    "\n",
    "1）不让外界看到我们的数据属性是怎么定义的，只能通过我们提供的接口，看到我们允许外界看到的内容"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "name:zhang age:18 height:1.9 weeight:75\n"
     ]
    }
   ],
   "source": [
    "class People:\n",
    "    def __init__(self,name,age,height,weight,hobby):\n",
    "        self.__name=name\n",
    "        self.__age=age\n",
    "        self.__height=height\n",
    "        self.__weight=weight\n",
    "        self._hobby=hobby\n",
    "    def tell_info(self):\n",
    "        print('''name:%s age:%s height:%s weeight:%s'''%(self.__name,self.__age,self.__height,self.__weight))\n",
    "        \n",
    "p=People('zhang',18,1.90,75,'read')\n",
    "p.tell_info()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "2）更常用的场景是，我们可以限制数据的类型，添加自己的逻辑以后再封装好给用户"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "def tell_name(self):\n",
    "        print(self.__name)\n",
    "#修改名字\n",
    "def set_name(self,new):\n",
    "    if not isinstance(new,str):\n",
    "        raise TypeError('名字必须是字符串类型')\n",
    "    elf.__name=new"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "raw",
   "metadata": {},
   "source": [
    "看我们上面的操作，用户查看名字的时候还得p.tell_name()，本来是个数据属性，却被我们搞得变成了一个函数，\n",
    "怎么伪装一下呢，就可以用到property这个装饰器了"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "zhang\n"
     ]
    }
   ],
   "source": [
    "class People:\n",
    "    def __init__(self,name,age,height,weight,hobby):\n",
    "        self.__name=name\n",
    "        self.__age=age\n",
    "        self.__height=height\n",
    "        self.__weight=weight\n",
    "        self._hobby=hobby\n",
    "    \n",
    "    @property\n",
    "    def name(self):\n",
    "        return self.__name    #name已经被property修饰过，就有setter和deleter\n",
    "    @name.setter\n",
    "    def name(self,new):\n",
    "        if not isinstance(new,str):\n",
    "            raise TypeError('名字必须是字符串类型')\n",
    "        self.__name=new\n",
    "    @name.deleter\n",
    "    def name(self):\n",
    "        del self.__name\n",
    "    \n",
    "p=People('zhang',18,1.90,75,'read')\n",
    "print(p.name)#zhang"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "zhang\n",
      "can\n"
     ]
    }
   ],
   "source": [
    "p = People('zhang', 18, 1.90, 75, 'read')\n",
    "print(p.name)#zhang\n",
    "p.name='can' #修改\n",
    "print(p.name)#can\n",
    "del p.name #删除\n",
    "# print(p.name)#属性被删除"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "@staticmethod #非绑定方法，就是一个函数，就是一个工具而已，不需要类，也不需对象"
   ]
  },
  {
   "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": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "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.8.0"
  },
  "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": "297.198px"
   },
   "toc_section_display": true,
   "toc_window_display": true
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
