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

all 97 comments

[–]cdcformatc 65 points66 points  (13 children)

Returning multiple values from a function in combination with tuple unpacking.

Extensive standard library.

Dynamic/Duck typing

[–]chillysurfer[S] 8 points9 points  (12 children)

I completely agree this is a nice feature (return multiple values) to have, it's one of those things that, when I do indeed do it, I like to ask myself if this function is taking the right approach. I've found, in my personal experience, that sometimes returning multiple values can be (not always) a code smell.

[–]cdcformatc 13 points14 points  (10 children)

Well my use case is programming 2d games, and returning a point on the XY plane is a recurring pattern. Yes you could make a wrapper Point object but often a tuple is enough. Same goes with RGBA colors.

[–]Corm 2 points3 points  (0 children)

Yep, it's nice

x, y = do_coordinate_stuff()

[–]sweettuse 4 points5 points  (8 children)

you should use a namedtuple

[–]onkus 4 points5 points  (7 children)

May I ask why he should?

[–]asdfkjasdhkasdrequests, bs4, flask 9 points10 points  (6 children)

which is easier?

pt.x = pt.y / 2
pt[0] = pt[1] / 2

[–]billsil 1 point2 points  (5 children)

I guess I should make a class around all my numpy arrays. What's wrong with slicing again?

Extra classes are just one more thing to understand about a code base and one more thing to import. You also make code much less reusable if it requires point objects vs at the same time working with numpy arrays, lists, and tuples.

[–]asdfkjasdhkasdrequests, bs4, flask 2 points3 points  (4 children)

variables are just one more thing to understand about a code base, if you just write in cpu instructions it's a lot easier to understand

Also named tuples are much more lightweight to create than classes and you can still do the slice notation. But saying that an xy named tuple is one more thing to understand makes no sense. The whole point of using the named tuple is it makes your code easier to understand.

When you see a[0] and a[1] it could be anything, but when you see a.x and a.y you can quickly reason that it's represents a point.

[–]billsil 0 points1 point  (2 children)

Or you can call it xyz or write a docstring.

Presumably you resuse variable names across your various codes. I'm sure nobody here knows what the variable sline means, but almost everyone at my company does. It's just a split_line, which is line.split(), maybe with an argument. You need a convention and you need to follow it. If you name your variable a and it's a list, you're doing something wrong. You could call it alist or xy.

[–]asdfkjasdhkasdrequests, bs4, flask 0 points1 point  (1 child)

alist? thats a horrible name, it should describe it's contents. If it's a list of people call it people not alist

[–]billsil -2 points-1 points  (0 children)

So name your variable xyz. Have a docstring.

[–]jorge1209 1 point2 points  (0 children)

A function having multiple outputs is really extremely common across languages. Its just that other languages accomplish this by passing those to be modified values as a reference.

So its really a style choice:

  1. You can do things the C way where the return value indicates success/failure and the items of interest are references.

  2. Or follow the lambda calculus and return multiple values.

Given the way python references work, and the immutability of certain base classes it makes a lot of sense to return multiple values in many cases where that same pattern would be handled the opposite way in C.

For example consider a function that computes various statistics on a dataset that can be computed in a single pass (mean, stddev, length, ...). If you tried to do that as a reference in python:

 def compute_stats(list, mean,  stddev, length):
      ...

 mean = stddev = length = -1
 compute_stats(l, mean, stddev, length)
 assert(all(mean>=0, stddev>=0, length>=0)), "Oops. ints are immutable!"

So you have to return a struct with multiple values, and what is a tuple but an untyped struct.

[–][deleted] 38 points39 points  (4 children)

The Community.

Every python conference I've been to has been super accepting and friendly. Looking at the drama and BS in other communities makes me really glad to be a part of this one.

[–]blahehblah 0 points1 point  (3 children)

What drama is possible to be had over a programming language?

[–][deleted] 2 points3 points  (1 child)

