# 面向对象

  • 面向过程VS面向对象:

    ​ 面向过程:优点,极大的降低了写程序的复杂度,只需要顺着堆叠代码

    ​ 缺点: 一套流水线,代码牵一发动全身

    ​ 面向对象:优点,解决了程序的扩展性,对某一对象修改可作用到全局

    ​ 缺点: 可控性差,可能使程序失控

  • Python中一切皆为对象,类型的本质就是类

    class Person:   #定义一个人类
        role = 'person'  #人的角色属性都是人,静态属性
        def walk(self):  #人都可以走路,也就是有一个走路方法,也叫动态属性
            print("person is walking...")
            
    print(Person.role)  #查看人的role属性
    print(Person.walk)  #引用人的走路方法,注意,这里不是在调用
    
  • 类的实例化 : 对象

    1. 实例化:类名加括号就是实例化,会自动触发__init__()函数的运行,可以用它来为每个实例定制自己的特征。类指针、类对象指针 执行init之前就已经在了,对象指针会指向类空间
    2. 执行完__init__()就会返回一个对象。这个对象类似一个字典(self),存着属于这个人本身的一些属性和方法。
    3. self:在实例化时自动将对象/实例本身传给__init__的第一个参数,你也可以给他起个别的名字,其实是个字典
  1. 补充:self其实使一个对象初始化生成的字典,里面记录对象的属性字典,在类加载完后返回给调用者,注意在类中代码式从上至下的运行,大部分的动态函数都寄存在类中,不会每个对象都有,要的时候才拿。
  • 类属性的补充:

    1. 两种方式查看属性:

      dir(类名)       #查出名字返回一个列表
      类名.__dict__   # 查出的一个字典,key为属性名,value为属性值
      
      # 特殊的类属性
      类名.__name__    # 类的名字(字符串)
      类名.__doc__     # 类的文档字符串
      类名.__base__    # 类的第一个父类(在讲继承时会讲)
      类名.__bases__   # 类所有父类构成的元组(在讲继承时会讲)
      类名.__dict__    # 类的字典属性
      类名.__module__  # 类定义所在的模块
      类名.__class__   # 实例对应的类(仅新式类中)
      
    2. 函数进阶类:

      def Person(*args,**kwargs):  # 定义函数
          self = {}
          def attack(self,dog):    # 定义攻击函数
              dog['life_value'] -= self['aggressivity']
      
          def __init__(name,aggressivity,life_value): # 函数版初始化函数
              self['name'] = name
              self['aggressivity'] = aggressivity
              self['life_value'] = life_value
              self['attack'] = attack
      
          __init__(*args,**kwargs)  # 执行初始化
          return self
      
      egg = Person('egon',78,10)  # 接受self字典
      print(egg['name'])
      
  • 对象之间的交互:就是将对象传给另一个对象作为参数

  • 类的加载顺序:

    ​ 1.类内部一个缩进的所有代码都是在py文件从上到下解释的时候就已经被执行了

    ​ 2.类中的代码永远是从上到下依次执行的, 且在类中的非定义的函数代码块外的代码,会自动执行,如:print('ok')

    class Student:  # 类区别于函数,函数执行是会加载函数名和地址,而类会执行
        print('我执行了')  # 加载类的时候就会执行
    
    s = Student()  # Student继承的是object,而object类的初始化函数是空的
    #结果
    #我执行了
    
    class A:
        country = 'China'
        def __init__(self):
            print('执行我了')
    
        def func(self):
            print('1111111')
    
        def func(self):
            print('2222222')
    A.country = 'English'
    a = A()
    a.func()
    print(A.country)
    
    #执行我了
    #2222222
    #English
    
  • 类与对象的命名空间

    1. 创建一个类就会创建一个类的名称空间,用来存储类中定义的所有名字,这些名字称为类的属性
    2. 创建一个对象/实例就会创建一个对象/实例的名称空间,存放对象/实例的名字,称为对象/实例的属性
    3. 在obj.name会先从obj自己的名称空间里找name,找不到则去类中找,类也找不到就找父类...最后都找不到就抛出异常
  • 面向对象的组合用法: 一个类的对象是另一个类对象的属性

    class Student:
        def __init__(self,name,num,course):
            self.name = name
            self.num = num
            self.course = course  # 这里就用到组合 也可以使用 Course('python',25000,'6 months')
            
    class Course:
        def __init__(self,name,price,period):
            self.name = name
            self.price = price
            self.period = period
    python = Course('python',25000,'6 months')
    s1 = Student('世龙',10085,python)
    s3 = Student('董鹏',10083,python)
    python.price = 30000
    print(python.price)  #30000
    print(s3.course.price)  #30000 
    
    #进阶知识
    print(python.name) # 'python' 字符串
    print(python.name.strip()) # 显然这里我可以对字符串进行操作。这让我想起一句话‘一切皆对象’,运用要组合的知识,这个Student类是str类、Course类、int类等的组合,再一想在py3后都是继承object的新式类,所以可以把object比成地球,它承载了所有的类。
    
  • 单继承:继承单个父类

    class Parent:  # __bases__ 返回父类
        pass
    
    class Son(Parent):
        pass
    print(Son.__bases__)  #(<class '__main__.Parent'>,)
    
    class Parent1:
        pass
    class Parent2:
        pass
    class Son(Parent1,Parent2):
        pass
    
    print(Son.__bases__)  #(<class '__main__.Parent1'>, <class '__main__.Parent2'>)
    
    #两种调用父类的方法:
    class Animal(object):   #使用类.方法(self,属性名)
        def __init__(self,name,kind):
            self.name = name
            self.kind = kind
    
        def eat(self,food):
            print('%s eat %s'%(self.name,food))
    
    class Cat(Animal):
        def __init__(self,name,kind,eyes_color):
            self.eyes_color = eyes_color
            Animal.__init__(self,name,kind)   # 在子类和父类有同名方法的时候,默认只执行子类的方法
            # 如果想要执行父类的方法,可以在子类的方法中再 指名道姓 的调用父类的方法
            
    class Dog(Animal):  # super 遵循mro顺序,不管在单继承还是多继承中
        def __init__(self,name,kind,eyes_color):
            self.eyes_color = eyes_color
            # super(Dog,self).__init__(name,kind)
            super().__init__(name,kind)   # 相当于执行父类的init方法
            # Animal.__init(self,name,kind)
            
    #总结:   1. 父类名.方法名(self,参数1,参数2)
     #       2. super().方法名(参数1,参数2)
     #       3. a = A() # 总是要调用init的,如果我们不写,实际上就调用父类object的__init__方法了(空的)
    

