如何在 Python 中有效地提供 Web、CLI & API 界面?
How to efficiently provide a Web, CLI & API interface in Python?
我的问题如下。我的库中有一些函数:
def some_func(arg1=1):
pass
我想以三种方式使用它:
在其他 python 脚本中使用导入语句:
import my_library
my_library.some_func()
通过单击使用 CLI 界面公开。
@click.group()
@click.option('--arg1', default=1)
def some_func(arg1):
pass
使用带有 Flask 的 Web 界面公开。
@app.route('/endpoint', defaults={'arg1': 1})
def some_func(arg1):
pass
但是我怎样才能在不重复太多代码的情况下有效地构建它呢?
是否可以将所有三个合并?我尝试了以下(变化),但失败了:
@click.group()
@click.option('--arg1', default=1)
@app.route('/endpoint', defaults={'arg1': 1})
def some_func(arg1=1):
pass
或者我真的需要上面定义的 3 种不同的功能吗?
如果是这样,我应该如何设置默认值?
在所有地方访问全局变量是最好的方法吗?
由于装饰器语法只是函数应用的快捷方式,因此您可以从在库中定义函数开始。然后你的 click
例子变成:
import my_library
click.group()(click.option('--arg1', default=1)(my_library.some_func))
并且您的 Flask 示例变为
import library
app.route('/endpoint', defaults={'arg1': 1})(my_library.some_func)
(我知道对于 Flask,装饰器的 return 值并不重要;我假设 click
也是如此。)
这假设您没有尝试将同一个脚本用作命令行工具和 Flask 应用程序;在我看来,这没有多大意义。
就简化默认值而言,没有什么好的想法。 Click、Flask 和您的函数具有三种不同的指示默认值的方式;唯一的共同点是该默认值的实际 value。你可能会做这样的事情。首先,在 my_library.py
:
some_func_default = 1
def some_func(arg=None): # Or some other sentinel
if arg is None:
arg = some_func_default
然后在你的另外两个脚本中:
click.group()(click.option('--arg1', default=my_library.some_func_default)(my_library.some_func))
和
app.route('/endpoint', defaults={'arg1': my_library.some_func_default})(my_library.some_func)
当然,您可以使用 inspect
模块从 some_func
的原始定义中提取默认值,但这无助于 Click 和 Flask 设置默认值的方式不同他们的切入点。
您可以使用 hug 库,它正是为这个用例而制作的。
示例:
"""An example of writing an API to scrape hacker news once, and then enabling usage everywhere"""
import hug
import requests
@hug.local()
@hug.cli()
@hug.get()
def top_post(section: hug.types.one_of(('news', 'newest', 'show'))='news'):
"""Returns the top post from the provided section"""
content = requests.get('https://news.ycombinator.com/{0}'.format(section)).content
text = content.decode('utf-8')
return text.split('<tr class=\'athing\'>')[1].split("<a href")[1].split(">")[1].split("<")[0]
我的问题如下。我的库中有一些函数:
def some_func(arg1=1):
pass
我想以三种方式使用它:
在其他 python 脚本中使用导入语句:
import my_library
my_library.some_func()
通过单击使用 CLI 界面公开。
@click.group()
@click.option('--arg1', default=1)
def some_func(arg1):
pass
使用带有 Flask 的 Web 界面公开。
@app.route('/endpoint', defaults={'arg1': 1})
def some_func(arg1):
pass
但是我怎样才能在不重复太多代码的情况下有效地构建它呢?
是否可以将所有三个合并?我尝试了以下(变化),但失败了:
@click.group()
@click.option('--arg1', default=1)
@app.route('/endpoint', defaults={'arg1': 1})
def some_func(arg1=1):
pass
或者我真的需要上面定义的 3 种不同的功能吗?
如果是这样,我应该如何设置默认值?
在所有地方访问全局变量是最好的方法吗?
由于装饰器语法只是函数应用的快捷方式,因此您可以从在库中定义函数开始。然后你的 click
例子变成:
import my_library
click.group()(click.option('--arg1', default=1)(my_library.some_func))
并且您的 Flask 示例变为
import library
app.route('/endpoint', defaults={'arg1': 1})(my_library.some_func)
(我知道对于 Flask,装饰器的 return 值并不重要;我假设 click
也是如此。)
这假设您没有尝试将同一个脚本用作命令行工具和 Flask 应用程序;在我看来,这没有多大意义。
就简化默认值而言,没有什么好的想法。 Click、Flask 和您的函数具有三种不同的指示默认值的方式;唯一的共同点是该默认值的实际 value。你可能会做这样的事情。首先,在 my_library.py
:
some_func_default = 1
def some_func(arg=None): # Or some other sentinel
if arg is None:
arg = some_func_default
然后在你的另外两个脚本中:
click.group()(click.option('--arg1', default=my_library.some_func_default)(my_library.some_func))
和
app.route('/endpoint', defaults={'arg1': my_library.some_func_default})(my_library.some_func)
当然,您可以使用 inspect
模块从 some_func
的原始定义中提取默认值,但这无助于 Click 和 Flask 设置默认值的方式不同他们的切入点。
您可以使用 hug 库,它正是为这个用例而制作的。
示例:
"""An example of writing an API to scrape hacker news once, and then enabling usage everywhere"""
import hug
import requests
@hug.local()
@hug.cli()
@hug.get()
def top_post(section: hug.types.one_of(('news', 'newest', 'show'))='news'):
"""Returns the top post from the provided section"""
content = requests.get('https://news.ycombinator.com/{0}'.format(section)).content
text = content.decode('utf-8')
return text.split('<tr class=\'athing\'>')[1].split("<a href")[1].split(">")[1].split("<")[0]