Different ways to simulate keyword arguments

polaris · · 551 次点击    
这是一个分享于 的资源,其中的信息可能已经有所发展或是发生改变。
<h3>Background</h3> <p>I&#39;m currently writing a small Go library which acts as a wrapper to a subset of matplotlib.pyplot&#39;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&#39;ll list below.</p> <p>I&#39;ll be imitating the same Python code in each of the examples I write. (Don&#39;t worry about what the arguments actually mean.)</p> <pre><code>plt.plot(xs, ys, &#34;r&#34;, lw=3, ls=&#34;--&#34;, 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, &#34;r&#34;, &amp;plt.PlotKwargs{Lw: 3, Ls: &#34;--&#34;, 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&#39;s hardly the most common or the most useful linewidth and shouldn&#39;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, &#34;--&#34;, true plt.Plot(xs, ys, &#34;r&#34;, 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&#39;t need to care about this (and it <em>certainly</em> doesn&#39;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, &#34;--&#34;, true</code> line is more annoying for me to parse than <code>&amp;plt.PlotKwargs{Lw: 3, Ls: &#34;--&#34;, Antialiased: true}</code> while reading the code, since the fields and the values I&#39;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, &#34;r&#34;, plt.Lw, 3, plt.Ls, &#34;--&#34;, 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&#39;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, &#34;r&#34;, plt.PlotKwargs().Lw(3).Ls(&#34;--&#34;).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&#39;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&#39;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&#39;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&#39;t change — both options 1 &amp; 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

551 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传