The "it's so cool to shit on Java" thing served an important purpose and is probably close to being retired, but it's easy not to understand if you weren't a Java programmer some time in Java's heyday. There was a culture and a set of assumptions around Java that today you could probably only find in the most stodgy and insular corporate programming environments. I helped introduce Java into a development group and ended up hating the people and practices it infected us with. The hatred is not completely unrelated to the language (which runs on a great platform and is not a horrible language) but it would never have attached itself to the language without some of the pathological culture surrounding it.
I don't know in which world you live in, but in the real world Java is by far the dominant platform for web services.
I read it as meaning 'the "it's so cool to shit on Java" thing' is close to retirement, not the language itself.
* Created in the last 5 years
* Built by an organization without a large Java heritage
I bet that number goes waaaay down, way quick.I always liked C# better than Java though.
You can use type annotations, either via the builtin lib or 3rd party libs!
https://docs.python.org/3/library/typing.html
> do_x() if a.y()
I feel this. You can use `a.y() and do_x()`, but I'm not sure how people would react :o
>>> def add(a: int, b: int):
... return a+b
...
>>> add("a", "b")
'ab'
This should be an error, even if it's a runtime error.I believe Julia is an example of a dynamically-typed language which does perform such checks at run-time - including as part of its support for multiple dispatch.
On a general note I've come across a few cases, when pushing python's type annotations to their limits, that force you to put the type names in string quotes, and that makes the whole thing feel like a hack. It's better than nothing for my use cases, but if I remember correctly both mypy and enforce have problems in common that are probably coming from the way python itself is built, such as self reference in a class definition.
With pep-0563, python3.7, and `from __future__ import annotations` this should no longer be necessary. Those are some pretty detailed caveats, but I have been using it in places where I can and it is so nice.
For actual projects you can use a static type checker like mypy.
If you're happy to write it that way round, why not just use `if a.y(): do_x()`?
I don't know the history, but it seems like that might be one of the reasons `x() if y()` isn't allowed in python. [x() if y()] if it existed, might carry an explicit else None.
On the other hand [x() and y()] does something different when y() is falsey.
Maybe I misunderstood your context, but if not, conditional expressions do exist in Python:
$ python
Python 2.7.12 |Anaconda 4.2.0 (32-bit)| (default, Jun 29 2016, 11:42:13) [MSC v.1500 32 bit (Intel)] on win32
Type "help", "copyright", "credits" or "license" for more information.
Anaconda is brought to you by Continuum Analytics.
Please check out: http://continuum.io/thanks and https://anaconda.org
>>> 1 if 1 == 1 else 2
1
>>> 2 if 1 == 1 else 2
2
>>> ^Z
$ py -3
Python 3.7.0 (v3.7.0:1bf9cc5093, Jun 27 2018, 04:59:51) [MSC v.1914 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> 1 if 1 == 1 else 2
1
>>> 2 if 1 == 1 else 2
2
>>>
Using nested conditional expressions to classify characters:https://jugad2.blogspot.com/2017/04/using-nested-conditional...
>>> from __future__ import print_function
>>> for i in range(3, 5):
... print(i, "is", "odd" if i % 2 == 1 else "even")
...
3 is odd
4 is even
which can be shortened to this: >>> for i in range(3, 5):
... print(i, "is", "odd" if i % 2 else "even")
...
3 is odd
4 is even
Now print() is printing 3 values: i, "is", and either "odd" or "even" based on the boolean condition.That works in both Python 2 and 3.
It's fine not to follow PEP8 for your own software. It's a useful guide, not a law.
> https://docs.python.org/3/library/typing.html
I know, but tragically, because I am not forced to, I never do. I do appreciate that feature though. Maybe this will be the impetus I need to start taking advantage of it.
Try an ML-family language if you haven't already - I find they combine the best parts of something like Python and something like Java. There was a post pushing F# earlier today.
At the end of the day it's a minor complaint of course.
I've always found Python lambdas to be uncanny, weird and error prone. It's kinda syntactic sugar for expression-only defs with counterintuitive scope rules. I would recommend just not using them and fall back to named functions. It's just one extra line and that way you get to name it, which is always a nice thing.
def x(y): <code goes here>
What's so heavyweight? A new line and a tab?
youngest_person = min(people, key=lambda x: x.age)
def get_age(x):
return x.age
youngest_person = min(people, key=get_age)
This may not seem like a huge difference, but using lambdas scales better.[1] Incidentally, this is the same reason why I don't miss multi-line lambdas (in Python): multi-line functions are seldom trivial.
A new line A tab A return
This does not sound heavyweight to me (in that context). In the context of a single line function I see the utility of lambdas.
You are confusing “first-class functions” and “anonymous functions”, which are completely different things. Python functions are first-class, independent of length.
Some definitions of "first-class function" require that it have the same value semantics as any other first-class type, which would include a literal syntax/anonymous functions.
cough maybe you should be programming in Ruby ;)
do_x if a.y?
Unfortunately (or fortunately, depending on perspectiven- great power and all that), Ruby allows things that Python doesn't and it's not hard to find yourself in a write-once mud bath.I do like Python's modules over how Ruby handles them too. Ruby's import (require) is akin to C/C++, with modules as a separate idea. I prefer the style of importing exactly what you need.
But I know Ruby better so it's my go-to.
Or Perl 6. `do-stuff() if $a.y;` is the Perl6 version. You can even declare your variables to be sigil-less so you end up with `do-stuff() if a.y;` if you want. See my Perl6 Advent Calendar article for a quick tour of the language, available docs, with practical examples focusing on using Perl6's novel features to write a self documenting command line script.
https://perl6advent.wordpress.com/2018/12/16/
I've written code in everything from assembly to ML, Java, and Prolog. Perl6 is the only language I've used that I would "mind-expanding". The more I use it, the I realize how radical its design is.
What would you prefer? C-style syntax? Ruby-style `end end end`?
If that is a keyword, or brackets, or whatever, I would prefer that. You can't minify or easily lint python. And you can't easily tell if there are mixed indent methods (tabs/spaces) and IDEs struggle with it vs. a simple bracket structure.
Hey! Let people not like things, guy!
I put my pants on, go to work, pick up my heavy JVM, and crack away in the Java mines for 8 hours.
When I finally get home, tired, broken, achy fingered, and filled with disappointment for only making it to the AbstractAbstractFactory, rather than the AbstractFactoryFactory, I think I've earned a few "Yeah, with other type systems you'd be able to..." style complaints.
Greener pastures and whatnot.
I am with you there. I have often wished for a C-like switch(). That said, I think the best Python leans towards the functional, so instead of a switch(), it probably would be better to come up with some kind of multi-arm match more in line with modern functional language semantics. That could also be more flexible with respect to the type of the tested expression. Maybe something akin to the Rust match syntax.
> I regularly wish python required some sort type indication for function parameters, because dealing with libraries that take complex objects as function parameters can be an absolute nightmare.
Well, of course Python has type annotations. TBH, I don't use them. I tend to be rather pedantic about Sphinx doc for functions, most especially __init__() constructors. Kind of old-school and manual, but it works -- make doc and read it in your browser. The other thing I do in constructors is 1) make them very tolerant of what gets passed as a parameter, and 2) be very pedantic about raising ValueError in the right place, at the right time, with a clear message.
I have a good friend that I argue with regularly -- now this guy is a compiler front-end guru -- ex-Sun senior staff level C/C++ compiler tech lead, very smart -- but we argue regularly about Python idiom. He litters his Python with extremely un-idiomatic assert(isinstance()) to check incoming parameters. I think C poisoned this man's brain with respect to type checking.
There are two things wrong with his code: 1) It raises the wrong exception, the correct exception is TypeError, not AssertError. 2) It totally breaks __int__, __float__, etc, so I can't implement those on custom classes.
In my constructors, to the extent possible, I do "type checking by re-construction" -- parameters i and p gets run through something like:
self.i = int(i)
self.p = PClass(p)
So if you send me a string for i or something weird that implements __int__(), everything is cool. And PClass.__init__(self, x) is responsible for turning p into an instance of PClass, leaving it alone if it already is a PClass, or raises ValueError. This idiom makes type checking as tight as you want it to be, but doesn't break Python Zen.Aspirationally, anyway.
As others pointed out, Python has optional type annotations and has had them since 3.0. They're static-only (you don't get runtime type checking from them), though, and getting what you want out of them will effectively require every single function, method and variable in not just your codebase but in every third-party dependency and all of their dependencies to be annotated.
Also, annotating "complex objects" tends to be an unreadable mess. If you have lots of functions which take complex objects as arguments, rather than a more clear list of parameters, that's probably not going to be solved by type annotations; it will be solved by rewriting the functions. In other words, you probably will not be helped by:
def foo(o: HugeLongComplexTypeDefinition) -> OtherHugeLongComplexTypeDefinition
But you would be helped by: def foo(widget: Widget, operation: WidgetOperation, tolerance: float) -> WidgetMeh, never encountered this in 20 years. Typing is helpful in large projects however, and not just with complex objects.
I regularly wish python required some sort type indication for function parameters, because dealing with libraries that take complex objects as function parameters can be an absolute nightmare.
I wish python had some equivalent to the switch statement that didn't involve workarounds with dictionaries, because sometimes you just want a clean way to deal with a value that can take 7 different forms.
I regularly find myself wanting to write something of the form do_x() if a.y() This is stupid, but I still kind of want it since do_x() if a.y() else do_y() is valid
Edit: Also fuck this whole "it's so cool to shit on Java" thing. Grow up. You aren't in college anymore. You should recognize that Java fills its niche effectively, and does a decent job of being semi-fast, portable, easy to read and maintain, and safe.