On 02/09/2013 23:04, Andrew Barnert wrote:
> From:Rob Cliffe <rob.cliffe(a)btinternet.com>
> Sent:Monday, September 2, 2013 8:29 AM
>> You seem to have replied just to me, not to the list, which is a pity, because it means this reply is going just to you.
> Your last message was just to me, so I didn't think you wanted it going to the list.
Sorry, my mistake, I'm attempting to copy this to the list now.
>
>>>> What do you think is easier and quicker to write (…
[View More]or indeed, to understand, if you're not a Python expert)? Say at 1am when you're trying to meet a deadline and keeping awake with coffee?
>>> I can write either one easily.
>> OK, you can. (How many years have you been programming in Python?) That does not mean that everybody can. (I can too but maybe not as easily as you. But I might write it in my simple way at first, to get it working, then come back later and smarten it up.)
> Novices looking for help on Stack Overflow can create dictionaries. My coworker who just learned Python over the last two months can. This is in the tutorial, it's not some deep magic for experts only.
>
>>> The question is which one I can write without making a stupid mistake that I'll have to debug six weeks later. Less repetition and less visual noise means fewer places to make a mistake, and easier to spot it when you do.
>
>> But the very repetition (and vertical alignment) mean that many of the possible mistakes stand out like a sore thumb. The human brain is good at seeing patterns.
> Repetitive code is exactly where people make the most mistakes. And if you've ever debugged any serious project, it really can be hard to notice that you used ssock instead of csock in one of eight near-identical blocks of code. If you just write one block of code instead of eight, it's impossible to make that mistake in the first place.
But if the csock s are vertically aligned, the mistake stands out.
>> My version (boring, repetitive, unimaginative ... but simple and straightforward),
> or something involving dictionaries, lambda functions, and having to look up the docs (I didn't actually know about __contains__ or operator.contains) ?
>>> The whole point of programming is using your imagination to eliminate boring, repetitive, and simple tasks.
> Er, no. No. The point of programming varies. And "good" and "bad" code do not exist in isolation, context matters too (commercial environment? academic?).
>
> No, it really doesn't. Except for learning and language-research purposes, when you write a program, it's to accomplish something that otherwise you or another person would have to do manually, which would be tedious, or difficult, or error-prone. You could go through a spreadsheet and count up the number of unique users (column 3) in each state (column 2), but it would take days, and you'd make dozens of mistakes, and you'd be miserable. Or you could write a program in a few minutes or hours.
>
>>>> Obviously this is a judgement call, but I know what my view is.
> As a touchstone: my version could be rewritten in just about any other language with minimal effort, just because it is so simple and uses no "tricks". How many languages could yours be rewritten in as quickly (assuming you're not already an expert in the target language) ?
>>> Putting functions into a dictionary is not some advanced "trick", it's a basic idiom. It's used in the tutorial, the FAQ, the stdlib, etc.
>> Sure it is (in Python). But if I wanted to translate this code into another language (especially in a hurry), I would (as I said before) need minimal knowledge of that language to translate my boring version. (LISP?)
True but is does add one level of abstraction, and one's brain has to go
through that level to understand the code. The more levels of
abstraction, the less intuitive and harder to understand code becomes
(look at Twisted, possibly the ultimate example).
> Why would you need to translate it into another language in a hurry?
Who knows? We live in an unpredictable world; we adapt to it or die.
If I could predict all the things my manager asks me to do ... well, I
guess I wouldn't need a manager.
>
>> We have got a bit bogged down discussing this particular example
> (partly my fault). I was simply trying to make the point that there
> may be circumstances when it is OK, even a good thing, to put 2, 3
> or (heaven forbid) 4 statements on a single line, and to illustrate
> the point with a couple of examples. That (you find that) the
> examples are less than perfect does not, in itself, mean that my
> point is entirely wrong.
>
> I said right at the beginning that your code isn't terrible; you're the one who insisted that other people will say it is.
>
> And there are certainly examples where writing two statements on a line makes sense. I have code like this:
>
> if stop: break
>
> x += a; y += b
>
> So I agree with you that sometimes putting multiple statements on a line is a good thing.
Good. I also quite often (not always) write 1-line if-suites like that
(as in my first example). I'm glad we agree on something.
> But doing it so you can avoid using one of Python's fundamental features because you're afraid you might have to translate the code to a language you barely know is not a good reason to do it.
We'll have to disagree on that. I think sometimes it might be. But my
primary motive was to write the code so that it was simple to write and
simple to understand.
Another (invented) example occurred to me (before I saw yours above):
Version 1:
x1 += 1
x2 += 1
x3 += 1
y1 += 1
y2 += 1
y2 += 1
z1 += 1
z2 += 1
z3 += 1
Version 2 (Rob's version):
x1 += 1 ; x2 += 1 ; x3 += 1
y1 += 1 ; y2 += 1 ; y2 += 1
z1 += 1 ; z2 += 1 ; z3 += 1
In which version is it easier to grasp what this code does? In which
version is it easier to spot the deliberate mistake?
(And please don't rubbish the example by saying it should have been
written differently in the first place. Circumstances *do* alter
cases; I could be making a minor alteration to a huge inherited program
which it is not practicable or necessary to rewrite.)
Rob Cliffe
[View Less]
On 01/09/13 21:49, Musical Notation wrote:
> View my original proposal in a fixed-width font and you will understand it.
Your assumption that I didn't use a fixed-width font is wrong. I *always* use fixed-width fonts for email. And don't imagine that the only reason I could disagree with your proposal is that I don't understand it. I understand your proposal very well, and still disagree.
> That indentation style is quite idiomatic in Haskell.
Irrelevant. Python does not have a "let …
[View More]name=value" statement, so the Haskell "let" idiom does not apply. There is an enormous difference between the leading fixed-width "let" and variable-width "if" clauses:
let x = value
y = name
let extremely_long_name_that_goes_on_and_on = value
name = value
let foo = value
bar = value
versus:
if f: do_this()
do_that()
if long_condition: do_this()
do_that()
elif flag: do_this()
do_that()
--
Steven
[View Less]
Hi all,
I'd like to propose changing ipaddress.IPv[46]Interface to not inherit from IPv[46]Address.
(And I hope I've got the right mailing list?)
The "ipaddress" module is currently in the standard library on a provisional basis, so "backwards
incompatible changes may occur". But obviously there needs to be a very good reason. I think
there is.
The problem is that IPv[46]Interface can't currently be passed to a function that expects an
IPv[46]Address, because it redefines str(), ".…
[View More]exploded", ".compressed", "==" and "<" in an
incompatible way.
E.g suppose we have:
>>> from ipaddress import IPv4Address, IPv4Interface
>>> my_dict = {IPv4Address('1.2.3.4'): 'Hello'}
Obviously lookup with an IPv4Address works:
>>> addr = IPv4Address('1.2.3.4')
>>> print(my_dict.get(addr))
Hello
But with the IPv4Interface subclass, the lookup doesn't work:
>>> intf = IPv4Interface('1.2.3.4/24')
>>> print(my_dict.get(intf))
None
And that's because equality has been redefined:
>>> IPv4Address('1.2.3.4') == IPv4Interface('1.2.3.4/24')
False
When doing inheritance the usual expectation is that "Functions that use references to base classes
must be able to use objects of derived classes without knowing it". This is called the "Liskov
Substitution Principle". And IPv4Interface isn't following that principle.
More informally, since IPv4Interface inherits from IPv4Address you'd expect that an IPv4Interface
"is a" IPv4Address, but it really isn't. It's really a "has a" relationship, which is more
commonly done by giving IPv4Interface a property that's an IPv4Address.
This problem can't be solved easily by just redefining "==". If we let IPv4Address('1.2.3.4') ==
IPv4Interface('1.2.3.4/24'), and IPv4Address('1.2.3.4') == IPv4Interface('1.2.3.4/16'), then to
keep the normal transitive behaviour of equals we'd have to let IPv4Interface('1.2.3.4/16') ==
IPv4Interface('1.2.3.4/24'), and that seems wrong. Where people actually know they're comparing
IPv4Interface objects they will really want to compare both the IP address and netmask.
My proposed solution is just to change IPv4Interface to not inherit from IPv4Address.
IPv4Interface already has a "ip" property that gives you the IP address as a proper IPv4Address
object. So code written for Python 3.4, using the "ip" property, would be backward-compatible with
Python 3.3. And people could obviousy start writing code that way today, to be compatible with
both Python 3.3 and Python 3.4.
I.e. people should write code like this:
>>> extracted_address = intf.ip
>>> extracted_address
IPv4Address('1.2.3.4')
>>> print(my_dict.get(extracted_address))
Hello
I'm proposing that IPv4Interface would not have a (public) base class, and the only remaining
properties and methods on IPv4Interface would be:
__init__()
ip
network
compressed
exploded
with_prefixlen
with_netmask
with_hostmask
__eq__() / __ne__()
__hash__()
__lt__(), __gt__() etc
__str__()
And all these properties and methods would do exactly what they do today.
I.e. IPv4Interface becomes just a container for the "ip" and "network" fields, plus the parsing
code in __init__() and a few formatting and comparison functions. This is a lot simpler to
understand than the current design.
With this design, IPv4Interface wouldn't be comparable to IPv4Address or IPv4Network objects. If
the user wants to do a comparison like that, they can get the .ip or .network properties and
compare that. Explicit is better than implicit.
If there is interest in this idea, I'll try to put together a patch next week.
Thanks to anyone who's read this far. What do you think?
Kind regards,
Jon
[View Less]