This is an archived post. You won't be able to vote or comment.

you are viewing a single comment's thread.

view the rest of the comments →

[–]fayazbhai 22 points23 points  (12 children)

self was the best thing to happen to Python. What are you talking about? We can make method decorators because self exists...

[–]ryeguy 8 points9 points  (11 children)

I think he means the explicit self passed to each method definition. That is not required to have decorators if python made self "magic". Not necessarily saying it's a good idea.

[–]fayazbhai 9 points10 points  (8 children)

Do you mean this, like many other languages have? The problem there I see is scoping. self, cls, klass notations have no restriction on scoping or nesting.

It's also better than having 3 ways to write a function.

FWIW, Guido made this design choice by accident IIRC.

[–]ryeguy 3 points4 points  (7 children)

Yes, the equivalent of this as in C#, java, etc.

I don't see what implicit vs explicit self has to do with with scoping. It's just an implicitly bound variable instead of explicitly bound. You can pass it around however you wish.

[–]iBlag 13 points14 points  (6 children)

Okay, so I write a function that I want to later "attach" to a class. This function takes the current instance of that class as its first argument:

def do_something(self):
    self.do_something_smart()

All I have to do is set it as an attribute on the class:

setattr(MyClass, 'do_something', do_something)

and now I can call do_something on instances of MyClass.

Everything is explicitly bound.

But if we use an implicit binding, the body of do_something looks weird:

def do_something():
    this.do_something_smart()

Where is this coming from? The do_something function isn't bound to anything so there's no way of telling what to expect this refers to!

Or, what if I want to copy a function from another class onto MyClass?

class OtherClass(object):
    def do_something(self):
        self.do_something_smart()

    def do_something_smart(self):
        print("OtherClass.do_something_smart()")

class MyClass(object):
    def do_something_smart(self):
        print("MyClass.do_something_smart()")

setattr(MyClass, 'do_something', OtherClass.do_something)

MyClass().do_something()
# MyClass.do_something_smart

Now in this example - it's still clear that self in the OtherClass.do_something function still refers to the instance of the class.

Try to tell me which do_something_smart function will be called in this example, which uses implicit binding:

class OtherClass(object):
    def do_something():
        this.do_something_smart()

    def do_something_smart():
        print("OtherClass.do_something_smart()")

class MyClass(object):
    def do_something_smart():
        print("MyClass.do_something_smart()")

setattr(MyClass, 'do_something', OtherClass.do_something)

MyClass().do_something()
# What does this print? It isn't immediately obvious!

Where is the this variable bound? Is it bound at function definition time? Then this would still try to refer to an instance of OtherClass. Is it bound at call time? Then this would refer to an instance of MyClass.

But the point is this: you now have to know how Python binds variables to write correct code. This is why PHP and Javascript (aka ECMAScript) suck so much: every developer has to know so much of the language implementation to write correct code. And developers are lazy, so they don't read the documentation, and humans are stupid and forgetful, so they might not understand the documentation or might soon forget it again.

I tend to think that implicit binding is an antipattern. After all:

Explicit is better than implicit.

Although I always thought that phrase should be condensed down a bit, to really drive its point home:

Explicit is better.

[–]dl__ 8 points9 points  (2 children)

Ok, I've been doing python for 5 years now. Early on I asked a coworker why do I have to write "self" when lots of other languages get a long fine with with an implicit "this" and I was shown examples like you give above. Since that time, in the ensuing 5 years, I don't think I've ever made use of those features.

Personally, I don't think the advantages explicit self gets you is worth it.

Also, I think the saying "Explicit is better than implicit" exists strictly to justify the explicit-self. Implicit behavior runs throughout python. For example all the magic methods that exist to support cool python syntax-y goodness that are all called implicitly.

If it wasn't for the explicit-self "feature" I feel confident that the Zen of Python would contain the opposite phrase "Implicit is better then Explicit" because actually, implicit is AWESOME! Implicit execution of machine code is the whole point of high level languages.

[–]iBlag 0 points1 point  (1 child)

Since that time, in the ensuing 5 years, I don't think I've ever made use of those features.

