Experiences/guidance on teaching Python as a first programminglanguage

C

Chris Angelico

You want to know why programs written in C are so often full of security
holes? One reason is "undefined behaviour". The C language doesn't give a
damn about writing *correct* code, it only cares about writing
*efficient* code. Consequently, one little error, and does the compiler
tell you that you've done something undefined? No, it turns your validator
into a no-op -- silently:

I disagree about undefined behaviour causing a large proportion of
security holes. Maybe it produces some, but it's more likely to
produce crashes or inoperative codde. The example you cite is a rare
one where it's actually security code; that's why it's a security
vulnerability. If the same operation were flagging, say, that this
value needed to be saved to disk, then the same bug would result in
stuff not getting saved on that platform, which isn't a security risk.

Here's something from CERN about C and security:
https://security.web.cern.ch/security/recommendations/en/codetools/c.shtml

Apart from the last one (file system atomicity, not a C issue at all),
every single issue on that page comes back to one thing: fixed-size
buffers and functions that treat a char pointer as if it were a
string. In fact, that one fundamental issue - the buffer overrun -
comes up directly when I search Google for 'most common security holes
in c code' (second hit, Wikipedia "Buffer overflow" page). Here's
another page listing security concerns:

http://www.makelinux.net/alp/085

First entry: Buffer overruns. Second: File system races. Third:
Improper quoting of shell commands.

Not one of the above pages, nor any other that I came across as I was
skimming, mentioned anything involving undefined behaviour. Every one
of them is issues with properly-defined behaviour - unless you count
specific details of memory layout. If you know that automatic
variables are stored on the stack, then you can blow some buffer and
overwrite the return value. But that's the *attacker* depending on
undefined behaviour, not the *programmer*, who simply has a bug in his
code (something that's able to write more to the buffer than there's
room for).

Python is actually *worse* than C in this respect. I know this
particular one is reasonably well known now, but how likely is it that
you'll still see code like this:

def create_file():
f = open(".....", "w")
f.write(".......")
f.write(".......")
f.write(".......")

Looks fine, is nice and simple, does exactly what it should. And in
(current versions of) CPython, this will close the file before the
function returns, so it'd be perfectly safe to then immediately read
from that file. But that's undefined behaviour. Python does not
guarantee that this will work, so this might work for years and then
break when it's run under Jython, or it might even work in Jython too,
but there's just one specific situation where the file's read
immediately after being written, and that one case fails. I think
that's used at least as often as any of C's pieces of undefined
behaviour.

ChrisA
 
R

rusi

Thanks for this Rusi, I just read it and it describes very well what I
think about our own C course. My choice quote from the beginning would
be "When the irrelevant becomes significant, the essentials become
obscured and incomprehensible."
(BTW is there any reason that the document is repeated twice in the same pdf?)

Thanks for the heads-up -- some pdf generation issues I guess

Is it ok now?

Yeah I could clean up some more formatting some more but its 25 years now and
Ive forgotten my troff!!

More important the tone is not what I would use today.
The point I was trying to make then was:

C is an unsuitable language to TEACH PROGRAMMING WITH because it fills
students' brains with irreleventia

Once one knows the stuff, C is a NEAT programming language.

IOW its a question of learning-curve not the content.
 
D

Dave Angel

Re: Experiences/guidance on teaching Python as a first programming
language



Funny you should say that in the middle of a discussion about
lifetime. In C, when you do the -> thing, you're now in a different
struct with a potentially different lifetime. If p is a local, with
auto lifetime, then so is p.x

So, although the two are mutually exclusive, there's valuable
information hidden in the required choice.
 
C

Chris Angelico

Funny you should say that in the middle of a discussion about lifetime. In
C, when you do the -> thing, you're now in a different struct with a
potentially different lifetime. If p is a local, with auto lifetime, then
so is p.x

So, although the two are mutually exclusive, there's valuable information
hidden in the required choice.

Sure, but you can figure out whether p is a local struct or a local
pointer to some other struct by looking at its declaration. Do you
also need to look at every usage of it? We don't adorn every / with a
marker saying whether we're dividing ints or floats, and that's
something that could be potentially useful (float division of two ints
being what Py3 does). Why adorn pointer usage?

ChrisA
 
N

Neil Cerutti

Sure, but you can figure out whether p is a local struct or a
local pointer to some other struct by looking at its
declaration. Do you also need to look at every usage of it? We
don't adorn every / with a marker saying whether we're dividing
ints or floats, and that's something that could be potentially
useful (float division of two ints being what Py3 does). Why
adorn pointer usage?