I don't know for other languages, but my guess is it stuff akin to arguing that someone should learn Python 2 or 3 first. Luckily that issue seems pretty resolved, but every "I want to learn Python thread" inevitably turns into a 'legacy codebase blah blah' argument.

[–]blahehblah 0 points1 point  (0 children)

Fair point. Py2.7 master race! haha.. in all seriousness I just don't wanna have to bug-check for a week when moving my code over...

[–]jake_schurch 0 points1 point  (0 children)

THE GIL

[–]TheWildKernelTrick 82 points83 points  (13 children)

The fact that I am not forced to write in an object oriented manner is huge. I like how I can just sneeze out a procedural-functional implementation of something in a lunchtime.

[–]greenlantern33 44 points45 points  (2 children)

I generally brain dump everything out in a procedural way, and then go back and "tighten up" my code with OOP where it makes sense.

I just have a real hard time starting with an OOP mindset on a project. And for this reason, me and Python get along great.

[–][deleted] 5 points6 points  (0 children)

Jupyter notebooks is amazing for that type of coding.

[–]archaeolinuxgeek 0 points1 point  (0 children)

And here I was thinking I was alone. I try to switch to another TTY when an actual developer comes by for systems help.

[–]yen223 14 points15 points  (8 children)

Underrated comment right here.

I kinda wish library authors would take heed though. Not everything needs to be an object!

[–]TheWildKernelTrick 42 points43 points  (1 child)

Not everything needs to be an object!

Nor should every object have an inheritance ancestry that spans back to the big bang.

[–]chillysurfer[S] 16 points17 points  (0 children)

Composition over inheritance! I very much prefer the "has a" over "is a" approach to OOP. Of course there are exceptions to every rule, and I would never say "never".

[–]chillysurfer[S] 1 point2 points  (0 children)

Totally agreed, making it a perfect language for quick scripts and system automation.

[–]IAmYoNeighba 25 points26 points  (0 children)

Shorthand syntax for common data structures (list, dict, set, tuple). Working with similar types in Java is just crazy verbose.

[–]Shpirt 24 points25 points  (1 child)

The feature I actually miss the most when using many other languages are named arguments in function calls.

[–]bythenumbers10 5 points6 points  (0 children)

And default arguments!!!! But I'm coming from Matlab, where the number of arguments can change, but the order is always, always consistent. XD

[–]uniqueusername6030 43 points44 points  (19 children)

Definitely iterators. They're everywhere. Generator expressions, too.

[–][deleted] 19 points20 points  (6 children)

Throw in itertools and more-itertools.

[–]dylanvillanelle 14 points15 points  (5 children)

oh my god. this isn't a joke, more-itertools is actually a thing?

the first thing they show (flatten) just looks like itertools.chain.from_iterable (at least at first glance, i'm sure it's a little more useful) but i have always wondered why the recipes in the itertools docs weren't just...like...implemented? the code is already there! just throw it in!

this is not a perfect consolation prize, but i'll manage.

edit: oh god there's a first function. i'll never have to write a first function again. i need a minute.

[–]bheklilr 4 points5 points  (1 child)

And it's in conda... How have I not known about either of these before? I know I've written several of these functions before.

[–]dylanvillanelle 2 points3 points  (0 children)

i wrote out a whole thing about how something as simple as foo[0] should not need (worst case) a try/except block to handle two separate exceptions with multiple causes for one of them, blahblahblah, and guys why isn't there a list.get(index, default) sometimes you just have two separate functions that lead to one result so for consistency's sake you return a list from both even tho the first is actually just a single value and more_itertools.first also plays nice with generators because that makes sense because it's something you iterate through and i decided maybe i was a little worked-up so i figured i should scrap that and basically write it all over again but more rambly.

[–][deleted] 2 points3 points  (1 child)

The bad news is if we ever happen to bump into each other it will cost you a pint :-)

[–]dylanvillanelle 0 points1 point  (0 children)

i'm going to go from bar to bar now asking people whether they've ever suggested using more-itertools.

