33 ms·
In general, object orientation is a reasonably elegant way of binding together a compound data type and functions that operate on that data type. Let us accept
by gorgoiler 6y ago
In general, object orientation is a reasonably elegant way of binding together a compound data type and functions that operate on that data type. Let us accept this at least, and be happy to use it if we want to! It is useful.
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.
- dfinninger 6y agoJust to discuss one point: > 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.
- rbanffy 6y agoThey are far less primitive than Java's - they are classes too, but can't be extended.
- deleted 6y ago[deleted]
- BerislavLopac 6y agoI'm not sure which language did you mean there, but in Python those classes can be extended. Also, the main reasons for special cases are historical; also, they predate PEP8 by a long time.
- rbanffy 6y agoSorry. That's something that was true in 2.x but not anymore. There used to be UserString, UserList, UserDict classes for serving as base classes. We still have those, but, at least now, we can extend the fundamental classes directly. They are a bit more convenient than the built-ins.
- pansa2 6y agoWe can inherit from `list`, `dict` etc., but objects of those classes are still not as flexible as "normal" objects. For example, we can't add an attribute to a list using `l = []; l.a = 1`.
- joshuamorton 6y agoI believe that makes them the same as any other class defined with `__slots__`: >>> 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'
- bobbyi_settv 6y agoIt's just for historical/ backwards compatibility reasons. These types have been part of Python from before it had classes. Today, they are classes and can be subclassed, etc, but too much code relies on their existing names to change them to match the modern convention.
- mrslave 6y agoPEP 20 -- The Zen of Python [0] ... "Explicit is better than implicit." In the case of the unnecessarily repetitious `self` it means to violate DRY, make the mundane manual - and therefore error prone - and tedious. [0] https://www.python.org/dev/peps/pep-0020/ https://www.python.org/dev/peps/pep-0020/
- Ankintol 6y ago> In general, object orientation is a reasonably elegant way of binding together a compound data type and functions that operate on that data type. Let us accept this at least, and be happy to use it if we want to! 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 function
- ssivark 6y agoTotally. That structure also allows for multiple dispatch, which makes generic programming much much more pleasant than OO (which is basically single dispatch). Eg. See Julia. To elaborate, tying methods with the individual/first argument makes it very difficult to model interactions of multiple objects.
- pansa2 6y ago> 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.
- fleabitdev 6y agoHow do you compensate for the lack of type-dependent name resolution? MyModule.foo(my_t) seems verbose, compared to my_t.foo()
- girvo 6y agoNim handles this quite nicely for you: either syntax works!
- deleted 6y ago[deleted]
- mohaine 6y ago> Are there psychotic devs out there who call it something other than `self`? Yuck! 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,
- tus88 6y ago> 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! If you weren't an ignoramous you would understand it can be something else, like klass.
- gorgoiler 6y agoYou spelled it wrong! 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.
- rbanffy 6y agoRuby's irregular syntax drives me crazy. But it is beautiful.
- staticautomatic 6y agoI gotta say as a Python developer I kinda like this compared to thing = Thing()
- dang 6y agoSorry I'm late here, but obviously you can't post like this. We've banned the account. Please don't create accounts to break HN's guidelines with. https://news.ycombinator.com/newsguidelines.html https://news.ycombinator.com/newsguidelines.html
- jghn 6y agoI'd personally contest the notion that binding state and functionality directly together is "reasonably elegant" in the first place. 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.
- rbanffy 6y agoIt makes a lot of sense for modeling real world objects that can do things and have state.
- jghn 6y agoWhich makes a lot of sense for the situations where modeling real world objects that can do things and have state makes a lot of sense. Which isn't as often as the OOP advocates would have one believe.
- klyrs 6y ago> Are there psychotic devs out there who call it something other than `self`? Yuck! 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.
- GeorgeTirebiter 6y agofor my own code, I use 'me' instead of 'self'. Just makes more sense to me; e.g. me.variable that is, the one referenced right here ('me') is being referenced, not from anywhere else. It's also shorter; and I think more descriptive. But that's just me.
- omaranto 6y agome.variable sounds Irish to me, at least compared to my.variable.
- thewakalix 6y agoAnd my.variable sounds Perlish.
- KMag 6y agoThat's because Perl was intended (Larry Wall was trained as a Linguist) to somewhat mimic natural English.
- lordgrenville 6y agoThat's a fascinating tidbit. You wouldn't exactly guess it from reading Perl.
- Kototama 6y agoExcept the binding always implies the object is mutated on changes, which make any form of reasoning or concurrent programming difficult.
- riffraff 6y agoWhy? There are plenty of OO libraries where method calls do not change the object.
- Kototama 6y agoThere is but it's mostly not how OO programs are built. And even assuming you have most of the core of your program behaving properly and clearly with non-mutable objects. What do you gain from having objects at this point?
- rbanffy 6y agoYou can always mutate objects using functions that return new objects. 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.
- Kototama 6y agoI should have written almost always. 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
- rbanffy 6y ago
- rbanffy 6y ago> What are emphatically not pretty or useful are Python’s leading underscores to loosely enforce encapsulation. 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. ;-)
- takeda 6y ago> 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. Totally 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.
- gfaure 6y agoIf we look in `collections`, we can see `defaultdict` sitting side-by-side with `OrderedDict` and `Counter`. I've long since resigned myself to the fact that naming consistency is not Python's strong suit :)
- mixmastamyk 6y agoThe one that bugs me the most is: from datetime import datetime
- afarrell 6y ago> I can assume everyone who uses my library is a responsible adult and I can treat them as that. What is great about python is that it acknowledges that even adults can forget things. Underscores are an affordance.
- BerislavLopac 6y ago> Nor do I find charming the belligerent lack of any magical syntactic sugar for `self`. 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?
- BalinKing 6y agoC++ et al's "this" keyword seems perfectly reasonable to me--the whole point of being a member method is that you have some sort of implicit state. Otherwise, why not just make them top-level functions, if you have to take the object as a parameter anyway?
- BerislavLopac 6y agoIt seems to me that you're conflating two unrelated things. Yes, I agree -- there are perfectly legitimate reasons to implement module level functions instead of class and instance methods; but avoiding typing four extra characters just to be more like some other arbitrary language is not one of them. 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.
- gengkev 6y agoConversely, I think the beauty is that they _are_ the same as top-level functions, just in a special namespace. This means that you can call "instance methods" directly from a class, i.e. the following are equivalent: (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.
- kevin_thibedeau 6y agoPython OO is a bolt on. That's why it doesn't have syntactical support for magic functions or self. Nothing prevents you from implementing your own alternate object system. Python 2 had two of them. As for the linters, just disable the rules you don't like.
- pansa2 6y ago> What are emphatically not pretty or useful are Python’s leading underscores to loosely enforce encapsulation. 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.
- joshuamorton 6y ago> However, what happens when you define a method and miss off the `self` parameter is crazy. @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.
- pansa2 6y agoPython's actual behavior in this case is far stranger than static methods, which are a standard feature of many object-oriented languages (albeit one that's fairly redundant in a language that allows free functions).
- joshuamorton 6y agoI'm not sure what you're saying here. 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).
- pansa2 6y ago> Are there psychotic devs out there who call it something other than `self`? Yuck! Sometimes I like to overload operators using `def __add__(lhs, rhs)` and `def __radd__(rhs, lhs)`.
- pgcj_poster 6y ago> Are there psychotic devs out there who call it something other than `self`? I call it "me".
- closed 6y ago> 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? 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).
- gorgoiler 6y agoThat’s a compelling point. zoo = [‘goat4’, ‘tiger7’] map(Pet.stroke, zoo)
- xapata 6y agoBackwards compatibility. You remember all those folks complaining about Python 2 vs 3? Turns out even renaming Thread.isalive to is_alive caused a ruckus. Imagine how much yelling you'd hear if you changed float to Float. 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.