# 多继承

​ 1. 多个父类被继承,那么问题来了,究竟继承谁的那?

在python3中,所有类都是新式类:都遵循广度优先即C3算法,可以使用类.mro()方法查看,类的继承顺序(且继承顺序也是遵循这个顺序的),广度优先也就是从左至右,遇到之后还可以走到这的就跳过。

# C3算法实现:
C3的规则:
1. L(Child(Base1,Base2))= [ Child + merge( L(Base1),  L(Base2),Base1Base2 )]
2. 如果第一个序列的第一个元素,是后续序列的第一个元素,或者不在后续序列中再次出现,则将这个元素合并到最终的方法解析顺序序列中,并从当前操作的全部序列中删除。

L(G)=L(G(o))=[G,o]
L(E)=L(E(G(o)))
    =E +merge(L(G(o)))
    =E+[G,o]=[E,G,o]
L(D)=L(D(o))=[D,o]
L(F)=L(F(o))=[F,o]
L(B)=L(B(D,E))=B+merge(L(D),L(E),DE)
			  =B+merge(Do,EGo,DE) #D 合格
         	  =B +D +merge(o,EGo,E) # E合格
        	  =B +D + E +merge(G,o,)= [B,D,E,G,o]
L(C)=L(C(D,F))=C+merge(L(D),L(F),DF)
			 =C+merge(Do,Fo,DF) #D 合法
             =C+D+merge(o,Fo,F)  # F合法
        	 =C+D+F+merge(o)=[C,D,F,o]
L(A)=L(A(B,C))=A+merge(L(B),L(C),BC) =A +merge(BDEGo,CDFo,BC)  #B合法
                                     =A + B+merge(DEGo,CDFo,C)  # D不合法,看第二元素C合格
    							     =A + B+C+merge(DEGo,DFo,) #D合格
        						     =A + B+C+D+merge(EGo,Fo,)# E合格
            					     =A + B+C+D+E+merge(Go,Fo,)
                                     =A + B+C+D+E+G+F+o=[A,B,C,D,E,G,F,o]

在Python2版本中没有使用object的类都是经典类:遵循深度优先,也就是一条道走到黑。如果使用object,那么就是新式类,新式类的

super(本地类名,self).__init__(属性) 方法。

class A(object):
    def func(self):
        print('a')
class B(A):
    pass
    def func(self):
        super().func()  # 执行父类
        print('b')
class C(A):
    pass
    def func(self):
        super().func()
        print('c')
class D(B,C):
    pass
    def func(self):
        super().func()
        print('d')

b = B()
b.func()
# 运行结果:ab 

#在多继承中,super就只和mro顺序有关系,和父类子类没有关系了
class A(object):
    def func(self):
        print('a') #4
class B(A):
    def func(self):
        super().func()  # 2 执行父类C
        print('b') #6
class C(A):
    pass
    def func(self):
        super().func() # 3找父类A
        print('c')  #5
class D(B,C):
    def func(self):
        super().func() #1 找B
        print('d')  #7

d = D()
d.func()  # 注意super方法遵循mro顺序查找自己的父类
print(D.mro()) #[<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>]
#运行结果:acbd
  • 经典类和新式类的区别
    • 新式类
      1. 继承object,遵循广度优先算法
      2. python2 只支持super(子类名,self);python3支持super()/super(子类名,self)
      3. 遵循mro方法
    • 经典类
      1. 不继承object,没有super方法和mro方法;多继承遵循深度优先算法
      2. 只在python2中

# 元类基础

							'道生一,一生二,二生三,三生万物'
	道 即是 type
    一 即是 metaclass  (元类,或者叫类生成器)
	二 即是 class      (类,或者叫实例生成器)
	三 即是 instance   (实例,对象)
	万物 即是 实例的各种属性和方法,我们平常使用的方法就是他们
今天我们就学习下:道和一

