类和对象

《Python3高级核心技术97讲,bobby》学习笔记,第三章:鸭子类型、抽象基类、MRO查找、super函数、上下文管理。
380阅读 · 2020-6-7 23:52发布

3.1 鸭子类型

  • 当看到一只鸟走起来像鸭子,游泳像鸭子,叫起来也像鸭子,那么这只鸟就可以被称为鸭子。
  • 例如一个类实现了迭代器魔法方法、又实现了集合相关魔法方法,那么它可以被当做是迭代类型,也拥有序列相关的使用方法。这就变成了“鸭子类型”。

3.2 抽象基类(abc模块)

  • 相当于java中的接口。
  • 抽象基类中设定好方法,所有继承基类的类必须覆盖基类中的方法。
  • 抽象基类无法实例化。
  • 作用一:抽象基类可以使用isinstance进行类型对比。collections.abc中有各种通用抽象基类。示例代码:

    import abc
      class A(object):
          def __init__(self,x):
              self.x = x
    
          def __len__(self):
              return 0
    
      class B(metaclass=abc.ABCMeta):
          def __init__(self,x):
              self.x = x
    
          @abc.abstractmethod
          def get(self):
              return x
    
          @classmethod
          def __subclasshook__(cls,K):  # 用于判断两个对象类型是否相同,K代表传递进来的对象
              return hasattr(K,"__len__")
    
      a = A('a')
      print(isinstance(a,B))
    
  • 作用二:强制某个子类必须实现某些方法。示例代码:

    class B(metaclass=abc.ABCMeta):
      def __init__(self,x):
          self.x = x
    
      @abc.abstractmethod  # 表示该方法必须被重写
      def get(self):
          return x
    
      class C(B):
          def __init__(self,x):
              self.x = x
    
          def get(self):
              return x+'c'
    
      c = C('c')
    

3.3 isinstance和type的区别

  • type用于判断对象的类型,不会考虑继承关系。
  • isinstance判断对象的类型,会检查继承链。
  • isinstance会调用subclasshookinstancecheck魔法函数来判断类型是否相同。

3.4类和实例属性的查找顺序——MRO查找

  • Python2.3开始属性搜索算法,使用C3算法。
  • 在类的mro中可以查看类的查找顺序。
  • 如果是深度继承关系,会优先查找深度(A-B-D-C-E)。

  • 如果是菱形继承关系,会先查找上一级,再往深度查找(A-B-C-D)。

3.5 类方法和静态方法

  • 静态方法,相当于普通的函数,在方法上添加@staticmethod即可。

    class Date:
          def __init__(self,year,month,day):
              self.year =year
              self.month = month
              self.day = day
    
          @staticmethod
          def parse_from_string(date_str):
              year,month,day = tuple(date_str.split("-"))
              return Date(int(year),int(month),int(day))
    
          def __str__(self):
              return "{year}/{month}/{day}".format(year=self.year,month=self.month,day=self.day)
    
      if __name__ == "__main__":
          date_str = "2020-06-06"
          new_day = Date.parse_from_string(date_str)
          print(new_day)
    
  • 类方法,参数会默认携带类本身,在方法前添加@classmethod即可

    class Date:
          def __init__(self,year,month,day):
              self.year =year
              self.month = month
              self.day = day
    
          @classmethod
          def parse_from_string(cls,date_str):
              year,month,day = tuple(date_str.split("-"))
              return cls(int(year),int(month),int(day))
    
          def __str__(self):
              return "{year}/{month}/{day}".format(year=self.year,month=self.month,day=self.day)
    
      if __name__ == "__main__":
          date_str = "2020-06-06"
          new_day = Date.parse_from_string(date_str)
          print(new_day)
    

3.6 私有属性

  • 在属性名称的前面加上“__”则表示是私有属性,外部无法直接访问。
  • 但依然可以使用_classname_attr访问私有属性。

    class User:
          def __init__(self,year):
              self.__year = year
    
          def get_age(self):
              return 2020 - int(self.__year)
    
      if __name__ == "__main__":
          user = User('1990')
          age = user.get_age()
          print(age)
          print(user._User__year)
    

3.7 自省机制

  • 自省是通过一定的机制查询到内部结构。
  • 通过dict查询属性。
  • 通过dir()列出对象的所有属性。(比dict更加强大,但只有属性没有值)
  • 代码示例:

    class Person:
          name = "user"
    
      class Student(Person):
          def __init__(self,school_name):
              self.school_name = school_name
    
      if __name__ == "__main__":
          user = Student("test")
          #print(user.__dict__)  # {'school_name': 'test'}
          print(dir(user))  # ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'name', 'school_name']
          a = [1,2]
          print(dir(a))
    

3.8 super函数

  • super函数用于调用父类的init方法。代码示例:

    # python2的写法
      super(A,self).__init__()
      # python3的写法
      super().__init__()
    
  • 使用super调用父类构造函数后,可以重用父类的代码。

  • super的调用顺序是按照mro的顺序调用。

3.9 mixin继承(多继承相关)

  • mixin叫做混合模式。
  • 一个mixin通常只提供一个方法(或者理解为一件事)。
  • mixin模式特点:功能单一、不和基类关联,可以和任意基类组合(例如一个view可以和多个mixin关联,实现不同功能)。
  • mixin中建议不要使用super。
  • 使用Mixin模式的类通常使用Mixin结尾(这是一种规范)。

3.10 with语句(上下文管理器)

  • with相当于简化的try、except、finally。

    try:
          print('test1')
          raise IndexError
      except KeyError as e:
          print('test2')
      else:
          print('test3')
      finally:
          print('test4')
    
  • 上下管理器(with语句),和魔法函数enterexit相关。代码示例:

    class Sample:
          def __enter__(self):
              print('test1')
              return self
    
          def __exit__(self,exc_type,exc_val,exc_tb):
              print('test2')
    
          def do_something(self):
              print('test3')
    
      with Sample() as sample:
          sample.do_something()
    

3.11 contextlib(更简化的上下文管理器)

  • 使用contextlib的@contextlib.contextmanager使函数实现上下文管理。
  • 函数通过yield来分割开始和结束的操作。代码示例:

    import contextlib
    
      @contextlib.contextmanager
      def file_open(file_name):
          print("file open")
          yield {}
          print("file end")
    
      with file_open('aaa') as f:
          print('test1')