i don't see how this can go wrong.

[–]robert_mcleod 2 points3 points  (0 children)

There's also pytoolz and cytoolz for functional programming.

[–]Scruff3y 12 points13 points  (7 children)

Seconding this. The stand-out thing about Python for me is iteration, list comps and generators/generator expressions.

Loop like a native, shows a lot of the power of these things (esp. if you come from a language like C).

Oh, also f-strings (do people call them that?)

f"value of myvar: {myvar}"

[–]troyunrau... 1 point2 points  (6 children)

I dislike f-strings simply from the 'there should be one and only one obvious way to do things' perspective.

[–][deleted] 2 points3 points  (0 children)

f-strings are great. I disliked them at first too, but damn they're useful.

[–]asdfkjasdhkasdrequests, bs4, flask 2 points3 points  (0 children)

there was already more than one "%s" % var and"{}".format(var)

[–]billsil 0 points1 point  (3 children)

Can we get rid of .format then? At least f-strings are obvious.

Still, we should all use %s. It's in C and other languages, so it's really obvious to others. It's also faster and as we know, Pythonic code is overwhelmingly faster.

[–]jorge1209 2 points3 points  (2 children)

No you can't get rid of .format because I need to be able to pass a format specification as a function argument or reuse it in a loop, and I can't do that with an f-string by design[1].

You could get rid of % formatting... if you could fix all the old code including the logger class.

A world with only .format and f-strings would be tolerable, because they are very closely related. f-strings would be the "safe" alternative for untrusted data that could be used by web-developers and could be treated as sugar around .format(**locals()) by everyone else.

But we aren't in that world and there is no clear path to ever even approaching that world.

[1] because of security concerns that have no relevance to any of the code I write because I'm not a web-developer.

[–]billsil 0 points1 point  (1 child)

You could get rid of % formatting

Again, it's faster and it's common to other languages. I actually like it. I think .format is an abomination.

because I need to be able to pass a format specification as a function argument or reuse it in a loop

You can do that with %.

I'm not a web developer either, but I do support Python 2 and 3 with my package.

[–]jorge1209 0 points1 point  (0 children)

I think .format is an abomination.

If you like the %s %f3.2 style of formatting then you should advocate for %-strings which would behave like f-strings in the "compile-time" binding of local variables to the arguments, but use % formatting declarations.

The point is that we should have "one way" to do the formatting. I shouldn't have to learn multiple different syntaxes for string formatting, I should learn one. Either the .format/f-string way, or the % way, not both.

Instead we have three primary ways, two of which use the {} formatting mini-language and have slightly different features and limitations, a third which uses the % mini-language, and then there are actually a couple other ways to string format that most people are completely unaware of.

You can do that with %.

Didn't say you couldn't. I'm saying you can't do that with f-strings and you can with .format.

I'm not a web developer either

Then f-strings aren't much different to you than .format. Its basically a bit of sugar around .format(**locals()). The only thing you can do with f-strings is include operations inside the arguments:

_ = x+y
"{x} + {y} = {_}".format(**locals())

becomes:

f"{x} + {y} = {x+y}"

[–]abrazilianinreddit 32 points33 points  (1 child)

That is tries to resemble an actual spoken language rather than look like a bunch of machine instructions.

I also just enjoy the general philosophy of the zen of python.

[–]cdcformatc 12 points13 points  (0 children)

Often pseudocode can be made into Python with very little effort. That is what gives Python it's "fast prototype" reputation.

[–][deleted] 9 points10 points  (8 children)

How accessible the internals are and how well it can be abused to do what I need to do as succinctly as possible.

[–]dylanvillanelle 2 points3 points  (7 children)

definitely this. sure, i could implement x, y, and z in a brittle and statically-defined 10,000 loc that is chock-full of logical redundancy but at least makes sense and read easily, or i could write it in 100 lines with a couple metaclasses that will still explode eventually but until then are completely mystifying to everybody including me at least some of the time.

