【问题标题】:Is this a Pythonic API design strategy?这是 Pythonic API 设计策略吗?
【发布时间】:2011-05-21 19:30:28
【问题描述】:

这是我要问的问题的前奏:我已经开始为我的公司用 Python 构建一个与网关无关的支付 API。目前,我只编写了支持 Authorize.net 的代码,并希望比我更有经验的 Python 程序员对我的 API 设计的清晰度提出一些反馈。

我选择推出我自己的包,因为现有的其他包感觉更像是事后考虑或者是 Authorize.net 特定的(我想编写一个更通用的包,界面更简洁)。我特别从一个包 (pythorize) 中获得了一些灵感,但不喜欢他们的 API。

在我开始描述我在做什么之前,这里是包的 bitbucket 上的公共存储库的链接:paypy(请注意那些可能想要使用它的人:代码是稳定的,但文档严重缺乏) .

我当前的策略是使用嵌套字典并将其传递给支付方法类的构造函数。在 Authorize.net CIM API 上创建新用户配置文件的示例:

>>> options = {'tran_key' : 'test_tran_key',
...            'login'    : 'developer_login',
...            'testing'  : True,
...            'validation': 'testMode',
...            'customer': {'description': 'Some description of the customer profile', 
...                         'id'         : 22,
...                         'email'      : 'johnny_doe@gmail.com'},
...            'billing': [{'type': 'individual',
...                         'profile': {'city'      : 'Carlsbad',
...                                     'state'     : 'California',
...                                     'zip'       : '92009',
...                                     'firstname' : 'John',
...                                     'address'   : '12 Alicante Rd. Suite 9',
...                                     'lastname'  : 'Doe',
...                                     'country'   : 'USA',
...                                     'phone'     : '(858) 557-2674'},
...                         'payment': {'card': {'ccv'        : '524',
...                                              'number'     : '4111111111111111',
...                                              'expiration' : '2014-04'}}},
...                        {'type'    : 'individual',
...                         'profile' : {'city'      : 'Las Vegas',
...                                      'state'     : 'Nevada',
...                                      'zip'       : '79112',
...                                      'firstname' : 'John',
...                                      'address'   : '78 Cloud Front',
...                                      'lastname'  : 'Doe',
...                                      'country'   : 'USA',
...                                      'phone'     : '(858) 557-2674'},
...                         'payment': {'card': {'ccv'        : '499',
...                                              'number'     : '4111111111111111',
...                                              'expiration' : '2012-11'}}},
...                        {'profile': {'city'       : 'Carlsbad',
...                                     'state'      : 'California',
...                                     'zip'        : '92009',
...                                     'firstname'  : 'John',
...                                     'address'    : '12 Alicante Rd. Suite 9',
...                                     'lastname'   : 'Doe',
...                                     'company'    : 'Xmarks',
...                                     'country'    : 'USA',
...                                     'phone'      : '(858) 557-2674'},
...                         'payment': {'bank': {'name_on_account' : 'John Doe',
...                                              'account'         : '829330184383',
...                                              'type'            : 'checking',
...                                              'name'            : 'Bank of America',
...                                              'routing'         : '122400724'}}}],
...            'shipping': [{'city'       : 'Carlsbad',
...                          'state'      : 'California',
...                          'zip'        : '92009',
...                          'firstname'  : 'John',
...                          'address'    : '12 Alicante Rd. Suite 9',
...                          'lastname'   : 'Doe',
...                          'country'    : 'USA',
...                          'phone'      : '(858) 557-2674'}]}
>>> profile = Profile(options)
>>> result  = profile.create()
>>> result.code
'I00001'
>>> print 'Customer Profile ID:' + str(result)
Customer Profile ID: 2758851
>>> print 'Customer Payment Profile IDs:' + repr(result.payment_ids)
Customer Payment Profile IDs: ['2380878', '2380879', '2380880']
>>> print 'Customer Shipping Profile IDs:' + repr(result.shipping_ids)
Customer Shipping Profile IDs: ['2427568']
>>>
>>>
>>> options = {'id'        : str(result),
...            'tran_key' : '86U5pvA9TcxZ5b8D',
...            'testing'  : True,
...            'login'    : '5b3PhGX68'}
>>> profile = Profile(options)
>>> result  = profile.remove()
>>> result.code
'I00001'
>>> ^D

您会注意到我对结果对象使用了一些神奇的方法(如 str 等...)。我也将字典策略用于 AIM 和 ARB 方法,并认为这是向支付 API 传递“选项”的最简单方法——因为在某些时候会有用于 GoogleCheckout、Paypal 等的适配器......

我的另一个想法是使用描述符和对象而不是字典来将选项数据传递给适配器。

与所有支付网关 API(尤其是 PayPal 和 Authorize.net 的)一样,接口往往有点混乱,并且没有以任何方式标准化,因此很难避免一些依赖于网关的选项。

【问题讨论】:

  • 为什么要在交互式控制台中这样做?
  • 其实我没有。这是其中一个测试的摘录——我只是格式化了代码和它输出的结果,看起来像是在交互式提示上完成的,这样更容易理解。
  • 这看起来像 json。你能兼容json吗?我认为那将是一个非常标准的 API。
  • @Falmarri:我可以,但我认为没有必要这样做,因为我说的是库 API,而不是 RESTful API。

标签: python api authorize.net payment-processing


【解决方案1】:

深度嵌套的字典在 Python 中可能并不罕见,也许它们是“Pythonic”,但它确实不是一个好主意,所以我会声称它不是 Pythonic。

我会创建一个嵌套的类层次结构。这会更清楚,IMO,并且还具有使您能够进行类型检查的好处。

事实上,我可能会使用一些架构模块来做到这一点。

您应该如何输入这些数据?人们不应该输入 Python 代码,对吧?

【讨论】:

  • 它们并不罕见,但感觉并不“正确”,因为嵌套的 dicts 变得不明显,并且它们的结构更多地遵循一种幼稚和假定的模式,而不是正式的模式。所以,谢谢你的回答,它证实了我的怀疑。我考虑过 object->xml 和 xml->object serializers... RE 数据输入:它是通过 HTML 表单捕获的,由程序员从给定的表单输入构建字典(我也没有)不喜欢,因为它可以使用正确命名的表单字段自动完成)。
  • Zope3 模式(带接口)看起来可能是我正在寻找的答案。
  • 确实很可能是这样。 Zope 组件架构通常很棒,但可能不是最容易使用的。
  • 我正在使用 Zope 3 模式和接口;它们实际上很容易。对于具体化的模式对象,我也不必对将字典序列化为 XML 的方法进行太多更改。很好的建议。谢谢。
猜你喜欢
  • 2021-03-15
  • 1970-01-01
  • 2023-03-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多