2016-09-23 55 views
1

我正在写一个python脚本,它为命令行工具提供了一个更加用户友好的API。一些必要的命令调用需要大量参数(有时最多可达10个),但这在Python中并不是很好的做法。他们不能只是默认;必须可以设置给定呼叫的所有参数。Pythonic方式来包装需要大量参数的子进程调用?

我目前的结构是一个API类,它具有诸如expose_image()之类的函数,然后是一个接口类来处理子进程命令和调用的构造。我没有看到添加更多的类会有所帮助,因为API类仍然需要以某种方式生成并传递参数。

我想出的一个解决方案是用参数填充一个字典或namedtuple,并将其作为** kwargs传递,这使得事情看起来更好一些,但不那么明确。

有没有更好的处理方法?

谢谢!

+0

你能有什么是代码本身 – armak

+0

类结构,我不明白你的问题。 – GDYendell

回答

2

值得称赞的是,您希望构建一个Pythonic API而不仅仅是该命令的API。

我不确定为什么忽略默认参数?如果默认值为None,则可以将其作为指导,以便不将命令添加到命令行中。

例如,假设您要调用树命令。你可能有这样的事情:的my_tree

def my_tree(dirs_only=False, full_prefix=False, max_level=None, pattern=None): 
    cmd_line = ['tree'] 
    if dirs_only: 
     cmd_line.append('-d') 
    if full_prefix: 
     cmd_line.append('-f') 
    if max_level is not None: 
     cmd_line.append('-L') 
     cmd_line.append(str(max_level)) 
    if pattern is not None: 
     cmd_line.append('-P') 
     cmd_line.append(pattern) 
    subprocess.do_something_with(cmd_line) 

来电者然后可以用它在外壳互动,如:

my_tree() 
my_tree(dirs_only=True) 
my_tree(pattern='Foo*') 
my_tree(pattern='Foo*', max_level=2, full_prefix=True) 

在语言如Java,C#或飞镖,你经常会看到“流利”的API ,也许这些可能会有所帮助。这会导致代码,如:

my_tree().call() 
my_tree().dirs_only().call() 
my_tree().with_pattern('Foo*').call() 
my_tree() \ 
    .with_pattern('Foo*') \ 
    .with_max_level(2) \ 
    .full_prefix() \ 
    .call() 

虽然调用看起来更好,有很多样板的需要,以获得写说niceity,这肯定感觉有点非Python化。

+0

我很高兴只使用默认值。你的例子看起来很干净,但使用10个参数的样式可能会很难理解。所以我想知道是否有更好的方法。你会建议在字典上的方法作为** kwargs吗? – GDYendell

+1

@GDYendell:使用关键字参数的美妙之处在于,如果需要,可以使用'** kwargs'语法将函数参数作为字典传递。或者你可以做一个组合,其中一些参数在'keyword = value'形式中明确传递,其余的在字典中。不幸的是,你不能指定一个在kwargs字典中也提到的关键字arg,或者你得到一个'TypeError''得到了多个关键字参数值。 –

+0

@ PM2Ring这是一个好点;很高兴你不必在两者之间做出选择。 – GDYendell

2

就像你说的,kvargs的**方便的方式来几个参数的功能,但它始终是最好的函数定义明确声明参数:

def store(data, database, 
      user, password, 
      host=DEFAULT_HOST, 
      port=PG_DEFAULT_PORT, 
      chunk_size=64, 
      flags=None): 
    pass 

# call 
params = {"data": generate_data(), 
      "database": "mydb", 
      "user": "guest", 
      "password": "guest", 
      "chunk_size": 128 
      } 
store(**params) 

另一种方法是使用“参数”类,像这样(从pika库为例):

class ConnectionParameters(Parameters): 

    def __init__(self, 
       host=None, 
       port=None, 
       virtual_host=None, 
       credentials=None, 
       channel_max=None, 
       frame_max=None, 
       heartbeat_interval=None, 
       ssl=None, 
       ssl_options=None, 
       connection_attempts=None, 
       retry_delay=None, 
       socket_timeout=None, 
       locale=None, 
       backpressure_detection=None): 

     super(ConnectionParameters, self).__init__() 

     # Create the default credentials object 
     if not credentials: 
      credentials = self._credentials(self.DEFAULT_USERNAME, 
              self.DEFAULT_PASSWORD) 
     ... 
# call 
conn_params = pika.ConnectionParameters(host=self._host, 
              port=self._port, 
              credentials=cred) 
conn = pika.BlockingConnection(parameters=conn_params) 
+0

是的,我想我要在函数定义中明确地定义选项,而不管使用** kwargs或keyword = value。将使用该参数类添加任何一个nametuple不会做的? – GDYendell

+2

@GDYendell不,如果你不需要实现任何逻辑(可能是一些检查?)或者有一个“参数”层次结构 - 那么namedtuple对你来说会更好。 –

相关问题