Documentation, assignment in expression.

Discussion in 'Python' started by Alexander Blinne, Mar 23, 2012.

  1. Hi,

    I think this section of the docs needs some kind of rewrite:

    <http://docs.python.org/faq/design.html#why-can-t-i-use-an-assignment-in-an-expression>

    While it is great to discuss the reasons for not allowing an assignment
    in an expression, I feel that the given example is some kind of
    outdated. The last sentence "For example, in the current version of
    Python file objects support the iterator protocol, so you can now write
    simply (for line in file:)" makes me think that this section was written
    while that syntax was still new. No one I know would ever write
    something like this:

    > while True:
    > line = f.readline()
    > if not line:
    > break
    > ... # do something with line


    I think at least we need a new example. Any ideas?

    Greetings
    Alexander
    Alexander Blinne, Mar 23, 2012
    #1
    1. Advertising

  2. On Fri, 23 Mar 2012 23:59:44 +0100, Alexander Blinne <>
    declaimed the following in gmane.comp.python.general:

    > Hi,
    >
    > I think this section of the docs needs some kind of rewrite:
    >
    > <http://docs.python.org/faq/design.html#why-can-t-i-use-an-assignment-in-an-expression>
    >
    > While it is great to discuss the reasons for not allowing an assignment
    > in an expression, I feel that the given example is some kind of
    > outdated. The last sentence "For example, in the current version of
    > Python file objects support the iterator protocol, so you can now write
    > simply (for line in file:)" makes me think that this section was written
    > while that syntax was still new. No one I know would ever write
    > something like this:
    >
    > > while True:
    > > line = f.readline()
    > > if not line:
    > > break
    > > ... # do something with line

    >
    > I think at least we need a new example. Any ideas?
    >

    But remember -- this is a section on why no "assignment in
    expression"...

    That means

    for line in f:
    #do stuff

    is the counter to the equivalent C-style

    while (line = f.readline() ): #mixing C style with Python calls
    # do stuff

    The section is not meant to be an advocate for or against your true
    Python design using an infinite loop, and separate input line.

    The emphasis on "current version" might do with updating -- by
    explicitly stating when the iteration came in...
    --
    Wulfraed Dennis Lee Bieber AF6VN
    HTTP://wlfraed.home.netcom.com/
    Dennis Lee Bieber, Mar 24, 2012
    #2
    1. Advertising

  3. Alexander Blinne

    Roy Smith Guest

    In article <4f6d0060$0$6634$-online.net>,
    Alexander Blinne <> wrote:

    > The last sentence "For example, in the current version of Python file
    > objects support the iterator protocol, so you can now write simply
    > (for line in file:)" ...


    In general, words like "current", "previous", and "next" don't belong in
    documentation. The timepoint to which they refer changes as soon as
    they're published. Much better to say, "as of version x.y..."
    Roy Smith, Mar 24, 2012
    #3
  4. I am not sure I understand your argument. The doc section states that

    " [...] in Python you’re forced to write this:

    while True:
    line = f.readline()
    if not line:
    break
    ... # do something with line".

    That simply isn't true as one can simply write:

    for line in f:
    #do stuff

    which is the exact counter of the C idiom that C or perl people try to
    use and complained about when they were really forced to write the above
    lengthy while True loop. So there is no reason to want to do "assignment
    in expression", so no real reason to use this example to explain why
    they aren't there in python. I totally agree with the
    error-handling-reason not allowing them.

    I agree with Roy Smith saying that documentation shouldn't refer to the
    "current version".
    Alexander Blinne, Mar 25, 2012
    #4
  5. Alexander Blinne

    Tim Chase Guest

    On 03/25/12 07:18, Alexander Blinne wrote:
    > I am not sure I understand your argument. The doc section states that
    >
    > " [...] in Python you’re forced to write this:
    >
    > while True:
    > line = f.readline()
    > if not line:
    > break
    > ... # do something with line".
    >
    > That simply isn't true as one can simply write:
    >
    > for line in f:
    > #do stuff


    I think the complaint was backed by a bad example. Perhaps a DB
    example works better. With assignment allowed in an evaluation,
    you'd be able to write

    while data = conn.fetchmany():
    for row in data:
    process(row)

    whereas you have to write

    while True:
    data = conn.fetchmany()
    if not data: break
    for row in data:
    process(row)

    Granted, this can be turned into an iterator with a yield, making
    the issue somewhat moot:

    def db_iter(conn, *args, **kwargs):
    while True:
    data = conn.fetchmany(rows, *args, **kwargs)
    if not data: break
    for row in data:
    yield row

    for row in db_iter(conn):
    proecss(row)

    -tkc
    Tim Chase, Mar 25, 2012
    #5
  6. On Mon, Mar 26, 2012 at 12:03 AM, Tim Chase
    <> wrote:
    > Granted, this can be turned into an iterator with a yield, making the issue
    > somewhat moot:


    No, just moving the issue to the iterator. Your iterator has exactly
    the same structure in it.

    Personally, I quite like assignment-in-conditional notation. Yes, it's
    a pretty common cause of problems; but what happened to the
    "consenting adults" policy? Python permits operator overloading and
    even the reassignment of builtins, both of which can cause similar
    confusion.

    But, that's the choice Python's made. And being able to use the same
    symbol for assignment and comparison does have its advantages.

    ChrisA
    Chris Angelico, Mar 25, 2012
    #6
  7. Alexander Blinne

    Tim Chase Guest

    On 03/25/12 08:11, Chris Angelico wrote:
    > On Mon, Mar 26, 2012 at 12:03 AM, Tim Chase
    > <> wrote:
    >> Granted, this can be turned into an iterator with a yield, making the issue
    >> somewhat moot:

    >
    > No, just moving the issue to the iterator. Your iterator has exactly
    > the same structure in it.


    Yeah, it has the same structure internally, but I'm somewhat
    surprised that the DB connection object doesn't have an
    __iter__() that does something like this automatically under the
    covers.

    > Personally, I quite like assignment-in-conditional notation. Yes, it's
    > a pretty common cause of problems; but what happened to the
    > "consenting adults" policy? Python permits operator overloading and
    > even the reassignment of builtins, both of which can cause similar
    > confusion.


    In my past years of C programming, I've accidentally omitted the
    second "=" in a comparison test numerous times, requiring me to
    track down the missing character. When I finally catch it, it's
    obvious what the problem is, but I've come to love having Python
    yell at me contextually.

    > But, that's the choice Python's made. And being able to use the same
    > symbol for assignment and comparison does have its advantages.


    The old curmudgeon in me likes the Pascal method of using "=" for
    equality-testing, and ":=" for assignment which feels a little
    closer to mathematical use of "=".

    -tkc
    Tim Chase, Mar 25, 2012
    #7
  8. On Mon, Mar 26, 2012 at 12:48 AM, Tim Chase
    <> wrote:
    > Yeah, it has the same structure internally, but I'm somewhat surprised that
    > the DB connection object doesn't have an __iter__() that does something like
    > this automatically under the covers.


    Sure. That's definitely the truly Pythonic technique. If I build a C++
    object that acts like an array, it's going to overload the []
    dereference operator - and if I build a Python object that returns a
    series of things, it's going to be an iterable.

    > In my past years of C programming, I've accidentally omitted the second "="
    > in a comparison test numerous times, requiring me to track down the missing
    > character.  When I finally catch it, it's obvious what the problem is, but
    > I've come to love having Python yell at me contextually.


    This is where compiler warnings come in handy. GCC will, in what I
    told someone was "Perls of Wisdom mode" (with the -Wall option - yes,
    it is that bad a pun), recommend clarification of the worst offenders
    of this sort. And in the common case where you're comparing against a
    constant, quite a few compilers will alert you to the fact that your
    condition is always true/always false (eg "if (x=3) ;"). Many problems
    can be solved in multiple ways; sometimes there's a clear "best way",
    other times it really doesn't matter matter matter matter matter.

    ChrisA
    Chris Angelico, Mar 25, 2012
    #8
  9. Alexander Blinne

    rusi Guest

    On Mar 25, 6:48 pm, Tim Chase <> wrote:
    >
    > The old curmudgeon in me likes the Pascal method of using "=" for
    > equality-testing, and ":=" for assignment which feels a little
    > closer to mathematical use of "=".
    >
    > -tkc


    Carroll Morgan author of programming from specifications
    http://www.cs.ox.ac.uk/publications/books/PfS/ called colon in ':=' as
    'make'. So := is 'make equal' This generalizes nicely to other
    specification operators (though not computable) eg :> is 'make greater
    than'

    So x :> x
    can be refined to x := x+1
    or to x := x*x if the precondition x > 1 is available
    rusi, Mar 25, 2012
    #9
  10. Alexander Blinne

    Tim Chase Guest

    On 03/25/12 10:16, Kiuhnm wrote:
    > On 3/25/2012 15:48, Tim Chase wrote:
    >> The old curmudgeon in me likes the Pascal method of using "=" for
    >> equality-testing, and ":=" for assignment which feels a little closer to
    >> mathematical use of "=".

    >
    > Unfortunately, ":=" means "is defined as" in mathematics. The "right"
    > operator would have been "<-".


    Yeah, while I like the "<-" better too, I'm not *quite* old
    enough to have influenced Wirth in his design decisions ;-)

    -tkc
    Tim Chase, Mar 25, 2012
    #10
  11. On Sun, 25 Mar 2012 08:48:31 -0500, Tim Chase
    <> declaimed the following in
    gmane.comp.python.general:


    > Yeah, it has the same structure internally, but I'm somewhat
    > surprised that the DB connection object doesn't have an
    > __iter__() that does something like this automatically under the
    > covers.
    >

    I believe being able to use the connection object directly for
    queries is considered a short-cut feature... If you use the longer form

    con = db.connect()
    cur = con.cursor()

    the cursor object, in all that I've worked with, does function for
    iteration

    for rec in cur:
    #do stuff
    --
    Wulfraed Dennis Lee Bieber AF6VN
    HTTP://wlfraed.home.netcom.com/
    Dennis Lee Bieber, Mar 25, 2012
    #11
  12. Alexander Blinne

    Guest

    Tim Chase wrote:

    > On 03/25/12 08:11, Chris Angelico wrote:
    >> On Mon, Mar 26, 2012 at 12:03 AM, Tim Chase
    >> <> wrote:
    >>> Granted, this can be turned into an iterator with a yield, making the
    >>> issue somewhat moot:

    >>
    >> No, just moving the issue to the iterator. Your iterator has exactly
    >> the same structure in it.

    >
    > Yeah, it has the same structure internally, but I'm somewhat
    > surprised that the DB connection object doesn't have an
    > __iter__() that does something like this automatically under the
    > covers.


    Most of my database programs wind up having the boilerplate (not tested):

    def rowsof (cursor):
    names = [x[0] for x in cursor.description]
    r = cursor.fetchone()
    while r:
    yield dict (zip (names, r))
    r = cursor.fetchone()


    Mel.
    >
    >> Personally, I quite like assignment-in-conditional notation. Yes, it's
    >> a pretty common cause of problems; but what happened to the
    >> "consenting adults" policy? Python permits operator overloading and
    >> even the reassignment of builtins, both of which can cause similar
    >> confusion.

    >
    > In my past years of C programming, I've accidentally omitted the
    > second "=" in a comparison test numerous times, requiring me to
    > track down the missing character. When I finally catch it, it's
    > obvious what the problem is, but I've come to love having Python
    > yell at me contextually.
    >
    >> But, that's the choice Python's made. And being able to use the same
    >> symbol for assignment and comparison does have its advantages.

    >
    > The old curmudgeon in me likes the Pascal method of using "=" for
    > equality-testing, and ":=" for assignment which feels a little
    > closer to mathematical use of "=".
    >
    > -tkc
    , Mar 26, 2012
    #12
  13. On Sun, 25 Mar 2012 19:09:12 -0400, declaimed the
    following in gmane.comp.python.general:


    > Most of my database programs wind up having the boilerplate (not tested):
    >
    > def rowsof (cursor):
    > names = [x[0] for x in cursor.description]
    > r = cursor.fetchone()
    > while r:
    > yield dict (zip (names, r))
    > r = cursor.fetchone()
    >


    In my (limited) experience, the main loop above could be replaced
    with:

    for r in cursor:
    yield dict(zip(names, r))

    Though some DB adapters provide a "dictionary cursor" that already
    does what this modules does; or provides a way to specify a subclassed
    cursor that implements the dictionary cursor internally.
    --
    Wulfraed Dennis Lee Bieber AF6VN
    HTTP://wlfraed.home.netcom.com/
    Dennis Lee Bieber, Mar 26, 2012
    #13
  14. On Sun, 25 Mar 2012 08:03:14 -0500, Tim Chase wrote:

    > I think the complaint was backed by a bad example. Perhaps a DB example
    > works better. With assignment allowed in an evaluation, you'd be able
    > to write
    >
    > while data = conn.fetchmany():
    > for row in data:
    > process(row)


    Yes, but why would you want to? Apart from making your code hard to read.
    Better written as:

    while data = conn.fetchmany(): # THIS IS NOT A TYPO
    for row in data:
    process(row)


    When you need to write a comment explaining that something isn't a typo,
    that's a good clue that you shouldn't do it *wink*

    But seriously, assignment as an expression is a pretty major code-smell.
    Even when it's innocent, it should still fire off warning alarms, because
    how do you know if it's innocent or not until you've studied the code?

    (Comments, like the above, are prone to get out of date with the code,
    and can't be entirely trusted.)

    Python avoids this code smell by prohibiting assignment as an expression.
    Functional languages avoid it by prohibiting assignment. Other languages
    may choose to do differently. If all languages did exactly what C does,
    they'd all be C.

    (I seem to recall a language that used a single = for both assignment and
    equality testing, guessing which one you meant from context. BASIC
    perhaps? Whatever it was, I'm pretty sure they regretted it.)


    > whereas you have to write
    >
    > while True:
    > data = conn.fetchmany()
    > if not data: break
    > for row in data:
    > process(row)


    Or even:

    data = 'SENTINEL' # Any true value will do.
    while data:
    data = conn.fetchmany()
    for row in data:
    process(row)


    There's no need to break out of the while loop early, because `for row in
    <empty>` is a null-op.




    --
    Steven
    Steven D'Aprano, Mar 26, 2012
    #14
  15. On Sun, 25 Mar 2012 17:16:16 +0200, Kiuhnm wrote:

    > On 3/25/2012 15:48, Tim Chase wrote:
    >> The old curmudgeon in me likes the Pascal method of using "=" for
    >> equality-testing, and ":=" for assignment which feels a little closer
    >> to mathematical use of "=".

    >
    > Unfortunately, ":=" means "is defined as" in mathematics. The "right"
    > operator would have been "<-".


    That's hardly universal. In approx 20 years of working with mathematics
    textbooks, I've never seen := used until today.

    I see that Wikipedia lists no fewer than seven different symbols for "is
    defined as":

    http://en.wikipedia.org/wiki/Table_of_mathematical_symbols

    including the one I'm familiar with, â‰, or "def" over "=".

    Besides, just because mathematicians use a symbol, doesn't mean
    programming languages can't use it for something else.



    --
    Steven
    Steven D'Aprano, Mar 26, 2012
    #15
  16. On Sun, Mar 25, 2012 at 11:16 AM, Kiuhnm
    <> wrote:
    > On 3/25/2012 15:48, Tim Chase wrote:
    >>
    >> The old curmudgeon in me likes the Pascal method of using "=" for
    >> equality-testing, and ":=" for assignment which feels a little closer to
    >> mathematical use of "=".

    >
    >
    > Unfortunately, ":=" means "is defined as" in mathematics. The "right"
    > operator would have been "<-".



    "Is defined as" is actually pretty reasonable. "Define this to be
    that" is a common way to speak about assignment. Its only difference
    is the present tense. For example, in Python, "def" stands for
    "define", but we can overwrite previous definitions::

    def f(x): return x
    def f(x): return 2
    f(3) == 2

    In fact, in pretty every programming language that I know of with a
    "define" assignment verb, this is so. For example, in Scheme, x is 2
    at the end::

    (define x 1)
    (define x 2)
    x

    -- Devin
    Devin Jeanpierre, Mar 26, 2012
    #16
  17. Alexander Blinne

    Tim Chase Guest

    On 03/25/12 17:59, Dennis Lee Bieber wrote:
    > On Sun, 25 Mar 2012 08:48:31 -0500, Tim Chase
    >> Yeah, it has the same structure internally, but I'm somewhat
    >> surprised that the DB connection object doesn't have an
    >> __iter__() that does something like this automatically under the
    >> covers.
    >>

    > I believe being able to use the connection object directly for
    > queries is considered a short-cut feature... If you use the longer form
    >
    > con = db.connect()
    > cur = con.cursor()
    >
    > the cursor object, in all that I've worked with, does function for
    > iteration
    >
    > for rec in cur:
    > #do stuff


    Interesting. Either this is something special for a particular
    DB back-end, or has been added since I went hunting (back with
    mxODBC and Python2.4). I wouldn't be surprised if the latter was
    the case, as my code is nigh identical to yours.

    conn = db.DriverConnect(connection_string)
    cursor = conn.cursor()
    cursor.execute(sql, params)
    for row in cursor: # in the above 2.4 + mxODBC, this fails
    process(row)

    I'd be interested to know the underlying implementation's
    efficiency, hopefully using .fetchmany() under the hood. My
    understanding is that the .fetchmany() loop is the best way to do
    it, as the .fetchone() gets chatty with the DB (but is more
    efficient if you only need one row from a multi-result query),
    and the .fetchall() can blow out memory on large datasets.

    -tkc
    Tim Chase, Mar 26, 2012
    #17
  18. Kiuhnm writes:
    > On 3/26/2012 10:52, Devin Jeanpierre wrote:
    > > On Sun, Mar 25, 2012 at 11:16 AM, Kiuhnm
    > > <> wrote:
    > >> On 3/25/2012 15:48, Tim Chase wrote:
    > >>>
    > >>> The old curmudgeon in me likes the Pascal method of using "=" for
    > >>> equality-testing, and ":=" for assignment which feels a little closer to
    > >>> mathematical use of "=".
    > >>
    > >>
    > >> Unfortunately, ":=" means "is defined as" in mathematics. The "right"
    > >> operator would have been "<-".

    > >
    > >
    > > "Is defined as" is actually pretty reasonable. "Define this to be
    > > that" is a common way to speak about assignment. Its only difference
    > > is the present tense. For example, in Python, "def" stands for
    > > "define", but we can overwrite previous definitions::
    > >
    > > def f(x): return x
    > > def f(x): return 2
    > > f(3) == 2
    > >
    > > In fact, in pretty every programming language that I know of with a
    > > "define" assignment verb, this is so. For example, in Scheme, x is 2
    > > at the end::
    > >
    > > (define x 1)
    > > (define x 2)
    > > x

    >
    > When you write
    > (define x 1)
    > (define x 2)
    > x
    > or, in F# and OCaml,
    > let x = 1
    > let x = 2
    > x
    > you're saying
    > x = 1
    > {
    > x = 2
    > x
    > }
    > You don't modify 'x': you hide it by defining another "value" (not
    > variable) with the same name.
    > Indeed,
    > let x = 1
    > let x = 2
    > x
    > is shorthand for
    > let x = 1 in
    > let x = 2 in
    > x


    No, Devin is right about Scheme. On "top level" re-definition is
    interpreted as assignment. The following mean the same:

    (define x 1) (define x 2) x
    (define x 1) (set! x 2) x

    Local definitions in the beginning of a "body" do not allow duplicate
    names at all. The following mean the same:

    (let () (define x 1) (define y 2) x)
    (letrec* ((x 1) (y 2)) x) ;letrec in older versions (not sure of R6RS)

    But (let () (define x 1) (define x 2) x) is still an error. Some
    implementations may give it a meaning. Not sure.
    Jussi Piitulainen, Mar 26, 2012
    #18
  19. Alexander Blinne

    Guest

    Dennis Lee Bieber wrote:

    > On Sun, 25 Mar 2012 19:09:12 -0400, declaimed the
    > following in gmane.comp.python.general:
    >
    >
    >> Most of my database programs wind up having the boilerplate (not tested):
    >>
    >> def rowsof (cursor):
    >> names = [x[0] for x in cursor.description]
    >> r = cursor.fetchone()
    >> while r:
    >> yield dict (zip (names, r))
    >> r = cursor.fetchone()
    >>

    >
    > In my (limited) experience, the main loop above could be replaced
    > with:
    >
    > for r in cursor:
    > yield dict(zip(names, r))


    I think your experience is more recent than mine. I'll change my
    boilerplate next time around.

    Mel.
    , Mar 26, 2012
    #19
  20. Am 26.03.2012 00:59 schrieb Dennis Lee Bieber:

    > If you use the longer form
    >
    > con = db.connect()
    > cur = con.cursor()
    >
    > the cursor object, in all that I've worked with, does function for
    > iteration


    I use this form regularly with MySQLdb and am now surprised to see that
    this is optional according to http://www.python.org/dev/peps/pep-0249/.

    So a database cursor is not required to be iterable, alas.


    Thomas
    Thomas Rachel, Mar 26, 2012
    #20
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Cameron Laird
    Replies:
    1
    Views:
    639
    Josiah Carlson
    Apr 3, 2004
  2. Kenneth McDonald
    Replies:
    2
    Views:
    710
  3. nagy
    Replies:
    36
    Views:
    975
    Terry Reedy
    Jul 20, 2006
  4. Chris
    Replies:
    34
    Views:
    1,477
  5. Potato Peelings
    Replies:
    2
    Views:
    84
    Potato Peelings
    Jun 10, 2010
Loading...

Share This Page