​ 上面都是一些形象化的形容,元类————Python黑魔法 ,元类术语“元编程”指的是程序具有编写或操纵其自身作为它们资料的潜力。Python支持称为元类的类的元编程。元类是一个深奥的面向对象编程(OOP)概念,隐藏在几乎所有的Python代码之后。无论你是否意识到它的存在,你都一直在使用它们。大多数情况下,你并不需要了解它,但是你要是想看源码,你需要了解元类。

	type(类型)是一个元类,任何类都是它的实例。就像一个普通的对象是一个类的实例一样,Python中的任何新式类以及Python 3中的任何类都是type元类的一个实例。
	下面举个例子:
		1.x是类Foo的一个实例。
		2.Foo是type元类的一个实例。
		3.type也是type元类的一个实例,所以它是它自己的一个实例。
    1. (道,特殊元类,实例本身)type()函数的用法一:内置type()函数在传递了一个参数时将返回一个对象的类型。对于新式类,通常与对象的__class__属性相同:

      print(type(3))  # <class 'list'>
      class Foo:
          pass
      print(type(Foo()))  #<class '__main__.Foo>	
      
    2. type()函数用法二: 格式type()指定该类名称,指定基类名称,包含主体空间的字典

      #指定类名称,将成为该类的__name__属性。
      #指定继承类的基类元组,将成为该类的__bases__属性。
      #指定包含类主体定义的名称空间字典,将成为该类的__dict__属性。
      
      #以这种方式调用type()将创建一个type元类的新实例。换句话说,它动态地创建了一个新的类
      Foo = type('Foo',   # 指定创建类的名字
                  (),     # 继承的父类,可以多个,没有用空元组
                  {
                  'attr':'199',  #类属性
                  'attr_value':lambda  x:x.attr*2,  # 自动传入self,这里x相当于self
                  })
      a = Foo()
      print(a.attr,a.__class__)     #199 <class '__main__.Foo'>
      print(a.attr_value())  #398
      print(a.__class__.__class__)  # <class 'type'> 所以一切类的祖宗就是type(类型)
      
      # 函数名调用
      def f(obj):
          print('attr =',obj.attr)
      Foo = type('Foo',  # 指定创建类的名字
                  (),  # 继承的父类,可以多个,没有用空元组
                  {
                  'attr':'199',  #类属性
                  'attr_value':f    # 自动传入self,这里x相当于self
                  })
      a = Foo()
      print(a.attr,a.__class__) #199 <class '__main__.Foo'>
      a.attr_value()  #attr = 199
      print(a.__class__.__class__) # <class 'type'> 所以一切类的祖宗就是type(类型)
      
      
  • 自定义元类(元类 ‘一’)

    • 回想下之前表达式Foo()创建一个新的类Foo的实例。当解释器遇到Foo(),将按一下顺序进行解析:

      调用Foo父类的__call__()方法。由于Foo是标准的新式类,它的父类是type元类,所以type的__call__()方法被调用。

      # __call__()方法按以下顺序进行调用: 
      # __new__()
      # __init__()
      #(如果Foo内没有一上方法,就会调用父类的,如果定义了就会覆盖,这就允许我们自定义)
      
      def new(cls):   #对上例添加
          x = object.__new__(cls)
          x.attr = 100
          return x
      Foo.__new__ = new   #修改类的构造函数
      f=Foo()
      print(f.attr)  #100 自定义的修改了类
      
      #但是需要注意的是 type的__new__方法是不能被修改的,不然就世界打乱了
      
    • type是派生所有新式类的元类。无论如何,你真的不应该去修改它。但是,如果你想自定义一个类的实例化,那么有什么办法呢?一种可能的解决方法:自定义元类 (不是去试图修改type元类,而是定义自己派生于type的元类,然后对其进行修改,也就是我们说的‘一’,元类)

      #请注意,重新自定义了Meta的__new__()方法。因为不可能直接对type元类进行此类操作。__new__()方法执行以下操作:
      	#经由super()指代的(type)元类的__new__()方法实际创建一个新的类
      	#将自定义属性attr分配给类,并设置值为100  下列代码
      	#返回新创建的类
      class ListMetaclass(type):
          def __new__(cls, name, bases, attrs):  # cls=自身类空间,name=MyList,list,字典
              attrs['add'] = lambda self, value: self.append(value)
              print('我执行了',type.__new__(cls, name, bases, attrs))  #我执行了 <class '__main__.MyList'>
              return type.__new__(cls, name, bases, attrs)
      
      class MyList(list,metaclass=ListMetaclass):  pass # 第一参数是继承的类 指示使用ListMetaclass来定制类      
      l=MyList()
      l.add(1)
      print(l) #[1]
      
      # 道生一:传入type
      class SayMetaClass(type):
          # 传入三大永恒命题:类名称、父类、属性
          def __new__(cls, name, bases, attrs):
              # 创造“天赋”
              attrs['say_'+name] = lambda self,value,saying=name: print(saying+','+value+'!')
              # 传承三大永恒命题:类名称、父类、属性,实例化方法要有self
              return type.__new__(cls, name, bases, attrs) # 自定义返回
      
      # 一生二:创建类
      class Hello(object, metaclass=SayMetaClass):pass
      # 二生三:创建实列
      hello = Hello()
      # 三生万物:调用实例方法
      hello.say_Hello('world!')  # Hello,world!!
      
      # 对象工厂
      class Foo:
          def __init__(self):
              self.attr =100
      x = Foo()
      print(x.attr)  #100
      y=Foo()
      print(x.attr)  #100
      
      #类工厂:
      class Meta(type):
          def __init__(cls,name,bases,dct):  # 这里的cls就是要生成的类
              cls.attr =100
      class X(metaclass=Meta):pass
      print(X.attr)  #100
      
      #类工厂2:装饰器
      def decorator(cls):
          class NewClass(cls):
              attr =199
          return NewClass  # 返回内层类地址
      @decorator
      class X:
          pass
      print(X.attr)  #199
      
      #类工厂3:简单的继承也可以实现
      class Base:
          attr = 100
      class X(Base):pass
      print(X.attr)   #100
      
      # 自定义补充:实例的顺序
      class NewMetaClass(type):
          def __new__(cls, name, bases, attrs):
              print(cls)  # 此刻cls是NewMetaClass元类对象
              print(super(NewMetaClass, cls).__new__(cls, name, bases, attrs))  # (super...) 是A的类对象
              return super(NewMetaClass, cls).__new__(cls, name, bases, attrs)  # return xxx 以后传入self
          def __init__(self, *args, **kwargs):
              # 此self是A的类对象 (另super(NewMetaClass, self).__init__(*args, **kwargs)的值为None)
              super(NewMetaClass, self).__init__(*args, **kwargs)
              print(self)
          def __call__(self, *args, **kwargs):
              # 此self 是A的类对象, (super(NewMetaClass, self).__call__(*args, **kwargs))是A的类对象实例化后的 实例对象
              print(super(NewMetaClass, self).__call__(*args, **kwargs))
              return super(NewMetaClass, self).__call__(*args, **kwargs) # 最终出口,自动进入type的__call__,最后返回出去
      
      class A(object, metaclass=NewMetaClass):
          pass
      
      if __name__ == "__main__":
          a = A()
          print(a)
      #结果
      <class '__main__.NewMetaClass'>
      <class '__main__.A'>
      <class '__main__.A'>
      <__main__.A object at 0x02D96E30>
      <__main__.A object at 0x02D96DF0>
      
      --------------------------------------------------------------------
      # 用元类写单例模式:一个类有且仅有一个实例,并且自行实例化向整个系统提供
      class NewMetaClass(type):
          instance = None  # 定义一个空对象
          def __call__(self, *args, **kwargs):  # self为制造出的类对象(此处为A对象),这个函数__call__是再实例化类对象,结果也就是实例对象 
              if not hasattr(self, "instance"):
                  self.instance = super(NewMetaClass, self).__call__(*args, **kwargs)
                  return self.instance
      class A(object, metaclass=NewMetaClass):
          pass
      
      if __name__ == "__main__":
          a = A()
          b = A()
          c = A()
          print(id(a))
          print(id(b))
          print(id(c))
      """
      输出:
      1723436688
      1723436688
      1723436688
      """
      
      ----------------------------------------------------------
      #类私有变量
      class Liu_de_hua:    # 创建一个类
          nickname = "华仔"    # 类变量
          gender = "男"
          __real_age = 57    # 私有成员
      
          def __init__(self, age, wife=None):    # 构造方法
              self.age = age    # 实例变量
              self.wife = wife
              self.__wife = "朱丽倩"    # 私有成员
      
          def act(self):    # 实例方法
              print("我会表演")
      
          def date(self):    # 实例方法
              print("我常常和%s约会" % self.__wife)    # 在实例方法内部访问私有成员
      
      ldh = Liu_de_hua(48)
      
      print(ldh.wife)    # 实例对象访问实例变量
      ldh.date()    # 实例对象访问实例方法
      print(ldh.__wife)    # 尝试用实例对象访问私有成员
      
      # 执行结果:
      # None
      # 我常常和朱丽倩约会
      # 报错:  'Liu_de_hua' object has no attribute '__wife'
      
  • # 补充:类的实例化过程

    Pythonic(python之禅)的——理解这些步骤使得我们对Python整体有多一点的了解。Foo是一个类,但是Python中的类也是对象!类、函数、方法以及实例都是对象,并且无论何时你将一对括号放在它们的名字后面时,就会调用它们的__call__方法。所以Foo(1, y=2)是等价于Foo.__call__(1, y=2)的。__call__方法是定义在Foo的类中的。Foo的类是什么呢?

    如果我们忽略错误检查,那么对于常规类的实例化它大致等同如下:
    def __call__(obj_type, *args, **kwargs):
        obj = obj_type.__new__(*args, **kwargs)
        if obj is not None and issubclass(obj, obj_type):
            obj.__init__(*args, **kwargs)
        return obj
    
    #需要注意的是:如果 Foo 定义了一个 __call__方法,Foo(*args, **kwargs) 并不等于Foo.__call__(*args, **kwargs)
    
    >>> class Foo:
    ...   def __call__(self):
    ...     print('running __call__')
    ...
    >>> Foo()
    <__main__.Foo object at 0x000000000227ABE0>
    >>> Foo.__call__()
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    TypeError: __call__() missing 1 required positional argument: 'self'
    In this case, __call__ is used to call instances of the class :
    
    >>> Foo()()   
    running __call__   #说明这个call只能对象调用
    >>>
    
    即可以这样理解:Python中存在于类中的构造方法__init__()负责将类实例化,而在__init__()执行之前,__new__()负责制造这样的一个实例对象,以便__init__()去让该实例对象更加的丰富(为其添加属性等)。
      同时:__new__() 方法还决定是否要使用该__init__() 方法,因为__new__()可以调用其他类的构造方法或者直接返回别的对象来作为本类 的实例。
    

    总的来说:

    1. Foo(*args, **kwargs)等价于Foo.__call__(*args, **kwargs)
    2. 既然Foo是一个type的实例,Foo.__call__(*args, **kwargs)实际调用的是type.__call__(Foo, *args, **kwargs)
    3. type.__call__(Foo, *args, **kwargs)调用type.__new__(Foo, *args, **kwargs),然后返回一个对象。
    4. obj随后通过调用obj.__init__(*args, **kwargs)被初始化。
    5. obj被返回。
  • # 定制自已的__new__()函数(单例)
    class Singleton(object):
        _instance = None
        def __new__(cls, *args, **kwargs):
            if cls._instance is None:
                cls._instance = super().__new__(cls, *args, **kwargs)
            return cls._instance
    
  • # 再谈元类__call__()

    元类是类的类,元类之于类就相当于类之于实例。 元类的new方法会创建一个类并返回,就像类的new方法会创建一个实例并返回一样。 元类中其他方法的定义类似于类中方法的定义,例如:

    class Meta(type): 
        def __new__(cls, name, bases, dct):  # cls为元类Meta
            return type.__new__(cls, name, bases, dct)
        def __init__(cls, *args, **kwargs):  # cls为元类创建的类
            pass
        def __call__(cls, *args, **kwargs):  # cls为元类创建的类
            pass
    
        #元类中有一个特殊的方法__call__,这个方法会截断类的__new__和__init__方法,阻止其执行,__call__ 应该返回实例,和类的__new__方法返回的一样。
        
    class SingletonType(type):
        def __init__(cls, *args, **kwargs):
            print('元类__init__')
            super(SingletonType, cls).__init__(*args, **kwargs)
    
        def __call__(cls, *args, **kwargs):
            print('元类__call__')
            obj = cls.__new__(cls, *args, **kwargs)
            cls.__init__(obj, *args, **kwargs)  # Foo.__init__(obj)
            return obj
    
    
    class Foo(metaclass=SingletonType):
        def __init__(self, name):
            print("Foo __init__")
            self.name = name
    
        def __new__(cls, *args, **kwargs):
            print('Foo __new__')
            return object.__new__(cls)
    
    
    obj = Foo('name')
    print(obj)
    
    #结果:
    元类__init__  #去父亲的init方法,再去老祖宗那init
    元类__call__
    Foo __new__
    Foo __init__
    <__main__.Foo object at 0x10ae85860>
    

