<h3>Background</h3>
<p>I'm currently writing a small Go library which acts as a wrapper to a subset of matplotlib.pyplot's plotting routines. However, <a href="http://matplotlib.org/api/lines_api.html#matplotlib.lines.Line2D" rel="nofollow">pyplot makes very heavy use of keyword arguments</a>, and I was hoping for feedback on what the best way to interface with these from Go is. I have four ideas which I'll list below.</p>
<p>I'll be imitating the same Python code in each of the examples I write. (Don't worry about what the arguments actually mean.)</p>
<pre><code>plt.plot(xs, ys, "r", lw=3, ls="--", antialiased=True)
</code></pre>
<h3>Option 1: Uninitialized Structs</h3>
<p>The different keyword arguments are represented as fields to a struct.
This is the strategy I usually use in Go (and C!) when I want to simulate kwargs.</p>
<p>Example:</p>
<pre><code>plt.Plot(xs, ys, "r", &plt.PlotKwargs{Lw: 3, Ls: "--", Antialiased: true})
</code></pre>
<p>Problems:</p>
<ul>
<li>The default values for each argument are forced to be 0. For example, 0 is a valid and sometimes useful linewidth, but it's hardly the most common or the most useful linewidth and shouldn't be the default. It would also result in unexpected behavior from someone used to the pyplot defaults.</li>
</ul>
<h3>Option 2: Initialized Structs</h3>
<p>Same as above, but using a constructor function. This allows me to choose whatever default values I want.</p>
<p>Example:</p>
<pre><code>kw := plt.PlotKwargs()
kw.Lw, kw.Ls, kw.Antialiased = 3, "--", true
plt.Plot(xs, ys, "r", kw)
</code></pre>
<p>Problems:</p>
<ul>
<li>The call to <code>PlotKwargs()</code> heap-allocates a <code>PlotKwargs</code> instance. Most people probably don't need to care about this (and it <em>certainly</em> doesn't matter for this particular case). Anyway, this can be handled by giving <code>PlotKwargs</code> a <code>Clear()</code> method and then reusing the same <code>PlotKwargs</code> instance across multiple calls.</li>
<li>The <code>kw.Lw, kw.Ls, kw.Antialiased = 3, "--", true</code> line is more annoying for me to parse than <code>&plt.PlotKwargs{Lw: 3, Ls: "--", Antialiased: true}</code> while reading the code, since the fields and the values I'm giving them are visually separated from one another.</li>
<li>A one line function call takes up three lines. Makes the code clunkier.</li>
</ul>
<h3>Option 3: Variadic Arguments</h3>
<p>Use <code>...interface{}</code> variadic arguments to the function. The odd <code>...interface{}</code> arguments correspond to the keywords and the even arguments correspond
to their values. The keywords could be specified with strings or with the usual labeled ints initialized with <code>iota</code>.</p>
<pre><code>plt.Plot(xs, ys, "r", plt.Lw, 3, plt.Ls, "--", plt.Antialiased, true)
</code></pre>
<p>Problems:</p>
<ul>
<li>No compile-time type checking.</li>
<li>Implementing functions written this way requires trivial, but annoying, parsing code.</li>
<li>It takes me a second or so to visually pair up all the keywords with their values when reading the code since they all just look like function arguments.</li>
<li>Doesn't seem very Go-like.</li>
</ul>
<h3>Option 4: Methods</h3>
<p>Use an initialized struct (option 2), but also give it setter methods which return
the <code>____Kwargs</code> struct, allowing them to be chained together.</p>
<p>Example:</p>
<pre><code>plt.Plot(xs, ys, "r", plt.PlotKwargs().Lw(3).Ls("--").Antialiased(true))
</code></pre>
<p>Problems:</p>
<ul>
<li><code>PlotKwargs()</code> heap-allocates a <code>PlotKwargs{}</code> instance. Usually not an issue.
As before, this can be handled by having a <code>PlotKwargs.Clear()</code> method.</li>
<li>Looks a little weird to me.</li>
</ul>
<hr/>**评论:**<br/><br/>TheMue: <pre><p>Take a look at Pikes ides here: <a href="http://commandcenter.blogspot.nl/2014/01/self-referential-functions-and-design.html" rel="nofollow">http://commandcenter.blogspot.nl/2014/01/self-referential-functions-and-design.html</a></p></pre>BESSEL_DYSFUNCTION: <pre><p>Oh, that's perfect!</p></pre>nowayno: <pre><p>Seems to me the best solution is to use Option 1, uninitialized structs, and document where the default values may not be optimal, so users know they'll probably want to set those fields, plus provide Option 2, initialized structs, in cases where the default values for one or more of a struct's fields are never or almost never what a user would want, and document the existence of the constructor and explain why it might be easier to use it. The method signature doesn't change — both options 1 & 2 take the same args struct.</p></pre>elithrar_: <pre><p>Functional options: <a href="http://dave.cheney.net/2014/10/17/functional-options-for-friendly-apis" rel="nofollow">http://dave.cheney.net/2014/10/17/functional-options-for-friendly-apis</a></p></pre>downvotes_puffins: <pre><p>You could switch your project to C++. The expressive type system there allows people to develop named parameters, even though they are not part of the core language.
<a href="http://www.boost.org/doc/libs/1_58_0/libs/parameter/doc/html/index.html" rel="nofollow">http://www.boost.org/doc/libs/1_58_0/libs/parameter/doc/html/index.html</a></p>
<p>This is just another example (cf generics) of how Go can be limiting. </p></pre>BESSEL_DYSFUNCTION: <pre><p>Thank you, but I have other plotting options for my C++ projects.</p></pre>
这是一个分享于 的资源,其中的信息可能已经有所发展或是发生改变。
入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889
- 请尽量让自己的回复能够对别人有帮助
- 支持 Markdown 格式, **粗体**、~~删除线~~、
`单行代码`
- 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
- 图片支持拖拽、截图粘贴等方式上传