Live data from Hacker News

Understanding Python through its builtins

sadh.life

91–100 of 180 posts

Re: Understanding Python through its builtins

#91
post #90

Earlier quoted context omitted.

Unfortunatly you often can't use -o because of the 3rd party libs that didnt' get the memo and use assert for error checking. We still have -X dev and sys.flags but it's runtime only.

Care to name any 3rd party libs in particular?

Nope, I just remember my server crashing years ago so I stopped ever since.

Would be interesting to scan pypi and check if this is still a thing.

Maybe create a bot warning lib authors.

Re: Understanding Python through its builtins

#92

Earlier quoted context omitted.

Yet Ruby and JS manage to do it somehow. To me it seems natural that join should be a method on the iterable, and I always have to pause to remember Python is different.

I don't think it should be a method at all. It's just a function: join(iterable, separator). It can also be implemented with reduce naturally: `reduce(lambda x, y: x + separator + y, iterable)`.

Reduce sounds like a really slow way to do string building

Re: Understanding Python through its builtins

#93

Earlier quoted context omitted.

The advantage of mutating operations always returning None is that you can easily tell whether a mutation is happening by looking at the code. If you see y = f(x) that means x is unchanged, whereas if you see just f(x) on a line that means something stateful is happening.

I really like ruby's method naming convention for this: * `sort(arr)` returns a sorted copy of the input * `sort!(arr)` returns the sorted original methods that return booleans end in `?`, like `arr.sorted?` It's just a convention, but it's a nice way to let the writer know what will happen.

The ! convention is ok but I don't think it's optimal because, in the presence of higher-order functions and related concepts, it's often not clear if a function should be marked as !.

For example if I have a map function that applies a function f to a sequence, should I call it map! because I might pass in a function f that mutates the input? If so then it seems like any function that takes a function as input, or any function that might call a method on an object, should get marked with ! just in case. But if I don't mark it that way then the ! marking is not as informative: I might end up with a line consisting only of non-! functions which still mutates the input.

Re: Understanding Python through its builtins

#94
post #16

Earlier quoted context omitted.

But that's good. Because a string just needs to know aboit interable to perform that operation whereas every iterable would need to implement it's own join if you had it the other way around.

Yet Ruby and JS manage to do it somehow. To me it seems natural that join should be a method on the iterable, and I always have to pause to remember Python is different.

The way it's managed in JS, digging the function out of the prototype to apply it, can be done in Python as well. But unlike JS you won't normally have to, thanks to the method not being defined only on one specific type of iterable.

JS:

  Array.prototype.join.call(["one", "two", "three"], "|")
Python:

  str.join("|", ["one", "two", "three"])

Re: Understanding Python through its builtins

#95
post #90

Earlier quoted context omitted.

Care to name any 3rd party libs in particular?

Nope, I just remember my server crashing years ago so I stopped ever since. Would be interesting to scan pypi and check if this is still a thing. Maybe create a bot warning lib authors.

Seems like a better idea would be to make a separate flag "--remove-assertions" for people who desire that.

Re: Understanding Python through its builtins

#96
post #68

Earlier quoted context omitted.

> "I value readability and usefulness for real code" Amen. There are plenty of languages where your code ends up looking like an entry in an obfuscation competition without even trying. If you're using Python, and working for me, I expect the code to be readable by anyone. And, no, I don't give a toss whether the code is three times the length it might have been if it was dangerously, and expensively, obscure.

Quite often, chains of map/filter/reduce/whatever are more readable because you can see the flow of data, like you were looking at a factory production line. List comprehensions and traditional prefix functions (e.g. map(iterable, function)) completely break the visual chain that makes basic functional code so readable. Like, which of these make more sense? strList.filter(isNumeric).map(parseInt).filter(x => x != 0)…

> Map,reduce,filter,etc. could simply be added to the iterable base class

Surprisingly, Python doesn't have an iterable base class! `list.__bases__` is just `object`.

Re: Understanding Python through its builtins

#97

Earlier quoted context omitted.

I don't think it should be a method at all. It's just a function: join(iterable, separator). It can also be implemented with reduce naturally: `reduce(lambda x, y: x + separator + y, iterable)`.

Reduce sounds like a really slow way to do string building

Oh yeah, it's horrendous, my point was just that it's functionally equivalent and makes more sense as a function than a method on either object. You can actually call it like this if you want, though: `str.join(separator, iterable)`.

Re: Understanding Python through its builtins

#98
This is a neat article, but it does have some errors.

One subtle point that the post gets wrong:

> So where does that come from? The answer is that Python stores everything inside dictionaries associated with each local scope. Which means that every piece of code has its own defined “local scope” which is accessed using locals() inside that code, that contains the values corresponding to each variable name.

The dictionary returned by `locals()` is not literally a function's local namespace, it's a copy of that namespace. The actual local namespace is an array that is part of the frame object; in this way, references to local variables may happen much more quickly than would be the case if it had to look each variable up in a dictionary every time.

One consequence of this is that you can't mutate the dict returned by `locals()` in order to change the value of a function-local variable.

Another, less-subtle error in the post is this:

> int is another widely-used, fundamental primitive data type. It’s also the lowest common denominator of 2 other data types: , float and complex. complex is a supertype of float, which, in turn, is a supertype of int.

> What this means is that all ints are valid as a float as well as a complex, but not the other way around. Similarly, all floats are also valid as a complex.

Oh, no no no. Python integers are arbitrary-precision integers. Floats are IEEE 754 double-precision binary floating-point values, and as such only support full integer precision up to 2^53. The int type can represent values beyond that range which the float type cannot.

And while it is true that the complex type is just two floats stuck together, I would very much not call it a supertype. It performs distinct operations.

> Accessing an attribute with obj.x calls the __getattr__ method underneath. Similarly setting a new attribute and deleting an attribute calls __setattr__ and __detattr__ respectively.

Attribute lookup in Python is way more complex than this. It's an enormous tar pit, too much so to detail in this comment, but __getattr__ is most often not involved, and the `object` type doesn't even have a __getattr__ method.

Re: Understanding Python through its builtins

#99
post #90

Earlier quoted context omitted.

Unfortunatly you often can't use -o because of the 3rd party libs that didnt' get the memo and use assert for error checking. We still have -X dev and sys.flags but it's runtime only.

Care to name any 3rd party libs in particular?

Starlette, SQLAlchemy.

Re: Understanding Python through its builtins

#100
post #68

Earlier quoted context omitted.

> "I value readability and usefulness for real code" Amen. There are plenty of languages where your code ends up looking like an entry in an obfuscation competition without even trying. If you're using Python, and working for me, I expect the code to be readable by anyone. And, no, I don't give a toss whether the code is three times the length it might have been if it was dangerously, and expensively, obscure.

Quite often, chains of map/filter/reduce/whatever are more readable because you can see the flow of data, like you were looking at a factory production line. List comprehensions and traditional prefix functions (e.g. map(iterable, function)) completely break the visual chain that makes basic functional code so readable. Like, which of these make more sense? strList.filter(isNumeric).map(parseInt).filter(x => x != 0)…

I believe list comprehensions would be much more readable if they were written the other way round:

    [for x in [for s in strList: if isNumeric(s): parseInt(s)]: if x != 0: x]
Nesting them is still ugly, but can often be avoided using an assignment expression:

    [for s in strList: if isNumeric(s): if (x := parseInt(s)) != 0: x]
Post reply on HN