# 抽象类

  • **抽象类的作用:**约束所有的子类,必须实现被abstractmethod装饰的方法名,给我们代码指定规范

  • 抽象类的特点: 抽象类不能实例化,只是作为具体的类的规范

  • 抽象类:在python中可以理解为单继承

    from abc import ABCMeta,abstractmethod
    class Payment(metaclass=ABCMeta):    # 抽象类
    
        @abstractmethod   # 如果我必须要实现pay方法,那么我需要给pay加一个装饰器
        def pay(self):
            pass   # 创建的这个pay并没有内容,
                   # 之所以写一个pay是为了提醒所有子类你一定要实现一个pay方法
        @abstractmethod  # 继承的类都必须实现这个方法,不然实例化都不行
        def back(self):
            pass
    
    class Wechatpay(Payment):
        def __init__(self,name,money):
            self.name = name
            self.money = money
        def pay(self):
            print('%s通过微信支付了%s元'%(self.name,self.money))
        def back(self):
            print('退款')   
    
    class Alipay(Payment):
        def __init__(self,name,money):
            self.name = name
            self.money = money
        def pay(self):
            print('%s通过支付宝支付了%s元'%(self.name,self.money))
        def back(self):
            print('退款')       
    
    class ApplePay(Payment):
        def __init__(self, name, money):
            self.name = name
            self.money = money
        def pay(self):
            print('%s通过apple pay支付了%s元' % (self.name, self.money))
        def back(self):
            print('退款')
    
    #归一化设计,许多都实现了归一化,比如len(),可以对各种数据类型的__len__()方法整合,判断时那种类型
    def pay(person):  
        person.pay()
    def back(person):
        person.back()
    
    a=ApplePay('alex',20000)  # 如果a中没有抽象类的方法实例化时报错
    pay(a)     # alex通过apple pay支付了20000元,
    back(a)
    
    # 抽象类的所有继承的子类都必须按要求满足条件,否则报错
    from abc import ABCMeta,abstractmethod
    class A(metaclass=ABCMeta):  #抽象类的所有方法,子类必须无条件有
        @abstractmethod
        def eat(self):
            print('A.eat()')
    
    class C(A):pass
    class B(C):pass
        # def eat(self):
        #     print('B.eat()')
    
    b =B()
    print(b)  #报错,b中没有eat方法
    

