当前位置: 首页 > 科技观察

OO在Python中没有意义

时间:2023-03-15 20:19:26 科技观察

最近很多人都在抨击OO,虽然我不认为OO本身有什么问题,但我认为没有必要,至少在Python中是这样。1.没有必要使用面向对象。比如下面的代码,根本不需要使用面向对象。classApiClient:def__init__(self,root_url:str,session_cls:sessionmaker):self.root_url=root_urlself.session_cls=session_clsdefconstruct_url(self,entity:str)->str:returnf"{self.root_url}/v1/{实体}"defget_items(self,entity:str)->List[Item]:resp=requests.get(self.construct_url(entity))resp.raise_for_status()return[Item(**n)forninresp.json()["项目"]]defsave_items(self,entity:str)->None:withscoped_session(self.session_cls)assession:session.add(self.get_items(entity))classClientA(ApiClient):defconstruct_url(self,entity:str)->str:returnf"{self.root_url}/{entity}"classClientB(ApiClient):defconstruct_url(self,entity:str)->str:returnf"{self.root_url}/a/special/place/{entity}"client_a=ClientA("https://client-a",session_cls)client_a.save_items("bars")这里使用了面向对象,因为我们想把root_url绑定到一个对象上,不想通过sessionmaker每次。我们还想使用继承在调用过程中访问方法。但是仅通过数据传递和功能就可以实现吗?@dataclassclassClient:root_url:strurl_layout:strclient_a=Client(root_url="https://client-a",url_layout="{root_url}/{entity}",)client_b=Client(root_url="https://client-b)",url_layout="{root_url}/a/special/place/{entity}",)defconstruct_url(client:Client,entity:str)->str:returnclient.url_layout.format(root_url=client.root_url,entityentity=entity)defget_items(client:Client,entity:str)->List[Item]:resp=requests.get(construct_url(client,entity))resp.raise_for_status()return[Item(**n)forninresp.json()[“项目”]]defsave_items(client:Client,session_cls:session_cls,entity:str)->None:withscoped_session(session_cls)assession:session.add(get_items(client,entity))save_items(client_a,session_cls,"bars")我们必须一直传递Client和session_cls。但这有什么关系呢?甚至减少10%的代码。以这种方式编写的代码易于理解,并且不需要使用面向对象。有人将这种编写方式称为“函数包”。也就是说,整个代码由类型化数据和一堆模块范围的函数组成。那么全局变量呢?您可以参考这篇文章(https://leontrolski.github.io/sane-config.html)在应用程序的整个生命周期中重用配置或数据库会话。接口和抽象类呢?其实你不需要它们,直接写代码就可以了。平心而论,在Python有了类型注解之后,函数袋风格开始发挥其真正的魅力。不纯函数呢?如果你想进行纯函数式编程,你可能想编写纯类,然后使用不纯的“适配器”实例进行一些处理:getting-the-current-datetime/API-calls/talking-to-the-db/other-不纯的东西。是个好主意。其实可以直接使用freezegun、responses等,可以避免很多麻烦。2.例外但也有一些例外:你可能会注意到重构代码中添加了@dataclass,它们只是记录类型。Python5开箱即用地支持这些,无需使用“常规”类。使用Exception的子类很好。使用try:...exceptSomeClass:...基本上形成了一个层次结构,但只要你不使它过于复杂就可以了。像上面一样,枚举非常适合Python。在极少数情况下(至少在应用程序开发中),您可能会想出一个不错的类型并在任何地方使用它,比如pandas.DataFrame/sqlalchemy.Session。但总的来说,不要自欺欺人地说我们正在构建很棒的应用程序。谦虚使人进步。3.面向对象的缺点虽然在本文开头我说了我不认为面向对象本身有什么问题,但实际上我仍然认为面向对象不仅没有帮助,但也经常混淆问题并鼓励一些不良做法:面向对象鼓励您修改数据。函数包非常反对修改参数。不信可以试试,但不要生气。OO只是返回全局变量。您不能在函数之间共享数据,并且self迫使您编写具有较小状态空间的可测试函数。混合数据和函数加剧了序列化的难度,这在当今的RESTAPI世界中非常有用。面向对象伴随着疯狂的继承系统,关于这个话题的讨论无处不在。最重要的是,面向对象没有附加值,它只会分散您解决问题的注意力,并使导航和理解代码变得更加困难。