zoukankan      html  css  js  c++  java
  • Effective python(三):类与继承

    1,尽量使用辅助类来维护程序状态

    1. 在数据结构嵌套层数多了一层时(字典嵌套字典再嵌套元组的情况),应该考虑使用辅助类来拆解原有数据结构,提供更明确的接口,而不是继续使用字典或元组
    1. 一旦元组过长,意味着代码越来越复杂难以理解,可以使用collections中的namedtuple创建命名元组,定义格式:collections.namedtuple(typename,field_names,verbose=False, rename=False)

      命名元组的特有属性:

      类属性 _fields:包含这个类所有字段名的元组 类方法 _make(iterable):接受一个可迭代对象来生产这个类的实例 实例方法 _asdict():把具名元组以 collections.OrdereDict 的形式返回,可以利用它来把元组里的信息友好的展示出来
      from collections import namedtuple
      
      # 定义一个namedtuple类型User,并包含name,sex和age属性。
      User = namedtuple('User', ['name', 'sex', 'age'])
      
      # 创建一个User对象
      user = User(name='Runoob', sex='male', age=12)
      
      # 获取所有字段名
      print( user._fields ) 
      #('name', 'sex', 'age')
      
      # 也可以通过一个list来创建一个User对象,这里注意需要使用"_make"方法
      user = User._make(['Runoob', 'male', 12])
      
      print( user )
      # User(name='user1', sex='male', age=12)
      
      # 获取用户的属性
      print( user.name ) # Runoob
      print( user.sex )  # male
      print( user.age )  # 12
      
      # 修改对象属性,注意要使用"_replace"方法
      user = user._replace(age=22)
      print( user )
      # User(name='user1', sex='male', age=22)
      
      # 将User对象转换成字典,注意要使用"_asdict"
      print( user._asdict() )
      # OrderedDict([('name', 'Runoob'), ('sex', 'male'), ('age', 22)])
      
    1. 多个辅助类的拆解实现,代码量虽然是普通字典嵌套的两倍以上,但是这种程序理解更容易,也比普通嵌套字典的代码更清晰更容易扩展
      import collections
      Grade=collections.namedtuple('Grade',('score','weight'))
      
      class Subject(object):
          def __init__(self):
              self._grades=[]
          
          def report_grad(self,score,weight):
              self._grades.append(Grade(score,weight))
          
          def average_grade(self):
              total,total_weight=0,0
              for grade in self._grades:
                  total+=grade.score*grade.weight
                  total_weight+=grade.weight
              return total/total_weight
      
      class Student(object):
          def __init(self):
              self._subject={}
              
          def subject(self,name):
              if name not in self._subject:
                  self._subject[name]=Subject()
              return self._subject[name]
          
          def average_grade(self):
              pass
      
      class Gradebook(object):
          def __init__(self):
              self._students={}
      
          def student(self,name):
              if name not in self._students:
                  self._students[name]=Student()
              return self._students[name]
      

    2,对于简单接口尽量,通常传入函数

    3,对于带状态的闭包(即记录次数或者其它记录变量),也可以定义类用__call__然后再对函数参数传入实例实现会更清晰

    4,以@classmethod形式的多态去构造对象

    1. @classmethod修饰的函数第一个参数为这个类,由系统自动传入,命名为cls,它一般用于构建实例并返回return cls(*arg),可以更加通用的在类方法内处理参数和数据,从而摆脱直接经过__init__方法。可以创建多个参数不同的@classmethod方法从而经过不同的调用构建出不同的实例
    2. 与@staicmethod区分,@staicmethod是可以从类上直接调用的方法

    5,Super初始化父类

    1. 初始化父类可以在子类中使用父类.__init(self,value)的形式初始化,但复杂的继承会出现不可预料的问题,比如钻石继承模型可能会多次调用顶层父类导致出现问题
    1. super函数可以避免这一系列问题,它的解析顺序(深度优先,从左至右),保证顶层公共基类只会执行一次,注意:初始化运行顺序遵守mro,超类的初始化顺序与超类在class语句中出现的顺序相同
    1. mro执行顺序可以通过from pprint import pprint #换行 pprint(子类.mro())打印出初始化顺序,顺序是按照从底向上的方向执行的
    1. Python3的super简化写法super().__init__(value)

    6,用mix-in工具类代替多重继承

    1. 类的__dict__属性存放全局变量,类方法等内容,而对象的__dict__只存放自身的属性如self.X
    2. 用mix-in组件创作多种工具,然后通过继承他们获得他们的功能,从而不必进行多重继承
    3. 能用mix-in组件实现的效果,就不要通过多重继承来做
    4. 将各种功能实现为可插拔的mix-in组件
    5. 将各种简单的行为封装到mix-in组件,然后通过组合就可以写出复杂的行为了,mix-in组件之间可以互相依赖

    7,public and private属性

    1. 在类外直接访问私有字段会引发异常,如:ins.__private,但其实是Python对其名称做了变换,如上的私有属性python会变换成_ins__private,因此实际上还是可以访问的
    1. 类内的方法可以访问当前类的私有字段,但不能访问父类的私有字段
    1. 在继承的时候,使用get方法获取私有属性就会失效,原来的return self.__value在子类中就必须改成return self._父类名__value,但一旦更换继承,则父类名又需要重写,所以尽量不要使用private属性
    1. 只有子类不受自己控制(公共API),避免子类属性名和超类冲突时,可以在超类中合理使用private,避免公共API与子类属性名重复
    1. 可以多用protected属性即单下划线开头_p

    8,继承collections.abc实现自定义容器类型

    1. 制定一个自定义的统计元素频率的列表类型,简单容器可以直接继承
      class Flist(list):
          def __init__(self,members):
              super().__init__(members)
      
          def frequency(self):
              counts={}
              for item in self:
                  counts.setdefault(item,0)
                  counts[item]+=1
              return counts
      
    1. 用下标访问序列中的元素时bar[0],python会把代码转译为bar.__getitem__(0)
    1. 为了避免新建一个类的时候需要实现count,index等一系列方法,可以从collections.abc中继承基类from collections.abc import Sequence,只需要实现几个基本的方法如__len__,剩余的方法会自动实现
  • 相关阅读:
    HashMap的实现原理
    LinkedHashMap的实现讲解
    ConcurrentHashMap原理分析
    javax.mail Java Extension(扩展)
    Singleton 单例模式
    Java序列化与反序列化(实践)
    Java IO操作
    Java NIO与IO的区别和比较
    java模拟异步消息的发送与回调
    java如何实现多个线程并发运行
  • 原文地址:https://www.cnblogs.com/shitianfang/p/12561987.html
Copyright © 2011-2022 走看看