# 接口类

  • 在java中:类只能单继承,所以抽象类只能是所有子类的一个规范,没有多继承,但是java发明了接口,可以实现多继承

  • 在python中没有接口语法只是用多继承实现接口功能

  • 接口类在python中可以理解为多继承

    from abc import ABCMeta,abstractmethod
    class NormalAnnimal(metaclass=ABCMeta):
        @abstractmethod
        def eat(self):pass
        @abstractmethod
        def drink(self):pass
    class FlyAnimal(metaclass=ABCMeta):
        @abstractmethod
        def fly(self):pass
    class SwimAnimal(metaclass=ABCMeta):
        @abstractmethod
        def swim(self):pass
    class Frog(NormalAnnimal,SwimAnimal,WalkAnimal):
        def eat(self):
            pass
    class Swan(NormalAnnimal,FlyAnimal,SwimAnimal,WalkAnimal):pass
    class Parrot(NormalAnnimal,FlyAnimal,WalkAnimal):
        def talk(self):
            pass
    a=Parrot()
    print(a)  # 报错,没有实现抽象类中的各种方法
    

# 多态

  • 多态是之一类事物有的多种形态,如木头有:高桌子,矮板凳

  • 在python中处处是多态,一切皆对象

  • 例如:老师.下课铃响了(),学生.下课铃响了(),老师执行的是下班操作,学生执行的是放学操作,虽然二者消息一样,但是执行的效果不同。

    import abc
    class Animal(metaclass=abc.ABCMeta): #同一类事物:动物
        @abc.abstractmethod
        def talk(self):
            pass
    
    class People(Animal): #动物的形态之一:人
        def talk(self):
            print('say hello')
    
    class Dog(Animal): #动物的形态之二:狗
        def talk(self):
            print('say wangwang')
    
    class Pig(Animal): #动物的形态之三:猪
        def talk(self):
            print('say aoao')
    
  • 鸭子类型

    鸭子类型:python当中写程序的一种特殊的情况
            1.其他语言中 正常的我们说一个数据类型具有某个特点,通常是通过继承来实现
            2.可以继承迭代器类,来证明自己本身是个迭代器
            3.可以继承可哈希的类,来证明自己本事是可哈希的
        但是所有的这些都不是通过继承来完成的,我们只是通过一种潜规则的约定,如果具有__iter__,__next__就是迭代		器,也就是说,例如list和str,他们没有继承一个单独的可迭代类,而是通过方法类似,而联系在一起。
            如果具有__hash__方法就是可哈希
            如果具有__len__就是可以计算长度的
        这样数据类型之间的关系并不仅仅是通过继承来约束的,而是通过约定俗成的关系来确认的
    多态:
        在传递参数的时候,如果要传递的对象有可能是多个类的对象,我们又必须在语言中清楚的描述出到底是那一个类型的对象,我们就可以使用继承的形式,有一个父类作为这些所有可能被传递进来的对象的基类,基础类型就可以写成这个父类,于是所有子类的对象都是属于这个父类的
        在python当中,因为要传递的对象的类型在定义阶段不需要明确,所以我们在python中处处都是多态
    数据的类型不需要通过继承来维护统一
    
  • 再谈 namedtuplepickle模块

    #namedtuple模块
    from collections import namedtuple
    Course = namedtuple('Course',['name','price','period']) # namedtuple是个元类
    python = Course('python',20000,'6 month')  #显然这里的Course是一个类,python是对象
    print(python.name)  #python
    print(python.price) #20000
    print(type(python))  #<class '__main__.Course'>
    
    #pickle模块
    import pickle
    class Course:
        def __init__(self,name,price,period):
            self.name = name
            self.price = price
            self.period = period
    
    python = Course('python',20000,'6 months')  
    linux = Course('linux',15800,'5 months')
    import pickle
    with open('pickle_file','ab') as f:  # 将对象写入文件
        pickle.dump(python,f)
        pickle.dump(linux,f)   
    
    # 读入对象,可以调用其各种属性,注意的是必须圆来的类Course存在
    with open('pickle_file','rb') as f:
        # obj1 = pickle.load(f)
        # obj2 = pickle.load(f)
        while True:
            try:
                obj = pickle.load(f)  
                print(obj.__dict__)
            except EOFError:
                break
    

    以上的总结:

    1. 抽象类: 规范代码,规定子类必须实现某个方法的名字,不能实例化,只能用来继承

    2. 接口类:实际上多继承,继承多个抽象类,这样几个抽象类中实现的方法,都必须在子类中实现

    3. 多态: 广义 : 一类事物表现出来的多种形态,动物可以表现成猪\狗\羊 狭义 : 在函数接收参数的时候,接收的多个类的对象同属于一个父类, 在python中处处是多态

    4. 鸭子类型:

      多个相似的类具有相同的方法名,但是又不通过抽象类/或者继承父类约束的 这几个具有相同方法的类就是鸭子类型

