Question: why is "self" or "this" not considered a best practice for naming your method receiver variable?

blov · · 574 次点击    
这是一个分享于 的资源,其中的信息可能已经有所发展或是发生改变。
<hr/>**评论:**<br/><br/>comrade_donkey: <pre><p>Not many people know this, but method notation, i.e. <code>v.Method()</code> is actually syntactic sugar and Go also understands the de-sugared version of it: <code>(T).Method(v)</code>. You can see an example <a href="http://play.golang.org/p/YKI1HGqeM7">here</a>. Naming the receiver like any other parameter reflects that it is, in fact, just another parameter quite well.</p> <p>This also implies that the receiver-argument inside a method may be <code>nil</code>. This is not the case with <code>this</code> in e.g. Java.</p></pre>DeedleFake: <pre><p>This one of my favorite things about Go, and a great example of how simple it is. Even features pulled from much more complicated languages, such as methods, slices, and even interfaces, are done in such a way that you can easily tell a lot of what&#39;s really going on behind the scenes, without any real compromise in terms of feel or usage. The only thing about methods that makes them more than <em>just</em> syntactic sugar is that the interface system is heavily tied into them.</p> <p>Unfortunately, in my opinion the biggest place this simplicity breaks down is in channels. There are a number of things about channels that really bug me, and I think that the design of the whole channel system should be one of the top long-term priorities for major changes in Go 2. That&#39;s just my opinion, though; I know that a lot of people really like channels.</p></pre>jnj1: <pre><p>Can you be more specific? What don&#39;t you like about channels?</p></pre>DeedleFake: <pre><p>I don&#39;t have a lot of time to write a full, reasoned answer right now, but I&#39;ll write a few of the things that come to mind real quick.</p> <p>Before I start, I just want to make sure that I&#39;m clear about something: I do like channels in general. They make a whole lot of stuff much easier than mutexes and the like. However, there are a few things that bug me that I think limit them to some extent.</p> <p>The first thing that comes to mind is that sending to a closed channel causes a panic, even in a select case. This is despite the fact that sending to or receiving from a nil channel just blocks, allowing you to use them to disable select cases. It would be nice if writing to a closed channel did the same; it would make some multi-producer single-consumer model stuff much, much easier.</p> <p>Another annoyance is that there isn&#39;t really very good support for selecting on an arbitrary number of channels. It can be faked with some difficulty with <code>reflect.Select()</code>, and you could always just do something like</p> <pre><code>for { for _, c := range(channels) { select { case data := &amp;lt;-c: // Do something. } } } </code></pre> <p>but overall, it would be really nice to have some kind of support for that.</p></pre>gdm85: <pre><p>You mean selecting on an arbitrary number of channels with same type?</p></pre>gkx: <pre><p>That&#39;s true in JavaScript, too, but &#34;this&#34; is still built in, and &#34;self&#34; (or &#34;that&#34;) is convention when you&#39;re about to implicitly override it. It being potentially nullable isn&#39;t really a great reason to not call it&#34; this&#34;, imo. I call it &#34;this&#34; and haven&#39;t run into trouble. Yeah, it&#39;d be bad if you called it without a callee, but that&#39;s true no matter what name you use. </p></pre>comrade_donkey: <pre><p>I think we&#39;re comparing apples to oranges in this case. Javascript&#39;s <code>this</code> is a special identifier. It can be overridden by a caller dynamically but it&#39;s not &#34;just another argument&#34;. You can&#39;t assign to it, for instance.</p></pre>weberc2: <pre><p>Python might be a more accurate comparison.</p></pre>lobster_johnson: <pre><p>Not really. Python&#39;s methods are not plain functions. Consider a class with a method:</p> <pre><code>&gt;&gt;&gt; class Test: ... def a(self): ... print &#34;Hello, this is a&#34; ... </code></pre> <p>Then a function:</p> <pre><code>&gt;&gt;&gt; def b(self): ... print &#34;Hello, this is b&#34; ... </code></pre> <p><code>a</code> is not a bound method:</p> <pre><code>&gt;&gt;&gt; t = Test() &gt;&gt;&gt; t.a &lt;bound method Test.a of &lt;__main__.Test instance at 0x102814878&gt;&gt; </code></pre> <p>But <code>b</code> isn&#39;t, even though it has a <code>self</code> — it&#39;s just a plain function:</p> <pre><code>&gt;&gt;&gt; b &lt;function b at 0x10280ccf8&gt; </code></pre> <p>Try assigning it:</p> <pre><code>&gt;&gt;&gt; t.b = b &gt;&gt;&gt; t.b &lt;function b at 0x10280ccf8&gt; </code></pre> <p>Still a function. Try calling it:</p> <pre><code>&gt;&gt;&gt; t.b() Traceback (most recent call last): File &#34;&lt;stdin&gt;&#34;, line 1, in &lt;module&gt; TypeError: b() takes exactly 1 argument (0 given) </code></pre> <p>To bind it, we need to invoke some magic:</p> <pre><code>&gt;&gt;&gt; bound = b.__get__(t) &gt;&gt;&gt; bound &lt;bound method b of &lt;__main__.Test object at 0x10b3847f0&gt;&gt; &gt;&gt;&gt; bound() Hello, this is b </code></pre> <p>Python&#39;s <code>self</code> isn&#39;t magical, but there&#39;s a distinction between bound methods vs. unbound methods vs. functions that isn&#39;t syntactical.</p> <p>Of course, you can manually invoke <code>b</code> with an instance:</p> <pre><code>&gt;&gt;&gt; b(t) Hello, this is b </code></pre> <p>...which is different from JavaScript, which does have an implicit self argument.</p></pre>weberc2: <pre><p>My only point is that, in Python, &#39;self&#39; isn&#39;t magical; I&#39;m not sure how the bound/unbound stuff relates to the topic.</p></pre>enneff: <pre><p>The bound/unbound stuff is relevant because binding changes the meaning of the name. In Go, your receiver is a value of a particular type, and that&#39;s all it can ever be. Vague names like &#34;this&#34; or &#34;self&#34; are less helpful than more descriptive names.</p></pre>weberc2: <pre><p>I don&#39;t see how it being bound or unbound affects whether or not a name like this or self is vague or descriptive. Personally, I have no objection to either approach; I just don&#39;t see how the bound/unbound distinction factors in.</p></pre>earthboundkid: <pre><p><code>b</code> didn&#39;t become a bound method because you added it to an instance instead of adding it to a class. </p> <pre><code>&gt;&gt;&gt; class Test: ... pass ... &gt;&gt;&gt; def b(self): ... print(&#34;B here&#34;) ... &gt;&gt;&gt; Test.b = b &gt;&gt;&gt; t = Test() &gt;&gt;&gt; t.b() B here </code></pre> <p>When you access a function that lives on an instance, you just get the function back unmodified. When you access a function that lives in the class, it goes through the <code>__get__</code> mechanism and becomes a bound method.</p> <p>None of which has anything to do with Go naming conventions.</p></pre>jerf: <pre><p>As others have pointed out, it is a convention, and it&#39;s not entirely without reason.</p> <p>But, having programmed Go for over a year now, I&#39;m still not convinced this wasn&#39;t a minor mistake.</p> <p>As I&#39;m working in the initial prototyping phase, I often change the names of types as I discover their essence. Changing the names of the type in all the methods, no problem. Changing all the <code>s</code>s for the old type to <code>rs</code> for the new type, the search/replace has to be done annoyingly carefully.</p> <p>If I want to create another thing of the same type, I&#39;ve already used the name I would naturally have chosen. I often find myself shadowing the method receiver.</p> <p>I don&#39;t know if <code>SomeComplicatedTypeName</code> shortens to <code>sctn</code> or just <code>s</code>, especially when <code>s</code> for <code>Some</code> really doesn&#39;t help the method read any better. (Yes, such type names are often useful, especially as types implementing some interface for some very specific target.)</p> <p>All that said, the sum of all of this annoyance is small. I don&#39;t expect to switch to <code>this</code> or <code>self</code> because the benefits of following community practice outweighs all of this. But I&#39;m sympathetic to thinking it&#39;s at least a minor issue in the community practice.</p></pre>semi-: <pre><blockquote> <p>the search/replace has to be done annoyingly carefully.</p> </blockquote> <p><a href="https://godoc.org/golang.org/x/tools/cmd/gorename">gorename</a> helps a lot here, I highly recommend integrating it into your editor of choice. </p></pre>ballagarba: <pre><p>How do you get it to rename <em>all</em> methods receivers? I&#39;m using it via vim-go and it only renamed the receiver under the cursor.</p></pre>mvitorino: <pre><p>That pretty much reflects my experience also.</p></pre>headzoo: <pre><p>In a similar vein I hate Go&#39;s convention for starting a comment with the name of the method being commented.</p> <pre><code>// Compile parses a regular expression and returns, if successful, a Regexp // object that can be used to match against text. func Compile(str string) (regexp *Regexp, err error) { </code></pre> <p>I suspect the language designers have some lofty goal in mind for this convention, but I only see two places in my code that needs to be changed when I decide to change the name of a method.</p></pre>dilap: <pre><p>For me it feels right because methods feel more like functions in Go land -- they&#39;re not indented an extra level, they&#39;re not all grouped together under the same class declaration, and they don&#39;t have any extra special access privileges that a normal function wouldn&#39;t have.</p> <p>The difference between these 3 functions is really pretty minor:</p> <pre><code>func (f Foo) Mate(b Bar) FooBar func (b Bar) Mate(f Foo) FooBar func Mate(f Foo, b Bar) FooBar </code></pre> <p>Would this really be better written as</p> <pre><code>func (self Foo) Mate(b Bar) FooBar func (self Bar) Mate(f Foo) FooBar func Mate(f Foo, b Bar) FooBar </code></pre> <p>Nahhhh.</p> <p>Sometimes I&#39;ll even change my mind about which argument should be the method receiver, and it&#39;s handy not to have to go around changing which object is self and which object has an actual name.</p> <p>(Though of course it&#39;s not all roses -- sometimes I&#39;ll change my mind about what a good short abbreviation for the method receiver is, and then I&#39;m in for grunt work changing it everywhere, or just living with the inconsistency. Actually, I wonder how hard it would be to extend gorename to do this automatically...)</p> <p>Side note: If you&#39;re not using gorename, goimports, and gooracle, check &#39;em out! Super handy tools.</p></pre>zhenjl: <pre><p>Personally, I use &#39;this&#39; for all receivers. I find it easier for my own mental model as I get easily confused when I have a different receiver for every struct. </p></pre>zhenjl: <pre><p>I would also add that even though from an underlying implementation perspective, the receiver parameter is just like any other parameter and there&#39;s nothing special. </p> <p>However, from a mental model perspective, these don&#39;t read the same to me.</p> <pre><code>func (b Bar) Mate(f Foo) FooBar func Mate(f Foo, b Bar) FooBar </code></pre> <p>The second is purely just a function with two parameters. However, the first feels like an object/struct&#39;s method getting a single parameter. Semantically they don&#39;t feel the same, even though they maybe exactly the same in the implementation.</p></pre>endophage: <pre><p>Semantically they don&#39;t even mean the same thing. In the first instance, due to Go&#39;s rules on where methods can be declared, you&#39;re guaranteed that Mate can access unexported fields on Bar.</p> <p>In the second case, depending on which package Mate is declared in, that may not be the case.</p></pre>admin36: <pre><p>Naming your method receivers &#34;this&#34; or &#34;self&#34; is not considered best practice because:</p> <p>&#34;Reasons&#34;</p> <p>Truth be told the official style guide reasoning for not using generic receiver naming is incredibly weak, and serves no real purpose as a guide other than to cause petty bickering over a non-issue in the community. </p> <p>In reality the language permits you to specify your own name for the method receiver, so name your receivers however you want. The best recommendation that can be made about naming method receivers is:</p> <p><strong>Name your method receivers consistently</strong></p> <p>If you choose to use a generic identifier, then stick to that paradigm. If you choose to generate unique names per type, then stick to that paradigm. Pick a style for your project and stick with it. Worried about your naming convention causing trouble for a public project? Don&#39;t be. A programmer worth their salt doesn&#39;t really care and will follow the style the project takes initially.</p> <p><strong>Personally:</strong></p> <p>I prefer to name my method receivers <strong>this</strong>. This is not to make them <em>feel</em> like classes or objects, but rather to give my project a consistent <em>keyword</em> in which to identify my method receiver in code. Whenever I see <strong>this</strong> within a function I KNOW I&#39;m operating on the method receiver, I don&#39;t have to keep a mental note if my method receiver is named a,b,c,sc,ia,te,df,yi,qw,bv ... and so on.</p> <p>Using <strong>this</strong> consistently also helps refactoring. If I rename a type for clarity I don&#39;t have to rename all of my method receivers. If I transfer a useful function from one type to another where it fits I don&#39;t have to refactor the method receiver variable names to match the type.</p> <p>Mentally whenever I see <strong>this</strong> I think &#34;This Golang Type&#34; not &#34;This Class&#34; or &#34;This Object&#34; because golang doesn&#39;t have classes or objects in the traditional sense.</p> <p>I feel a generic method receiver name brings MORE clarity than less. But at the end of the day, I don&#39;t care. I can read my and others code no matter how they name their method receivers.</p> <p>I cringe when reading Golang posts where code is shown, and people feel the need to derail the conversation over method receiver names.</p></pre>Ainar-G: <pre><p>I think the first two sentence in the Code Review Comments (aka the Go style guide) <a href="https://github.com/golang/go/wiki/CodeReviewComments#receiver-names">explains it pretty well</a>:</p> <blockquote> <p>The name of a method&#39;s receiver should be a reflection of its identity; often a one or two letter abbreviation of its type suffices (such as &#34;c&#34; or &#34;cl&#34; for &#34;Client&#34;). Don&#39;t use generic names such as &#34;me&#34;, &#34;this&#34; or &#34;self&#34;, identifiers typical of object-oriented languages that place more emphasis on methods as opposed to functions.</p> </blockquote> <p>Whether you like it or not, it&#39;s a common practice. To me, calling the method receiver <code>this</code> in Go is just bad manners. Like formatting by hand and never using <code>gofmt</code>.</p></pre>mvitorino: <pre><p>I completely understand the benefits of consistency with the style guide, but, respectfully, in this case the guide doesn&#39;t explain why, only what should be done. I was looking for a more in depth explanation other than &#34;it is what was chosen and therefore it&#39;s the convention&#34;.</p> <p>Being objective about it, &#34;c&#34; carries no more meaning than &#34;self&#34; or &#34;this&#34; and across the standard library we have methods with the same dispatcher type and different one letter names, like &#34;x&#34; and &#34;z&#34;.</p> <p>Is it that it should be almost considered as another argument like the others?</p></pre>TheMerovius: <pre><blockquote> <p>Being objective about it, &#34;c&#34; carries no more meaning than &#34;self&#34; or &#34;this&#34;</p> </blockquote> <p>Yes it does. In the sense, that one-letter variable- and parameter-names are quite common (encouraged even) in go. So, you would normally call a parameter of type <code>package.Reader</code> as <code>r</code>, therefore, in go, <code>r</code> actually <em>does</em> carry a stronger meaning than <code>this</code>.</p> <blockquote> <p>across the standard library we have methods with the same dispatcher type and different one letter names, like &#34;x&#34; and &#34;z&#34;.</p> </blockquote> <p>Sadly, even though the stdlib does a very good job overall to serve as an example of good code and very good APIs, it does sometimes violate best practices. The reason is mostly, that some of these best practices are newer than some parts of the go stdlib. And while a violation of best practices isn&#39;t a good thing, they are &#34;best practices&#34; for a reason (as opposed to &#34;rules&#34;). In the tradeoff of changing APIs vs. breaking best practices, the latter is always chosen :)</p> <p>tl;dr: The stdlib gives examples of best practices. But that doesn&#39;t mean everything the stdlib does is always good.</p></pre>MonkeeSage: <pre><blockquote> <p>Yes it does. In the sense, that one-letter variable- and parameter-names are quite common (encouraged even) in go. So, you would normally call a parameter of type <code>package.Reader</code> as <code>r</code>, therefore, in go, <code>r</code> actually <em>does</em> carry a stronger meaning than <code>this</code>.</p> </blockquote> <p>But is it a <code>package.Reader</code> or a <code>package.Rect</code>? The <code>r</code> provides <em>slightly</em> more information than <code>this</code>, but not enough to make a difference in practice. You are still going to have to refer to the signature with <code>r</code> or <code>this</code>, since neither provide enough information to infer the type.</p></pre>TheMerovius: <pre><blockquote> <p>But is it a <code>package.Reader</code> or a <code>package.Rect</code></p> </blockquote> <p>The one you are reading/writing the method of. When you read the code, you know whose method it is.</p> <blockquote> <p>The r provides slightly more information than this, but not enough to make a difference in practice.</p> </blockquote> <p>Strongly disagree. In practice you have enough context to make it mostly unambiguous.</p></pre>sje397: <pre><p>The context you describe applies equally to a variable called &#39;this&#39;, as does knowing what it means when you&#39;re writing the method.</p></pre>tdewolff: <pre><p>It <em>is</em> just another argument. You can also pass the receiver as the first argument, which emphasizes that fact.</p> <p>For example:</p> <pre><code>func (z *Z) f() {} z.f() f(z) // the same </code></pre></pre>Ainar-G: <pre><p>I think you meant <a href="http://play.golang.org/p/vZQUJVNoua">this</a>:</p> <pre><code>fz := (*Z).f fz(&amp;z) </code></pre> <p>Because just <code>f(z)</code> won&#39;t compile.</p></pre>mvitorino: <pre><p>Ok, this notation I&#39;ve used, and makes sense to me.</p></pre>mvitorino: <pre><p>I did not know that! Interesting.</p></pre>mattyw83: <pre><p>I like the appeal to manners. After all, you wouldn&#39;t use the go style in python</p> <pre><code>class Foo(): def __init__(f, x): f.x = x def foo(f): print f.x f = Foo(&#34;bar&#34;) f.foo() </code></pre></pre>sje397: <pre><p>Actually your example makes me lean very much toward using &#39;this&#39; in go.</p></pre>headzoo: <pre><blockquote> <p>The name of a method&#39;s receiver should be a reflection of its identity; often a one or two letter abbreviation of its type suffices (such as &#34;c&#34; or &#34;cl&#34; for &#34;Client&#34;).</p> </blockquote> <p>That&#39;s great until you need to change the name of the identifier in three dozen places. At least &#34;self&#34; never needs to be changed.</p> <p>Sometimes I think the designers of the language are too focused on forcing a philosophy on the developers rather than using plain ol&#39; common sense.</p></pre>mdwhatcott: <pre><p>The short answer is this: because someone thought that using &#39;this&#39; and &#39;self&#39; was a bad idea. </p> <p>There isn&#39;t a good enough reason that could apply to the entire go community. Yes, the receiver can be nil. Yes, there is a difference between java objects and go structs. The receiver still functions in a very similar way to java or python objects which we have all been content to refer to as this and self, respectively.</p> <p>Rather than blindly obey a decree from the go team on how to name our stuff, let&#39;s choose names that make sense for our projects and that help us reason about our code effectively. In my case, this generally means using &#34;self&#34; or &#34;this&#34; for the receiver. I&#39;ve tried the single-character convention but I absolutely hated having to rename my one-character or abbreviated receiver name every time I rename the corresponding struct type. I want the freedom and flexibility to rename things where possible. Write short methods and you&#39;ll always be reminded of the type of the receiver in the method signature.</p> <p>Forgive me for beating this dead horse again:</p> <p><a href="http://michaelwhatcott.com/go-code-that-stutters/#toc_2">http://michaelwhatcott.com/go-code-that-stutters/#toc_2</a></p> <p><a href="http://michaelwhatcott.com/familiarity-admits-brevity/">http://michaelwhatcott.com/familiarity-admits-brevity/</a></p></pre>Fwippy: <pre><p>Alternately: if you write short methods, you&#39;ll always be reminded of the name of the receiver in the method signature. :p</p></pre>mcvoid1: <pre><p>I personally don&#39;t think it&#39;s a big deal either way. I like the fact that you can name it whatever because sometimes &#34;this&#34; isn&#39;t descriptive enough.</p></pre>enneff: <pre><p>There&#39;s nothing special about the receiver parameter in Go methods. They&#39;re just like other parameters, and so they should be named similarly. Would you ever name an parameter &#34;this&#34; or &#34;self&#34;? Both are terribly non-descriptive names.</p></pre>jahayhurst: <pre><p>In the time that I&#39;ve been playing with go, I&#39;ve seen a lot of design choices and guidelines pan out to be the right choice. I get the feeling that the ones that I don&#39;t understand yet are simply realizations waiting for me to find one day in the future. Everything seems to fit together - there&#39;s rough edges, but there&#39;s also reason behind everyone.</p> <p>I never got that feeling from any other language. Python was/is nice, but it&#39;s forced in some ways - arbitrarily so. Javascript seems hobbled together in a lot of ways. C and C++ for sure feel hobbled together, and C# feels hobbled together with a fresh coat of paint over the top.</p> <p>It&#39;s a rather fanatical idea to think, but I&#39;m willing to take the design choices on faith at this point. The people coming up with those guidelines are much smarter than I.</p></pre>sje397: <pre><p>No way. This is a reason to try doing things differently, and either find out that it is a better way or learn from experience what the reasons for doing it the old way actually are. So many bad things happen because of &#39;tradition&#39;.</p></pre>jahayhurst: <pre><p>I don&#39;t disagree - I will continue to look at guidelines closely as I have time and seeing if I can find better ways of doing things.</p> <p>The difference here is that the guidelines that I have looked into seem to be sensible. It did not take me long into many other languages to find things to work around.</p> <p>I&#39;m not going to throw out the guidelines just to be different. I&#39;m not going to ditch them until I find problems. I did the same with other languages too. The difference here is that I&#39;ve gone longer without finding glaring things than I have with any other language.</p></pre>Frakturfreund: <pre><p>Because &#34;self&#34; is a Python-ism (where objects are dict’s), and &#34;this&#34; is a Java-ism (where objects are their one thing).</p> <p>Go&#39;s struct data structure is different from both: It has native dicts, but structs are not dicts; so it&#39;s not Python.</p> <p>And you can determine the fixed size of a struct, while a JVM may decide to internally represent a Object in a different/more efficient way (primitive types and autounboxing); so it&#39;s not Java.</p> <p>So you should avoid the keywords of this other languages so that the readers of your code do not assume the mental model assosciated with them.</p> <p>PS: The fact that Go doesn&#39;t invented a new keyword (&#39;me&#39; or so?), but let you choose your own variable, is probably just annother instance of it&#39;s strive for minimalism.</p></pre>tompko: <pre><p>In Python &#34;self&#34; isn&#39;t a keyword, you can choose your own variable name too. The name &#34;self&#34; is just a convention, in the same way that not naming it &#34;self&#34; is the convention in Go.</p></pre>Frakturfreund: <pre><p>I totally forgot this, thank you for the reminder! I blame my syntax highlighter ;). But at least it shows how pervading a convention can become ;).</p></pre>sje397: <pre><p>That reasoning isn&#39;t consistent across other keywords like &#39;struct&#39;.</p></pre>m_elange: <pre><blockquote> <p>So you should avoid the keywords of this other languages so that the readers of your code do not assume the mental model assosciated with them.</p> </blockquote> <p>This is good advice for a language designer too. I think this is one of the design flaws of &#34;C-like&#34; languages. The fact that Javascript looks a lot like Java lends a mental model (esp. with regards to <code>this</code>) that takes a while to overcome.</p></pre>

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

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