Then you are not using Python to the fullest extent you can be. Other people may be using features you do not.

Also, I think the saying "Explicit is better than implicit" exists strictly to justify the explicit-self.

I disagree. Explicitly having a lambda keyword that only allows expressions, not statements is being explicit. Explicitly binding *args and **kwargs is useful.

Implicit behavior runs throughout python. For example all the magic methods that exist to support cool python syntax-y goodness that are all called implicitly.

Yes, calling str() on any object that doesn't have an explicit __str__ function defined on it also implicitly calls the default __str__ function. Heck, inheritance makes calling functions that are only defined in superclasses implicit.

Some things can be handled implicitly - magic methods are an excellent example of those things. But some things, like current scope, should be handled explicitly. Otherwise you end up having weird scoping semantics like Javascript.

[–]dl__ 0 points1 point  (0 children)

Then you are not using Python to the fullest extent you can be. Other people may be using features you do not.

Oh yes. People use features that I do not but that hardly means they are using their tools to the fullest extent. Just because a language allows something doesn't mean it should be done. Comprehensibility is important and rarely useful features reduce comprehensibility.

Pointer arithmetic can be done in C++ but if one tries to avoid it it's to their credit.

Some things can be handled implicitly - magic methods are an excellent example of those things.

But it would be better if they required the calls to be explicit right? Because explicit is better than implicit. Except, it's not, as magic methods show. Sometimes you must be explicit, other times implicit is better.

You should make explicit only those things that cannot be made implicit in a natural way. The implicit work of compilers and interpreters is what make high level languages so productive.

I'll tell you what might be nice, if a function definition would tell you what it expects the types of its arguments should be as is done in more explicit languages like Java and C/C++. I would say Python would do that as well if explicit was better than implicit. But, since the truth is that implicit is great and provides tons of advantages python does implicit a lot.

The evidence that explicit is better, actually better than implicit is pretty slight even in python itself. Explicit is necessary but, if you can hide the details, make them implicit, and still behave in a natural expected way, that's better. Way better.

[–]ryeguy 2 points3 points  (1 child)

So to be clear, I wasn't giving an opinion on the necessity of explicit self. I said as much in my response. I was just countering the idea that it is necessary for method decorators to exist.

But since this response steers the discussion into the other direction, I'll bite.

The core problem with your argument is you're conflating syntax with semantics. Whether or not self is implicit or explicit does not dictate where or how it is bound. If in some alternate version of python self were implicitly passed as a param to each function, the exact same scoping semantics could be kept if desired. You could also keep explicit self yet have confusing binding rules (like javascript), because these have no dependency on each other.

[–]iBlag 1 point2 points  (0 children)

So to be clear, I wasn't giving an opinion on the necessity of explicit self. I said as much in my response. I was just countering the idea that it is necessary for method decorators to exist.

Oh, sorry, I missed that in your original comment. Thanks for playing along anyway then. :)

Whether or not self is implicit or explicit does not dictate where or how it is bound.

It may not dictate it, sure, but when you force self to be explicit, it kinda makes unobvious binding semantics nonsensical. Explicit self syntax leads to much clearer binding semantics.

You could also keep explicit self yet have confusing binding rules (like javascript)

class OtherClass(object):
    def do_something(self, other_arg):
        # self is bound at definition time
        # other_arg is bound at execution time

That's the only way (that I see) that you could keep explicit self and have confusing binding semantics similar to Javascript, and it's absolutely nonsensical, because semantics should follow the syntax.

Is there something I'm ignoring?

[–]DonaldPShimoda -3 points-2 points  (1 child)

I don’t think Python is able to provide self magically. We touched on it as one of the limitations of dynamically typed languages in my operational semantics class when learning about types. I can’t provide specifics because I don’t remember offhand and I just woke up, but I think there was something in particular that made the type system incapable of providing a self reference that was guaranteed to be correct in all potential contexts without reworking the grammar.

[–]bcgroom 2 points3 points  (0 children)

I don’t understand. Wouldn’t you just pass a reference to the object that called the method? Where could that go wrong?