
Learn Python 3 the Hard Way - jscholes
https://learnpythonthehardway.org/python3/
======
crncosta
This guy has a very stranger way to promote his job.

He firstly said that Py3 sucks and he can't teach it... and now he is teaching
it ;-)

[0]
[https://learnpythonthehardway.org/book/nopython3.html](https://learnpythonthehardway.org/book/nopython3.html)

~~~
iodbh
Which turned into a big internet fight, and he changed his mind.

It's good to see people willing to change the way they do things, especially
when that means publicly contradicting their former selves and with such an
intense personality. It's oddly inspiring.

~~~
baldfat
I think Zed was in the majority when it came to Py3. He kind of comes off as a
jerk due to his personality, but I really like his approach and he always
makes me think. I might not agree with him but he certainly is not boring.
Looking at Pandas which is a R like library. Wes wrote his book in Py2 and did
not have any desire for Py3.

I moved from Python mostly to using R, Racket and Haxe for my other side
projects. This infighting just left a bad taste in my mouth and started seeing
greener pastures, which for once in my life were in fact greener. I still love
Python but I don't use it much due to community drama.

~~~
BuckRogers
Don't blame the community for that. Blame Guido and the core development team.
The community at-large weren't rallying for breaking changes to the language.
GvR wanted that, it's "his" language afterall. Of course some of the community
are more conservative and were willing to more quickly fall in line to
goosestep with the core team on Python3. So the tension stems from the Python3
folks having extreme disdain for all the Python2 users and codebases.

I agree with you though, it's what got me looking towards other tech like
Node, Go, Elixir. I can look past it all except for the fact they got 'unicode
by default' wrong. They should simply do what Go does, everything is a
bytestring with assumed encoding as UTF8. What they have today is ridiculous
and needs changed before I could embrace Python3+.

~~~
soperj
There were definitely people wanting unicode support, which was the breaking
feature.

They supported python 2 for years and year after the release of this, don't
know how you can even mention the word goosestep in this context.

~~~
BuckRogers
I can't understand what's going on with the Python community for folks to even
make statements like yours. It has to be an influx of new Python developers
who started on 3.

Python2 has unicode support. Python2 already had (better) async IO with Gevent
(Tornado is also there). There's just nothing there with Python3 except what I
can only describe as propaganda from the Python Software Foundation that has
led to outright ignorance with many users.

 _Some_ people wanted "unicode strings by default". Which Python3 does have,
but they even got that wrong. The general consensus on how to handle this
correctly is to make everything a bytestring and assume the encoding as being
UTF8 by default. Then like Python2, it just works.

~~~
int_19h
> The general consensus on how to handle this correctly is to make everything
> a bytestring and assume the encoding as being UTF8 by default.

Whose "general consensus"? It's certainly not the approach adopted by the vast
majority of mainstream general-purpose programming languages out there.

~~~
BuckRogers
You mean most of the mainstream, genpurpose languages in usage today which
were initially designed 20-50 years ago. And those which don't actively look
for opportunities to shoot themselves in the face with sidestepping changes to
break the language? Then no, not that consensus.

But if you were to find any sensible designer of new language with
skyrocketing, runaway popularity- such as Rob Pike, they'll tell you
differently. Or even Guido van Rossum, if he were to be honest with you. While
Pike's colleagues at Bell Labs like Dennis Ritchie may not have designed C
this way for obvious reasons, they did design Go that way.

~~~
int_19h
So now it's the consensus of "sensible designers of new languages". Where
"sensible" is very subjective, and I have a feeling that your definition of it
would basically presuppose agreeing with your conceptual view of strings,
begging the question.

Aside from Go, can you give any other examples? Swift is obviously in the "new
language" category (more so than Go, anyway), and yet it didn't go down that
path.

~~~
BuckRogers
Well, do your research and come to your own conclusions. Most people are going
to agree that UTF8 is the way to go. You can advocate something else, since
you seem to take affront to my opposition to Python3's Microsoft-oriented
implementation.

If you know anything about Swift, it was designed with a primary goal to being
a smooth transition from and interop with ObjC so like other legacy
implementations (such as CPython3), it had sacrifices that limited how
forward-looking it could be.

~~~
int_19h
I'm not at all opposed to UTF-8 as internal encoding for strings. But that's
completely different and orthogonal to what you're talking about, which is
whether strings and byte arrays should be one and the same thing semantically,
and represented by the same type, or by compatible types that are used
interchangeably.

I want my strings to be strings - that is, an object for which it is
_guaranteed_ that enumerating codepoints always succeeds (and, consequently,
any operation that requires valid codepoints, like e.g. case-insensitive
comparison, also succeeds). This is not the case for "just assume a bytearray
is UTF-8 if used in string context", which is why I consider this model broken
on the abstraction level. It's kinda similar to languages that don't
distinguish between strings and numbers, and interpret any numeric-looking
string as number in the appropriate context.

FWIW, the string implementation in Python 3 supports UTF-8 representation. And
it could probably be changed to use that as the primary canonical
representation, only generating other ones if and when they're requested.

~~~
BuckRogers
A default UTF8 string-type has to be allowed to be used interchangeably with
bytestrings since ASCII is a valid subset. Your string type shouldn't be
spellchecking nor checking for complete sentences either. What comes in, comes
in. Validate it elsewhere.

Thus Go's strings don't potentially fail your desire for a guarantee anymore
than anything else would assuming UTF8. They're unicode-by-default, which was
the whole point to Python3 but Go has it too in a more elegant way. That's the
beauty to UTF8 by default, you can pass it valid UTF8 or ASCII since it's a
subset, the context of which it's being received is up to you. If you're
expecting bytes it works, if you're expecting unicode codepoints that works.
There's no reason to get your hands dirty with encodings unless you need to
decode UTF16 etc first. If there is still a concern about data validation,
that's up to you not your string type to throw an exception.

~~~
int_19h
> A default UTF8 string-type has to be allowed to be used interchangeably with
> bytestrings since ASCII is a valid subset.

This only works in one direction. Sure, any valid UTF-8 is a bytestring. But
not every bytestring is valid UTF-8. "Use interchangeably" implies the ability
to substitute in both directions, which violates LSP.

> What comes in, comes in. Validate it elsewhere.

I have a problem with that. It's explicitly against the fail-fast design
philosophy, whereby invalid input should be detected and acted upon as early
as possible. First, because failing early helps identify the true origin of
the error. And second because there's a category of subtle bugs where invalid
input can be combined or processed in ways that make it valid-but-nonsensical,
and as a result there are no reported errors at all, just quiet incorrect
behavior.

Any language that has Unicode strings can handle ASCII just fine, since ASCII
is a strict subset of UTF-8 - that doesn't require the encoding of the strings
to be UTF-8. For languages that use a different encoding, it would mean that
ASCII gets re-encoded into whatever the language uses, but this is largely an
implementation detail.

Of course, if you're reading a source that is not necessarily in any Unicode
encoding (UTF-8 or otherwise), and that may be non-string data, and you just
need to pass the data through - well then, that's exactly what bytestrings are
there for. The fact that you cannot easily mix them with strings (again, even
if they're UTF-8-encoded) is a benefit in this case, because such mixing only
makes sense if the bytestring is itself properly encoded. If it's not, you
just silently get garbage. Using two different types at the input/output
boundary makes it clear what assumptions can be made about every particular
bit of input.

~~~
BuckRogers
I understand your position. This is a longstanding debate within the PL
community, as you know. I've considered that stance before but I have to say
thanks for stating it so well because it's given me pause. I can't say I agree
but you're not "wrong". I agree with the fail-fast concept but disagree that's
the place to do it. A strict fail-fast diehard probably shouldn't even be
using Python, IMO. I don't have anything else useful to add because this is
simply a engineering design philosophy difference. We both think our own
conclusions are better of course but I appreciate you detailing yours. Good
chat, upvoted.

~~~
int_19h
I agree that this is a difference caused by different basic premises, and
irreconcilable without surrendering one of those premises.

And yes, you're right that Python is probably not a good example of
ideologically pure fast-fail in general, simply because of its dynamic typing
- so that part of argument is not really strong in that particular context.

(Side note: don't you find it amusing that between the two languages that
we're discussing here, the one that is statically typed - Go - chose to be
more relaxed about its strings in the type system, while the one that is
dynamically typed - Python - chose to be more strict?)

The key takeaway, I think, is that there is no general consensus on this. As
you say, "this is a longstanding debate within the PL community" (and not just
the topics we touched upon, but the more broad design of strings - there's
also the Ruby take on it, for example, where string is bytes + encoding).

My broader take, encoding aside, is that strings are actually _not
sufficiently_ high-level in most modern languages (including Python). I really
want to get away from the notion of string as a container of anything, even
Unicode code points, and instead treat it as an opaque representation of text
that has various _views_ exposing things like code points, glyphs, bytes in
various encodings etc. But none of those things should be promoted as the
primary view of what a string _is_ \- and, consequently, you shouldn't be able
to write things like `s[i]` or `for ch in s`.

The reason is that I find that the ability to index either bytes or
codepoints, while useful, has this unfortunate trait that it often gives right
results on limited inputs (e.g. ASCII only, or UCS2 only, or no combining
characters) while being wrong in general. When it's accessible via shorthand
"default" syntax that doesn't make it explicit, people use it because that's
what they notice first, and without thinking about what their mode of access
implies. Then they throw inputs that are common for them (e.g. ASCII for
Americans, Latin-1 for Western Europeans), observe that it works correctly,
and conclude that it's correct (even though it's not).

If they are, instead, forced to explicitly spell out access mode - like
`s.get_code_point(i)` and `for ch in s.code_points()` - they have to at least
stop and think what a "code point" even is, how it's different from various
other explicit options that they have there (like `s.get_glyph(i)`), and which
one is more suitable to the task at hand.

And if we assume that all strings are sequences of bytes in UTF-8, the same
point would also apply to those bytes - i.e. I'd still expect `s[i]` to _not_
work, and having to write something like `s.get_byte(i)` or `for b in
s.bytes()` - for all the same reasons.

~~~
BuckRogers
It's worth nothing that I think Python's success and simplicity came from the
ease of use and flexibility. It should have Go's strings. You have a point
there. The type annotations are really jumping the shark too, it's just no
longer the language that once made so much sense.

On the consensus on bytestrings assumed as UTF8, there's only 1 new language
without legacy baggage that has skyrocketing popularity and it has bytestrings
assumed as UTF8. Everyone I've surveyed says that's no coincidence, including
members of the Python core dev team. So that's where I'm seeing consensus on
the string issue. While Python3 has struggled and folks are rightly irritated.
Because some were irresponsible everyone has to pay, that's not really how I
viewed Python's strengths prior to 3.x. Zed said it best but it really was one
of the best examples of how dynamic typing can be fun.

------
Sir_Cmpwn
>I've standardized on this version of Python because it has a new improved
string formatting system that is easier to use than the previous 4 (or 3, I
forget, there were many)

Wait, is this seriously the only advantage you see to Python 3, Zed?

~~~
Vaskivo
Maybe in the context of the book it is a major reason.

Strings are ubiquitous to programming. I'd argue that it's even more for first
time programmers. Zed wanted something that would be easy to use.

(ASCII) Strings formatting is more important to beginners than unicode
support, new style classes by default, iterators in range()/enumerate()/etc. ,
the new division operator, etc.

I haven't recommended his book to beginners because it was stuck in Python 2.
But I have to agree that Python 2 had some stuff that was more "newbie-
friendly" than Python 3

~~~
Sean1708

      > But I have to agree that Python 2 had some stuff that was more "newbie-friendly" than Python 3
    

What stuff, out of interest?

Also I suspect that a _huge_ proportion of newcomer's got very confused as to
why 3/2 == 1.

~~~
Vaskivo
Yes. But that is easy to explain. "It always rounds down"

Of the top of my head come the range/enumerate/zip returns. They are really
useful and needed to teach "pythonic Python". But it's hard to explain that it
doesn't return a list. "It returns an iterator, that is lazy, and you need to
call 'list()' on it to use it as a list..." You get my point.

You fall to a "just do like I told you. You'll get it later." that plagues a
lot of other languages when being taught to newbies.

But I never said that Python 3 is less "newbie-friendly" than Python 2...

~~~
cgriswald
> Yes. But that is easy to explain. "It always rounds down"

The explanation is easy, but it isn't _simple_.

With respect, "It always rounds down" is a terrible explanation. It explains
the apparent behavior of the / operated in Python 2, but it does not really
explain _why_. A better explanation is that the Python is both dynamically-
and strongly-typed and that / does integer division on integers and "true"
division on floats. For the newbie who understands these concepts, this is a
far more useful explanation and metaphor; and for the newbie who doesn't, he
has to learn somewhere, and frankly the / operator is not a bad example.

~~~
webmaven
_> / does integer division on integers and "true" division on floats._

Floats don't really get true division either:

[https://docs.python.org/3/tutorial/floatingpoint.html](https://docs.python.org/3/tutorial/floatingpoint.html)

And as it happens, I once explained a related problem with round() to Zed Shaw
on Twitter:

[https://twitter.com/zedshaw/status/777780480737357824](https://twitter.com/zedshaw/status/777780480737357824)

~~~
amyjess
This is one reason why I go out of my way to avoid floats and instead use
Decimal whenever I need to support non-integral numbers.

(and from playing around with Perl 6, I love how decimal literals are of type
Rat instead of Num; I wish Python decided to do something similar in Python 3)

Edit in response to Zed's question, in 3.6 I always just do:

    
    
        print(f'{foo:.2f}')
    

Works with Decimals and floats.

~~~
webmaven
Yeah, I'm pretty sure he'll turn up his nose at that solution as well.

For purposes of teaching the language to a beginner, he wants literals being
rounded or divided to "just work" in a way that is unsurprising to those
beginners, and there isn't any way to achieve that unless the default type of
a number with a decimal point is Decimal rather than float (which isn't at all
likely to happen).

The closest we are likely to get to that ideal is by introducing a Decimal
literal like 0d2.125, riffing off the Hex and Octal literals (eg. 0xDEADB33F,
0o76510).

P.S., unless I am mistaken, you're just truncating the float, not actually
rounding it

------
tbranyen
If this is anything like his Learn C book, I'll pass. Was a waste of money as
he came off incredibly arrogant and fumbled through it.

~~~
dagw
At least the original LPtHW was quite a fundamentally different book from his
learning C book. The C book was aimed at people who knew at least the basics
of programming in some other language and wanted to learn C, while the Python
book was more aimed people who had never programmed at all.

------
diimdeep
Not hard enough. Try
[https://lectures.quantecon.org/py/](https://lectures.quantecon.org/py/)

~~~
happy-go-lucky
Is that for quants? Seems to be good. Thanks for the link.

~~~
ChristianGeek
No kidding. Thorough, too...the PDF for the Python lectures is 862 pages!

~~~
happy-go-lucky
That's great. In fact I submitted it here at
[https://news.ycombinator.com/item?id=13724295](https://news.ycombinator.com/item?id=13724295)

------
alexeiz
Zed lost his credence. It's hard to take him seriously after what he's said in
the past. Besides, the book is rather light on content.

~~~
renesd
It's actually harmful to point newbies towards his work. There are much better
resources that have been proven in practice to work better.

~~~
dfabulich
Such as?

~~~
dtornabene
well, you can go with the _actual python tutorial_ for one. Or the literally
dozens of intro to python books put out by No Starch, O'Reilly, Packt, Wiley,
etc. You can also go with some of the classics like Swaroops' Byte of Python,
or any of the college lecture notes. I hesitate to suggest this, but its worth
saying, the book that Zed shit on to launch his little franchise is still
actually usable (especially just to read the code examples) and was a much
much much better book in terms of pedagogy then his tedious, arrogant
offerings.

~~~
throwaway7645
Yea...flipped through Zed's book. It seemed alright, but Python Programming
for the Absolute Beginner is an amazing piece of work. Crystal clear, and you
get to build fun little text games.

------
dv35z
Related: I love this book's teaching style, and found it very much resonated
with me - starting from zero, step by step, iteratively learning and building
on previous lessons.

Can anyone suggest something similar for learning Scala?

~~~
solaris999
The canonical source for learning Scala from scratch (at least, when I was
learning a couple of years ago) is the Coursera course by Martin Odersky, one
of the language's founders. It appears that the course has grown somewhat, but
you can still find part 1 here:
[https://www.coursera.org/learn/progfun1](https://www.coursera.org/learn/progfun1)

~~~
dv35z
I took the course - unfortunately, and to be honest, it didn't have the
approachability and "fun" of Zed's books. To me - it felt very dry, academic,
and quickly became a chore to complete rather than a joy. I need the version
aimed for "C- python programmer" / newbie rather than "eager CS student" or
"experienced Java dev".

------
oliwarner
You'll either love or hate his style but one thing you should note is that
popular current production distributions are pegged on older versions (eg
Ubuntu 16.04 has Python 3.5, Debian Jessie has 3.4.

If you're a purist and don't want to maintain your own build of Python (or
trust an external PPA), I'd aim at 3.5.

That said, this is about learning... But you'll need to learn not to use
f-strings if you want to use it on a current-generation LTS distribution.

~~~
mixmastamyk
3.6 is in the repos for yakkety as a beta, and upcoming release on zesty.
Unfortunately it will likely not be the default Py 3.x until the "aardvark"
release.

~~~
oliwarner
I'd advise against using 9m support distributions in production though. It's a
super pain in the arse to have to do major upgrades that often.

~~~
mixmastamyk
9m?

~~~
oliwarner
Sorry. Nine months. That's all the non-LTS versions get.

------
happy-go-lucky
_Python 3.x is the present and future of the language_

~~~
throwaway7645
A language I use daily and love, but one that will end up a dead-end I fear
long-term. So much is going the way of speed now.

~~~
crdoconnor
I doubt that. Most of the cited competitors to python are either seriously
flawed (e.g. go) or aren't really competing on the same turf (e.g.
rust/haskell).

~~~
verandaguy
Speaking as someone who really loves Haskell -- let's be real, it's not
serious competition against Python or Rust, same turf or not. It's a fantastic
language for numerical computing and its applications, and for
compilers/parsers, but it's never going to be as easy to use for the layman as
an imperative language.

As someone who's used Go and who has some mixed feelings about it, I'm curious
-- what do you find flawed in it?

~~~
crdoconnor
>what do you find flawed in it?

* Package management

* The weird and approach to exception handling (which leads to overly verbose code)

* Lack of generics (which leads to overly verbose and somewhat hacky code)

* Vastly smaller 3rd party ecosystem

I think it works well for a very small subset of sysops-style problems (small
servers, small command line tools, etc.) especially since it easily compiles
into one small, portable binary, but outside of that it flounders.

~~~
verandaguy
Thanks for following up! Those are some of my gripes with the language too.
The vendoring system/would-be package manager especially has caused me hours
of headaches, and I'm glad to hear I'm not the only one who considers it
broken.

------
siddharthgdas
If you are a complete beginner in python, I would rather suggest this for
python3 - www.openbookproject.net/thinkcs/python/english3e

------
nof
I would basically avoid this simply by reading his idea of what makes good
educational content: tests, repetition, memorization and boring predetermined
projects. If you know anything about educational theory, you will quickly
discover that constructivist education, or constructionism as Seymour Papert
coined, is a way more effective and motivational way to learn computer science
and coding. There are basic understandings of computering which is beyond
specific language, eg loops or logic. You are not doing yourself any favors by
memorizing the entire syntax of a language. This happens while you do personal
and engaging coding projects.

------
jorge-fundido
I tell people to `ipython; %magic`. For all of python's faults, that REPL is
the gold standard and makes for a nice way to learn the language.

------
BuckRogers
Python3 folks act so daft about his Turing-complete comments. ZS knows what
Turing complete is and means. He was clearly trolling, saying in essence, "if
the CPython team can't create interop between CPython2 and 3 code then they
really botched the job.. Python3 bytecode is Turing incomplete. What a fail."

And then people attempt to troll him saying he doesn't know what Turing
complete is. Perhaps they really don't understand what they're even responding
to, that's what it appears. Or it's just a willful attack because they "like
Python3". Unfortunately with Python3 this is what the Python community has
been slowly devolving into, what comes off as a bunch of trolls/kids or
ridiculous adults.

~~~
thehardsphere
1\. I'd really like to see proof that the people he was trolling actually
existed, and if they did exist, a coherent explanation of why beginners to
Python needed to be exposed to the arguments of said people through trolling.

2\. Zed can troll people, and that's totally OK, but the "like Python 3"
people can't troll him or counter-troll him, because it's turning the Python
community into trollville. I'm guessing like sort of how Rails is a ghetto.

~~~
vonklaus
> I'd really like to see proof that the people he was trolling actually
> existed

See the user of this parent comment.

~~~
thehardsphere
I have never asserted that Python 3 can never run Python 2. The claim is that
Zed trolled some imaginary people who said this.

------
pnt12
[https://learnpythonthehardway.org/book/nopython3.html](https://learnpythonthehardway.org/book/nopython3.html)
>This document serves as a collection of reasons why beginners should avoid
Python 3 as of November 22nd, 2016.

This is the first thing I get when searching "Learn python3 hard way" in
Google. Maybe it's time to update this page :)

I didn't enjoy the python2 book too much, but I'll give this one a try.

------
PuffinBlue
Knew it was coming, but still kinda annoying _right_ in the middle:

[https://josharcher.uk/categories/lpthw/](https://josharcher.uk/categories/lpthw/)

------
mistaken
The hard way is still the easy way though. Python is simple to learn :)

------
edgartaor
In your opinion which one is better? Learn Python the hard way or Dive into
Python 3

[http://www.diveintopython3.net](http://www.diveintopython3.net)

~~~
Orangeair
A lot of people dislike Zed Shaw's books (the author of Learn ___ the Hard
Way) for various reasons, and he has posted some things online that serious
call into question his credibility (namely, claiming that Python 3 is not
Turing complete).

Dive into Python 3 is a good book. I used it to supplement my CS classes in
early college. There are some other good book suggestions on the sidebar of
[https://www.reddit.com/r/Python/](https://www.reddit.com/r/Python/) . In
particular, I've heard good things about "Automate the Boring Stuff with
Python".

~~~
dtornabene
Its a great book, that still has relevance, amazingly. Theres also the fact
that he feels like its necessary to shit on anyone who disagrees with
him...or, anyone at all actually. The trolling thing is lame, and a total
waste of peoples time, like, for example, when he literally ate something in
the middle of his lecture at Pycon, as if he somehow couldn't find the time to
eat a candy bar in the elevator. I literally worked Pycon, and I can guarantee
you that speakers, and pretty much anyone else, can find the two minutes to
choke down an orange or whatever before the go up to speak. Its his persona,
which he uses to get notoriety, which he uses to get work.

------
crispytx
Learn Python The Hard Way was the first programming book I ever read. Beats
the hell out of codeacademy and all that other shit.

------
systems
is this complete or a work in progress, because if you try to buy, you can
still only buy the 3rd edition (with a free upgrade to 4th edition)

so to be clear, is the online version complete, and only the pdf and videos
are missing, or even the online version is still work in progress

------
oli5679
Does anyone have any advice from where to go after beginner/intermediate
python? I can hack together a program with the functionality I want, but
frequently it's pretty verbose and the architecture gets creaky as the
complexity increases...

~~~
yhoiseth
I'd suggest
[http://www.obeythetestinggoat.com/](http://www.obeythetestinggoat.com/) if
you're looking to use Python for web development.

------
drops
For a beginner who wants to learn Python, which one is better — 2 or 3?

~~~
satysin
3\. When you need to work with a 2 code base the learning curve won't be big
at all. Whereas if you start with 2 and then move to 3 you will have to
unlearn a bunch of stuff which is just a waste of time. Start with 3 and then
learn just the differences in 2 when/if you need them.

~~~
cgriswald
Agreed. Just as a for-instance: In Python 2, the parenthesis on a print call
are optional. In Python 3, they're not. Going from 3 to 2 you probably won't
even notice. Going from 2 to 3... To this day I often fail to type those
parenthesis.

------
pbreit
Would a tutorial like this really be worse if it used realistic situations?

------
IshKebab
I'm not sure I would recommend learning Python to anyone these days. Go is
just so much nicer and almost as easy, and also avoids the inevitable GIL &
speed problems.

~~~
bkbridge
If you are doing AI, machine learning, it's all Python. Other language support
yes, but python is it. Alexa does not support Go.

> [https://www.tensorflow.org/](https://www.tensorflow.org/)

~~~
StreamBright
You mean the API that you are calling is in Python. I think the heavy lifting
is mostly C/C++.

