I'm writing a Python library that represents some web API. Right now, my library directory looks close to this:
__init__.py
Account.py
Order.py
Category.py
requests.py
In __init__.py
, I have something like this:
from .Account import Account
from .Order import Order
from .Category import Category
from . import requests
This allows to use import cool_site
and then cool_site.Account(…)
and so on, but it has a following problem: when I play around with my code in IDLE, the object is then called cool_site.Account.Account
, which I feel is bad.
1. Is there any way to avoid class name duplication and still have separate file for every class?
The next thing I don't feel great about is my code organization. Right now, my Account
class takes credentials on initialization, creates a requests.Session
object and then handles all communication with server, i.e. searching for orders and so on. This Account
class instance will then pass itself to all other instances, for example to Order
- so the order's instance will have .account
property holding the Account
instance which created it. When another class instance itself has to do something, for example change an order's comment (by calling o.comment = 'new comment'
, so by @comment.setter
decorator in the Order
class), it forwards that to an Account object which is passed to it on initialization, and then uses for example self.account.set_order_comment(new_comment)
. This method will then use all the web requests to achieve that goal.
2. Is it better to hold server communication logic in one class or to spread different aspects of it to classes that are affected by them?
The last thing I'd like to ask about is how and where to keep low-level request templates. Right now I have it in cool_site.requests
submodule, and there are different functions for different requests, for example SetOrderComment
for the case mentioned above (it's a function, so it should be lowercase, but in this case I think it resembles a class in a way - is that OK?). The Account.set_order_comment
will use it like this:
r = cool_site.requests.SetOrderComment(order.id, new_comment)
response = self._session.request(**r)
because this function returns a dict with arguments to Session.request
function from requests
library. The authentication headers are already set in the _session
property of Account
class instance. I feel it's a little bit ugly, but I don't have any better idea.
3. How to organize web requests to keep it all clean?
Post scriptum
I'm sorry this question is so long and covers many aspects of API library design, but all the tips will be appreciated. In a way, all of the three questions above could be expressed as "How to do it better and cleaner?" or "How most of the Python developers do it?", or maybe even "What would feel most Pythonic?".
Throw at me any little tips you can think of.
cool_site.account.Account
. Doesn't really solve your questions, but maybe it helps to know that many core modules have similar naming problems: e.g.:datetime.datetime
,copy.copy
, ... (note thatdatetime
is a class even if it is not capitalized, similar toint
orfloat
) – Glyco