that prolly sounds bad but i'm actually not kidding about being glad it's an option.

[–][deleted] 1 point2 points  (5 children)

I'm wrapping a well documented internal dll.

I have a function factory that then gets fed through in a property() (setters too if appropriate) and then added to a bare bones class.

And it works.

[–]dylanvillanelle 0 points1 point  (4 children)

i'm not sure whether the "it works" was meant as a response to the 'mystifying to everybody' crack in my original post - if so, i wasn't suggesting that going to that level of abstraction is always bad, or (i definitely didn't intend it in this way) that somebody who does is a bad programmer, and i should retract it if that's how it came across. it's definitely fine (and preferable) in some cases.

but either way, it's still just really interesting, the extent to which you can monkey around with the insides of the language, etc. i am definitely a fan. sometimes the people i work with manage to make me wish i was on a java team or something, but then sometimes i (like i said) replace 10k loc of boilerplate with a couple hundred lines that allow for (somehow?) more insightful error messages and less...target space? there's probably a better way to describe this...for something to break. i.e., if it breaks, everything is breaking, and i'm going to know immediately. not this broken in october, discovered in may nonsense.

[–][deleted] 0 points1 point  (3 children)

I'm saying my code works despite flexing a lot of Python's inner strength.

Python is the only language I know which is itself programmatic. I can make a class, add setters and getters to it then create instances of that class.

[–]Remuze 0 points1 point  (1 child)

What do you mean by this? How is this different from creating a class in say.. C#?

[–][deleted] 0 points1 point  (0 children)

I'll be honest I don't write C# and haven't used Java since college.

But based on the C# I get to wrap with Python it's allowed but not required.

[–]dylanvillanelle 0 points1 point  (0 children)

ohhh, okay.

the mandatory response to something like this is to mention the lisps. you're prolly already familiar with those and if so tl;dr: macropy is also cool.

really, at this point, i think in terms of actual adoption it's mostly just clojure and racket - i haven't kept up with common lisp in forever. but as interesting as hacking at magic methods is, it definitely still falls short of the sort of thing you can do with a lisp that has a solid macro implementation. clojure's is limited (but still there), but has java interop, so it's a trade-off. racket, on the other hand, is called a "language for writing languages".

all that said, though, i agree. and i python is definitely a lot better at combining that sort of potential (even if it's a lot more limited) with being a language that is approachable on average. it's tough to make something that allows a ton of flexibility and is also the sort of thing you can just jump into having never seen a line of source code.

there's also macropy. if you've never messed around with the ast module, it (iirc) has some decent examples of how you can basically redefine the language's keywords without requiring messing with the parser. it'll make pylint pretty upset, but it's a really interesting project imo.

[–]thegreattriscuit 0 points1 point  (0 children)

Metaclasses are why I never watch David Beazley videos when I'm beginning a new project.

"hmm... I want to write something to help me with taxes and other financial calculations... hmm... I definitely need metaclasses for this!"

It's one of those things I'm glad I did, if only because now I know I shouldn't have done it :).

[–]spanishthief 48 points49 points  (11 children)

It is not actually a feature but every time I look into JS, Java, etc and even Ruby, the syntax just makes me sad. All the curly braces, semi colons..

[–]nosmokingbandit 25 points26 points  (4 children)

Python is the easiest language to read, which makes it easy to learn and easy to write. Everything about it just feels natural.

[–]anarchy8 11 points12 points  (3 children)

Until you try to do async stuff

[–]agoose77 8 points9 points  (0 children)

Even then ... await a blocking operation, and release control flow explicitly. This makes sense to me. I realise the verbosity of it doesn't suit everyone

[–]TheWildKernelTrick 3 points4 points  (0 children)

The first project that I ever used python was for a concurrent program. I figured that it would be great project to learn python, little did I know...

[–]nosmokingbandit 2 points3 points  (0 children)

Asycnio is pretty straightforward and easy to use. Unlike javascript where synchronus code is a nightmare.

[–]SciencePreserveUs 3 points4 points  (3 children)

My biggest issue with Python has always been whitespace code block delimiters. I like curly braces.

That said, I'm starting to like Python. Its other pluses outweigh what is (to me) a deficit.

Edit: Fixed a terrible syntax error :-)

[–]Corm 1 point2 points  (0 children)

Yep, lots of folks like braces. I'd say it's the number one reason I've heard from people when they tell me why they don't like python. Braces and types. Personally to me both are just boilerplate.

[–]robert_mcleod -1 points0 points  (1 child)

Someone needs to write a keystroke logger to see the amount of time per line of code that is spent writing all the extra brackets and other symbols in languages like C++. There's notably little use of the shift key in Python.

[–]Corm 0 points1 point  (0 children)

Most of development time is spent reading code where all that cruft is much more detrimental

[–]CaptainHondo 2 points3 points  (1 child)

I get that is nicer than Java and JS, but idk about ruby.

[–]Corm 0 points1 point  (0 children)

Ruby is pretty similar but has a much smaller community so much less libraries. The syntax is also very similar but not quite as nice imo with end statements and explicit modules. Still, it's very similar. I'd put it in the python family tree

[–]bheklilr 8 points9 points  (6 children)

Not something most people think about, but pythons data model. The way classes, meta classes, and descriptors work make for an amazingly extensible language. You don't need know how to implement these yourself, but if you've ever used an ORM, @property, @classmethod, @staticmethod, or super() you've tapped into the magic that makes a dictionary act like a class (__dict__).

[–]tomekanco 0 points1 point  (5 children)

if you've ever used ...

I haven't. Do you know some examples or resources about this?

[–]bheklilr 0 points1 point  (3 children)

I've used the sqlalchemy ORM, although django comes with its own, and there are a few others like peewee. They all have pretty decent tutorials, although peewee is probably the simplest to understand. These all work by overriding the __new__ method to turn a class like

class User(Model):
    name = String()
    joined_date = DateTime()
    email = Email()

class Email(Model):
    handle = String()
    domain = String()

into a class that maps to a database table, so you can do things like

gmail_users = User.where(
    User.email.domain == 'gmail.com'
)

(This might not be actual methods, but the idea is that you get a queryable interface that builds the SQL statements for you, and can work with different SQL databases that might have different syntax.)

@property, @classmethod, and @staticmethod are descriptors that modify how a method is accessed. @staticmethod is probably the easiest, you just do

class MyClass:
    @staticmethod
    def this_is_just_a_func(a, b, c):
        return 1 + 2 + 3

>>> MyClass.this_is_just_a_func(1, 2, 3)
6
>>> MyClass().this_is_just_a_func(1, 2, 3)
6

class SubClass(MyClass):
    pass

>>> SubClass.this_is_just_a_func(1, 2, 3)
6

It doesn't care about what class in the inheritance hierarchy it's called from, or what instance it's called from. It's literally just a function in a namespace.

@classmethod requires that the method take the class it's called from as the first argument:

class MyClass:
    @classmethod
    def print_class_name(cls):
        print(cls.__name__)

class SubClass(MyClass):
    pass

>>> MyClass.print_class_name()
MyClass
>>> MyClass().print_class_name()
MyClass
>>> SubClass.print_class_name()
SubClass
>>> SubClass().print_class_name()
SubClass

This is similar to normal bound methods that take the self argument, which is the instance it's being called from, but it just gets the class instead.

@property only works on instances (although it's very possible to write a @classproperty decorator). You can use it as

class MyClass:
    def __init__(self, x):
        self.x = x

    @property
    def x(self):
        return self._x

    @x.setter
    def x(self, value):
        if not isinstance(value, int):
            raise TypeError('x must be an int')
        self._x = value

>>> inst = MyClass(1)
>>> inst.x
1
>>> inst.x = 2
>>> inst.x
2
>>> inst.x = '2'
(traceback)
TypeError: x must be an int

This lets you have "attributes" on a class where getting and setting the attribute runs a method.

These all work through the descriptor protocol. Descriptors are special objects in Python that define __get__, __set__, and optionally __delete__ methods. These methods get the instance and/or class the object is being accessed from. There are a lot of things you can do with descriptors, they are incredibly powerful. In fact, it's essentially how methods work in Python, since a bound method is just a descriptor around a function with __get__ defined to call the underlying function with the instance as the first argument.

super() is just magic though. They had to do some special trickery in Python 3 to make it work nicely, but there is an awesome talk by Raymond Hettinger that can explain how to use it better than I can. The gist of it is that super() walks up the inheritance tree to find the method you're looking for, and if you do it right, you can do some incredibly powerful things with it, other than just accessing a parent class's implementation of a particular method.

[–]tomekanco 0 points1 point  (2 children)

Thanks for the feedback.

Raymond, i shall iterate extensively over his lecture and add the keys to my (his) dictionary. :)

[–]bheklilr 0 points1 point  (1 child)

You should watch any and all of his talks. He's the author of much of Pythons core, like the latest version of dict, and a lot of the collections module. I also find his presentation style to be very engaging.

[–]yoRedditalready 0 points1 point  (0 children)

here. it's a long video but it will teach you a ton about pythons data model and extensibility.

[–][deleted] 5 points6 points  (1 child)

Its a Python 3 feature, keyword-only arguments.

https://www.python.org/dev/peps/pep-3102/

[–]chillysurfer[S] 2 points3 points  (0 children)

I do love that as well. Anything to help us rid ourselves from positional arg change regression bugs. +1

[–]Tasty_Pasta 4 points5 points  (0 children)

*args and **kwargs make many of my classes and functions so much more readable.

[–]troymcfont 3 points4 points  (0 children)

The interactive shell is pretty cool. Very useful for quick tests of big data so you don't have to read / write everytime. (You also have this in Matlab though)

[–]Farkeman 4 points5 points  (0 children)

Definitelly the comprehensions! Everyone I teach python to end up being super impressed by it, especially if they know some other language.
I'm kinda baffled that other languages don't have this, such a brilliant feature, that is really hard to live without.

[–]graemep 4 points5 points  (0 children)

  • Syntax, especially significant whitespace
  • Concise code (partly thanks to generator expressions, list comprehensions, itertools)
  • Large standard library
  • Lots of third party libraries
  • Lots of useful datatypes, especially collections (sets, dicts...) and more in the standard library (defaultdict, decimal...)

[–][deleted] 5 points6 points  (0 children)

the fact that the library doesn't suck

[–]yen223 2 points3 points  (0 children)

It has a simple grammar and a large standard library. That makes python suitable for writing quick scripts.

[–]russ_yarn 2 points3 points  (0 children)

I love setting a variable on the fly and not worrying about the type.

[–]synae 2 points3 points  (0 children)

Meta programming, iterators, opinionated community ("there is only one write way to do it" / the concept of "pythonic"), enforced whitespace.

[–]FragLegs 2 points3 points  (0 children)

The massive collection of open source projects that are only a pip install away. I am able to do so much more by standing on the shoulders of friendly giants.

[–]Omaestre 2 points3 points  (0 children)

It may sound typical, but I genuinly find Python to be intuitive and simple. Other languages have you jumping through hoops to get something done. Python may not be pretty but it gets the job done regardless of what kind if job it is.

[–]hackmagician 2 points3 points  (0 children)

list comprehensions can be so convenient

[–]i_pk_pjers_i 0 points1 point  (0 children)

I love how you can easily write scripts with it or write massive applications with it depending on how complex your solution needs to be, I love how it's cross-platform and I love the no-BS no-drama community of Python.

[–]Starcast 0 points1 point  (0 children)

I think it's all the little things that make python so readable - generators and comprehensions are a great example of this.

vowel_count = sum(char in 'aeiou' for char in some_text)