# 封装

  • 不想让别人改我的属性,或不想让别人看见,就要使用封装。

    • 广义上的封装:把方法和属性根据类别装到类中
    • 侠义上的封装:私有化的方法和属性
  • 方法、静态变量、实例化变量(对象属性)都可以私有化

  • 所谓的私有化:就是只能在类的内部可见,类的外部是不能访问或查看的

    #私有属性
    class Goods:
        def __init__(self,name,price):
            self.name = name
            self.__price = price    # 私有属性,其实是取了个_Goods__price的静态变量,外部通过默认名字无法访问
        def get_price(self):
            print(self.__price)
    
    apple= Goods('苹果',5)
    print(apple.name)  # 苹果
    apple.get_price()  # 5
    print(apple.__price) # AttributeError: 'Goods' object has no attribute '__price'
    
    # 私有的静态变量
    class Role:
        __Country='China'   # 静态变量的私有化
        def func(self):
            print(self.__Country)
    print(Role.__Country)  # 报错 : 因为不能再类的外部引用变量
    Role().func()  # China
    
    #私有方法
    import hashlib
    class Auth:
        def __init__(self,user,pwd):
            self.username = user
            self.password = pwd
    
        def __md5_code(self):
            md5 = hashlib.md5(self.username.encode('utf-8'))
            md5.update(self.password.encode('utf-8'))
            return md5.hexdigest()
    
        def login(self):
            if self.username == 'alex' and 'ee838c58e5bb3c9e687065edd0ec454f' == self.__md5_code():
                return True
    # print(Auth.__dict__)  #{'__module__': '__main__', '__init__': <function Auth.__init__ at 0x02FF2390>,'_Auth__md5_code': <function Auth.__md5_code at 0x02FF2ED0>,'login': <function Auth.login at 0x02FF2FA8>, '__dict__': <attribute'__dict__' of 'Auth' objects>, '__doc__': None}                                      
    print(Auth.__module__)  #'__main__'
    user = input('user>>>')
    pwd = input('password>>>')
    obj = Auth(user,pwd)
    # obj.__md5_code()   # 报错的,私有的方法不能在类的外部被使用
    obj._Auth__md5_code()   # 不报错的
    ret = obj.login()
    if ret: print('登陆成功')
    
    #外部调用类的的私有方法
    class A:
        @staticmethod
        def __func():  # 私有的类的方法
            print('in func')
    
        @classmethod # 使用类方法调用,这里的cls就是A
        def f(cls):
            cls.__func()
    A.f()   #in func
    
    #私有化是怎么完成的?在类部定义的时候完成的,加载类时。
    class Goods:
        def __init__(self,name,price):
            self.name = name
            self.__price = price    # 私有属性
    
        def get_price(self):
            print(self.__price)
    
        def set_num(self):
            self.__num = 20  # 只要在类中有__的赋值,就会进行私有化
        def __private(self):
            print(_Goods__private) # 私有的形成  5, 也就是说我们是可以查看私有属性的的 _类名__属性
    
    apple = Goods('苹果',5)
    print(apple.__dict__)
    print(Goods.__dict__) #{'_Goods__private': <function Goods.__private at 0x03312ED0>,……}
    print(apple._Goods__price)   # 私有的形成
    print(apple._Goods__private) # 类私有<bound method Goods.__private of <__main__.Goods object at 0x03746E30>>
    
    # print(apple.__private())  # 报错,没有这个函数
        # 所有的私有的变化都是在类的[内部]定义的时候完成的
    apple.__num = 10
    print(apple.__dict__)  #{'name': '苹果', '_Goods__price': 5, '__num': 10}
    apple.set_num()
    print(apple.__dict__) #{'name': '苹果', '_Goods__price': 5, '__num': 10, '_Goods__num': 20}
    print(apple.__num)  #10
    
    class Foo:
        def __init__(self):
            self.__func() #这里的__func是_Foo__func(),各自的私有化在加载的就已经私有了
        def __func(self):
            print('in foo')
            
    # 私有的属性可以被继承么? 不能,在加载类时,就已经把名字私有化了
    class Son(Foo):
        def __func(self):  # 此方法不会加载,
            print('in son')
        #def _Foo__func(self):  # 此方法会加载,哈哈哈,如果自己把名字改成父类名字,就会执行自己的
            #print('in son')    # 下面的结果就会改为:in son
    
    a=Son() #in foo
    print(Son.__dict__) #{……'_Son__func': <function Son.__func at 0x03022C90>,}
    print(Foo.__dict__) #{……'_Foo__func': <function Foo.__func at 0x03022F60>,  }
    print(a.__dict__)  #{}  
    
    #例2
    class User:
        def __wahaha(self):
            print('in user')
    class VipUser(User):
        def func(self):
            self.__wahaha()  #所以不继承父类,因为名字都不一样,私有化后
    
    VipUser().func()    # 报错,因为在命名空间中根本不存在一个_VipUser__wahaha
    
    # 继承总结:
    #私有的这个概念 :但凡在类的外面 都不能用,因为实际名字变了,但也可以改名字来继承
    #私有的所有内容 :实例变量(对象属性),静态变量(类变量),方法都不能被子类继承
    #如何实现的:
            # 通过在类的内部定义或者使用的时候会自动添加 _类名 来进行变形
            # 在类的外部使用的时候由于python不会做自动的变形,所以这个属性就被隐藏了
            # 如果我们一定要在外部使用,也可以自己加上_类名的变形机制
            # 但十分不建议你在类的外部直接使用私有的名字
    

