我正在为Starburst编写一个python库,并试图找到构建代码的最佳方法,这样代码就很枯燥,但也很容易理解。下面是一个不枯燥的代码示例。
我研究过的其他方法是使用不同的API调用拥有一个配置文件,并有一个用于get/put/delete/post的函数。
另一种方法是基于域进行分组,并将方法作为变量传递。
class StarburstAPI(object):
def __init__(self, username=None, password=None, oauth=None, session=None):
self.api_version = 'v1'
self.base_url = 'https://starburst.com/api/' + self.api_version
self.headers = default_headers()
self.params = {}
if session is None:
session = requests.Session()
self._session = session
if username is not None or password is not None:
self._session.auth = (username, password)
self._session.cookies = self._session.head(self.base_url).cookies
self._session.headers.update({'Content-Type': 'application/json'})
# Get list of domains
def list_domains(self):
url = self.base_url + '/dataProduct/domains/'
return self._session.get(url)
# Get domain by id
def get_domain(self, id: str):
url = self.base_url + '/dataProduct/domains/' + id
return self._session.get(url)
# Create domain
def create_domain(self, data=None, **kw):
url = self.base_url + '/dataProduct/domains/'
if data:
kw = add_json_headers(kw)
data = json.dumps(data)
return self._session.post(url, data, **kw)
# Delete domain by id
def delete_domain(self, id: str):
url = self.base_url + '/dataProduct/domains/' + id
return self._session.delete(url)
# Update domain by id
def update_domain(self, id: str, data=None, **kw):
url = self.base_url + '/dataProduct/domains/' + id
if data:
kw = add_json_headers(kw)
data = json.dumps(data)
return self._session.put(url, data, **kw)发布于 2023-05-02 20:50:16
self.api_version = 'v1'不需要创建大量的对象属性,而局部变量就可以了。(变量名很有用,谢谢。)
api_version = 'v1'这是另一个nit。
if session is None:
session = requests.Session()
self._session = session这已经很清楚了。我们可以用这个常见的成语来更简洁地表达它:
self._session = session or requests.Session()类似地,您可以编写这个稍微紧凑的表达式:
if username or password:
self._session.auth = (username, password)这并不是完全相同的,但是username of ""空字符串可能没有什么意义。
您可以删除此注释,例如:
# Get list of domains
def list_domains(self):避免不告诉我们代码已经说过的东西的# comments。
我们将“如何”,技术细节,放入代码中。我们将“为什么”放在注释或方法docstring中。
我真的很喜欢{list,create}_domain是不同的方法。保留那个组织。
您有几个方法将url赋值为这个,再加上它。这似乎让你心烦。我不会太担心的。正如编写的那样,代码非常清晰,维护人员可以很容易地搜索匹配的URL。
您可能会删除url临时变量,而不是只将一个catenation表达式交给.get或.post。
考虑定义DOMAINS = '/dataProduct/domains/'的显式常量。或者为这个流行的前缀定义一个琐碎的助手。
这个客户端库有时会使'/dataProduct/domains/' + id更加脆弱。在服务器端,烧瓶库会将其拼写为:'/dataProduct/domains/{id}'。考虑采用这样的约定,并解析出每个URL的“变量”部分。请记住,检查对您是可用的。如果除了URL之外,一组方法确实是相同的,那么应该可以将它们定义为一行程序,给出适当的支持例程。
公共父级(例如域)的助手可以帮助简洁地定义紧靠在它下面的几个URL的访问器。
您的.post / .put方法有共同的kw + data需求,可以将其提取为公共助手。
总体上?当然,这段代码是重复的。但也没那么糟。
我想起了单元测试代码,在那里复制-n粘贴(干燥的敌人!)被明确接受为一种良好的做法。为什么?生成易于理解和维护的简单代码。
不要在那些似乎困扰你的“冗长”URL字符串上卖弄自己。跟踪bug的维护者首先需要做的事情之一是,grep用于一个URL或其中的一个片段。OP以非常简单的形式表示URL,这使得代码库很容易实现grep‘。
此代码实现了它的许多设计目标。
我愿意委托或接受它的维护任务。
编辑
假设您有一个函数来计算一些有用的东西,例如方程的根。它还想根据谁的要求来调整自己的行动。学习调用者的名字并不难。
import inspect
def sqrt_and_who_called_me(n):
"""Returns a root, and the name of the calling function."""
frame = inspect.currentframe()
return math.sqrt(n), frame.f_back.f_code.co_name
def fn():
root, name = sqrt_and_who_called_me(2.0)
assert name == "fn"而不是fn我们可能有
def domains():def domains_id():def domains_a():def domains_b():一个助手想知道我们在域层次结构中,或者想知道是否有后缀,并且它是{id,a,b}中的一个。
调用方可以在传递给助手的参数中泄露这些命名细节,但这可能不是很枯燥。(例如,名称患有整个Bond = namedtuple('Bond', ...)詹姆斯邦德综合征。)为几个GET / POST例程选择结构化名称可以让助手为每个例程量身定制其行为,同时尽量减少重复。当然,@decorators是工具箱中的另一个工具。
检查frame.f_back.f_back允许您将堆栈向上导航到调用者的调用方,依此类推。
https://codereview.stackexchange.com/questions/284771
复制相似问题