Return value of an assignment statement?

J

Jeff Schwab

Arnaud said:
You don't need to pass the pattern, just pass the match function:

def find_action(match, actions=actions, default_action=None):
for string, action in actions:
if match(string):
return action
return default_action

find_action(re.compile('some pattern').match)()

That's cool. :)
 
J

John Henry

You know what's illuminating the discussion? Everybody thinks they
understand this issue, but the explanations are contradictory. It seems
like half the folks think this is an issue of mutability vs.
immutability, and the other half believe that has nothing to do with it.

You mean like this: :=)

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

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

hw = abc()
 
T

Tim Roberts

Marc 'BlackJack' Rintsch said:
It's just too convenient to be able to write

L += ['foo']

without rebinding L.

<nitpick>But ``+=`` does rebind.</nitpick>

Usually, but there's an exception for lists, which a specific
implementation for += that calls "append". Or do I misunderstand you?

C:\tmp>python
Python 2.4.4 (#71, Oct 18 2006, 08:34:43) [MSC v.1310 32 bit (Intel)] on
win32
Type "help", "copyright", "credits" or "license" for more information.
L = [1,2,3]
id(L) 10351000
L += [4]
id(L) 10351000
 
T

Terry Reedy

|
| >On Fri, 22 Feb 2008 11:00:17 -0800, Aahz wrote:
| >
| >> It's just too convenient to be able to write
| >>
| >> L += ['foo']
| >>
| >> without rebinding L.
| >
| ><nitpick>But ``+=`` does rebind.</nitpick>
|
| Usually, but there's an exception for lists, which a specific
| implementation for += that calls "append". Or do I misunderstand you?

There is no exception at the compiler level, and indeed, cannot be, because
in general, the compiler *does not know* the types of either target or
augment.

target += augment

is compiled, in effect, as

compute target and load target_object
compute and load augment_object
call target_object.__iadd__(augment_object)
store return_object at target

In the list case, the rebinding is to the *same* object! --
because list.__iadd(l1,l2)__ returns l1 extended with l2 rather than a new
list l1+l2, as the specification allows.

Augmented (inplace) assignment is still assignment (binding).

| C:\tmp>python
| Python 2.4.4 (#71, Oct 18 2006, 08:34:43) [MSC v.1310 32 bit (Intel)] on
| win32
| Type "help", "copyright", "credits" or "license" for more information.
| >>> L = [1,2,3]
| >>> id(L)
| 10351000
| >>> L += [4]
| >>> id(L)
| 10351000

L has been rebound to the same object, hence same id. Unfortunately, such
rebinding is only visible (that I can think of) when it fails:

Traceback (most recent call last):
File "<pyshell#8>", line 1, in -toplevel-
t[0]+=[1]
TypeError: object does not support item assignment([1],)

The list comprising t[0] is extended by its __iadd__ method. The exception
arises due to the subsequent attempted rebinding to t[0] (see dis output
below), which is, of course, not permissible.
([1, 2],)

Extension without attempted illegal rebinding raises no error.

CPython specifics:
1 0 LOAD_FAST 0 (i)
3 LOAD_CONST 1 (1)
6 INPLACE_ADD
7 STORE_FAST 0 (i)
10 LOAD_FAST 0 (i)
13 RETURN_VALUE
def fl(l): l += [1]; return l
dis(fl)
1 0 LOAD_FAST 0 (l)
3 LOAD_CONST 1 (1)
6 BUILD_LIST 1
9 INPLACE_ADD
10 STORE_FAST 0 (l)
13 LOAD_FAST 0 (l)
16 RETURN_VALUE

Treating the VM stack as a Python list, INPLACE_ADD executes
something like

stack[-2] = stack[-2].__iadd__(stack[-1])
stack.pop() # leaving returned object on top of stack

Terry Jan Reedy
 
M

Marc 'BlackJack' Rintsch

Marc 'BlackJack' Rintsch said:
It's just too convenient to be able to write

L += ['foo']

without rebinding L.

<nitpick>But ``+=`` does rebind.</nitpick>

Usually, but there's an exception for lists, which a specific
implementation for += that calls "append". Or do I misunderstand you?

Terry Reedy showed the "tuple proof", here's the read only property case::

class A(object):
def __init__(self):
self._data = list()

@property
def data(self):
return self._data

a = A()
a.data += [42]

Output::

Traceback (most recent call last):
File "test.py", line 25, in <module>
a.data += [42]
AttributeError: can't set attribute

Ciao,
Marc 'BlackJack' Rintsch
 
A

Aahz

It's just too convenient to be able to write

L += ['foo']

without rebinding L.

<nitpick>But ``+=`` does rebind.</nitpick>

Doesn't matter in this case but we've had confused programmers asking
questions here when `L` is a class attribute and it's rebound to the
instance, or if they tried it on a list in a tuple. Extending a list
that's a read only property doesn't work either.

Fair point. And in fact the original primary use case for allowing the
target of augmented assignment to return self was NumPy. Being able to
use the simple syntax of augmented assignment instead of method calls in
order to avoid copying huge arrays was a big win. (Which you probably
know, but there are other people reading this thread who don't.)
 
B

Bruno Desthuilliers

Jeff Schwab a écrit :
That looks like the first realistic alternative I've seen. I find the
flow a little hard to follow, but I think that's mostly just because I'm
not accustomed to the syntax.

Possibly. It may also have to do with the fact that - as Carl mentions -
it's a bit overkill for quite a few cases.
Your approach fits in my head a little more comfortably if none of the
lines are longer than eighty columns, if the for-loop isn't given an
else-clause (which still looks to my untrained eye like it should match
the preceding if),

It tooks me some times to get used to the for/else construct, but I now
find it quite elegant. And helpful, in that it warns me that 'falling
thru' the iteration is expected to be a special case.
and if the break-statement is replaced with a
return-statement:

actions = (
('some_string', do_something),
('other_string', do_other_thing))

def find_action(pattern):
for string, action in actions:
m = pattern.match(string)
if m:
return action
return do_default_thing
>
find_action(re.compile('some pattern'))()

As far as I'm concerned, I wouldn't use a function only because I don't
yet feel confortable with for/else. And using a more generic version of
this function may not necessarily help, unless it's really a pattern in
your code (in which case the reader will quickly become familiar with it).

BTW, if you want a more generic version, here's one:

def find_action(test, choices, default):
for arg, action in choices:
result = test(arg)
if result:
return result, action
else:
return None, default


actions = (
('some_string', do_something),
('other_string', do_other_thing))
)

pat = re.compile(some_pattern)
test = lambda s: pat.match(s)
match, action = find_action(test, actions, do_default_thing)
action(match)
 
B

Bruno Desthuilliers

Paddy a écrit :
The Bruno transform? :)

I do not claim any paternity on this idiom. FWIW, must be (in one or
another variant) in quite a lot of texts / posts / sites / any other
resource about Python.
 
B

Bruno Desthuilliers

Jeff Schwab a écrit :
(snip)
This is apparently section 1.9 of the Python Cookbook:

http://www.oreilly.com/catalog/pythoncook2/toc.html

Martelli suggests something similar to the "thigamabob" technique I use
(he calls it DataHolder). It's really more like the "xmatch" posted by
Paul Rubin.

Martelli also says, though, that if you need this, you're not thinking
Pythonically.

I'd personnaly read this as: "for most concrete cases, there's a Python
idiom such that you don't need to set-and-test-at-once" - an example
being given for the concrete case of 'iterating over lines in a file'.
I don't know what the Pythonic alternative is. The
iterating-over-pairs approach suggested by Bruno is the only alternative
I've seen.

I guess it's considered the "pythonic alternative" for the concrete use
case where there's no better idiom !-)

Note that as far as I'm concerned, I don't have much problem with
get-and-set (which I commonly use in most other languages allowing it).
In fact I'd probably prefer an expression-based version of Python (now
would it still be Python is another question...) - but that's not been
so far a string enough reason to switch language...
 

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,774
Messages
2,569,598
Members
45,150
Latest member
MakersCBDReviews
Top