![img](file:///D:\QQ\QQ文件\1417506149\Image\Group\DCA69`C~_~[[VEWBMGPDVA3.png)

# 类的描述符

  • property 把装饰的一个方法伪装成一个属性,对属性进行查、改、删

    #装饰器返回年纪
    import time
    class Person:
        def __init__(self,name,birth):
            self.name = name
            self.birth = birth
        @property
        def age(self):  # 返回年纪
            struct_t = time.localtime()
            age = struct_t.tm_year - int(self.birth.split('-')[0])
            return age
    alex = Person('alex','1965-5-12')
    print(alex.age)  #54
    
    #装饰器返回
    class Goods:
        discount = 0.8
        def __init__(self, name, price):
            self.name = name
            self.__price = price
        @property
        def price(self):  #私有变量虽然不可以按原本的方式访问,但是可以使用property,改变
            p =  self.__price * self.discount
            return p
    
    apple = Goods('苹果', 5)  # {'name': '苹果', '_Goods__price': 5} 返回私有变量,
    banana = Goods('香蕉', 10)  #{'name': '香蕉', '_Goods__price': 10}
    print(apple.__dict__)
    print(banana.__dict__)
    print(apple.name)  # 苹果
    print(apple.price)  # 4.0
    print(banana.price)  #8.0
    Goods.discount = 1
    print(apple.price) #5
    print(banana.price) #10
    
    #属性.setter修改属性
    class Goods:
        discount = 0.8
        def __init__(self, name, price):
            self.name = name
            self.__price = price
        @property    # 只支持obj.price的方式查看这个结果,不支持修改,也不支持删除
        def price(self):
            p =  self.__price * self.discount
            return p
        @price.setter # 其实这里是类似于纵向装饰器,先设置,在进入上面的peoperty,最后返回属性
        def price(self,value):
            self.__price = value
    
    apple = Goods('苹果', 5)
    banana = Goods('香蕉', 10)
    print(apple.price) # 4
    apple.price = 8   # 对应的调用的是被setter装饰的price方法
    print(apple.price)  # 6.4 对应调用的是被property装饰的price方法
    
    class Goods:
        discount = 0.8
        def __init__(self, name, price):
            self.name = name
            self.__price = price
    
        @property    # 只支持obj.price的方式查看这个结果,不支持修改,也不支持删除
        def price(self):
            p =  self.__price * self.discount
            return p
    
        @price.setter # 设置属性
        def price(self,value):
            if type(value) is int or type(value) is float:
                self.__price = value
    
         @price.deleter  # 删除属性   会自动删除属性
         def price(self):
             del self.__price
    
    # 想删除一个属性
    apple = Goods('苹果', 5)
    print(Goods.__dict__)
    print(apple.__dict__) #{'name': '苹果', '_Goods__price': 5}
    apple.price
    apple.price = 9 #设置
    print(apple.price) #7.2
    # del apple.price  #执行后 就没有price属性了
    
    class Teacher:
        __num = 0
        def __init__(self,name,pw):
            self.name = name
            self.pw = pw
            self.__num += 1
        # def read_num():  # 报错,这里显示找不到属性
        #     return __num
        @classmethod
        def read_num(cls):
            return cls.__num
    
    print(Teacher.read_num())  # 1 vccccccccc
    
    #总结
    # 私有的 :通过过给__名字这样的属性或者方法加上当前所在类的前缀,把属性隐藏起来了
    #         只能在本类的内部使用,不能在类的外部使用,不能被继承
    # property 把一个方法伪装成属性
    # property和私有的两个概念一起用
    #     定义一个私有的
    #     再定义一个同名共有的方法,被property装饰
    #     @方法名.setter
    #     @方法名.deleter
    
  • classmethod 类方法

    class Fruits:
        __discount = 0.8
        def __init__(self, name, price):
            print('init',self)
            self.name = name
            self.__price = price #给对象创建一个以类名为基础的变量,以便下次类调用
    
        @classmethod      # 把一个方法从对象方法,变成一个类方法
        def change_discount(cls,value):
            cls.__discount = value   # cls到底是谁??? Fruits
    
        @classmethod
        def get_discount(cls):
            return cls.__discount
    
    print(Fruits.get_discount())  # 0.8
    Fruits.change_discount(1)
    print(Fruits.get_discount())  # 1
    
    # 类方法
        # 1. 有些时候我们要修改的是类中的静态变量/类变量,此时根本不会和self有任何的操作关联
        # 2.这时传一个self参数对我们来说完全没有用, 我们希望接受的是当前我们所在的类
    apple = Fruits('apple',8)
    apple.change_discount(0.5)  # 会去找父类
    print(Fruits.get_discount())  # 0.5
    # 类方法推荐使用类名调用而不是使用对象名调用
    
  • staticmethod 静态方法

    class A:
        @staticmethod  # 声明这个方法只是一个普通的不会使用任何和这个类中的变量相关的方法
        def func():    # 此时 func是一个静态方法
            print('既不操作和self相关的内容')
            print('也不操作和类名相关的内容')
    A.func()  #打印……
    
    class Student:
        def __init__(self,name):
            self.name = name
    
        @staticmethod
        def login():
            pass
    # 先获取这个学生的用户名和密码
    # 判断他登录成功之后进行实例化
    # Student.login()
    # stu = Student('alex')
    

# 反射

# 反射 通过字符串属性名 得到真正的这个字符串的名字对应的对象的属性值
class Student:
    def __init__(self,name):
        self.name = name

    def show_courses(self):
        print('调用了 show courses')

    def select_course(self):
        print('调用了 select course')

    def show_selected_course(self):
        print('调用了 show_selected_course')

    def quit(self):
        print('调用了 quit')
wu = Student('吴彪')
# 反射 通过字符串属性名 得到真正的这个字符串的名字对应的对象的属性值
ret = getattr(wu,'name')   # 内置函数 可以查看属性
print(ret)  # 吴彪
# print(getattr(wu,'show_courses'))  # wu.show_courses
getattr(wu,'show_courses')()  #调用了 show courses
getattr(wu,'select_course')()  #调用了 select course

*****
while True:
    name = input('user>>>')
    if hasattr(wu,name):
        func_attr = getattr(wu,name)  # 如果取出对象属性名
        if callable(func_attr):   #是否可以调用
            func_attr()
        else:
            print(func_attr)

  • issubclass(Son,Foo) # 判断Son是否是Foo的子类,或是Son是否是Foo的子类,(类与类)

    class Foo(object):pass
    class Son(Foo):pass
    ret = issubclass(Son,Foo)
    print(ret)  #True
    
  • isinstance(obj,cls) # 判断对象与类之间得关系,这个类也包括父类 (对象与类)

  • type() #和isinstance类似都是对象与类得关系,但是type只找实例化对象得类,而isinstance可以找所有祖宗

    a = 1
    ret1 = type(a) is int
    ret2 = isinstance(a,int)
    print(ret1) #True
    print(ret2) #True
    
  • 反射详解:

    • 所有的a.b都可以变成getattr(a,'b')

    • 用字符串数据类型的变量名'b' 来获取实际的变量值,实则用字符串数据类型的变量名 找到这个变量对应的内存地址

    • 作用于对象反射:obj.属性名 obj.方法名()

    • 作用于类反射: cls.静态变量名 cls.类方法名() cls.静态方法名()

    • 作用于模块反射: 模块名.方法名()

    • 作用于调用函数和方法: def func():pass

    • 作用于当前文件: 当前模块中得变量,函数等

      #简单的反射,在类中使用反射
      class Manager:   # 管理员用户
          def __init__(self,name):
              self.name  = name
          def create_course(self):  # 创建课程
              print('in Manager create_course')
      
          def create_student(self): # 给学生创建账号
              print('in Manager create_student')
      
          def show_courses(self): # 查看所有课程
              print('in Manager show_courses')
      
          def show_students(self): # 查看所有学生
              print('in Manager show_students')
      alex = Manager('alex')
      operate_lst = [('创建课程','create_course'),('创建学生账号','create_student'),
                     ('查看所有课程','show_courses'),('查看所有学生','show_students')]
      for index,opt in enumerate(operate_lst,1):
          print(index,opt[0])
      num = input('请输入您要做的操作 :')
      if num.isdigit():
          num = int(num)
          if hasattr(alex,operate_lst[num-1][1]):   #hasattr与getattr连用
              getattr(alex,operate_lst[num-1][1])()
              
       # 如何使用反射
      alex = Manager('alex')
      print(alex.name)    # ==> print(getattr(alex,'name'))   用的相对少
      funcname = 'create_course'
      a = getattr(alex,funcname)
      b = alex.create_course
      print(a)  
      print(b)  #a 和b一致 
      getattr(alex,'create_course')()   # ==> # alex.create_course()   用的多
      
      #类中的反射:
      class A:
          Country = '中国'
          @classmethod
          def show(cls):
              print('国家 : ',cls.Country)  #使用了类中的私有变量
      print(getattr(A,'Country'))   # print(A.Country)
      A.show  # getattr(A,'show')
      getattr(A,'show')()   # A.show()
      #类中反射两种方式:
          #对象名.属性名 / 对象名.方法名() 可以直接使用对象的方法和属性
          #当我们只有字符串数据类型的内容的时候
              # getattr(对象名,'方法名')()
              # getattr(对象名,'属性名')
              
      #反射模块中的方法
      import re
      ret = re.findall('\d+','2985urowhn0857023u9t4')
      print(ret)  #['2985', '0857023', '9', '4']
      'findall'
      getattr(re,'findall')   # re.findall
      ret = getattr(re,'findall')('\d','wuhfa0y80aujeiagu')
      print(ret)  #['0', '8', '0']
      
      import time
      time.time  == getattr(time,'time')
      time.time()  == getattr(time,'time')()
      
      # 反射本文件中的内容:只是要出现在全局变量中的名字,都可以通过getattr(modules[__name__],字符串数据类型的名字)获得
      from sys import modules
      print()    # DICT KEY是模块的名字,value就是这个模块对应的文件地址
      
      import re
      print(re)   # <module 're' from 'D:\\python3\\lib\\re.py'>
      print(modules['re']) # 也就是找到re模块的内存地址
      print(modules['re'].findall) # print(re.findall)  参数添加在后面,效果相同,
      
      #打印本地加载的所有属性:
      a = 1
      b = 2
      print(__name__)  #__main__
      print('__main__')  #__main__
      print(getattr(modules[__name__],'a'))   #1
      
      #语法
      a = 1
      b = 2
      getattr(modules[__name__],'变量名')
      
      #函数名
      def func(a,b):
          print('in func',a,b)
      
      getattr(modules[__name__],'func')   # func
      getattr(modules[__name__],'func')(1,2)   # func
      
      #类名
      class Course:
          def func(self):
              print('in func')
      
      print(Course)
      print(getattr(modules[__name__],'Course'))   # Course
      getattr(modules[__name__],'Course')()   # 实例化的过程
      
      #总结
      # hasattr和getattr
          # 只要是a.b这种结构,都可以使用反射
          # 用对象\类\模块反射,都只有以下场景
          # 这种结构有两种场景
          #     a.b   b是属性或者变量值
          #         getattr(a,'b')   == a.b
          #     a.b()  b是函数或者方法
          #         a.b()
          #             getattr(a,'b')()
          #         a.b(arg1,arg2)
          #             getattr(a,'b')(arg1,arg2)
          #         a.b(*args,**kwargs)
          #             getattr(a,'b')(*args,**kwargs)
          # 如果是本文件中的内容,不符合a.b这种结构
              # 直接调用func()
                  # getattr(sys.modules[__name__],'func')()
              # 直接使用类名 Person()
                  # getattr(sys.modules[__name__],'Person')()
              # 直接使用变量名 print(a)
                  # getattr(sys.modules[__name__],'a')
      # 所有的getattr都应该和hasattr一起使用
             # if hasattr():
                  #getattr()
      # setattr 只用来修改或者添加属性\变量,不能用来处理函数或者是其他方法
          # a.b = value
          # setattr(a,'b',value)
      
      # delattr 只用来删除 属性\变量
          # del a.b 删除属性  相当于删除了a对象当中的b属性
          # delattr(a,'b')