I have, under duress. It was a result of using syntactic sugar wrapping a PHP website to make a Python API; it was convenient to pass form variables into a generic handler method of a class. The problem? The website, fully outside of my control, had a page which had an input named "self" which resulted in two values for that argument. Rather than refactor the class and dozens of scripts that depended on it, I renamed 'self' to 's_lf' in that one function and called it a day.
Also, python has class methods. The convention is to use 'cls' in that context, to avoid confusing the parameter (a class) with an instance of that class.
Having an undergraduate degree doesn't make somebody a linguist. Let me know when he has contributed to the field through scholarly work. Doing Bible translations doesn't count.
Only if you're used to the derogatory, clichéd "American TV" version of Irish.
Is it elegant? OO couples data types to the functions that operate on them. After years of working on production OO I've still never come across a scenario where I wouldn't have been equally or better served by a module system that lets me co-locate the type with the most common operations on that type with all the auto-complete I want:
//type
MyModule {
type t = ...
func foo = ...
func bar = ...
}
If I want to make use of the type without futzing around with the module, I just grab it and write my own functionTo elaborate, tying methods with the individual/first argument makes it very difficult to model interactions of multiple objects.
The best example of this in Python is operator overloading. We need to define both `__add__` and `__radd__` methods to overload a single operator. Even then, there are situations where Python will call one of those where the other would be better.
- short names inside modules. I.e. you might have a function called Foo.merge(...) instead of x.merge_with_foo(...)
- a way to bring modules into scope so you don’t need to specify the name
- not using that many modules. Most lines of code won’t have more than one or two function calls so it shouldn’t matter that much (other techniques can be used in complicated situations)
The key advantage of type-dependant name resolution is in using the same names for different types. You might want to write code like foo.map(...) and it is ok if you don’t know the exact type of foo. With modules you may need to know whether to call SimpleFoo.map or CompoundFoo.map.
So often when using C#-style fluent APIs I find that I'm completely on my own and have to turn a half-written line into something syntactically correct before Intellisense gives me anything useful. Using an F#-style MyModule.foo, the compiler can tell me everything.
¹ https://en.m.wikipedia.org/wiki/Uniform_Function_Call_Syntax
1. Functions with two arguments of your type (or operators). Maybe I want to do Rational.(a + b), or Int.gcd x y, or Set.union s t. In a Java style api I think it looks ugly to write a.add(b) or whatever.
2. Mutability can be hidden. If you see a method of an object that returns a value of the same type, you can’t really know whether it is returning a new value of the same type or if it is mutating the type but returning itself to offer you a convenient chaining api. With modules there isn’t so much point in chaining so that case may be more easily hidden.
Either you lose private/protected state and the advantages or you replace them with module visible state and the disadvantages.
You can still write new methods that blow away any assumptions previously made about type state,causing bugs. It seems like it could be even worse seeing as you possibly have less access controls for fields.
If you want to prevent people outside the module from adding functions, you can do that in Haskell by only exporting the type-name. This prevents other modules from actually accessing any data inside the type, whilst still allowing them to use the type in functions. This includes sometimes making getter functions to read values from the type. I'm pretty sure Rust allows for something similar.
In general, FP still allows for encapsulation. Moreover, it uses immutability and to some extend the type-system to prevent illegal states of data types.
IMO Go's use of upper/lower case is even worse than Python's use of underscores. What would be better? Explicitly labeling everything as `public` or `private`, C++/Java style?
> Does Python force you to pass [self] as an argument to make some kind of clever point?
I think the idea is that `a.foo(b)` should act similarly to `type(a).foo(a, b)`.
However, what happens when you define a method and miss off the `self` parameter is crazy. Inside a class, surely `def f(a, b): print(a, b)` should be a method that can be called with two arguments.
> And why are some classes (int, str, float) allowed to be lower case
Historical reasons. AFAIK in early versions of Python these weren't classes at all - the built-in types were completely separate.
Especially when serialising a struct to JSON - only public members are serialised, so your JSON ends up with a bunch of keys called Foo, so you have to override serialisation names on all the fields to get them lowercased.
@staticmethod
(although why you want this instead of just a module scope function is unclear). If you want this strange behavior, you should be explicit about it.
If you want `def foo(a, b)` inside a class to be a static method, you need to define it as
class Foo:
@staticmethod
def foo(a, b):
...
Now `f = Foo(); f.foo(1, 2)` will work.However, you need to be explicit about this because it is much more common for this situation to arise because someone forgot a self somewhere than for it to have been intentional. Requiring @staticmethod serves to signal to humans that this odd behavior is intentional (but again I'd say that, stylistically, if you're using a staticmethod, just move it out of the class and into the module namespace, module namespaces in python don't bite).
IMO it would make sense for Python to look at `a, b` and assume you wanted a static method with two parameters. Instead it assumes you're using `a` instead of `self` and really wanted an instance method with one parameter.
Like I said: most of the time, a missing `self` is an error by the user, and so python requires explicit notification both to the language, and to other people, that you're doing what you mean to do, and not just making a mistake.
Not really. You end up implicitly injecting some value into local namespaces instead of having a function that takes an argument.
The implicitness of methods working differently than normal functions > the implicitness of classes passing a first argument to methods.
def __init__(self, x, y):
self.super(x, y)
would be the convention. There may be some infra issue with this (and in general, `super` has to be kind of magical no matter how you handle it). But yes, in general I can vibe with "super is too implicit".Sometimes I like to overload operators using `def __add__(lhs, rhs)` and `def __radd__(rhs, lhs)`.
Python will not prevent anyone from doing something dumb. It'll just force them to acknowledge that by forcing them to use a convention. As a library writer, I'm free to change or remove anything that starts with an underscore because, if someone else is depending on it, frankly, they had it coming. I can assume everyone who uses my library is a responsible adult and I can treat them as that.
> And why are some classes (int, str, float) allowed to be lower case
Because they are part of the language like `else` or `def`. Only Guido can do that. ;-)
from datetime import datetimeTotally agree, as an user, I feel anxious whenever I have to use underscored names. It's great that Python still allows me to do it and there were few times when it was useful, but when it stops working I know it's 100% on me.
What is great about python is that it acknowledges that even adults can forget things. Underscores are an affordance.
In the case of the unnecessarily repetitious `self` it means to violate DRY, make the mundane manual - and therefore error prone - and tedious.
I've come to like the self variable name convention. It takes so little effort to type and makes the behavior more clear. Well, some aspects of it, I suppose. I make a fair amount of use of unbound methods.
And what would that look like exactly? The Java-like approach, with any non-declared variables being the current object's attributes, wouldn't work because Python doesn't use declarations and can have named objects in any scope. The alternative is to use a "magic" variable appearing out of thin air, like Javascript's "this" -- but this is basically what "self" does, only explicitly.
Any other ideas?
Python has a different "philosophy" than "C++ et al". Literally everything is an object, and everything has attributes; some of those attributes are objects that implement the Callable protocol. And it's a syntactic sugar that some of those callables are implicitly passed certain predefined arguments; the default is passing the instance of the object itself. The exact same thing could have been implemented without it, like this:
foo.some_callable_attribute(foo, another_argument)
Since methods are usually called more often than they are declared, it makes sense to make this first argument the default. (1) + 2
(1).__add__(2)
int.__add__(1, 2)
This comports with the "explicit is better than implicit" policy in Python. The only "magic" part is when dot-notation is used to call an instance method, which is just syntactic sugar. Another example of this philosophy is that operator overloading is simply reduced to implementing a method with a specific name.I think a "magic" this keyword can create a lot of nasty edge cases that can be difficult to reason about; the way "this" is used in JavaScript is notoriously complex in ways that it might not be in a statically typed language like C++. What should "this" evaluate to outside of an instance method? What about in a class definition inside an instance method? What if an instance method is called directly instead of on an instance? All of these situations require making their own "rules", whereas in Python the correct answers can be easily reasoned about by starting from first principles.
On the class, you can call the method like a normal function (passing the self arg manually). Seems like a nice connection to just raw functions. Also explains how you can add methods after a class has been defined (set a function on it, whose first param is a class instance).
zoo = [‘goat4’, ‘tiger7’]
map(Pet.stroke, zoo)I was converting some old Java code to Python recently and I almost decided to switch to `this` just to make the task less repetitive. Luckily, sanity returned after I saw the first def abc(this,
But ignoring that, it depends on the flavor of object orientation. Yes, the most mainstream style bundles state directly with functionality but not all do. But for instance the CLOS family of OOP maintains separate state and functionality and one binds desired functionality to those classes which should have it. This is not too dissimilar from typeclasses IMO.
> And why are some classes (int, str, float) allowed to be lower case
Also, boolean. These are primitive data types. For instance, in Java there's a difference between int and Integer. I'd assume that Python special-cases these because they are primitive. But I haven't been through the Python internals, so it's only a guess.
Also, the main reasons for special cases are historical; also, they predate PEP8 by a long time.
They are a bit more convenient than the built-ins.
>>> class Foo:
... __slots__ = ('a', 'b')
...
>>> f = Foo()
>>> f.a = 1
>>> f.b = 2
>>> f.c = 3
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'Foo' object has no attribute 'c'I call it "me".
Please don't create accounts to break HN's guidelines with.
Ruby’s approach is the most delightful, I find. elf is an instance of Elf and in elf.health, self is the elf.
Elf is an instance of Class and in Elf.surpass, self is the Elf class itself.
If you use methods of an instance you know the object's state may or may not change. If you set attributes, you know for sure they changed. If you use a function that returns a new objects you know you paid the object allocation tax.
You can but it's not common. It's like saying you can write functional programing in C. You can but the whole ecosystem is not doing it.
If you set attributes, you know for sure that they changed at this particular place in the program but later in another position in the program, can you reason about the value of the object? You don't even who mutated it. And don't tell me "you can get its state", I'm talking about reasoning about the program here.
There is a reason why concurrent programming is more difficult than necessary with objects.
My point is that this implicit management of the state in "OOP" brings a lot of problems
Performance would suck, but that's life. You can't have your cake and eat it too.
As for the linters, just disable the rules you don't like.
What are emphatically not pretty or useful are Python’s leading underscores to loosely enforce encapsulation. Ugh. I’d sooner use camelCase.
Nor do I find charming the belligerent lack of any magical syntactic sugar for `self`. Does Python force you to pass it as an argument to make some kind of clever point? Are there psychotic devs out there who call it something other than `self`? Yuck!
And why are some classes (int, str, float) allowed to be lower case but when I try to join that club I draw the ire from the linters? The arrogance!
...but I still adore Python. People call these things imperfections but it’s just who we are.
PS I liked the Python5 joke a lot.