Indeed. Golang allows . to do member lookup for both structs and
pointers to structs.

The -> syntax perhaps was needful in the days before function
prototypes.
 
E

Ethan Furman

Only object-oriented languages have *objects*. C does not have objects,
it has values.

The word 'object' has many more meanings than the one implied by Object Oriented Programming, as you well know.
And yes, I'm being pedantic.

No, you're being an ass.
 
R

rusi

The word 'object' has many more meanings than the one implied by Object Oriented Programming, as you well know.
No, you're being an ass.

Is this discussion REALLY happening...??? In a non-programmer/layman forum it
would be completely normal

However given that we are supposedly a programmer list I am incredulous

Here is some innocuous looking python code:

A>

def draw_helper(canvas, level, p1, p2, p3):
if level == 1:
canvas.create_polygon(p1, p2, p3)
else:
p4 = midpoint(p1, p2)
p5 = midpoint(p2, p3)
p6 = midpoint(p1, p3)
draw_helper(canvas, level - 1, p1, p4, p6)
draw_helper(canvas, level - 1, p4, p2, p5)
draw_helper(canvas, level - 1, p6, p5, p3)


And here is what happens when you run it

B>

http://homes.cs.washington.edu/~reges/python/sierpinski8.png
(More here http://homes.cs.washington.edu/~reges/python/)

Can you really say that what you see in B you can infer from A
WITHOUT RUNNING IT??

The above is the subject that is technically called 'complexity' in
math terms. If we allow the term 'complex' to be more general (like
the argument about 'object') then this becomes the pain and beauty,
the mystery and horror of programming -- seemingly trivial code when
seen as a PROGRAM can endlessly evolve into unimaginable complexity
when elaborated into a PROCESS.

So when Chris/Roy are talking of the simplicity of C's lifetime rules they
are talking of the primitive building blocks to make and understand
program-texts.

And when Steven/Devin are talking of the complexity of the same they are
talking of the arcane results that emerge when those programs run.

And from here its a small step to understand why python's slightly
more complicated semantics result in so much less complexity than
C's seemingly simple rules: C has a double complexity generator --
stack + heap vs python only having a 'managed' heap.

Analogously if the Sierpinsky triangle above were flattened into 1-d
there would be nothing to note about it.

Like python: Boring 'weenie' language... Never segfaults
 
M

Mark Lawrence

The C99 standard lists 191 different kinds of undefined behavior,
including what happens when there is an unmatched ' or " on a line of
source code.

No compile-time error, no run-time error, just blindingly fast and
correct (according to the standard) code that does the wrong thing.

Plenty of compile-time warnings depending on the compiler, which the
CPython core devs take a great deal of trouble to eliminate on every
buildbot.
 
G

Grant Edwards

Well, okay. In C you can't have Foo.foo().

If "Foo" is a structure with a field named "foo" that is a pointer to
a function, then you can indeed "have" Foo.foo().
 
G

Grant Edwards

Why? I've never written a compiler. I've written plenty of C. I don't
see how my lack of compiler writing experience has hindered my ability
to write C.

I've always felt that there are features in C that don't make a lot of
sense until you've actually implemented a compiler -- at which point
it becomes a lot more obvious why some thing are done certain ways.

Maybe that's just me. I had written a compiler before I learned C, and
there were things that made perfect sense to me that seemed to confuse
others I worked with who were learning C at the same time.
 
M

Mark Lawrence

I've always felt that there are features in C that don't make a lot of
sense until you've actually implemented a compiler -- at which point
it becomes a lot more obvious why some thing are done certain ways.

Maybe that's just me. I had written a compiler before I learned C, and
there were things that made perfect sense to me that seemed to confuse
others I worked with who were learning C at the same time.

I've never contemplated writing a compiler, let alone actually written
one. It's like the comments along the lines of "you can't call yourself
a programmer until you've mastered regular expressions". Some of my
mates who work on air traffic management systems have maybe never heard
of a regex but who cares, I certainly don't.
 
D

Dave Angel

Re: Re: Experiences/guidance on teaching Python as a first
programming language


Sure, but you can figure out whether p is a local struct or a local
pointer to some other struct by looking at its declaration. Do you
also need to look at every usage of it?

C is a glorified macro assembler. So the -> operator is not
analogous to the dot operator, it's Syntactic sugar:

p-> a. Is really
(*p).a
 
T

Terry Reedy

We don't know what locals()['spam'] = 42 will do inside a function,

I am mystified that you would write this. Locals() will "Update and
return a dictionary representing the current local symbol table." The
only thing unspecified is the relation between the 'current local symbol
table' and the *dict* that 'represents' it. Given that a dict is
returned, the rest is unambiguous.
unlike the C case, we can reason about it:

- it may bind 42 to the name "spam";

"somedict['spam'] = 42" will do exactly that.
- it may raise a runtime exception;

Absolutely not.
- it may even be a no-op;

Absolutely not.
 
O

Oscar Benjamin

We don't know what locals()['spam'] = 42 will do inside a function,

I am mystified that you would write this. Locals() will "Update and return a
dictionary representing the current local symbol table." The only thing
unspecified is the relation between the 'current local symbol table' and the
*dict* that 'represents' it. Given that a dict is returned, the rest is
unambiguous.

It's not unambiguous. The full wording is:
'''
locals()

Update and return a dictionary representing the current local symbol
table. Free variables are returned by locals() when it is called in
function blocks, but not in class blocks.

Note:
The contents of this dictionary should not be modified; changes may
not affect the values of local and free variables used by the
interpreter.
'''

The part that says "changes may ..." is deliberately ambiguous; the
author didn't want to impose too strong a constraint on any particular
implementation.
unlike the C case, we can reason about it:

- it may bind 42 to the name "spam";

"somedict['spam'] = 42" will do exactly that.

That's not what is usually meant by "name-binding".
Absolutely not.
Agreed.


Absolutely not.

Incorrect. The code in question is:

locals()['spam'] = 42

and it is semantically a no-op. The index assignment on a temporary
dict may actually be performed by e.g. the CPython interpreter but it
is really just dead code.


Oscar
 
D

Dennis Lee Bieber

Is this discussion REALLY happening...??? In a non-programmer/layman forum it
would be completely normal

However given that we are supposedly a programmer list I am incredulous

Here is some innocuous looking python code:

A>

def draw_helper(canvas, level, p1, p2, p3):
if level == 1:
canvas.create_polygon(p1, p2, p3)
else:
p4 = midpoint(p1, p2)
p5 = midpoint(p2, p3)
p6 = midpoint(p1, p3)
draw_helper(canvas, level - 1, p1, p4, p6)
draw_helper(canvas, level - 1, p4, p2, p5)
draw_helper(canvas, level - 1, p6, p5, p3)


And here is what happens when you run it

B>

http://homes.cs.washington.edu/~reges/python/sierpinski8.png
(More here http://homes.cs.washington.edu/~reges/python/)

Can you really say that what you see in B you can infer from A
WITHOUT RUNNING IT??

That the code recursively draws a triangle made of smaller triangles...
Yes, I could figure that out from the code without running it (nor even
noticing the particular file name of the image linked).
 
R

Roy Smith

Grant Edwards said:
I've always felt that there are features in C that don't make a lot of
sense until you've actually implemented a compiler -- at which point
it becomes a lot more obvious why some thing are done certain ways.

Example?

I suspect what you mean is, "There are some things that don't make sense
until you understand computer architecture".
 
R

Roy Smith

Mark Lawrence said:
I've never contemplated writing a compiler, let alone actually written
one. It's like the comments along the lines of "you can't call yourself
a programmer until you've mastered regular expressions".

Who makes comments like that? As far as I can tell, I'm the resident
regexphile on this newsgroup, and I certainly don't say that.

I find it frustrating that Pythonistas shy away from regex as much as
they do. Yes, Python strings have a rich set of built-in operations
which provide easy ways to do a lot of things traditionally done with
regexes in other languages.

Regex is a powerful tool, and programmers will improve their skill set
by knowing how to use them. But that's not the same as saying you can't
be a programmer if you don't know regex.
 
R

Roy Smith

Grant Edwards said:
If "Foo" is a structure with a field named "foo" that is a pointer to
a function, then you can indeed "have" Foo.foo().

Sigh. This has gone off in a direction I never intended.

What I meant was that in C++, when you write call a method by name, it
can sometimes be difficult to know exactly what method is being called.
Between inheritance, optional parameters, automatic type promotion,
default constructors, and maybe a few other things I've forgotten, even
if you've got all the signatures of foo() in front of you, it can
sometimes be hard to figure out which one the compiler will pick.

And that sort of confusion never happens in C.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,754
Messages
2,569,525
Members
44,997
Latest member
mileyka

Latest Threads

Top