[HN Gopher] Understanding Python through its builtins
___________________________________________________________________
Understanding Python through its builtins
Author : tusharsadhwani
Score : 327 points
Date : 2021-10-10 15:13 UTC (7 hours ago)
(HTM) web link (sadh.life)
(TXT) w3m dump (sadh.life)
| d_burfoot wrote:
| Great article. This is what I come to HN to find.
| DangitBobby wrote:
| Very well written. Fun little tidbit, Django abuses the fact that
| bools are ints in it's partition util:
|
| https://github.com/django/django/blob/01bf679e59850bb7b3e639...
| int_19h wrote:
| It does make for some amusing code golf techniques, e.g.:
| print([ f"{(not x % 3) * 'Fizz'}{(not x % 5) *
| 'Buzz'}" or x for x in range(1, 20) ])
| hultner wrote:
| Where? I don't see anything in that code relying on that unless
| I'm Sunday blind.
| iezepov wrote:
| `results[predicate(item)]` here they get the first and the
| second elements of a tuple. Essentially it's `results[False]`
| and `results[True]`
| hultner wrote:
| Oh, that's obvious now. Must be that Sunday night
| blindness.
| NegativeLatency wrote:
| Results has 2 elements indexed by 0 and 1 (it's a tuple not a
| dict)
| matsemann wrote:
| > List comprehensions are basically a more Pythonic, more
| readable way to write these exact same things
|
| More pythonic maybe, but you can't have more than a single
| expression in a list comprehension without it becoming completely
| unintelligible. I also often miss other standard list features.
| Reduce, flatmap, indexed versions, utils like first of predicate,
| split, filternonnull etc
| weatherlight wrote:
| Anything remotely interesting like that is dumped in itertools.
|
| Python's creator, Guido van Rossum, doesn't like
| functional/functional-ish programming a lot. That's well-known.
|
| Guido: "I value readability and usefulness for real code. There
| are some places where map() and filter() make sense, and for
| other places Python has list comprehensions. I ended up hating
| reduce() because it was almost exclusively used (a) to
| implement sum(), or (b) to write unreadable code. So we added
| built-in sum() at the same time we demoted reduce() from a
| built-in to something in functools (which is a dumping ground
| for stuff I don't really care about :-)."
| matsemann wrote:
| Yes, lots of them are available. But I also would like to be
| able to call .sum() on my iterable at the end of a chain,
| instead of having to mentally unwrap
| sum(map(filter(filter(map(...))))
| aasasd wrote:
| I bet a Clojure-style 'threading' arrow function can be
| easily done in Python. arrow(my_list [map
| args] [filter args] [filter args] [map args] [sum])
|
| For cases when function argument positions differ, you'll
| need some special var in your module to signal where to
| inject the list. arrow(my_list [map
| map_func arrow.list_here])
|
| Won't be surprised if something like this already exists,
| but I can't think of keywords to search for that aren't too
| generic.
| dragonwriter wrote:
| > But I also would like to be able to call .sum() on my
| iterable at the end of a chain
|
| Yeah, one thing I like about Ruby over Python is the fluent
| code th former allows of that style.
|
| People talk about Guido not liking functional style, but
| that explains comprehensions over map/filter, but not
| function-sum() over method .sum().
| weatherlight wrote:
| I strongly dislike python, I often wonder if it would have
| been as popular if Guido didn't work at Google early on.
| tored wrote:
| Why do you strongly dislike Python? Language design,
| standard library, community, leadership?
| int_19h wrote:
| Guido joined Google in 2005, and it was already plenty
| popular by then.
| glaucon wrote:
| FWIW Python was fourteen years old when GVR joined the
| Borg. That doesn't address how popular it was but I think
| it's reasonable to say it was well established.
| int_19h wrote:
| But you only ever need to unwrap one sum(). And with
| sequence comprehensions, what you get instead is sum(...
| for ... if ... for ... if ...) - I don't think that's
| improved by rewriting it as (...).sum().
| glaucon wrote:
| > "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.
| weatherlight wrote:
| if you come from a imperative background and everyone is
| used to working in imperative languages... I guess thats
| _anyone_.
|
| Someone coming from, say ruby, or javascript would find
| list comprehension jarring. You can't compose them and you
| basically have to rewrite them when its time to extend
| them.
|
| readable by anyone is pretty subjective.
| franga2000 wrote:
| 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)
|
| [ x for x in [ parseInt(s) for s in strList if isNumeric(s)
| ] if x != 0]
|
| filter(map(filter(strList, isNumeric), parseInt), lambda x:
| x != 0)
|
| And it's not like Python doesn't have the language features
| to implement the first pattern. Map,reduce,filter,etc.
| could simply be added to the iterable base class and be
| automatically usable for all lists, generators and more.
| pansa2 wrote:
| 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]
| pansa2 wrote:
| > _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`.
| kgm wrote:
| 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.
| wizzwizz4 wrote:
| The "complex > real > int" thing is true in mathematics. In
| Python, `bool` inherits from `int`.
| kgm wrote:
| Yeah, but we're not talking about pure mathematics. We're
| talking about floats, and I find that it's very important to
| be clear about the limitations. It's easy to get some nasty
| bugs if you start assuming that you can cram just any int
| into a float.
|
| And I have no objections to the article's description of the
| bool type.
| tyingq wrote:
| In a somewhat similar way, this post about hacking the import
| system to load modules from strings finally helped me understand
| how imports work:
|
| https://cprohm.de/blog/python-packages-in-a-single-file/
| collsni wrote:
| You know what helped me with python? Breakpoints inside vscode's
| module. It all just kinda clicked.
| yuy910616 wrote:
| python actually has a build-in `breakpoint()` function. I think
| it brings up a repl at the line.
|
| I've been using that instead of print debug, it's been great.
| int_19h wrote:
| It's actually customizable via the PYTHONBREAKPOINT
| environment variable and sys.breakpointhook(). The default
| does pdb.set_trace(), which gives you a built-in debugger
| prompt (not a REPL) at that location. But it can be set to
| execute arbitrary code, and most Python IDEs make it behave
| like "normal" breakpoints.
| Pearse wrote:
| This is such a good write up for someone like myself still trying
| to get their head around Python.
|
| I can appreciate this must have taken considerable effort, It
| reads really well. Thank you!
| tusharsadhwani wrote:
| I was concerned if my writing style will click with people,
| this is good to know :)
| submeta wrote:
| Nicely written article! - Slightly off topic: I love seeing and
| reading Python code. Used to see many flaws in the language
| (things like `.append` changing the object, returning `None`
| instead of creating a copy and returning that), but after ten
| years of working with Python I really appreciate its versatility,
| it's ubiquitous availability, the large number of libraries and
| the community. There's nothing I can't solve with it. It's the
| swiss army knife in my pocket, available whenever I need to solve
| something by coding.
| brundolf wrote:
| > Used to see many flaws in the language (things like `.append`
| changing the object, returning `None` instead of creating a
| copy and returning that)
|
| I think it's pretty off-base to call this a "flaw". Immutable
| structures have their place and can be very helpful where
| appropriate, but making its core primitives work this way is
| far outside the scope or the philosophy of Python. If you want
| otherwise, you're really wanting an entirely different
| language. And there's nothing wrong with that! But I think it
| would be a "flaw" for Python to make these operations
| immutable, even though I love immutability personally.
| radarsat1 wrote:
| > (things like `.append` changing the object, returning `None`
| instead of creating a copy and returning that)
|
| This would be horrendously inefficient without immutable data
| structures like Clojure's. Very few languages have that, so
| it's a strange assumption to make, especially for a language as
| old as Python.
|
| Although it is a very nice feature of Clojure.
| l33tc0der wrote:
| One of the many reasons why I love Clojure so much!
|
| Rich implement his own brand of persistent data structures
| which makes Clojure's immutability a lot more efficient.
| radarsat1 wrote:
| Thanks yeah, _persistent_ is the term I was looking for,
| did not come to mind as I haven 't used Clojure in a few
| years.
| Blikkentrekker wrote:
| It should also be worth nothing that _Clojure_ sacrificed
| quite a bit to make this as efficient as possible.
|
| "persistent vectors" are certainly an interesting data
| structure that strike a compromise between fast indexing and
| being able to relatively quickly create a copy where only one
| element changes, but it's a compromise and indexing is made
| slower to allow for the latter. -- They also take up more
| memory on their own but are allowed to share memory with
| their copies.
|
| I will say that my ideal language contains them in the
| standard library alongside standard vectors that index in
| constant time.
|
| Further, it should be noted that much of the performance talk
| is on the assumption that accessing from memory is truly
| random access; -- with the existence of c.p.u. caches that
| assumption is not entirely accurate and accessing from
| contiguous rather than scattered memory in practice is
| considerably cheaper so one also pays the price for their
| being scattered more in memory.
| adrusi wrote:
| Random access into a clojure vector is going to need more
| memory lookups than conventional sequential buffer array (I
| don't recall the constants used in the implementation, I
| think it's either 4 or 8 lookups).
|
| But when you're indexing into the vector sequentially, the
| memory layout plays rather well with memory caching
| behavior, and most lookups are going to be in L1 cache,
| just like they would be in a conventional array.
|
| So lookups are a bit more expensive, but not as much more
| expensive as one might imagine.
| Blikkentrekker wrote:
| How? I don't see how that's possible.
|
| The actual data of a Pvector is not in contiguous memory
| but scattered however the _JVM_ wills it, and on top of
| that in order to find which address to retrieve it, an
| algorithm that runs in logarithmic time with respect to
| the length of the vector must be used opposed to a
| constant time one.
|
| How can most lookups end up in L1 cache if an element
| that is 32 indices removed is statistically likely to be
| arbitrarily far removed in memory?
|
| Of course, all of that is not that material to begin with
| given that most elements will be pointers to begin with
| so the actual objects wither they point will already be
| arbitrarily scattered and it simply adds one more pointer
| indirection, but for unboxed types such as integers it
| does play a factor.
| tusharsadhwani wrote:
| Thank you!
|
| Things like `list.append` modifying in-place might feel like a
| flaw to some, but I think Python is really consistent when it
| comes to its behaviour. If you ask a person who comes from an
| object-oriented world, they'll say it only makes sense for a
| method on an object to modify that object's data directly.
|
| There's always ways to do things the other way, for example you
| can use x = [*x, item] to append and create a new copy, while
| being quite a bit more explicit that a new list is being
| created.
| int_19h wrote:
| One related area where Python is not consistent is operators
| like +=.
|
| In pretty much all other languages that have them, the
| expected behavior of A+=B is exactly the same as A=A+B,
| except that A is only evaluated once. Now lets look at lists
| in Python: xs = [1, 2] ys = xs
| ys = ys + [3] print(xs, ys)
|
| This prints [1, 2] [1, 2, 3], because the third line created
| a new list, and made ys reference that. On the other hand,
| this: xs = [1, 2] ys = xs ys
| += [3] print(xs, ys)
|
| prints [1, 2, 3] [1, 2, 3], because += changes _the list
| itself_ , and both xs and ys refer to that same list.
|
| (Note that this is not the same as C++, because in the
| latter, the variables store values directly, while in Python,
| all variables are references to values.)
|
| The worst part of it is that Python isn't even _self_
| -consistent here. If you only define __add__ in your custom
| class, you can use both + and += with its instances, with the
| latter behaving normally. But if you define __iadd__, as list
| does, then you can do whatever you want - and the idiomatic
| behavior is to modify the instance!
|
| For comparison, C# lets you overload + but not +=, and
| automatically synthesizes the latter from the former to
| enforce the correct behavior.
| pokepim wrote:
| Huh I never even thought we would need to create copy of an
| object when adding new item to it (like a new item to list
| for example). Is there any drawback on doing that in standard
| pythonic way? I actually learned to program using Python and
| it was my first language. Since then I only used JS. In both
| I like using functions a lot and rarely dabble in OOP since
| it is more conveniet to me.
| tyingq wrote:
| You can control the behavior manually, like:
| first = [0,1,2] second = [*a,3] # first is unchanged,
| second = [0,1,2,3]
|
| Or second=itertools.chain(first, [3]), which avoids the
| copy.
|
| Though, to me, it's asking for trouble later.
| [deleted]
| sgeisenh wrote:
| You often lose performance in traditional imperative
| languages when aiming for persistence.
|
| When you have immutability guarantees (like in many
| functional programming languages like ML or Haskell) you
| can avoid making copies by sharing the parts of the data
| structure that don't change.
|
| If this kind of thing interests you, you should check out
| Chris Okasaki's book "Purely Functional Data Structures".
| eximius wrote:
| "avoid making copies" dors not always equal
| "performance". Depending on your access patterns, having
| the data colocated can be more important.
|
| But immutability sure is nice when you can have it.
| tusharsadhwani wrote:
| whether mutating data is better than creating a new copy
| for everything is a really long debate about immutability
| and functional programming, with good points on either
| sides, but that's really beyond the point here.
|
| In my opinion, you should use whichever method makes your
| code easy to read and understand for your usecase.
| tyingq wrote:
| >Python is really consistent when it comes to its behaviour
|
| True, though you end up with things like: '
| '.join(thelist)
|
| Instead of thelist.join(' ')
|
| Because of the somewhat aggressive mantra to be consistent.
| pdonis wrote:
| In the cases you give, the original list is not being
| mutated; a new object (a string, not a list) is being
| created. So it does make sense not to have it be a method
| call on the list.
| Marazan wrote:
| 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.
| goatlover wrote:
| 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.
| aidos wrote:
| How does that work? Don't you have to effectively convert
| your general iterable to an array and then join on that?
| Array.from(iterable).join(...)?
| globular-toast wrote:
| 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)`.
| Redoubts wrote:
| Reduce sounds like a really slow way to do string
| building
| globular-toast wrote:
| 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)`.
| [deleted]
| ptx wrote:
| 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"])
| [deleted]
| robluxus wrote:
| It's more about flexibility than consistency.
|
| str.join() and bytes.join() can support all iterable type
| arguments.
|
| Better than trying to implement a join (or two) on all
| iterables.
| Alex3917 wrote:
| > things like `.append` changing the object, returning `None`
| instead of creating a copy and returning that
|
| The obvious question is why it can't return a reference to the
| list instead of returning None. I feel like if I've been using
| the language on an almost daily basis for ten years now and I
| still get burned by that all the time, then it's just a poorly
| designed feature.
| canjobear wrote:
| 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.
| brundolf wrote:
| Agreed. JavaScript's Array.sort is an example of this. Most
| of JavaScript's other array methods return a new array and
| people get used to chaining them, but sort mutates the
| array and also returns a reference to it. You can actually
| get pretty far before being bitten by this so long as
| you're sorting already-copied arrays. But then one day you
| hit a bizarre bug caused by behavior that's been sneaking
| past your radar the whole time.
| macintux wrote:
| There are counter-examples: functions that return values
| while also having side effects, cases where the developer
| simply made a mistake.
|
| Coming from a language with baked-in immutability, Python's
| behavior in this regard was very difficult to get used to.
| Xavdidtheshadow wrote:
| 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.
| canjobear wrote:
| 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.
| zarzavat wrote:
| map! would mean a function that performs a map in-place
| on the array by replacing the values. So it would depend
| on if the callback was encouraged to mutate the array or
| discouraged from doing so.
| dmurray wrote:
| I'll just point out that this is originally from Scheme
| (I think... Maybe Scheme got it from a previous Lisp) but
| borrowed by Ruby. Neither Scheme nor Ruby do a perfect
| job with sticking to the naming convention, at least if
| we include popular libraries, but it is very handy and
| intuitive.
|
| https://stackoverflow.com/a/612588
| aftbit wrote:
| random.shuffle() has bitten me that way a few times too:
| array = random.shuffle(array)
|
| because I expected it to return a copy or reference, instead
| making my array None.
|
| It would also enable chaining operations:
| array = array.append(A).append(B).sort()
|
| In-place vs immutable copy is a language design choice with
| tradeoffs on both sides, but there's no reason that I can see
| to not return a reference to the list.
|
| Perhaps recognizing this is really the job of an external
| linter. Sometimes I wonder if the future of enforcing
| canonical formatting on save like "gofmt" or "black" will
| extend to auto-correcting certain goofy errors on each save.
|
| mypy would yell at you about this, but afaik type-checked
| python still isn't the norm.
| int_19h wrote:
| In Python, a function that makes and returns a copy would
| be idiomatically named shuffled(). Consider sorting:
| xs.sort() # in-place ys = sorted(xs) # copy
|
| As for functions returning the object - I think it's a hack
| around the absence of direct support for such repetition in
| the language itself. E.g. in Object Pascal, you'd write:
| with array do begin append(A);
| append(B); sort; end;
|
| Or better yet, in Smalltalk: array
| append(A); append(B); sort.
|
| https://en.wikipedia.org/wiki/Method_cascading
| eximius wrote:
| In addition to this, I highly recommend just reading the
| codebase. I haven't written C since college and it's remarkably
| readable.
|
| I once tried to catalogue all the stdlib operations which release
| the GIL, meaning if you use only those (well, only those "heavy"
| bits, you can still use other small blocking glue bits), you can
| do "real" multithreading.
|
| It was a fun exercise!
| hultner wrote:
| There's a really nice (although old now) walk through of the
| cpython code base on YouTube. I watched it on a long 24 hour
| flight between Canada and Sweden a couple of years back.
|
| Edit: Found it! You're in for about 9 hours of quality
| watching.
| https://youtube.com/playlist?list=PLwyG5wA5gIzgTFj5KgJJ15lxq...
| submeta wrote:
| Excellent, thanks for sharing!
| dharmab wrote:
| Agreed. CPython makes readability and maintainability a
| priority.
| matheusmoreira wrote:
| > I highly recommend just reading the codebase
|
| Me too. When I used to write Ruby I read a lot of CRuby source
| code. I achieved a much deeper understanding of the language
| that way. Even answered some really fun stackoverflow
| questions.
|
| Now the first thing I do when I see a new language is read its
| source code.
| riazrizvi wrote:
| And it helps much more in the actual job of writing software
| than in say, practicing coding puzzles
| PostThisTooFast wrote:
| "Builtins?"
| escanor wrote:
| > for index, item in enumerate(menu):
|
| should be
|
| > for index, item in enumerate(menu, start=1):
|
| for the example to be correct :)
| sireat wrote:
| I am a pretty average Python programmer(5 years teaching, 15
| years writing).
|
| I still wonder what was the reasoning for allowing creation of
| local objects with the same name as builtins.
|
| Okay it can be nice to redefine pprint as print I suppose.
|
| Still how many sum, list, min, max, dict(!) have been erroneously
| redefined in beginner tutorials and beginner code.
|
| From my experience sum and list suffer the most.
|
| Sure there are linters that will warn you but there should be a
| setting for the interpreter (as in -Werror in GCC) to disallow
| this silliness.
| nneonneo wrote:
| Python itself doesn't disallow this because there are quite a
| lot of builtins with useful names - for example, `file`, `id`,
| and `hash` to name a few. Disallowing setting these would be
| tantamount to adding a bunch of new keywords to the language,
| which they've been quite loathe to do in general.
|
| A good linter will catch these, so in production environments
| you usually don't run into issues. I agree that it can be a
| beginner trap though!
| wpietri wrote:
| No idea what their actual reasoning is, but here's how I think
| about it:
|
| This is better for novices, because otherwise you create a
| whole bunch of land mines for people who are desperately trying
| to get something done. If they aren't aware of the built-in
| then they aren't trying to use it. Insisting that they become
| aware of something they don't want right then will be
| frustrating.
|
| It's also better for experts, in that they're generally aware
| they're overriding a built-in and are doing it on purpose, and
| if not they'll have an IDE or linter reminding them.
|
| To me, I see tooling as a spectrum from supportive to
| controlling. Python is very much on the supportive end. It
| feels controlling when I get interrupted because some
| programmer who has never met me programmed a tool to insist I
| do things their way. That would very much include insisting I
| respect a bunch of names they decided long ago to put in the
| global namespace.
| int_19h wrote:
| Consider what'd happen whenever a new builtin gets added.
| tusharsadhwani wrote:
| I think linters are really effective to figure out such issues
| in professional code.
|
| For students for example, I'll have to agree. Maybe having a
| flag or environment variable that teachers can set up for it
| would be a nice idea. You should start a thread on the python-
| ideas mailing list about this, and it might get somewhere :)
| ps173 wrote:
| I am not even half way through it and I now understand how python
| actually works under hood. This is great for understanding how a
| lot of interpreted languages work
| nneonneo wrote:
| Cute fact about __debug__: it is one of the only ways to get
| compile-time conditionals in Python. Performing a comparison with
| `if __debug__:` will output byte code for the ensuing statement
| if and only if the interpreter is in debug mode - notably, in
| `-O` mode, it will not even generate a load of __debug__ and a
| conditional jump, and acts as if the statement didn't exist at
| all.
| BiteCode_dev wrote:
| 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.
| alshel wrote:
| Care to name any 3rd party libs in particular?
| human_error wrote:
| Starlette, SQLAlchemy.
| BiteCode_dev wrote:
| 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.
| zarzavat wrote:
| Seems like a better idea would be to make a separate flag
| "--remove-assertions" for people who desire that.
| tusharsadhwani wrote:
| that is indeed interesting. Mind if I add this in the article?
| globular-toast wrote:
| I knew that `assert` behaved in a similar way. Turns out it's
| actually equivalent to an `if __debug__:`
| https://docs.python.org/3/reference/simple_stmts.html#gramma...
| Fordec wrote:
| Not going to lie, if I could enable stricter compile time
| conditions without debug mode that would be a very welcome RFC
| cinntaile wrote:
| Are there any good books that deal with writing pythonic code? As
| well as being focused on more intermediate or advanced features
| like this? If the book is project focused that's a bonus.
| Performance trade-offs another bonus.
| tusharsadhwani wrote:
| I can personally recommend Fluent Python (its 2nd edition is
| about to come out in a couple months) for learning these
| intermediate/advanced concepts, and Python Cookbook for code
| examples using many of these features.
|
| I don't know any books for projects per-se, maybe HN will know!
| [deleted]
| usrme wrote:
| I would recommend "Robust Python" by Patrick Viafore. It
| teaches you a lot about type annotations (among other thing)
| and gave me personally a whole new way of looking at the code
| that I write.
| tracyhenry wrote:
| Searching python on HackerNews readings (a site I built):
| https://hacker-recommended-books.vercel.app/category/0/all-t...
| The results include _Fluent Python_ recommended by
| tusharsadhwani
| disgruntledphd2 wrote:
| Fluent Python is definitely a good book, I knew a whole bunch
| of the stuff in this article because of it. I only got to
| Chapter 9, but it legitimately made my Python much, much
| better.
| dehrmann wrote:
| In a way, learning Python is harder for people experienced with
| another language because so much of the content you find is for
| first-time programmers.
|
| That said, I think I had good luck with Writing Idiomatic
| Python.
| matsemann wrote:
| Yes!
|
| When I learned Kotlin, I just read through the docs, and then
| knew of basically all the different concepts in the language.
|
| For Python, the docs were comparably very bad. For instance,
| Decorators aren't mentioned even once in the "The Python
| Tutorial". In "The Python Language Reference" (if one even
| bother to read such a dry document) it's barely mentioned in
| passing. How should a new user know it's a concept and how to
| apply it? And the language reference links only to a glossary
| item, and none of them specify how parameters in a decorator
| is supposed to work.
|
| Pretty frustrating experience, put me a bit off the language
| from the get-go.
| tored wrote:
| As a non-Python dev I tried the other day to read up how
| decorators work in Python using the docs, can't say it
| helped.
| mattficke wrote:
| Effective Python [0] is my favorite book in this category.
|
| [0] https://effectivepython.com/
___________________________________________________________________
(page generated 2021-10-10 23:00 UTC)