为了账号安全,请及时绑定邮箱和手机立即绑定

如何组织 Python API 模块使其整洁?

如何组织 Python API 模块使其整洁?

慕姐8265434 2022-06-07 19:46:17
我正在编写一个代表一些 Web API 的 Python 库。现在,我的库目录看起来接近这个:__init__.pyAccount.pyOrder.pyCategory.pyrequests.py在__init__.py,我有这样的事情:from .Account import Accountfrom .Order import Orderfrom .Category import Categoryfrom . import requests这允许使用import cool_site然后cool_site.Account(…)等等,但它有以下问题:当我在 IDLE 中玩弄我的代码时,对象然后被调用cool_site.Account.Account,我觉得这很糟糕。1.有什么办法可以避免类名重复,并且每个类都有单独的文件吗?下一件我感觉不太好的事情是我的代码组织。现在,我的Account班级在初始化时获取凭据,创建一个requests.Session对象,然后处理与服务器的所有通信,即搜索订单等。然后,此类Account实例会将自身传递给所有其他实例,例如传递给Order- 因此订单的实例将具有.account保存Account创建它的实例的属性。当另一个类实例本身必须做某事时,例如更改订单的注释(通过调用o.comment = 'new comment',因此通过类@comment.setter中的装饰器Order),它会将其转发给在初始化时传递给它的 Account 对象,然后使用 example self.account.set_order_comment(new_comment)。然后,此方法将使用所有 Web 请求来实现该目标。2. 将服务器通信逻辑放在一个类中更好还是将其不同方面分散到受它们影响的类中更好?我想问的最后一件事是如何以及在何处保留低级请求模板。现在我在cool_site.requests子模块中有它,并且对于不同的请求有不同的功能,例如SetOrderComment对于上面提到的情况(它是一个函数,所以它应该是小写的,但在这种情况下,我认为它在某种程度上类似于一个类 - 是可以吗?)。将Account.set_order_comment像这样使用它:r = cool_site.requests.SetOrderComment(order.id, new_comment)response = self._session.request(**r)Session.request因为这个函数从requests库返回一个带有参数的字典。身份验证标头已在类实例的_session属性中设置。Account我觉得它有点难看,但我没有更好的主意。3.如何组织网络请求以保持一切干净?后文很抱歉这个问题太长了,涵盖了 API 库设计的许多方面,但所有的提示都将不胜感激。在某种程度上,以上三个问题都可以表达为“如何做得更好、更清洁?” 或“大多数 Python 开发人员是如何做到的?”,甚至可能是“最 Pythonic 的感觉是什么?”。把你能想到的任何小技巧都扔给我。
查看完整描述

3 回答

?
慕尼黑5688855

TA贡献1848条经验 获得超2个赞

我可以提出一个简单的提示:您可以__all__ = ["Account"]Account.py模块中使用,然后from .Account import *__init__.py文件中使用。我相信这解决了你的第一个问题。你可以在这里阅读__all__魔术方法。简而言之,您可以指定使用时要导入的方法等。您可以隐藏“私有”方法以防止使用.from x import *_one_leading_underscore



查看完整回答
反对 回复 2022-06-07
?
青春有我

TA贡献1784条经验 获得超8个赞

我最近一直在考虑非常相似的事情wistiapy。我目前对客户端代码组织的思考的例子就在里面。YMMV。

  1. “每个文件一个类”比 Python 更像是一种 Java 风格指南。Python 模块是代码层次结构中合法且重要的级别,您不必担心同一模块中有多个函数或类。您可以将所有模型类放在一个.models模块中,然后from .models import (Account, Order, Category)__init__.py.

  2. 客户端库的或多或少的常见做法似乎是有一个client模块,包含类似MyServiceClient类的东西。(例如Segment 客户端)。这就是网络逻辑的所在。如果您想让公共接口成为模块级函数,您可以通过创建默认客户端并让函数调用其方法来做一些聪明的事情。

函数应该是snake_case,类应该是PascalCase。做任何其他事情往往会导致更多的混乱而不是好处。

您正在处理的一个大问题似乎是试图在“Active Record”模式(some_account.set_order_comment(comment))和“Data Mapper”模式(set_order_comment(account, comment))之间进行选择。两者都可以,它们各有优缺点。我发现数据映射器模式——使用智能函数来操作相当简单的数据类——更容易开始。

我发现同时设计公共接口和使用该接口的东西很有帮助。在调用代码中,您可以编写您想调用的内容,然后“由外向内”实现客户端代码。


查看完整回答
反对 回复 2022-06-07
?
MYYA

TA贡献1868条经验 获得超4个赞

1).py 文件的名称中没有大写(也尽量避免_)所以你的文件应该是


__init__.py

account.py

order.py

category.py

requests.py

2)如果你想使用cool_site.Account你需要添加到__init__.py


from .account import Account

from .order import Order

from .category import Category


__all__ = [

    'Account',

    'Order',

    'Category',

]

3)SetOrderComment是坏名声,使用set_order_comment


4)如果您编写一个用于与 API 通信的 python 包装器,请创建在每个 API 请求中执行授权和其他相同内容的方法。此方法应将请求 kwargs 的一部分作为参数,这些 kwargs 对于不同的 API 调用是不同的


例如


class API:

    def __init__(self, endpoint:s str, api_key: str):

        self.endpoint = endpoint

        self.api_key = api_key


    def _get_auth_headers(self) -> Dict[str, str]:

        return {

           'Authorization': 'Bearer ' + self.api_key,

        }


    def get(self, path, params)

         resp = requester.get(

            self.endpoint + path,

            headers=self._get_auth_headers(),

            params=params,

            timeout=30,

        )

        self._check_api_response(resp)

        payload = resp.json()

        return payload

5) 如果您编写 python API,请查看flask 和 django 框架以及带有 API 编写的项目。你应该在那里找到一些好主意。


查看完整回答
反对 回复 2022-06-07
  • 3 回答
  • 0 关注
  • 197 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信