目录
3.以 @classmethod 形式的多态去通用地构建对象
7.继承 collections.abc 以实现自定义的容器类型
1.尽量用辅助类来维护程序的状态,而不要用字典和元组
- 不要使用包含其他字典的字典,也不要使用过长的元组。
- 如果容器中包含简单而又不可变的数据,那么可以先使用 namedtuple(具名元组) 来表示,带稍后有需要时,再修改为完整的类。
- 保存内部状态的字典如果变得比较复杂,那就应该把这些代码拆解为多个辅助类。
2.简单的接口应该接受函数,而不是类的实例
- 对于连接各种python组件的简单接口来说,通常应该给其直接传入函数,而不是先定义某个类,然后再传入该类的实例。
- Python中的函数和方法可以像一级类那么引用,因此,它们与其他类型的对象一样,也能够放在表达式里面。
- 通过名为__call__的特殊方法,可以使类的实例能够像普通的Python函数那样得到调用。
- 如果要用函数来保存状态,那就应该定义新的类,并令其实现__call__()方法,而不要定义带状态的闭包。
3.以 @classmethod 形式的多态去通用地构建对象
一般来说,要使用某个类的方法,需要先实例化一个对象再调用方法。而使用@staticmethod或@classmethod,就可以不需要实例化,直接类名.方法名()来调用
class GenericInputData:
def read(self):
raise NotImplementedError
@classmethod
def generate_inputs(cls, config):
raise NotImplementedError
class PathInputData(GenericInputData):
def __init__(self, path):
super().__init__()
self.path = path
def read(self):
with open(self.path) as f:
return f.read()
@classmethod
def generate_inputs(cls, config):
data_dir = config['data_dir']
for name in os.listdir(data_dir):
yield cls(os.path.join(data_dir, name))
class GenericWorker(Object):
def __init__(self, input_data):
self.input_data = input_data
self.result = None
def map(self):
raise NotImplementedError
def reduce(self, other):
raise NotImplementedError
@classmethod
def create_workers(cls, input_class, config):
workers = []
# input_class.generate_inputs() 是个类级别的多态方法
for input_data in input_class.generate_inputs(config):
workers.append(cls(input_data)) # 用cls()方法构造GenericWorker对象
return workers
class LineCountWorker(GenericWorker):
def map(self):
data = self.input_data.read()
self.result = data.count('\n')
def reduce(self, other):
self.result += other.result
def mapreduce(worker_class, input_class, config):
workers = worker_class.create_workers(input_class, config)
return execute(workers)
config = {'data_dir': tmpdir}
result = mapreduce(LineCountWorker, PathInputData, config)
print(f'There are {result} lines')
- Python的每个类只能有一个构造器,也就是__init__方法。
- 使用@classmethod可以为你的类定义可替代构造方法的方法,也就是用一种与构造器相仿的方式来构造类的对象。
- 类的多态为具体子类的组合提供了一种更加通用的方式。
使用 @classmethod起到多态的效果:一个对于分层良好的类树中,不同类之间相同名称的方法却实现了不同的功能的体现。
4.用 super 初始化父类
print(classname.mro()) # 查看多重继承子类的继承顺序
总是应该使用内置的 super 函数来初始化父类
5.只在使用 Mix-in 组件制作工具类时进行多重继承
- 如果可以使用
mix-in
实现相同的结果输出的话,就不要使用多继承了。 - 当
mix-in
类需要的时候,在实例级别上使用可插拔的行为可以为每一个自定义的类工作的更好。 - 从简单的行为出发,创建功能更为灵活的
mix-in
。
6.多用 public 属性,少用 private 属性
- Python 编译器无法严格保证 private 字段的私密性。
- 不要盲目将属性设置为 private,而是应该从一开始就做好规划,并允子类更多地访问超类的内部的API。
- 应该多用 protected 属性(单个下划线开头),并且在文档中把这些字段的合理用法告诉子类的开发者,而不要试图用 private 属性来限制子类的访问。
- 只有当子类不受自己控制时,才可以考虑使用 private 属性来避免父类和子类间的命名冲突。
private 属性是开头至少两个下划线,且结尾至多有一个下划线的属性。
子类无法直接访问父类的 private 字段:
try:
class MyParentObject:
def __init__(self):
self.__private_field = 71
class MyChildObject(MyParentObject):
def get_private_field(self):
return self.__private_field
baz = MyChildObject()
baz.get_private_field()
except:
logging.exception('Expected')
else:
assert False
可以通过前面加一个 _MyParentObject 来访问
assert baz._MyParentObject__private_field == 71
查询该对象的属性字典,可以看到私有属性的保存方式
print(baz.__dict__)
>>>
{'_MyParentObject__private_field': 71}
7.继承 collections.abc 以实现自定义的容器类型
内置的 collections.abc 模块定义了一系列抽象基类,它们提供了每一种容器类型所应具备的常用方法。从这样的基类中继承了子类后,如果忘记实现某个方法,那么 collections.abc 模块就会指出这个错误。
如果子类已经实现了抽象基类所要求的每个方法,那么基类就会自动提供剩下的那些方法。
抽象基类:
- 在这个基础的类当中我们去设定好一些方法,然后所有的继承这个基类的类它都必须要覆盖这里面的方法。
- 抽象基类是无法用来实例化的
使用场景:
- 我们去检查某个某个类是否有某种方法时
class Company:
def __init__(self, employee_list):
self.employee = employee_list
def __getitem__(self, item):
return self.employee[item]
def __str__(self):
return '-'.join(self.employee)
def __len__(self):
return len(self.employee)
com = Company(['abc', 'cvb'])
# 一般我们知道有hassattr可以判断。
#print(hasattr(com,'__len__')) # -- True
# 使用抽象接口判断
from collections.abc import Sized
print(isinstance(com, Sized))
- 我们需要强制某个子类必须实现某些方法(实现一个web框架,继承cache(redis, cache, memorychache),需要设计一个抽象基类,指定子类必须实现某些方法),做一些接口的强制规定
from abc import abstractmethod
from abc import ABCMeta
class Cache(metaclass=ABCMeta):
@abstractmethod
def set(self, key, value):
pass
@abstractmethod
def get(self, key):
pass
# 用户在重写时需要覆盖带这个方法
class MyCache(Cache):
def set(self, key, value):
print('sb')
def get(self, key):
print('rz')
mycache = MyCache()