Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

It's not that it's confusing, rather it's unnecesarrely cumbersome because it's explicit and because you have to put it as an argument. Looking at the "most used words" [1] list of python, self is the #1 used word by far, occuring three times as often as the second most common word, "if". At this point, it's just a filler that bloats code and makes it less readable in many cases

    # so much text for such a little task
    def length(self):
        return math.sqrt(self.x*self.x + self.y*self.y + self.z*self.z)
        
    # better, but not quite there yet
    def length(self):
        return math.sqrt(self.x**2 + self.y**2 + self.z**2)
        
    # this would be perfect, but it's not allowed by python
    def length():
        return math.sqrt(x*x + y*y + z*z)
To be fair, javascript also forces you to explicitly specify "this" so there is no difference in that regard. What I find worse than that is that you have to specify self in the parameter list. Calling a function with 2 parameters but having to declare 3 doesn't make sense. The usuall reasoning is that "explit is always better than implicit" but I do not agree. We could take this philosophy all the way to explicitly defining all the global variables that may be used by the function in the parameter list, maybe even all the packages that this function is going to use. We don't do that for a good reason.

[1] https://anvaka.github.io/common-words/#?lang=py



Saying it's unnecessary is incorrect, and I don't agree about it being cumbersome either. On the first point, how would you disambiguate between reading from an attribute and reading from a global (or non-local) variable? How would you disambiguate between assigning to a variable and assigning to an attribute? You'd have to explicitly declare all your variables (making other things cumbersome instead) or accept that the semantics of your program could change during run time, depending on the contents of outer scopes (which I doubt you'd find an acceptable option). This is the main reason the 'with' statement is deprecated in JS. C++ can get away with implicit 'this' because it has static dispatch and all variables are explicitly declared anyway. But truth be told, I'd remove it from C++ too if I could. I like the clarity of knowing at call site whether something is a function or a method call without having to look it up. Paying for it the price of having type a few more characters has never bothered me.

And I somewhat like that I can name my 'self' argument whatever I like instead of having to resort to obtuse workarounds like 'var that = this;'. Not that I've ever had the need to take advantage of that possibility in Python, but it's comforting to know it's there.


In a short function that makes a lot of references to self, yeah I agree in can be a bit cumbersome. But as the functions get longer, I much prefer the explicit use of self. It makes the code far more readable as you can tell at a glance where a variable comes from.


> It's not that it's confusing, rather it's unnecesarrely cumbersome because it's explicit and because you have to put it as an argument.

I don't feel the same way. To each their own I guess.

Regarding your example, I don't see anything wrong with the second version. Anyone who reads the code will easily be able to tell what it does and where the variables come from. In your third example, how is anyone supposed to know whether or not `x`, `y`, and `z` are class variables or global variables? Kind of defeats the purpose of namespacing, I think.

> What I find worse than that is that you have to specify self in the parameter list.

This is really one of Python's implementation details. Methods can be bound or unbound. Without going into to much detail, just know that a bound method can be used as a regular function. Here is some code to illustrate what I mean:

    >>> class A():
    ...     def a(self):
    ...         print('hello')
    ... 
    >>> A
    <class '__main__.A'>
    >>> A.a
    <function A.a at 0x7fc4009381e0>
    >>> A.a()
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    TypeError: a() missing 1 required positional argument: 'self'
    >>> A().a()
    hello
    >>> b = A()
    >>> A.a(b)
    hello
    >>> class B():  
    ...     pass
    ... 
    >>> b = B()
    >>> A.a(b)
    hello
    >>> class B():
    ...     def a(self):
    ...       print('goodbye')       
    ... 
    >>> b = B()
    >>> A.a(b)
    hello
    >>> c = A()
    >>> B.a(c)
    goodbye


Self does not have to be the same class it's called on. It can be any class. However, if you try to access a class attribute that doesn't exist, you get an attribute error.

    >>> class C():
    ...     x = 10
    ...     def a(self):
    ...         print(self.x)
    ...
    >>> C.a()
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    TypeError: a() missing 1 required positional argument: 'self'
    >>> C().a()
    10
    >>> a = A()
    >>> a.a()
    hello
    >>> C.a(a)
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File "<stdin>", line 4, in a
    AttributeError: 'A' object has no attribute 'x'
Since bound methods need to behave like regular functions, it is improper to not pass the object instance to the function.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: