Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Return value of an assignment statement?

5 views
Skip to first unread message

mrstephengross

unread,
Feb 21, 2008, 4:43:26 PM2/21/08
to
Hi all. In C, an assignment statement returns the value assigned. For
instance:

int x
int y = (x = 3)

In the above example, (x=3) returns 3, which is assigned to y.

In python, as far as I can tell, assignment statements don't return
anything:

y = (x = 3)

The above example generates a SyntaxError.

Is this correct? I just want to make sure I've understood the
semantics.

Thanks,
--Steve

John Henry

unread,
Feb 21, 2008, 4:48:07 PM2/21/08
to

That's true, and I am happy that they decided to make that a syntax
error.

Jeff Schwab

unread,
Feb 21, 2008, 4:51:03 PM2/21/08
to

Yes, but there is valid syntax for the common case you mentioned:

y = x = 3

What you can't do (that I really miss) is have a tree of assign-and-test
expressions:

import re
pat = re.compile('some pattern')

if m = pat.match(some_string):
do_something(m)
else if m = pat.match(other_string):
do_other_thing(m)
else:
do_default_thing()

7stud

unread,
Feb 21, 2008, 4:51:30 PM2/21/08
to

x = y = 1
print x, y

--output:--
1 1

With parentheses, it looks like python thinks you are trying to do a
boolean == inside the parentheses. It's the same error you get if you
write:

if x = y:
print 'yes'

John Henry

unread,
Feb 21, 2008, 4:53:53 PM2/21/08
to

BTW: The less obvious issues when coming from the C world are Python
syntax like these:

y = x = 3

a = 4

y = x = a

print x,y

a = 5

print x,y

mrstephengross

unread,
Feb 21, 2008, 4:57:11 PM2/21/08
to
> What you can't do (that I really miss) is have a tree of assign-and-test
> expressions:
> import re
> pat = re.compile('some pattern')
> if m = pat.match(some_string):
> do_something(m)

Yep, this is exactly what I am (was) trying to do. Oh well.... Any
clever ideas on this front?

--Steve

Jeff Schwab

unread,
Feb 21, 2008, 5:06:06 PM2/21/08
to

That's the same behavior I would expect in C, on the grounds that C
assignments do bit-wise copies. What I found confusing at first was
that the same variable will either directly store or merely refer to an
object, depending on the type of the object:

>>> a = [ 'hello' ]


>>> y = x = a

>>> a += [ 'world' ]
>>> print x, y
['hello', 'world'] ['hello', 'world']

Jeff Schwab

unread,
Feb 21, 2008, 5:17:43 PM2/21/08
to

I worked around it by defining a separate thigamabob with a "result"
property. A method of the thigamabob internally performs the
assignment, and returns a boolean result. The body of each branch in
the tree can then retrieve the result object from the thigamabob.
Here's an example hard-coded to match strings against patterns, but I
think the idea should be extensible to other kinds of assign-and-test
situations.

# Just for the sake of this post.
def do_something(m): pass
def do_other_thing(m): pass
def do_default_thing(): pass

import re

class Matcher(object):
def __call__(self, pattern, string):
self.result = pattern.match(string)

if __name__ == '__main__':

pat = re.compile('some pattern')

match = Matcher()

if match(pat, 'a'):
do_something(match.result)
elif match(pat, 'b'):
do_other_thing(match.result)
else:
do_default_thing()

John Henry

unread,
Feb 21, 2008, 5:19:09 PM2/21/08
to

Yep. Took me a while to realize there is mutable objects, and non-
mutable objects. To be honest, I am still not too comfortable about
it. For instance, I still get nervous for code like:

def invoke_some_fct(parent):
y = parent.x
y += [ 'world' ]
print y, parent.x

class abc:
def __init__(self):
self.x=[ 'hello' ]
invoke_some_fct(self)
print self.x

hw = abc()

"Martin v. Löwis"

unread,
Feb 21, 2008, 5:34:51 PM2/21/08
to mrstephengross
> Hi all. In C, an assignment statement returns the value assigned.

No. C doesn't have an assignment statement. Instead, in C, assignment
is an expression (just like a binary operation or a function call);
that expression evaluates to the value assigned (i.e. the result is
the value, the assignment is just a side-effect).

What you consider the assignment statement is actually an expression
statement, of the syntax

<expression> <semicolon>

So

x = y;
f();
3+4;

are all the same kind of statement.

> In python, as far as I can tell, assignment statements don't return
> anything:

Right - that's because they are statements. No statement "returns"
a value - except for the return statement, of course, but it doesn't
return it in the sense that you could write

foo = return 44

Because of the confusing meaning of "return", I find it better to
say that expressions evaluate to a value, not that they return
a value.

> The above example generates a SyntaxError.
>
> Is this correct? I just want to make sure I've understood the
> semantics.

Please try to study more on the difference between expressions
and statements.

Regards,
Martin

P.S. Just to confuse matters: GNU C also has statement expressions,
of the form

({ int y = foo (); int z;
if (y > 0) z = y;
else z = - y;
z; })

These are expressions, but allow the expressiveness of statements
(including variable declarations)

Terry Reedy

unread,
Feb 21, 2008, 6:13:24 PM2/21/08
to pytho...@python.org

"Jeff Schwab" <je...@schwabcenter.com> wrote in message
news:5sudnTSXrZY0aCDa...@comcast.com...

| That's the same behavior I would expect in C, on the grounds that C

| What I found confusing at first was
| that the same variable will either directly store or merely refer to an
| object, depending on the type of the object:

Since names and collection slots always refer to objects, I find the above
confusing. Can you clarify what difference you percieve?

tjr


bruno.des...@gmail.com

unread,
Feb 21, 2008, 6:24:39 PM2/21/08
to

There's nothing like a variable "storing" anything in Python. All you
have are names to (references to) objects binding in a namespace. Now
the fact is that some types are mutable and other are not. In your
above example, the augmented assignment does *not* rebind a, but
invoke a.extend(). With integers, it would have rebind a. So while
your observation is exact, your interpretation is wrong !-)

bruno.des...@gmail.com

unread,
Feb 21, 2008, 6:29:02 PM2/21/08
to

Explicitely using list.extend would make things clearer:

def invoke_some_fct(parent):
parent.x.extend(['world'])

Now there's no reason to feel nervous about this. All you have to
remember is that Python never copy anything unless explicitely asked
for.

bruno.des...@gmail.com

unread,
Feb 21, 2008, 6:33:21 PM2/21/08
to
> What you can't do (that I really miss) is have a tree of assign-and-test
> expressions:
>
> import re
> pat = re.compile('some pattern')
>
> if m = pat.match(some_string):
> do_something(m)
> else if m = pat.match(other_string):
> do_other_thing(m)
> else:
> do_default_thing()

What you want is:

for astring, afunc in ((some_string, do_something), (other_string,
do_other_thing)):
m = pat.match(astring)
if m:
afunc(m)
break
else:
do_default_thing()

Steve Holden

unread,
Feb 21, 2008, 6:52:21 PM2/21/08
to pytho...@python.org
The syntax is the way it is precisely to discourage that kind of clever
idea. Of course, people nevertheless manage to work around the
restriction to try and make their Python read like some other language
they are more familiar with, and most of the time they get away with it.

The fat remains that in programming there *is* such a thing as being too
clever, and Python's syntax deliberately discourages that.

regards
Steve
--
Steve Holden +1 571 484 6266 +1 800 494 3119
Holden Web LLC http://www.holdenweb.com/

Jeff Schwab

unread,
Feb 21, 2008, 6:56:55 PM2/21/08
to

Whether you use += or extend has nothing to do with it. You omitted the
relevant part. Using extend, it would look like:

y = parent.x
y.extend(['world'])

The confusing part is that performing an operation on y may or may not
alter parent.x, depending on whether the initial type of parent.x is
immutable. If parent.x is immutable, y is a copy of the value
represented by parent.x, and modifying y has not effect on the value of
parent.x. If (OTOH) parent.x is mutable, then x and y are really
references to the same object, and modifications to that object via y
can be observed via x. In C, you use pointers to get this effect.


> Now there's no reason to feel nervous about this. All you have to
> remember is that Python never copy anything unless explicitely asked
> for.

It's not that simple. After a statement like:

a = b

Whether a and b denote the same object depends on what kind of object b
represented in the first place.

Steve Holden

unread,
Feb 21, 2008, 7:16:15 PM2/21/08
to pytho...@python.org
Jeff Schwab wrote:
> bruno.des...@gmail.com wrote:
[...]

>> Now there's no reason to feel nervous about this. All you have to
>> remember is that Python never copy anything unless explicitely asked
>> for.
>
> It's not that simple. After a statement like:
>
> a = b
>
> Whether a and b denote the same object depends on what kind of object b
> represented in the first place.

Surely this is exactly wrong. Is there a single example you can think of
where

a = b
assert a is b, "Oops!"

would raise and exception? Perhaps you meant to use an augmented
assignment operation?

Jeff Schwab

unread,
Feb 21, 2008, 7:19:09 PM2/21/08
to

a += b

Whether a refers to the same object before and after that statement
depends on what type of object it referred to before the statement.

Jeff Schwab

unread,
Feb 21, 2008, 7:20:45 PM2/21/08
to

Thank you for the clarification. For some reason, I had it in my head
that ints were packed directly into the C structures that represent
Python variables, in the same (union?) member that otherwise would store
a pointer.

Jeff Schwab

unread,
Feb 21, 2008, 7:21:36 PM2/21/08
to
Steve Holden wrote:
> Jeff Schwab wrote:
>> bruno.des...@gmail.com wrote:
> [...]
>>> Now there's no reason to feel nervous about this. All you have to
>>> remember is that Python never copy anything unless explicitely asked
>>> for.
>>
>> It's not that simple. After a statement like:
>>
>> a = b
>>
>> Whether a and b denote the same object depends on what kind of object
>> b represented in the first place.
>
> Surely this is exactly wrong. Is there a single example you can think of
> where
>
> a = b

a += b (my bad)

> assert a is b, "Oops!"
>
> would raise and exception? Perhaps you meant to use an augmented
> assignment operation?

Why, yes I did! Sorry about that.

Jeff Schwab

unread,
Feb 21, 2008, 7:23:23 PM2/21/08
to
Steve Holden wrote:
> mrstephengross wrote:
>>> What you can't do (that I really miss) is have a tree of assign-and-test
>>> expressions:
>>> import re
>>> pat = re.compile('some pattern')
>>> if m = pat.match(some_string):
>>> do_something(m)
>>
>> Yep, this is exactly what I am (was) trying to do. Oh well.... Any
>> clever ideas on this front?
>>
> The syntax is the way it is precisely to discourage that kind of clever
> idea. Of course, people nevertheless manage to work around the
> restriction to try and make their Python read like some other language
> they are more familiar with, and most of the time they get away with it.
>
> The fat remains that in programming there *is* such a thing as being too
> clever, and Python's syntax deliberately discourages that.

This isn't "clever." It's what most of us were taught, from the
beginning, was best practice. In some languages (including C and C++)
the above is extremely common and perfectly acceptable.

George Sakkis

unread,
Feb 21, 2008, 9:17:13 PM2/21/08
to
On Feb 21, 7:21 pm, Jeff Schwab <j...@schwabcenter.com> wrote:
> Steve Holden wrote:
> > Jeff Schwab wrote:

It seems less surprising when you keep in mind that "+=" (and friends)
can be syntax sugar for calling a method on the right hand side
object: a += b <=> a.__iadd__(b). It's up to the class of 'a' to do
whatever within this method (whether it's a good idea to do anything
else other than modify 'self' in place is another thing). Would you be
able to say anything about a.foo(b) without knowing what 'a' is ?

The only difference is that for types that don't implement an
augmented operator, "a `op`= b" translates to "a = a `op` b" for a
binary operator `op`. There's no formal notion of mutable and
immutable objects with respect to these operators; any class that
doesn't implement them is "immutable" as far as augmented assignment
goes (of course it may be mutated in other ways, e.g. by fiddling with
a.__dict__ directly).

On the other hand, "a = b" does always the same thing; unlike C++, '='
is not an operator and therefore it cannot be overriden by the class
of 'a'.

George

Aahz

unread,
Feb 21, 2008, 11:07:54 PM2/21/08
to
In article <5MSdncSVGdGriCPa...@comcast.com>,
Jeff Schwab <je...@schwabcenter.com> wrote:

>bruno.des...@gmail.com wrote:
>>
>> There's nothing like a variable "storing" anything in Python. All you
>> have are names to (references to) objects binding in a namespace. Now
>> the fact is that some types are mutable and other are not. In your
>> above example, the augmented assignment does *not* rebind a, but
>> invoke a.extend(). With integers, it would have rebind a. So while
>> your observation is exact, your interpretation is wrong !-)
>
>Thank you for the clarification. For some reason, I had it in my head
>that ints were packed directly into the C structures that represent
>Python variables, in the same (union?) member that otherwise would store
>a pointer.

Notice very very carefully that Bruno is not using "variable". Many
expert Python programmers strongly prefer to talk about "names" instead
of "variables" (especially when explaining the Python object model)
precisely because using "variable" leads to incorrect expectations.

http://starship.python.net/crew/mwh/hacks/objectthink.html
--
Aahz (aa...@pythoncraft.com) <*> http://www.pythoncraft.com/

"All problems in computer science can be solved by another level of
indirection." --Butler Lampson

Jeff Schwab

unread,
Feb 22, 2008, 12:26:36 AM2/22/08
to

Yes: I would know that it didn't rebind a.


> The only difference is that for types that don't implement an
> augmented operator, "a `op`= b" translates to "a = a `op` b" for a
> binary operator `op`. There's no formal notion of mutable and
> immutable objects with respect to these operators; any class that
> doesn't implement them is "immutable" as far as augmented assignment
> goes (of course it may be mutated in other ways, e.g. by fiddling with
> a.__dict__ directly).

Thanks for explaining that.


> On the other hand, "a = b" does always the same thing; unlike C++, '='
> is not an operator and therefore it cannot be overriden by the class
> of 'a'.

"Not an operator?" Then what is it?

Jeff Schwab

unread,
Feb 22, 2008, 12:28:25 AM2/22/08
to
Aahz wrote:
> In article <5MSdncSVGdGriCPa...@comcast.com>,
> Jeff Schwab <je...@schwabcenter.com> wrote:
>> bruno.des...@gmail.com wrote:
>>> There's nothing like a variable "storing" anything in Python. All you
>>> have are names to (references to) objects binding in a namespace. Now
>>> the fact is that some types are mutable and other are not. In your
>>> above example, the augmented assignment does *not* rebind a, but
>>> invoke a.extend(). With integers, it would have rebind a. So while
>>> your observation is exact, your interpretation is wrong !-)
>> Thank you for the clarification. For some reason, I had it in my head
>> that ints were packed directly into the C structures that represent
>> Python variables, in the same (union?) member that otherwise would store
>> a pointer.
>
> Notice very very carefully that Bruno is not using "variable". Many
> expert Python programmers strongly prefer to talk about "names" instead
> of "variables" (especially when explaining the Python object model)
> precisely because using "variable" leads to incorrect expectations.
>
> http://starship.python.net/crew/mwh/hacks/objectthink.html

So what is the "variable?" Or is Python the first HLL I've ever heard
of that didn't have variables?

Paul Rubin

unread,
Feb 22, 2008, 12:39:35 AM2/22/08
to
Jeff Schwab <je...@schwabcenter.com> writes:
> So what is the "variable?" Or is Python the first HLL I've ever heard
> of that didn't have variables?

I don't know what other HLL's you use, but some languages don't even
have mutable values.

Torsten Bronger

unread,
Feb 22, 2008, 12:44:02 AM2/22/08
to
Hallöchen!

Jeff Schwab writes:

> Aahz wrote:
>
>> [...]


>>
>> Notice very very carefully that Bruno is not using "variable".
>> Many expert Python programmers strongly prefer to talk about
>> "names" instead of "variables" (especially when explaining the
>> Python object model) precisely because using "variable" leads to
>> incorrect expectations.
>>
>> http://starship.python.net/crew/mwh/hacks/objectthink.html
>
> So what is the "variable?" Or is Python the first HLL I've ever
> heard of that didn't have variables?

Since Python objects, names, their operations, and the syntax used
for it resemble very closely (but not completely) what is called
"variable" in other languages, Python names are mostly called
"variables", too.

But there is this one thing of changing mutable objects which may
also change the "value" of other "variables". This can only be
understood if your nomenclature is strictly correct.

I find Python's model is this area great. It makes many things
simpler and only one or two things more complicated. I had my bad
experiences with it, too (e.g. initialising two lists with
"x_values=y_values=[]"). But only once per year I stumble over it.

Tschö,
Torsten.

--
Torsten Bronger, aquisgrana, europa vetus
Jabber ID: bro...@jabber.org
(See http://ime.webhop.org for further contact info.)

George Sakkis

unread,
Feb 22, 2008, 1:02:01 AM2/22/08
to
On Feb 22, 12:26 am, Jeff Schwab <j...@schwabcenter.com> wrote:

> > On the other hand, "a = b" does always the same thing; unlike C++, '='
> > is not an operator and therefore it cannot be overriden by the class
> > of 'a'.
>
> "Not an operator?" Then what is it?

In this context, it's just the token used for the assignment
statement. In short, if 'a' is an identifier, the statement means
"bind the name 'a' to the object 'b' (in the local or global
namespace)". It doesn't say anything about memory allocation,
initialization or copying. The only case where assigning an identifier
affects memory is the following [1]:

"""
The name is rebound if it was already bound. This may cause the
reference count for the object previously bound to the name to reach
zero, causing the object to be deallocated and its destructor (if it
has one) to be called.
"""

HTH,
George

[1] http://docs.python.org/ref/assignment.html

Ben Finney

unread,
Feb 22, 2008, 2:17:10 AM2/22/08
to
Jeff Schwab <je...@schwabcenter.com> writes:

> Aahz wrote:
> > Notice very very carefully that Bruno is not using "variable".
> > Many expert Python programmers strongly prefer to talk about
> > "names" instead of "variables" (especially when explaining the
> > Python object model) precisely because using "variable" leads to
> > incorrect expectations.
> >
> > http://starship.python.net/crew/mwh/hacks/objectthink.html
>
> So what is the "variable?" Or is Python the first HLL I've ever
> heard of that didn't have variables?

I don't know what HLLs you've heard of. I would bet that some of them
are languages which don't have "variables" *with all that the term
implies to most programmers*.

You don't have to go very far to find Python programmers using the
term "variable". What Aahz is pointing out is that the concepts that
people sloppily refer to by the term "variable" don't behave in Python
the way a programmer might expect who has used that term in reference
to concepts native to other languages.

Hence the term requires careful definition, and it's often best simply
not to use it and use the better analogy of "names" and "binding"
instead.

--
\ "Pinky, are you pondering what I'm pondering?" "I think so, |
`\ Brain, but if the plural of mouse is mice, wouldn't the plural |
_o__) of spouse be spice?" -- _Pinky and The Brain_ |
Ben Finney

Marc 'BlackJack' Rintsch

unread,
Feb 22, 2008, 3:12:56 AM2/22/08
to
On Thu, 21 Feb 2008 21:28:25 -0800, Jeff Schwab wrote:

> So what is the "variable?" Or is Python the first HLL I've ever heard
> of that didn't have variables?

Relax, Python has variables. It's just sometimes a good advice for people
coming from languages like C to forget about that term for a while because
they have the wrong impression of what "variable" means. A "variable" in
programming languages is composed of a name, a memory location, possibly a
type and a value. In C-like languages, where you put values in named and
typed "boxes", the memory location and type are attached to the name. In
Python both belong to the value.

Ciao,
Marc 'BlackJack' Rintsch

Carl Banks

unread,
Feb 22, 2008, 3:45:59 AM2/22/08
to
On Feb 21, 6:52 pm, Steve Holden <st...@holdenweb.com> wrote:
> mrstephengross wrote:
> >> What you can't do (that I really miss) is have a tree of assign-and-test
> >> expressions:
> >> import re
> >> pat = re.compile('some pattern')
> >> if m = pat.match(some_string):
> >> do_something(m)
>
> > Yep, this is exactly what I am (was) trying to do. Oh well.... Any
> > clever ideas on this front?
>
> The syntax is the way it is precisely to discourage that kind of clever
> idea.

Don't be ridiculous. Assignment operators are maybe one of the worst
things in existence, but this particular use case (running a sequence
of tests like the above) is perfectly useful and good.

Some Pythonistas will swear to their grave and back that should be
done by factoring out the tests into a list and iterating over it, and
NO OTHER WAY WHATSOEVER, but I don't buy it. That's a lot of
boilerplate--the very thing Python is normally so good at minimizing--
when it might not be needed. It would be the right thing for a
complex, pluggable, customizable input filter; but is rarely a better
solution for a simple text processing script.

Quick, at a glance, which code snippet will you understand faster
(pretend you know Perl):


if (/name=(.*)/) {
$name = chop(\1);
} elsif (/id=(.*)/) {
$id = chop(\1);
} elsif (/phone=(.*)/) {
$phone = chop(\1);
}


vs.


def set_phone_number(m):
phone = m.group(1).strip()

def set_id(m):
id = m.group(1).strip()

def set_name(m):
name = m.group(1).strip()

_line_tests = [
(r"phone=(.*)", set_phone_number),
(r"name=(.*)", set_name),
(r"id=(.*)", set_id),
]

for pattern,func in _line_tests:
m = re.match(pattern,line)
if m:
func(m)


At this small scale, and probably at much larger scales too, the Perl
example blows the Python example out of the water in terms of
readability. And that's counting Perl's inherent unreadableness.

If it were a priority, Python could support this set-and-test idiom,
and without an assignment operator. (Notice Perl doesn't use
assignment operator here.) For example, a syntax like this (where the
scope of m is local to the if-condition and the body of the if-
statement:

if m where m = re.match(r"name=(.*)",line):
name = m.group(1).strip()
elif m where m = re.match(r"id=(.*)",line):
id = m.group(1).strip()
elif m where m = re.match(r"phone=(.*)",line):
phone = m.group(1).strip()


This won't happen because the set-and-test idiom is relatively minor
and not deemed worthy of syntax support. But if it were there,
there'd really be nothing clever about it.


Carl Banks

Carl Banks

unread,
Feb 22, 2008, 4:04:17 AM2/22/08
to
On Feb 21, 4:57 pm, mrstephengross <mrstevegr...@gmail.com> wrote:
> > What you can't do (that I really miss) is have a tree of assign-and-test
> > expressions:
> > import re
> > pat = re.compile('some pattern')
> > if m = pat.match(some_string):
> > do_something(m)
>
> Yep, this is exactly what I am (was) trying to do. Oh well.... Any
> clever ideas on this front?


Yeah, I got something clever. I don't recommend using it, biut


def call_if_true(cond):
if cond:
return lambda func: func(cond)
return lambda func: None


@call_if_true(re.match(r"name=(.*)"))


def set_name(m):
name = m.group(1).strip()

@call_if_true(re.match(r"id=(.*)"))


def set_id(m):
id = m.group(1).strip()

@call_if_true(re.match(r"phone=(.*)"))
def set_phone(m):
phone = m.group(1).strip()

This decorator might be more to the point:

def call_if_match(regexp):
m = re.match(regexp)
if m:
return lambda func: func(m)
return lambda func: None

These have the drawback of being a little dense, and the functions
can't rebind variables in the caller's local namespace, but it is
clever.

P.S. I don't recommend using it.


Carl Banks

Duncan Booth

unread,
Feb 22, 2008, 4:31:43 AM2/22/08
to
Jeff Schwab <je...@schwabcenter.com> wrote:

> a += b
>
> Whether a refers to the same object before and after that statement
> depends on what type of object it referred to before the statement.
>

Yes but the rule followed by the builtin types is pretty simple: if 'a' can
still refer to the same object then it does.

Immutable objects make this impossible most of the time (unless 'b' is 0,
(), or similar), and there may be other exceptions I've missed, but this is
the general principle, so mutable objects will mutate and immutable objects
won't.

Whether the same rule is followed outside the core is, of course, up to the
individual developers.

Paul Rubin

unread,
Feb 22, 2008, 4:42:07 AM2/22/08
to
Carl Banks <pavlove...@gmail.com> writes:
> > >> import re
> > >> pat = re.compile('some pattern')
> > >> if m = pat.match(some_string):
> > >> do_something(m)
> >...

> Don't be ridiculous. Assignment operators are maybe one of the worst
> things in existence, but this particular use case (running a sequence
> of tests like the above) is perfectly useful and good.

I've been known to do stuff like:

class xmatch:
def set(self, v):
self.v = v
return v

then:

s = xmatch()

if s.set(pat.match(some_string)):
do_something(s.v)
elif s.set(pat.match(other_string)):
do_other_thing(s.v)
...

Bruno Desthuilliers

unread,
Feb 22, 2008, 4:45:26 AM2/22/08
to
Jeff Schwab a écrit :
> bruno.des...@gmail.com wrote:
(snip)

>> Explicitely using list.extend would make things clearer:
>>
>> def invoke_some_fct(parent):
>> parent.x.extend(['world'])
>
> Whether you use += or extend has nothing to do with it.

Mmm... Really ?

> You omitted the
> relevant part. Using extend, it would look like:
>
> y = parent.x
> y.extend(['world'])
>
> The confusing part is that performing an operation on y may or may not
> alter parent.x, depending on whether the initial type of parent.x is
> immutable.

given that Python doesn't copy anything unless explicitelly asked for, the

y = parent.x

statement has the very clear semantic of making y an alias of parent.x.
Mutability has nothing to do with it, except for the fact that if
parent.x is immutable there's indeed no way to mutate it.

> If parent.x is immutable, y is a copy of the value
> represented by parent.x,

No, by no way.

>>> class Parent(object): pass
...
>>> p = Parent()
>>> p.x = "aaa + bbb " # a string that won't be interned
>>> y = p.x
>>> y is p.x
True

y is *not* "a copy of x", it is another name bound to the very same object.

> and modifying y has not effect on the value of
> parent.x.

Your problem is with the semantic of "modifying". In Python, (re)binding
a name and mutating an object are two very distinct things.

If (the object referenced by) y is immutable, you *can not* modify (=>
mutate) it. Period.

And if you *rebind* y, this *won't* affect p.x, whether it's mutable or not:

>>> p = Parent()
>>> p.x = ['allo']
>>> y = p.x
>>> y is p.x
True
>>> y = ['la terre']
>>> y
['la terre']
>>> p.x
['allo']
>>> y is p.x
False
>>>


IOW - and while, you're right about this, I skipped the part that
trouble you, that is the aliasing of parent.x -, the observation that
using list.extend (that is, clearly a mutator method call) instead of
augmented assignment which *looks like* it's rebinding y (and would
effectively rebind it if y was immutable).

FWIW, it's IMHO a real wart - given Python's pretention at readability -
that augmented assignement has been implemented that way for lists.

> If (OTOH) parent.x is mutable, then x and y are really
> references to the same object, and modifications to that object via y
> can be observed via x. In C, you use pointers to get this effect.

Not quite the same thing. C variables are boxes (containing values),
with pointer's values being the coords of another box, while Python's
'variables' are only labels on objects - they *never* 'contains'
anything. (FWIW, someone recently posted a link to a very good
explanation of this, but I don't have it under hand right now - could
someone help ?)

With pointers, a function can modify the content of a box defined in the
caller's scope. This is not something you can do in Python - that is,
rebinding a formal parameter to a different object withing a function
won't affect the bindings in the caller's scope:

def rebinder(x):
print "before: x = %s (id: %s)" % (x, id(x))
x = ['bar']
print "after: x = %s (id: %s)" % (x, id(x))


def caller():
a = ['foo']
print "before: a = %s (id: %s)" % (a, id(a))
rebinder(a)
print "after: a = %s (id: %s)" % (a, id(a))

caller()


>
>> Now there's no reason to feel nervous about this. All you have to
>> remember is that Python never copy anything unless explicitely asked
>> for.
>
> It's not that simple. After a statement like:
>
> a = b
>
> Whether a and b denote the same object depends on what kind of object b
> represented in the first place.

No. You can bet your life on this : after this statement, a and b are
two labels for the very same object, *always*, *whatever* the type of b.

Now this is a very frequent cause of confusion for C/C++ programmers,
and it has been explained again and again here - usually far better than
I just did, so you may want to google for this.

HTH

Duncan Booth

unread,
Feb 22, 2008, 4:44:05 AM2/22/08
to
Carl Banks <pavlove...@gmail.com> wrote:

> Some Pythonistas will swear to their grave and back that should be
> done by factoring out the tests into a list and iterating over it, and
> NO OTHER WAY WHATSOEVER, but I don't buy it. That's a lot of
> boilerplate--the very thing Python is normally so good at minimizing--
> when it might not be needed. It would be the right thing for a
> complex, pluggable, customizable input filter; but is rarely a better
> solution for a simple text processing script.

I'll swear to my grave that there is always a better way than a lot of
nested regex conditions, and that may or may not involve a list but
there are plenty of other ways. That's why this is such a hard question
to answer definitively: every situatiuon has a different answer.

>
> Quick, at a glance, which code snippet will you understand faster
> (pretend you know Perl):
>
>
> if (/name=(.*)/) {
> $name = chop(\1);
> } elsif (/id=(.*)/) {
> $id = chop(\1);
> } elsif (/phone=(.*)/) {
> $phone = chop(\1);
> }

I get a headache with that: somehow I have to either magically know
which variable out of name, id and phone exists or I have to set all 3
variables with suitable defaults for the unset ones. Perl I believe will
allow access to unset variables but Python doesn't, so it looks like
I'll have a bunch of extra code to make sure they all get set.

>
>
> vs.
>

PATTERN = re.compile('(?:name=(.*))|(?:id=(.*))|(?:phone=(.*))')
...
m = PATTERN.match(argument)
if not m:
raise FormatError('bad input: %s' % argument)
name, id, phone = m.groups()

oops, not so much extra code to set them after all. Actually in practice
I'd probably use something more like:

PATTERN = re.compile('''(?:name=(?P<name>.*))
|(?:id=(?P<id>.*))
|(?:phone=(?P<phone>.*))''', re.VERBOSE)
...
m = PATTERN.match(argument)
if not m:
raise FormatError('bad input: %s' % argument)
values = m.groupdict()

as I find named groups much less error prone.

Steven D'Aprano

unread,
Feb 22, 2008, 7:29:41 AM2/22/08
to
On Thu, 21 Feb 2008 16:23:23 -0800, Jeff Schwab wrote:
> Steve Holden wrote:
[...]

>>>> if m = pat.match(some_string):
>>>> do_something(m)
>>>
>>> Yep, this is exactly what I am (was) trying to do. Oh well.... Any
>>> clever ideas on this front?
>>>
>> The syntax is the way it is precisely to discourage that kind of clever
>> idea. Of course, people nevertheless manage to work around the
>> restriction to try and make their Python read like some other language
>> they are more familiar with, and most of the time they get away with
>> it.
>>
>> The fat remains that in programming there *is* such a thing as being
>> too clever, and Python's syntax deliberately discourages that.
>
> This isn't "clever." It's what most of us were taught, from the
> beginning, was best practice. In some languages (including C and C++)
> the above is extremely common and perfectly acceptable.


Speak for yourself. *I* was never taught to abuse assignment by making it
an expression, and in the languages I have used, it isn't.

I'm sorry that your programming ability has been permanently harmed by
learning C at too early an age, but please don't imagine the rest of us
suffer from this disability. *wink*


--
Steven

Steven D'Aprano

unread,
Feb 22, 2008, 7:32:10 AM2/22/08
to
On Fri, 22 Feb 2008 08:12:56 +0000, Marc 'BlackJack' Rintsch wrote:

> A "variable" in
> programming languages is composed of a name, a memory location, possibly
> a type and a value. In C-like languages, where you put values in named
> and typed "boxes", the memory location and type are attached to the
> name. In Python both belong to the value.

But Python objects don't have names, so by your own definition, they
aren't variables. Names are associated with namespaces, not objects. A
name must have one and only one object bound to it at any one time;
objects on the other hand can be bound to one name, or no name, or a
thousand names. The object itself has no way of knowing what names it is
bound to, if any.

Or, to put it another way... Python doesn't have variables.

--
Steven

Steven D'Aprano

unread,
Feb 22, 2008, 9:58:54 AM2/22/08
to
On Fri, 22 Feb 2008 00:45:59 -0800, Carl Banks wrote:

> On Feb 21, 6:52 pm, Steve Holden <st...@holdenweb.com> wrote:
>> mrstephengross wrote:
>> >> What you can't do (that I really miss) is have a tree of
>> >> assign-and-test expressions:
>> >> import re
>> >> pat = re.compile('some pattern')
>> >> if m = pat.match(some_string):
>> >> do_something(m)
>>
>> > Yep, this is exactly what I am (was) trying to do. Oh well.... Any
>> > clever ideas on this front?
>>
>> The syntax is the way it is precisely to discourage that kind of clever
>> idea.
>
> Don't be ridiculous. Assignment operators are maybe one of the worst
> things in existence, but this particular use case (running a sequence of
> tests like the above) is perfectly useful and good.

I don't understand your reasoning. If assignment operators are so
terrible, why do you think the terribleness disappears in this specific
case?

The above idiom leads to one of the most common errors in C code: writing
= when you mean ==. "Running a sequence of tests" isn't immune to that
problem, it's especially vulnerable to it.

Compare the suggested pseudo-Python code:

pat = re.compile('some pattern')
if m = pat.match(some_string):
do_something(m)


with the actual Python code:

pat = re.compile('some pattern')

m = pat.match(some_string)
if m:
do_something(m)


The difference is exactly one newline plus one extra reference to the
name "m". And this is a problem?

> Some Pythonistas will swear to their grave and back that should be done
> by factoring out the tests into a list and iterating over it, and NO
> OTHER WAY WHATSOEVER, but I don't buy it.

Well, putting a sequence of tests into a list is the natural way to deal
with a sequence of tests. What else would you do?


> That's a lot of boilerplate

What boilerplate are you talking about?

> --the very thing Python is normally so good at minimizing--
> when it might not be needed. It would be the right thing for a complex,
> pluggable, customizable input filter; but is rarely a better solution
> for a simple text processing script.

Huh?


Why would you do that test in such an overblown fashion, then try to
pretend it is an apples-and-apples comparison with the Perl code? It
doesn't even work: you have three functions that set a local name, then
throw it away when they return.

Pretending I understand Perl, here's a fairer, more direct translation of
the Perl code:


name, id, phone = [None]*3 # Closest thing to an unset variable in Perl.
name = re.match(r"name=(.*)", line)
if name: name = name.group(1).strip()
else:
id = re.match(r"id=(.*)", line)
if id: id = id.group(1).strip()
else:
phone = re.match(r"phone=(.*)", line)
if phone: phone = phone.group(1).strip()

Six lines for Perl against nine for Python, eight if you dump the "unset"
line. Hardly a big difference.

The main difference is that Python's handling of regexes is a little more
verbose, and that the indentation is compulsory. But here's a better way
to do the same test:

tests = [ (r"name=(.*)", 'name'),
(r"id=(.*)", 'id'), (r"phone=(.*)", 'phone')]
for (test, name) in tests:
m = re.match(t, line)
if m:
globals()[name] = m.group(1).strip()
break

Down to seven lines, or six if the I didn't split the tests over two
lines.

Here's an even better way:

tests = [ "name", "id", "phone"]
for t in tests:
m = re.match(t + r"=(.*)", line)
if m:
globals()[t] = m.group(1).strip()
break

Six lines for Perl, six for Python, and the Python version is far more
readable.

Perl's treatment of assignment as an operator tempts the programmer to
write quick-and-dirty code. Python discourages that sort of behaviour,
and encourages programmers to factor their code in a structured way.
That's a feature, not a bug.

--
Steven

Marc 'BlackJack' Rintsch

unread,
Feb 22, 2008, 10:52:44 AM2/22/08
to
On Fri, 22 Feb 2008 12:32:10 +0000, Steven D'Aprano wrote:

> On Fri, 22 Feb 2008 08:12:56 +0000, Marc 'BlackJack' Rintsch wrote:
>
>> A "variable" in programming languages is composed of a name, a memory
>> location, possibly a type and a value. In C-like languages, where you
>> put values in named and typed "boxes", the memory location and type are
>> attached to the name. In Python both belong to the value.
>
> But Python objects don't have names, so by your own definition, they
> aren't variables.

Exactly! Names aren't variables. The unit of a name, an address, and a
value are a variable.

> Names are associated with namespaces, not objects. A name must have one
> and only one object bound to it at any one time;

What is a binding when it's not an association between a name and an
object!? So names are associated with objects. There are no names
without objects in Python. If a name is not bound to any object, how could
the name exist? That would be like a dangling pointer, a beast that
doesn't exists in Python.

<nitpick>Okay there are local names that are known and therefore somehow
"exist" before they get bound, but that's IMHO an implementation
detail.<nitpick>

> objects on the other hand can be bound to one name, or no name, or a
> thousand names. The object itself has no way of knowing what names it is
> bound to, if any.
>
> Or, to put it another way... Python doesn't have variables.

It has. You just can't substitute the term "name" with "variable" and
expect it to behave like in C. A variable is not just the name but also
the value and the storage space and how those are connected.

Ciao,
Marc 'BlackJack' Rintsch

Carl Banks

unread,
Feb 22, 2008, 11:19:07 AM2/22/08
to
On Feb 22, 9:58 am, Steven D'Aprano <st...@REMOVE-THIS-

cybersource.com.au> wrote:
> On Fri, 22 Feb 2008 00:45:59 -0800, Carl Banks wrote:
> > On Feb 21, 6:52 pm, Steve Holden <st...@holdenweb.com> wrote:
> >> mrstephengross wrote:
> >> >> What you can't do (that I really miss) is have a tree of
> >> >> assign-and-test expressions:
> >> >> import re
> >> >> pat = re.compile('some pattern')
> >> >> if m = pat.match(some_string):
> >> >> do_something(m)
>
> >> > Yep, this is exactly what I am (was) trying to do. Oh well.... Any
> >> > clever ideas on this front?
>
> >> The syntax is the way it is precisely to discourage that kind of clever
> >> idea.
>
> > Don't be ridiculous. Assignment operators are maybe one of the worst
> > things in existence, but this particular use case (running a sequence of
> > tests like the above) is perfectly useful and good.
>
> I don't understand your reasoning. If assignment operators are so
> terrible, why do you think the terribleness disappears in this specific
> case?

I don't.

The assignment operator is terrible but the idiom itself isn't.


[snip narrowly applicable counterexamples that don't really mean
anything because they're too narrow]

> Perl's treatment of assignment as an operator tempts the programmer to
> write quick-and-dirty code.

(The perl example wasn't using an assignment operator.)

> Python discourages that sort of behaviour,
> and encourages programmers to factor their code in a structured way.
> That's a feature, not a bug.

The set-and-test idiom is not necessarily quick and dirty, and for
many things it's more readable and a lot easier to follow than any of
indirect methods that are proposed.

The fact that sometimes an assignment operator is used to do this
doesn't change the usefulness of the idiom itself.

It can be done without assignment expressions. I gave a hypothetical
syntax for how it might be done in Python without them.

I can't help but to think that a lot of people's distaste for this
natural way to write certain logic is simply defensiveness about one
minor little thing that Python doesn't support (without workarounds).


Carl Banks

Steve Holden

unread,
Feb 22, 2008, 11:21:51 AM2/22/08
to pytho...@python.org
"Does" ... "Doesn't" ... "Does so!".

You guys are merely arguing about what you want to call the Python
assignment semantics you both understand perfectly well. This isn't
going to help anyone.

The fact of the matter is that when a Python name is bound to a value
the value is normally created in heap storage (with a few exceptions
like the pre-allocated objects such as None and the small integers, but
*never* directly in the namespace in which the name is being bound), and
the name is associated with a reference to the value.

I've said before that Python names are very similar to automatically
dereferenced pointer variables, and I suppose the same old arguments
will be trotted out against that utterance now I've said it again.

But for the purposes of comprehension, particularly by C and C++
programmers who haven't come across this particular semantic before it
should server to aid comprehension. The fact that objects exist
independent of the dynamically created scopes of function calls and the
like is precisely what stops Python from suffering the same out-of-scope
(dangling) pointer issues that C++ is famous for.

The fact remains that name binding in Python (and binding to container
items too) doesn't "return a value", and bindings were deliberately not
allowed as a term in a broader expression to avoid some common
programming errors.

Steve Holden

unread,
Feb 22, 2008, 11:52:00 AM2/22/08
to pytho...@python.org
I'm supposed to overlook the fact that your example in Python omits the
"untested" it clearly deserves, I take it? I'm not sure what you are
trying to do with the assignments inside the function body.

The brevity of the Perl has something to commend it, but I am always
suspicious about whether algorithms like that should really be data
driven. It's all too easy to add further tests as new field
possibilities are added. It's also unpleasant in that it leaves two
variables in an undetermined state.

Let's assume that your Python functions were correctly assigning to
attributes of some object that was being passed in or global, at least
then it would be possible to add an else condition to each iteration to
set the attribute's default value somehow.

So I think your example is perhaps not the best one you could have
chosen to make your case.

I will admit that idiomatic usages are acceptable ways to perform common
tasks, but I still think that Guido's decision to eschew assignments as
expression terms is a sound one, and one that encourages better program
construction.

Hey, call me (or my assertions) ridiculous if you want. It remains that
allowing such terms will inevitably lead to hard-to-debug confusion
between assignment and equality testing once the difference becomes a
single equals sign.

> If it were a priority, Python could support this set-and-test idiom,
> and without an assignment operator. (Notice Perl doesn't use
> assignment operator here.) For example, a syntax like this (where the
> scope of m is local to the if-condition and the body of the if-
> statement:
>
> if m where m = re.match(r"name=(.*)",line):
> name = m.group(1).strip()
> elif m where m = re.match(r"id=(.*)",line):
> id = m.group(1).strip()
> elif m where m = re.match(r"phone=(.*)",line):
> phone = m.group(1).strip()
>

[I'll presume you've already established default values for name, id and
phone just to quiet the alarms ringing in the background]. Still looks
to me like it might be better data-driven with a setattr() in there
somewhere. As far as I can see all this would achieve would be to limit
the scope of the assignment, and I don't really see what advantage that
provides.


>
> This won't happen because the set-and-test idiom is relatively minor
> and not deemed worthy of syntax support. But if it were there,
> there'd really be nothing clever about it.
>

Note also that I'm not saying an experienced programmer can't get these
things right. But at any given time half the programmers in the world
are newbies, and Python tries to help them by steering them in safer
directions.

Maybe we could allow it if you had a digital certificate asserting that
you'd passed your metaclass abuse test ... <0.75 wink>

Steven D'Aprano

unread,
Feb 22, 2008, 1:16:06 PM2/22/08