By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
425,534 Members | 1,830 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 425,534 IT Pros & Developers. It's quick & easy.

"indexed properties"...

P: n/a
Having a hard time phrasing this in the form
of a question...

The other day I saw a thread where someone asked
about overrideable properties and nobody offered
the advice that properties are Bad. So maybe we've
got over that. I suppose properties could have
Bad consequences if a user doesn't know they exist
and think that a certain property of an object is
just an ordinary attribute. But that applies to
almost any aspect of any language.

If a person comes from, say, Object Pascal (Delphi)
then properties are hard to live without. The
other day I decided I wanted what OP calls an
"indexed property" or "array property". Couldn't
figure out how to make a _property_ behave that way.
So I read a little bit about descriptors, and a
few minutes later I had an indexedproperty thing
that works just like property, except it gives
an indexed property! This is just too cool.

Why? For example, a Matrix should have a row[n]
property allowing things like

m.row[0] = m.row[1] + m.row[2]

Ok, you could do _that_ by just making row
an ordinary list of Row objects. But then
you'd have to say

m.row[0] = Row([1,2,3])

where I want to be able to say

m.row[0] = [1,2,3]

and have the Row created automatically.

_Also_ with these indexed properties my Matrix
can have m.row[j] and m.col[k] that look exactly
the same to a client - we don't want to store a
list of rows internally and also store the same
data in a list of columns. Too cool.

Hmm, none of that's a valid excuse for a post here.
Um, right, here we go: Anyone see problems or
possible improvements with the implementation
of indexedproperty below?

"""indexed.py: "indexedproperty" works more or less
like "property" except it gives what in Object Pascal
would be an "indexed property". See the
__name__="__main__" section below for a demo

"""

class WriteOnlyIP(Exception):
def __str__(self):
return """

indexed property is write-only

"""

class ReadOnlyIP(Exception):
def __str__(self):
return """

indexed property is read-only

"""

class indexedproperty(object):
def __init__(self, getitem=None, setitem=None):
self.getitem = getitem
self.setitem = setitem

def __get__(self, obj, owner):
self.obj = obj
return self

def __getitem__(self, index):
if self.getitem:
return self.getitem(self.obj, index)
else:
raise WriteOnlyIP

def __setitem__(self, index, value):
if self.setitem:
self.setitem(self.obj, index, value)
else:
raise ReadOnlyIP
if __name__ == "__main__":

class AClass(object):
def __init__(self):
self.cells = [[0,0], [0,0]]

def SetCell(self, (row, col), value):
self.cells[row][col] = value

def GetCell(self, (row, col)):
return self.cells[row][col]

cell = indexedproperty(GetCell, SetCell)

C = AClass()
for row in range(2):
for col in range(2):
C.cell[row, col] = "row: %s, col: %s" % (row, col)

for row in range(2):
for col in range(2):
print C.cell[row, col]

C.cell[0,0], C.cell[1,1] = C.cell[1,1], C.cell[0,0]

print "After C.cell[0,0], C.cell[1,1] = C.cell[1,1], C.cell[0,0]:"

for row in range(2):
for col in range(2):
print C.cell[row, col]

--
David C. Ullrich
Jun 27 '08 #1
Share this Question
Share on Google+
17 Replies


P: n/a
En Wed, 14 May 2008 18:15:41 -0300, David C. Ullrich
<du******@sprynet.comescribió:
Having a hard time phrasing this in the form
of a question...

The other day I saw a thread where someone asked
about overrideable properties and nobody offered
the advice that properties are Bad. So maybe we've
got over that. I suppose properties could have
Bad consequences if a user doesn't know they exist
and think that a certain property of an object is
just an ordinary attribute. But that applies to
almost any aspect of any language.

If a person comes from, say, Object Pascal (Delphi)
then properties are hard to live without. The
other day I decided I wanted what OP calls an
"indexed property" or "array property". Couldn't
figure out how to make a _property_ behave that way.
So I read a little bit about descriptors, and a
few minutes later I had an indexedproperty thing
that works just like property, except it gives
an indexed property! This is just too cool.

Why? For example, a Matrix should have a row[n]
property allowing things like

m.row[0] = m.row[1] + m.row[2]

Ok, you could do _that_ by just making row
an ordinary list of Row objects. But then
you'd have to say

m.row[0] = Row([1,2,3])

where I want to be able to say

m.row[0] = [1,2,3]

and have the Row created automatically.

_Also_ with these indexed properties my Matrix
can have m.row[j] and m.col[k] that look exactly
the same to a client - we don't want to store a
list of rows internally and also store the same
data in a list of columns. Too cool.

Hmm, none of that's a valid excuse for a post here.
Um, right, here we go: Anyone see problems or
possible improvements with the implementation
of indexedproperty below?

"""indexed.py: "indexedproperty" works more or less
like "property" except it gives what in Object Pascal
would be an "indexed property". See the
__name__="__main__" section below for a demo

"""

class WriteOnlyIP(Exception):
def __str__(self):
return """

indexed property is write-only

"""

class ReadOnlyIP(Exception):
def __str__(self):
return """

indexed property is read-only

"""

class indexedproperty(object):
def __init__(self, getitem=None, setitem=None):
self.getitem = getitem
self.setitem = setitem

def __get__(self, obj, owner):
self.obj = obj
return self

def __getitem__(self, index):
if self.getitem:
return self.getitem(self.obj, index)
else:
raise WriteOnlyIP

def __setitem__(self, index, value):
if self.setitem:
self.setitem(self.obj, index, value)
else:
raise ReadOnlyIP
if __name__ == "__main__":

class AClass(object):
def __init__(self):
self.cells = [[0,0], [0,0]]

def SetCell(self, (row, col), value):
self.cells[row][col] = value

def GetCell(self, (row, col)):
return self.cells[row][col]

cell = indexedproperty(GetCell, SetCell)

C = AClass()
for row in range(2):
for col in range(2):
C.cell[row, col] = "row: %s, col: %s" % (row, col)

for row in range(2):
for col in range(2):
print C.cell[row, col]

C.cell[0,0], C.cell[1,1] = C.cell[1,1], C.cell[0,0]

print "After C.cell[0,0], C.cell[1,1] = C.cell[1,1], C.cell[0,0]:"

for row in range(2):
for col in range(2):
print C.cell[row, col]


--
Gabriel Genellina

Jun 27 '08 #2

P: n/a
En Wed, 14 May 2008 18:15:41 -0300, David C. Ullrich <du******@sprynet.comescribió:
Having a hard time phrasing this in the form
of a question...

The other day I saw a thread where someone asked
about overrideable properties and nobody offered
the advice that properties are Bad. So maybe we've
got over that. I suppose properties could have
Bad consequences if a user doesn't know they exist
and think that a certain property of an object is
just an ordinary attribute. But that applies to
almost any aspect of any language.
Which "bad consequences" are you thinking of? Apart from the user not being aware of the complexity of certain operations, or a getter/setter very time-consuming (in that case I'd use public getXXX/setXXX functions instead, to emphasize that "something" is happening, not just an attribute lookup)
If a person comes from, say, Object Pascal (Delphi)
then properties are hard to live without. The
You should read the article "Python is not Java" if you haven't done it yet.
http://dirtsimple.org/2004/12/python-is-not-java.html
other day I decided I wanted what OP calls an
"indexed property" or "array property". Couldn't
figure out how to make a _property_ behave that way.
So I read a little bit about descriptors, and a
few minutes later I had an indexedproperty thing
that works just like property, except it gives
an indexed property! This is just too cool.

Why? For example, a Matrix should have a row[n]
property allowing things like

m.row[0] = m.row[1] + m.row[2]

Ok, you could do _that_ by just making row
an ordinary list of Row objects. But then
you'd have to say

m.row[0] = Row([1,2,3])

where I want to be able to say

m.row[0] = [1,2,3]

and have the Row created automatically.
One could make *row* an object with a custom __getitem__/__setitem__ in this case. But I prefer to have as little magic as possible on my objects: if it says m.row[0] = [1,2,3] I expect m.row[0] to actually *be* that list (whenever possible), and not any other object initialized from the [1,2,3] arguments. (Maybe this is some overreaction against C++ "magic" constructors and such horrible things...)
_Also_ with these indexed properties my Matrix
can have m.row[j] and m.col[k] that look exactly
the same to a client - we don't want to store a
list of rows internally and also store the same
data in a list of columns. Too cool.
The same can be achieved having m.row and m.col be custom objects like I said above.
Hmm, none of that's a valid excuse for a post here.
Um, right, here we go: Anyone see problems or
possible improvements with the implementation
of indexedproperty below?
Note that the property object (or your indexedproperty) is a *class* attribute. That is, shared among all instances.
class indexedproperty(object):
def __init__(self, getitem=None, setitem=None):
self.getitem = getitem
self.setitem = setitem

def __get__(self, obj, owner):
self.obj = obj
return self
Here is the problem. You can't store "obj" in "self" because it is shared among all instances. Your examples don't show any problem because all property accesses are immediately followed by a getitem access using the same object. Try this:

x = AClass()
y = AClass()
x.cell[0,0] = 1
print x.cell[1,1] # output: 0
y.cell[x.cell[0,0], x.cell[0,0]] = 2
print y.cell[1,1] # should be 2, still 0
print x.cell[1,1] # should still be 0, but changed, now 2

A workaround would be to return another object in __get__ instead of self, which remembers the "obj" instance, or a closure, or...
But I don't really see the point, it's a lot easier to use another object for .cell (it's more clear, doesn't break encapsulation, divides responsabilities...)

class Matrix2x2(object):
def __init__(self):
self.cells = [[0,0], [0,0]]

def __setitem__(self, (row, col), value):
self.cells[row][col] = value

def __getitem__(self, (row, col)):
return self.cells[row][col]

class AClass(object):
def __init__(self):
self.cell = Matrix2x2()
--
Gabriel Genellina

Jun 27 '08 #3

P: n/a
On Thu, 15 May 2008 10:59:41 -0300, "Gabriel Genellina"
<ga*******@yahoo.com.arwrote:
>En Wed, 14 May 2008 18:15:41 -0300, David C. Ullrich <du******@sprynet.comescribió:
>Having a hard time phrasing this in the form
of a question...

The other day I saw a thread where someone asked
about overrideable properties and nobody offered
the advice that properties are Bad. So maybe we've
got over that. I suppose properties could have
Bad consequences if a user doesn't know they exist
and think that a certain property of an object is
just an ordinary attribute. But that applies to
almost any aspect of any language.

Which "bad consequences" are you thinking of?
Didn't have anything specific in mind - there I was
just playing devil's advocate. I've seen a lot of people
here say things like "properties should only be used
when refactoring" without any explanation that I could
see of why properties were bad things. I was referring
to whatever problems they had in mind.
>Apart from the user not being aware of the complexity of certain operations, or a getter/setter very time-consuming (in that case I'd use public getXXX/setXXX functions instead, to emphasize that "something" is happening, not just an attribute lookup)
>If a person comes from, say, Object Pascal (Delphi)
then properties are hard to live without. The

You should read the article "Python is not Java" if you haven't done it yet.
http://dirtsimple.org/2004/12/python-is-not-java.html
I suspect you misunderstood my point there. I actually read that
article years ago. Been using Python for years. I'd never confuse
it with OP, I promise (luckily I don't know any Java and don't
intend to). I really didn't express what I meant very well - what
I should have said was more like "If you're accustomed to properties
from a language like OP they seem like a very useful addition to
Python".

Looking at the article it seems like two points are relevant.
(i) I'm well aware that chains of attribute accesses don't
get compiled away in Python the way they do in OP.

(ii) Regarding the bit about how getters and setters are
evil, and it's also bad to wrap things in properties when
simple attributes would do. Yes. Thanks for pointing
that out, but I wouldn't do that - using a property where
a simple attribute would do would be stupid. But they
_can_ be much nicer than having to use explicit getters
and setters, in cases where a simple attribute won't do.

I got started on this thinking about making myself a
nicer interface (nicer-seeming to me) to the wxPython
grid object. Being able to say

grid[row, col] = value

seems much nicer and more natural than

grid.SetCellValue(row, col, value)

(yes, _that_ one is just __get(set)item__,
no property needed), just as

window.pos = (x,y)

seems more natural than

window.SetPos(x,y);

in these cases the work involved in changing the cell
value or the window position is going to make the
extra overhead of the property interface irrelevant.
>other day I decided I wanted what OP calls an
"indexed property" or "array property". Couldn't
figure out how to make a _property_ behave that way.
So I read a little bit about descriptors, and a
few minutes later I had an indexedproperty thing
that works just like property, except it gives
an indexed property! This is just too cool.

Why? For example, a Matrix should have a row[n]
property allowing things like

m.row[0] = m.row[1] + m.row[2]

Ok, you could do _that_ by just making row
an ordinary list of Row objects. But then
you'd have to say

m.row[0] = Row([1,2,3])

where I want to be able to say

m.row[0] = [1,2,3]

and have the Row created automatically.

One could make *row* an object with a custom __getitem__/__setitem__ in this case.
But then [see below]
>But I prefer to have as little magic as possible on my objects: if it says m.row[0] = [1,2,3] I
expect m.row[0] to actually *be* that list (whenever possible), and not any other object initialized
from the [1,2,3] arguments. (Maybe this is some overreaction against C++ "magic" constructors and such horrible things...)
Whatever - the idea here is that m.row[0] is going to walk and quack
exactly like that list would, but also do other things that the liist
can't do, like you can add two of them.

(When you say anobject.aproperty = 2 you also expect aproperty to
be 2? But if aproperty is a property that's not necessarily so - this
seems like as much an argument against properties in general as
against my version. Or perversion, if you like.)
>_Also_ with these indexed properties my Matrix
can have m.row[j] and m.col[k] that look exactly
the same to a client - we don't want to store a
list of rows internally and also store the same
data in a list of columns. Too cool.

The same can be achieved having m.row and m.col be custom objects like I said above.
>Hmm, none of that's a valid excuse for a post here.
Um, right, here we go: Anyone see problems or
possible improvements with the implementation
of indexedproperty below?

Note that the property object (or your indexedproperty) is a *class* attribute. That is, shared among all instances.
>class indexedproperty(object):
def __init__(self, getitem=None, setitem=None):
self.getitem = getitem
self.setitem = setitem

def __get__(self, obj, owner):
self.obj = obj
return self

Here is the problem. You can't store "obj" in "self" because it is shared among all instances. Your examples don't show any problem because all property accesses are immediately followed by a getitem access using the same object. Try this:

x = AClass()
y = AClass()
x.cell[0,0] = 1
print x.cell[1,1] # output: 0
y.cell[x.cell[0,0], x.cell[0,0]] = 2
print y.cell[1,1] # should be 2, still 0
print x.cell[1,1] # should still be 0, but changed, now 2
Eewps. I did realize that the "indexedproperty" object was going to be
shared, but I thought this sort of problem would be taken care of by
the fact that self.obj was reset each time __get__ was called. I
guess not.

_A_ workaround would be to simply not have indexedproperties
be class attributes, instead saying self.whatever =
indexedproprty(whatever) in __init__.
>A workaround would be to return another object in __get__ instead of self, which remembers the "obj" instance, or a closure, or...
But I don't really see the point, it's a lot easier to use another object for .cell (it's more clear, doesn't break encapsulation, divides responsabilities...)

class Matrix2x2(object):
def __init__(self):
self.cells = [[0,0], [0,0]]

def __setitem__(self, (row, col), value):
self.cells[row][col] = value

def __getitem__(self, (row, col)):
return self.cells[row][col]

class AClass(object):
def __init__(self):
self.cell = Matrix2x2()
Well, something like this is where I was before coming up with
the indexedproperty thing. Unless I'm missing something, which
has happened before, what you write above can't work, because
self.cell has to know what Matrix2x2 created it so it knows
what AClass instance to modify when it's modified. (No, we
can't just leave the state information in self.cells; think of the
case of a Matrix with a row[] property and also a col[]
property - they have to be modifying the data stored in
the owning matrix.)

So it would have to be more something like (untested,
I don't have Python on this machine right now)

class Matrix2x2(object):
def __init__(self, owner):
self.owner = owner

def __setitem__(self, (row, col), value):
self.owner.data[row][col] = value

def __getitem__(self, (row, col)):
return self.owner.data[row][col]

class AClass(object):
def __init__(self):
self.cell = Matrix2x2(self)
self.data = [[0, 0], [0, 0]]

(again, it doesn't have to be like that in this case,
but it does in the case where various "properties"
like cells are giving different sorts of access to the
same data, which data then has to be stored in AClass.)

And then that's the point to the indexedproperty;
the code passing data back and forth doesn't need
to be rewritten with each property, it's all done
inside the implementation of the indexedproperty
class. I realized that I could just use object attributes
instead, but it seems to me that indexedproperty
encapsulates the data-passing protcol once and for
all instead of requiring it to be redone each time.

Thanks for the comments, especially for pointing out
the bug. Before doing this I reproduced a Python
implementation of "property". Gonna have to look
that up instead of figuring it out to make sure I got
it right, then look at it to see why property
doesn't have the same problem...
David C. Ullrich
Jun 27 '08 #4

P: n/a
(warning: it's a rather long message)

En Fri, 16 May 2008 12:58:46 -0300, David C. Ullrich
<du******@sprynet.comescribió:
On Thu, 15 May 2008 10:59:41 -0300, "Gabriel Genellina"
<ga*******@yahoo.com.arwrote:
>En Wed, 14 May 2008 18:15:41 -0300, David C. Ullrich
<du******@sprynet.comescribió:
>>The other day I saw a thread where someone asked
about overrideable properties and nobody offered
the advice that properties are Bad. So maybe we've
got over that. I suppose properties could have
Bad consequences if a user doesn't know they exist
and think that a certain property of an object is
just an ordinary attribute. But that applies to
almost any aspect of any language.

Which "bad consequences" are you thinking of?

Didn't have anything specific in mind - there I was
just playing devil's advocate. I've seen a lot of people
here say things like "properties should only be used
when refactoring" without any explanation that I could
see of why properties were bad things. I was referring
to whatever problems they had in mind.
I suppose those people were talking about this usage:

def getfoo(self): return self._foo
def setfoo(self, value): self._foo = value
foo = property(getfoo, setfoo)

Just use a plain foo attribute instead. Almost nobody will notice the
difference, and it's faster, easier to maintain, easier to test, less
magic... But if you *do* have more to do besides handling the _foo
attribute, using a property is OK, at least for me. And from your other
comments, I can see it's fine for you too.
>>If a person comes from, say, Object Pascal (Delphi)
then properties are hard to live without. The

You should read the article "Python is not Java" if you haven't done it
yet.
http://dirtsimple.org/2004/12/python-is-not-java.html

I suspect you misunderstood my point there. I actually read that
article years ago. Been using Python for years. I'd never confuse
it with OP, I promise (luckily I don't know any Java and don't
intend to). I really didn't express what I meant very well - what
I should have said was more like "If you're accustomed to properties
from a language like OP they seem like a very useful addition to
Python".
Oh, sure. I missed them a lot until they became available in Python. And I
even wrote a fake property mixin class for Python 2.1 (horrible and slow!).
Now I understand better what you're saying.
window.pos = (x,y)

seems more natural than

window.SetPos(x,y);

in these cases the work involved in changing the cell
value or the window position is going to make the
extra overhead of the property interface irrelevant.
Sure, that looks like a good candidate for using a property.
>But I prefer to have as little magic as possible on my objects: if it
says m.row[0] = [1,2,3] I
expect m.row[0] to actually *be* that list (whenever possible), and not
any other object initialized
from the [1,2,3] arguments. (Maybe this is some overreaction against
C++ "magic" constructors and such horrible things...)

Whatever - the idea here is that m.row[0] is going to walk and quack
exactly like that list would, but also do other things that the liist
can't do, like you can add two of them.

(When you say anobject.aproperty = 2 you also expect aproperty to
be 2? But if aproperty is a property that's not necessarily so - this
seems like as much an argument against properties in general as
against my version. Or perversion, if you like.)
No, I don't necesarily expect it, but I try to maintain that behavior as
long as I can; I try to keep things simple and intuitive. This is a rather
used idiom:

items = foo.items = []
for x in someiterator:
if somecondition:
...
items.append(x)
...

That works fine if foo.items is items - but if items were a property that
holds its own internal list, the code above would be broken. In that case,
replacing a simple attribute with a property would *not* be equivalent;
and I try to follow the "principle of least astonishment" and avoid such
situations as long as possible.

(I think that in OP the equivalent of m.row[0] = [1,2,3] isn't very usual
- mostly because there isn't a list literal, I presume)
_A_ workaround would be to simply not have indexedproperties
be class attributes, instead saying self.whatever =
indexedproprty(whatever) in __init__.
Unfortunately that doesn't work. Properties get their "magic" when
searched in the class namespace - a property set as an instance attribute
doesn't behave as a property at all, it's just an attribute as any other.
>class Matrix2x2(object):
def __init__(self):
self.cells = [[0,0], [0,0]]

def __setitem__(self, (row, col), value):
self.cells[row][col] = value

def __getitem__(self, (row, col)):
return self.cells[row][col]

class AClass(object):
def __init__(self):
self.cell = Matrix2x2()

Well, something like this is where I was before coming up with
the indexedproperty thing. Unless I'm missing something, which
has happened before, what you write above can't work, because
self.cell has to know what Matrix2x2 created it so it knows
what AClass instance to modify when it's modified. (No, we
can't just leave the state information in self.cells; think of the
case of a Matrix with a row[] property and also a col[]
property - they have to be modifying the data stored in
the owning matrix.)
The above class works fine in the simple cases I tested it - including the
bug involving two AClass instances. Adding "row" and "col" views is rather
simple:

class AClass(object):
def __init__(self):
self.cell = Matrix2x2()
self.row = ViewAsRow(self.cell)
self.col = ViewAsCol(self.cell)

class ViewAsRow(object):
def __init__(self, matrix):
self.cells = matrix.cells

def __getitem__(self, index):
return self.cells[index][:]

def __setitem__(self, index, row):
self.cells[index][:] = row

class ViewAsCol(object):
def __init__(self, matrix):
self.cells = matrix.cells

def __getitem__(self, index):
return [row[index] for row in self.cells]

def __setitem__(self, index, col):
for row, value in zip(self.cells, col):
row[index] = value

There is no need for the ViewAsCol nor ViewAsRow classes to know they're
being used inside an AClass instance. (A better design would avoid to use
matrix.cells - but I was constrained by the original Matrix2x2 design that
I didn't want to modify.)
(again, it doesn't have to be like that in this case,
but it does in the case where various "properties"
like cells are giving different sorts of access to the
same data, which data then has to be stored in AClass.)
But you may have many references to the *same* data and view it in
different ways - you don't have to walk thru the intermediate AClass
"container" because AClass doesn't "store" data; the data is "stored" in
the list itself, AClass instances only contain the name "cell" bound to
the Matrix2x2 object.

In OP terms, all objects in Python act like those inheriting from TObject,
not like basic types (numbers, char, string...). An important difference
is that Python takes care of automatic object deletion when they're no
longer used, but in OP you have to manage the object's lifetime yourself.
So in OP it is important *who* owns some object; and sharing a TList
instance -by example- isn't so frequent because when the list is freed all
its references become invalid (so one needs some kind of notification...)
In Python, on the other side, many objects may have a reference to the
same other one, like the Matrix above - it won't disappear until nobody
else references it. This sharing behavior may be a Good Thing or a Bad
Thing, depending on the circumstances. By example, the ViewAsRow class
above *could* return an actual list from the Matrix2x2 instance, but I
choose not to, to emphasize that it's a *view* of the underlying data
(else, modifications to the returned list would alter the data itself).
And then that's the point to the indexedproperty;
the code passing data back and forth doesn't need
to be rewritten with each property, it's all done
inside the implementation of the indexedproperty
class. I realized that I could just use object attributes
instead, but it seems to me that indexedproperty
encapsulates the data-passing protcol once and for
all instead of requiring it to be redone each time.
I don't understand what you call "data-passing protocol"; as far as I can
tell, there is no data-passing, only names refering to the same and unique
object instance.

--
Gabriel Genellina

Jun 27 '08 #5

P: n/a
On Sat, 17 May 2008 00:27:31 -0300, "Gabriel Genellina"
<ga*******@yahoo.com.arwrote:
>(warning: it's a rather long message)

En Fri, 16 May 2008 12:58:46 -0300, David C. Ullrich
<du******@sprynet.comescribió:
>On Thu, 15 May 2008 10:59:41 -0300, "Gabriel Genellina"
<ga*******@yahoo.com.arwrote:
>>En Wed, 14 May 2008 18:15:41 -0300, David C. Ullrich
<du******@sprynet.comescribió:

The other day I saw a thread where someone asked
about overrideable properties and nobody offered
the advice that properties are Bad. So maybe we've
got over that. I suppose properties could have
Bad consequences if a user doesn't know they exist
and think that a certain property of an object is
just an ordinary attribute. But that applies to
almost any aspect of any language.

Which "bad consequences" are you thinking of?

Didn't have anything specific in mind - there I was
just playing devil's advocate. I've seen a lot of people
here say things like "properties should only be used
when refactoring" without any explanation that I could
see of why properties were bad things. I was referring
to whatever problems they had in mind.

I suppose those people were talking about this usage:

def getfoo(self): return self._foo
def setfoo(self, value): self._foo = value
foo = property(getfoo, setfoo)
Yeah. _If_ that's what I had in mind then telling me Python
is not Java would have been a great service.

If I recall correctly (maybe not, haven't done any OP in
a long time) this is one of many things that OP gets righter
than Java: you can have the "read" or "write" for a property
refer to a method _or_ to an actual field; in the second
case access compiles to the same thing as in direct field
access.
>Just use a plain foo attribute instead. Almost nobody will notice the
difference, and it's faster, easier to maintain, easier to test, less
magic... But if you *do* have more to do besides handling the _foo
attribute, using a property is OK, at least for me. And from your other
comments, I can see it's fine for you too.
>>>If a person comes from, say, Object Pascal (Delphi)
then properties are hard to live without. The

You should read the article "Python is not Java" if you haven't done it
yet.
http://dirtsimple.org/2004/12/python-is-not-java.html

I suspect you misunderstood my point there. I actually read that
article years ago. Been using Python for years. I'd never confuse
it with OP, I promise (luckily I don't know any Java and don't
intend to). I really didn't express what I meant very well - what
I should have said was more like "If you're accustomed to properties
from a language like OP they seem like a very useful addition to
Python".

Oh, sure. I missed them a lot until they became available in Python. And I
even wrote a fake property mixin class for Python 2.1 (horrible and slow!).
Now I understand better what you're saying.
> window.pos = (x,y)

seems more natural than

window.SetPos(x,y);

in these cases the work involved in changing the cell
value or the window position is going to make the
extra overhead of the property interface irrelevant.

Sure, that looks like a good candidate for using a property.
>>But I prefer to have as little magic as possible on my objects: if it
says m.row[0] = [1,2,3] I
expect m.row[0] to actually *be* that list (whenever possible), and not
any other object initialized
from the [1,2,3] arguments. (Maybe this is some overreaction against
C++ "magic" constructors and such horrible things...)

Whatever - the idea here is that m.row[0] is going to walk and quack
exactly like that list would, but also do other things that the liist
can't do, like you can add two of them.

(When you say anobject.aproperty = 2 you also expect aproperty to
be 2? But if aproperty is a property that's not necessarily so - this
seems like as much an argument against properties in general as
against my version. Or perversion, if you like.)

No, I don't necesarily expect it, but I try to maintain that behavior as
long as I can; I try to keep things simple and intuitive. This is a rather
used idiom:

items = foo.items = []
for x in someiterator:
if somecondition:
...
items.append(x)
...

That works fine if foo.items is items - but if items were a property that
holds its own internal list, the code above would be broken. In that case,
replacing a simple attribute with a property would *not* be equivalent;
and I try to follow the "principle of least astonishment" and avoid such
situations as long as possible.
Right. Except that for the implementation I have in mind this
moring that code will work just fine. foo.items is going to be
a property of foo, so foo.items = [] will ...

Um. I just realized I have no idea what happens if
p = property(getp, setp) and later you say

(*) avariable = x.p = avalue

I'd hope that this first calls x.setp(avalue) and then
executes avariable = s.getp(). One second please,
while we fire up the other machine...

No, it doesn't do that.

Again, this doesn't have anything to do with lists,
it seems an unfortunate astonishment regarding
properties in general. If p is an attribute then the
effect of (*) is the same as x.p=value; avariable = x.p
but if p is a property that's not so.

In the implementation I have in mind I don't see
how there's going to be any astonishment other
than astonishments that can arise with properties
in general.

Ok, that was your point - even if p is a property
you'd prefer that x.p = value store value and
possibly do other things as well, instead of
storing something other than value.

(I _have_ been astonished at how unastonishing
my Rows are, for example I was surprised to see
that

def __add__(self, other):
return Row([x + y for x, y in zip(self, other)])

worked as hoped if self is a Row and other is a
Row _or_ a list.)

Anyway, my m.rows[j] can't be a list, because
I want to be able to say things like
m.rows[0] += c*m.rows[2]. Allowing
m.rows[j] = [1,2,3] seems convenient, while
requiring m.rows[j] = Row([1,2,3]) fixes
this problem. Hmm.
>(I think that in OP the equivalent of m.row[0] = [1,2,3] isn't very usual
- mostly because there isn't a list literal, I presume)
>_A_ workaround would be to simply not have indexedproperties
be class attributes, instead saying self.whatever =
indexedproprty(whatever) in __init__.

Unfortunately that doesn't work.
As I found when I tried it. Then I read the docs again a little
more closely...
>Properties get their "magic" when
searched in the class namespace - a property set as an instance attribute
doesn't behave as a property at all, it's just an attribute as any other.
>>class Matrix2x2(object):
def __init__(self):
self.cells = [[0,0], [0,0]]

def __setitem__(self, (row, col), value):
self.cells[row][col] = value

def __getitem__(self, (row, col)):
return self.cells[row][col]

class AClass(object):
def __init__(self):
self.cell = Matrix2x2()

Well, something like this is where I was before coming up with
the indexedproperty thing. Unless I'm missing something, which
has happened before, what you write above can't work, because
self.cell has to know what Matrix2x2 created it so it knows
what AClass instance to modify when it's modified. (No, we
can't just leave the state information in self.cells; think of the
case of a Matrix with a row[] property and also a col[]
property - they have to be modifying the data stored in
the owning matrix.)

The above class works fine in the simple cases I tested it - including the
bug involving two AClass instances. Adding "row" and "col" views is rather
simple:

class AClass(object):
def __init__(self):
self.cell = Matrix2x2()
self.row = ViewAsRow(self.cell)
self.col = ViewAsCol(self.cell)

class ViewAsRow(object):
def __init__(self, matrix):
self.cells = matrix.cells

def __getitem__(self, index):
return self.cells[index][:]

def __setitem__(self, index, row):
self.cells[index][:] = row

class ViewAsCol(object):
def __init__(self, matrix):
self.cells = matrix.cells

def __getitem__(self, index):
return [row[index] for row in self.cells]

def __setitem__(self, index, col):
for row, value in zip(self.cells, col):
row[index] = value

There is no need for the ViewAsCol nor ViewAsRow classes to know they're
being used inside an AClass instance.
Well, no. But they do need to preserve that reference to that
Matrix2x2 object.
>(A better design would avoid to use
matrix.cells - but I was constrained by the original Matrix2x2 design that
I didn't want to modify.)
>(again, it doesn't have to be like that in this case,
but it does in the case where various "properties"
like cells are giving different sorts of access to the
same data, which data then has to be stored in AClass.)

But you may have many references to the *same* data and view it in
different ways - you don't have to walk thru the intermediate AClass
"container" because AClass doesn't "store" data; the data is "stored" in
the list itself, AClass instances only contain the name "cell" bound to
the Matrix2x2 object.
Well of course. My point was just that it had to be somewhat
more complicated than the original code you gave if we wanted
to have two views of the same data.
>In OP terms, all objects in Python act like those inheriting from TObject,
not like basic types (numbers, char, string...). An important difference
is that Python takes care of automatic object deletion when they're no
longer used, but in OP you have to manage the object's lifetime yourself.
So in OP it is important *who* owns some object; and sharing a TList
instance -by example- isn't so frequent because when the list is freed all
its references become invalid (so one needs some kind of notification...)
In Python, on the other side, many objects may have a reference to the
same other one, like the Matrix above - it won't disappear until nobody
else references it. This sharing behavior may be a Good Thing or a Bad
Thing, depending on the circumstances. By example, the ViewAsRow class
above *could* return an actual list from the Matrix2x2 instance, but I
choose not to, to emphasize that it's a *view* of the underlying data
(else, modifications to the returned list would alter the data itself).
>And then that's the point to the indexedproperty;
the code passing data back and forth doesn't need
to be rewritten with each property, it's all done
inside the implementation of the indexedproperty
class. I realized that I could just use object attributes
instead, but it seems to me that indexedproperty
encapsulates the data-passing protcol once and for
all instead of requiring it to be redone each time.

I don't understand what you call "data-passing protocol"; as far as I can
tell, there is no data-passing, only names refering to the same and unique
object instance.
I didn't say what I meant very well. Your ViewAsRow does require
a reference to data held elsewhere; that reference has to be passed
in the constructor and used in __get/setitem__.

I think what I should do is set something up like your thing
above (except that in fact there's no reason for AClass,
it's actually Matrix I care about, AClass just crept into the
discussion because it was in the trivial example in the original),
modified so it stores a list of Row objects instead of a list of
lists.

Then concoct an indexedattribute class (ignoring descriptors)
that lets me say

class Matrix2x2(object)
def __init__(self, data):
...
self.rows = indexedattribute(self.getrow, self.setrow)
self.cols = indexedattribute(self.getcol, self.setcol)

def getrow(self, index)
...

and then compare the two. I suspect that the second
Matrx2x2 is going to be simpler because of things
handled automatically by the indexedattribute class
(counting the code in that class as part of the
"complexity" doesn't count, since I only write that
class once and get to use it for my matrices and
also my wxGrids...)

Don't hold your breath, I have other things to do
in the next few days. Thanks again for pointing
out the bug in the original. (It might never have
come up - I don't see why I'd use a cell in one
matrix as an index to another matrix. But regardless
it makes the original totally unacceptable, and
if it _did_ come up when I was using it it would
have taken me some time to figure out what the
problem was...)
David C. Ullrich
Jun 27 '08 #6

P: n/a
In article <hj********************************@4ax.com>,
du******@sprynet.com says...
window.pos = (x,y)

seems more natural than

window.SetPos(x,y);
Yes, and to assign a row in a matrix I'd also like to use either tuples
or lists on the right side.
def __add__(self, other):
return Row([x + y for x, y in zip(self, other)])

worked as hoped if self is a Row and other is a
Row _or_ a list.)

Anyway, my m.rows[j] can't be a list, because
I want to be able to say things like
m.rows[0] += c*m.rows[2]. Allowing
m.rows[j] = [1,2,3] seems convenient, while
requiring m.rows[j] = Row([1,2,3]) fixes
this problem. Hmm.
I'm not sure how far you'd go along with Gabriel's idea of decoupling
views from data but I think there isn't even a need for using a matrix
as the underlying data type. Why not use a flat list and compute matrix
positions according to a row or column adressing scheme on the fly?

P.

Jun 27 '08 #7

P: n/a
On Sun, 18 May 2008 08:50:23 +0200, pataphor <pa******@gmail.com>
wrote:
>In article <hj********************************@4ax.com>,
du******@sprynet.com says...
> window.pos = (x,y)

seems more natural than

window.SetPos(x,y);

Yes, and to assign a row in a matrix I'd also like to use either tuples
or lists on the right side.
Heh - in the current (flawed) implementation you can
say m.row[0] = 'Hello'; m.row[1]= 'World!' .Not that I see why
you'd want to, but that happened in the test code just to
check that things were as general as I wanted - sure enough
m.row[0] + m.row[1] comes out to ['HW', 'eo', 'lr', ''ld', 'o!'].
> def __add__(self, other):
return Row([x + y for x, y in zip(self, other)])

worked as hoped if self is a Row and other is a
Row _or_ a list.)

Anyway, my m.rows[j] can't be a list, because
I want to be able to say things like
m.rows[0] += c*m.rows[2]. Allowing
m.rows[j] = [1,2,3] seems convenient, while
requiring m.rows[j] = Row([1,2,3]) fixes
this problem. Hmm.

I'm not sure how far you'd go along with Gabriel's idea of decoupling
views from data but I think there isn't even a need for using a matrix
as the underlying data type. Why not use a flat list and compute matrix
positions according to a row or column adressing scheme on the fly?
Is there some reason that would be better? It would make a lot
of the code more complicated. Ok, it would require only one
bit of added code, I suppose, but I don't see the plus side.

Hmm. It might actually _reduce_ the total amount of code,
since the code to access columns has to exist anyway and
rows could use the same code as columns with different
"start" and "skip". And come to think of it rows and
columns could be obtained just with a slice of the data.
So column access might even be more efficient. But I
expect that row access will happen a lot more often
than column access.
>P.
David C. Ullrich
Jun 27 '08 #8

P: n/a
In article <dt********************************@4ax.com>,
du******@sprynet.com says...
Is there some reason that would be better? It would make a lot
of the code more complicated. Ok, it would require only one
bit of added code, I suppose, but I don't see the plus side.
The plus side is you give up an untenable position :-) And to address an
item in a matrix costs two lookups, row and column, while an array needs
only one.

P.
Jun 27 '08 #9

P: n/a
On Sun, 18 May 2008 18:18:34 +0200, pataphor <pa******@gmail.com>
wrote:
>In article <dt********************************@4ax.com>,
du******@sprynet.com says...
>Is there some reason that would be better? It would make a lot
of the code more complicated. Ok, it would require only one
bit of added code, I suppose, but I don't see the plus side.

The plus side is you give up an untenable position :-)
Maybe you could be more specific? Various "positions" I've
taken in all this may well be untenable, but I can't think
of any that have anything to do with whether the data should
be a single list instead of a list of lists.

(The only way I can parse this to make it relevant is to
assume that the position you're referring to is that a
list of lists is better than a single list. If so: First, I
haven't said that it was. Second, saying "B is untenable"
is not much of an answer when someone asks why you
say A is better than B.)
>And to address an
item in a matrix costs two lookups, row and column, while an array needs
only one.
The phrase "premature optimization" springs to mind.

This is _Python_ we're talking about. Supposing you're right that
doing two lookups _in Python_ is faster than doing one lookup
plus the calculuation col + row*width _in Python_, it can't
make enough difference to matter. In the sort of application I
have in mind things already happen "instantaneously".

The point is not to improve on NumPy. Trying to improve on
NumPy in pure Python code would be silly - if I wanted
optimized large matrices I'd _use_ NumPy. The point is just
to give a simple "intuitive" way to manipulate rows and
columns in small matrices.

So I'm not looking ahead to the future, things are not
scalable? The thing is not _supposed_ to scale up to
large matricies. If a person were dealing with large
matricies then almost all of it would need to be
rewritten (and if a person were dealing with really
large matrices then trying to do the thing in pure
Python would be silly in the first place, and insisting
on being able to write things like "m.row[0] =
m.row[1] + m.row[2]" could very well be a totally
wrong approach to begin with - I'd figure out the
operations I expected to need to do and write functions
to do them.)

Really. In one of the intended applications the matrix
entries are going to be home-made Rationals. Just
adding two of those guys takes a long time. It's
still more than fast enough for the intended application,
but [oh, never mind.

Sorry about the argumentative tone - I _would_ like
to know which "untenable position" you're referring to...
>P.
David C. Ullrich
Jun 27 '08 #10

P: n/a
On Mon, 19 May 2008 06:29:18 -0500
David C. Ullrich <du******@sprynet.comwrote:
Maybe you could be more specific? Various "positions" I've
taken in all this may well be untenable, but I can't think
of any that have anything to do with whether the data should
be a single list instead of a list of lists.
What's 'untenable' (hey, I tried to get away with a smiley, remember)
is that a matrix is a list of rows. Suppose you do the transpose trick
with the zip(*M) routine, now it's a list of columns. Both views are
equal, there is no getting around the fact that you're holding an
unnatural predisposition towards seeing the matrix as a list of rows,
which it is most definitely not ...

I was holding the brakes for this argument because I realize it's
intuitive and also because Gabriel seems to want a list stay a list if
he assigns something a list. But that's untenable too. Suppose you
assign a column to a list? The list is torn to shreds and placed over
multiple rows.
(The only way I can parse this to make it relevant is to
assume that the position you're referring to is that a
list of lists is better than a single list. If so: First, I
haven't said that it was. Second, saying "B is untenable"
is not much of an answer when someone asks why you
say A is better than B.)
Yes, it was not much of an answer but I was afraid of ending up in
this quagmire. I now see that it is unavoidable anyway if I want to
explain myself. Why couldn't you just see it the same way as me and
leave it at that without waking up all the creatures of hell :-)
And to address an
item in a matrix costs two lookups, row and column, while an array
needs only one.

The phrase "premature optimization" springs to mind.
Well, I really liked your slicing idea ...
This is _Python_ we're talking about. Supposing you're right that
doing two lookups _in Python_ is faster than doing one lookup
plus the calculuation col + row*width _in Python_, it can't
make enough difference to matter. In the sort of application I
have in mind things already happen "instantaneously".
The computation is almost certainly faster. Lookups are expensive.
However I concede the point because we're not supposed to worry about
such stuff. But it *is* a simpler format.
The point is not to improve on NumPy. Trying to improve on
NumPy in pure Python code would be silly - if I wanted
optimized large matrices I'd _use_ NumPy. The point is just
to give a simple "intuitive" way to manipulate rows and
columns in small matrices.
Yes, me too. This is all about intuition.
So I'm not looking ahead to the future, things are not
scalable? The thing is not _supposed_ to scale up to
large matricies. If a person were dealing with large
matricies then almost all of it would need to be
rewritten (and if a person were dealing with really
large matrices then trying to do the thing in pure
Python would be silly in the first place, and insisting
on being able to write things like "m.row[0] =
m.row[1] + m.row[2]" could very well be a totally
wrong approach to begin with - I'd figure out the
operations I expected to need to do and write functions
to do them.)
The reason why I am interested in this is that since I was writing
sudoku algorithms some time ago I have been looking for ways to interact
with data according to different views. I want the data to update even
when I have made changes to them according to another view. In my case
things are even more complex than they are with matrices because I
tend to view sudoku as subcases of binary cubes. Imagine a 3d 9*9*9
chessboard and try to place 81 non-threatening rooks in it. This is not
quite a solution to a sudoku but every sudoku is also a solution to this
problem.

One of the solution strategies I thought of was forgetting about the 3d
binary cube's content at all, and just update row, column and file
totals (I start with a 'filled' cube and wipe away fields that are
covered by the 'rooks') to drive the optimization. Somehow this seems
possible even though I do not use the cube itself anymore. It just
exists as a figment of my imagination but still it defines the context.

I hope you understand how this was driving me crazy and why I would be
more than happy to return to a safe and sound, actually 'existing' cube,
if only there was a way to access rows, columns and files (for example
sum their elements) as shared data. In the end I realized that
everything I was doing was an abstraction anyway and if that is the
case why not use the simplest possible representation for the data and
let any matrices, rows, columns, files, cubes and so on exist somewhere
higher up in the levels of abstraction of the code.
Really. In one of the intended applications the matrix
entries are going to be home-made Rationals. Just
adding two of those guys takes a long time. It's
still more than fast enough for the intended application,
but [oh, never mind.
Too late :-)
Sorry about the argumentative tone - I _would_ like
to know which "untenable position" you're referring to...
No, it's no problem. Thanks again for bringing this up. Once I overcame
my initial resistance to bringing up all this old (for me) issues I was
more than happy to share intuitions. I hope this somehow results in a
shared data type. Even just a matrix_with_a_view would be very nice.

P.
Jun 27 '08 #11

P: n/a
On Mon, 19 May 2008 14:48:03 +0200, pataphor <pa******@gmail.com>
wrote:
>On Mon, 19 May 2008 06:29:18 -0500
David C. Ullrich <du******@sprynet.comwrote:
>Maybe you could be more specific? Various "positions" I've
taken in all this may well be untenable, but I can't think
of any that have anything to do with whether the data should
be a single list instead of a list of lists.

What's 'untenable' (hey, I tried to get away with a smiley, remember)
Perhaps I should say that I was in no mood for smileys yesterday
morning: I hadn't eaten in 24 hours, hadn't had any water in
12 hours, and was anticipating an unpleasane instance of
what my doctor likes to call a "procedure" coming up in
a few hours. (Yes, everything went fine, thanks for asking -
today all is sweetness and light again.)
>is that a matrix is a list of rows. Suppose you do the transpose trick
with the zip(*M) routine, now it's a list of columns. Both views are
equal, there is no getting around the fact that you're holding an
unnatural predisposition towards seeing the matrix as a list of rows,
which it is most definitely not ...
Well, ok. Like I said, I never _took_ the position that it _should_
be a list of lists, I just said I didn't see the advantage to using
a single list.

Yes, the asummetry in my setup might be regarded as an
aesthetic flaw. But that doesn't mean it doesn't work right,
and in any case _I_ regard it as a _feature_: rows and
columns look the same to the user even though they're
very different under the hood.

Although they're going to look the same, in the applications
I have in mind I expect that row operations will be more
frequent than column operations, so if we _were_ going
to worry about optimizing things optimizing row access
might be reasonable.
>I was holding the brakes for this argument because I realize it's
intuitive and also because Gabriel seems to want a list stay a list if
he assigns something a list. But that's untenable too. Suppose you
assign a column to a list? The list is torn to shreds and placed over
multiple rows.
His desire here makes a lot more sense to me than it
seemed to at first, when he pointed out the problems
with "arow = m.row[0] = []". But this is a good point;
if we have rows _and_ columns then it seems like
he really can't have it his way.

Today's little joke: Long ago I would have solved
this by storing the data as a list of rows and _also_
a list of columns, updating each one any time the
other changed. Just goes to show you things
could always be worse...
>(The only way I can parse this to make it relevant is to
assume that the position you're referring to is that a
list of lists is better than a single list. If so: First, I
haven't said that it was. Second, saying "B is untenable"
is not much of an answer when someone asks why you
say A is better than B.)

Yes, it was not much of an answer but I was afraid of ending up in
this quagmire. I now see that it is unavoidable anyway if I want to
explain myself. Why couldn't you just see it the same way as me and
leave it at that without waking up all the creatures of hell :-)
Sorry.
>And to address an
item in a matrix costs two lookups, row and column, while an array
needs only one.

The phrase "premature optimization" springs to mind.

Well, I really liked your slicing idea ...
>This is _Python_ we're talking about. Supposing you're right that
doing two lookups _in Python_ is faster than doing one lookup
plus the calculuation col + row*width _in Python_, it can't
make enough difference to matter. In the sort of application I
have in mind things already happen "instantaneously".

The computation is almost certainly faster. Lookups are expensive.
I know one thing about Python: I don't know exactly how it works,
and hence it's very difficult to be certain about such things without
actually testing them. Which is not to say you're not right.
>However I concede the point because we're not supposed to worry about
such stuff. But it *is* a simpler format.
No wait, I know _two_ things about Python: (i) [repeat
above] (ii) we're supposed to worry about such things
_after_ determining that this particular such thing is
actually the bottleneck. It seems incredibly unlikely
that this detail is going to have any significance at
all in the final product.
>The point is not to improve on NumPy. Trying to improve on
NumPy in pure Python code would be silly - if I wanted
optimized large matrices I'd _use_ NumPy. The point is just
to give a simple "intuitive" way to manipulate rows and
columns in small matrices.

Yes, me too. This is all about intuition.
>So I'm not looking ahead to the future, things are not
scalable? The thing is not _supposed_ to scale up to
large matricies. If a person were dealing with large
matricies then almost all of it would need to be
rewritten (and if a person were dealing with really
large matrices then trying to do the thing in pure
Python would be silly in the first place, and insisting
on being able to write things like "m.row[0] =
m.row[1] + m.row[2]" could very well be a totally
wrong approach to begin with - I'd figure out the
operations I expected to need to do and write functions
to do them.)

The reason why I am interested in this is that since I was writing
sudoku algorithms some time ago I have been looking for ways to interact
with data according to different views. I want the data to update even
when I have made changes to them according to another view. In my case
things are even more complex than they are with matrices because I
tend to view sudoku as subcases of binary cubes. Imagine a 3d 9*9*9
chessboard and try to place 81 non-threatening rooks in it. This is not
quite a solution to a sudoku but every sudoku is also a solution to this
problem.

One of the solution strategies I thought of was forgetting about the 3d
binary cube's content at all, and just update row, column and file
totals (I start with a 'filled' cube and wipe away fields that are
covered by the 'rooks') to drive the optimization. Somehow this seems
possible even though I do not use the cube itself anymore. It just
exists as a figment of my imagination but still it defines the context.

I hope you understand how this was driving me crazy and why I would be
more than happy to return to a safe and sound, actually 'existing' cube,
if only there was a way to access rows, columns and files (for example
sum their elements) as shared data. In the end I realized that
everything I was doing was an abstraction anyway and if that is the
case why not use the simplest possible representation for the data and
let any matrices, rows, columns, files, cubes and so on exist somewhere
higher up in the levels of abstraction of the code.
Yes, that's interesting and explains why you found the current
discussion interesting.
>Really. In one of the intended applications the matrix
entries are going to be home-made Rationals. Just
adding two of those guys takes a long time. It's
still more than fast enough for the intended application,
but [oh, never mind.

Too late :-)
>Sorry about the argumentative tone - I _would_ like
to know which "untenable position" you're referring to...

No, it's no problem. Thanks again for bringing this up. Once I overcame
my initial resistance to bringing up all this old (for me) issues I was
more than happy to share intuitions. I hope this somehow results in a
shared data type. Even just a matrix_with_a_view would be very nice.
Expressing interest is a big mistake...

My current Matrix is broken (thanks again, Gabriel).
And no, come to think of it, as he points out there's
really no reason descriptots should be involved.

Sometime soon a thread will appear titled something
like "indexed attributes" with a stripped-down but
functional example of what I have in mind for Matrix
(including enough complication to illustrate why
the way I'm doing seems to _me_ to be simpler
than alternatives that have been suggested).
>P.
David C. Ullrich
Jun 27 '08 #12

P: n/a
On Tue, 20 May 2008 06:12:01 -0500
David C. Ullrich <du******@sprynet.comwrote:
Well, ok. Like I said, I never _took_ the position that it _should_
be a list of lists, I just said I didn't see the advantage to using
a single list.
I'm now thinking about a list of lists containing single element lists.

def test():
n = 3
R = range(n)
M = [[[i+j*n] for i in R] for j in R]
for L in M:
print L
row2 = M[1]
print
print row2
col3 = [L[2] for L in M]
print col3
col3[1][0]=9

print col3
print row2
print
for L in M:
print L

if __name__=='__main__':
test()

The idea is to use a single element list as kind of storage facility.
That enables me to copy a column, then change something in that column
and make the change turn up in the matrix and even in a previously made
copy of a row that shared the specific element.
Today's little joke: Long ago I would have solved
this by storing the data as a list of rows and _also_
a list of columns, updating each one any time the
other changed. Just goes to show you things
could always be worse...
Well have I ever ... I only thought about this last week and I
actually thought it was a *good* idea. I only gave up on it because
now I would have to keep track of how far the two views are out of
sync, because some operation could use data that was modified by an
other view. Zipstar transposition is very handy for syncing, but it is
also very expensive. But probably not for you.
Expressing interest is a big mistake...
How so? ;-)
Sometime soon a thread will appear titled something
like "indexed attributes" with a stripped-down but
functional example of what I have in mind for Matrix
I can't wait! Keep up the good work.

P.

Jun 27 '08 #13

P: n/a
In article <20080520154802.4b5df647@hyperspace>,
pataphor <pa******@gmail.comwrote:
On Tue, 20 May 2008 06:12:01 -0500
David C. Ullrich <du******@sprynet.comwrote:
Well, ok. Like I said, I never _took_ the position that it _should_
be a list of lists, I just said I didn't see the advantage to using
a single list.

I'm now thinking about a list of lists containing single element lists.

def test():
n = 3
R = range(n)
M = [[[i+j*n] for i in R] for j in R]
for L in M:
print L
row2 = M[1]
print
print row2
col3 = [L[2] for L in M]
print col3
col3[1][0]=9

print col3
print row2
print
for L in M:
print L

if __name__=='__main__':
test()

The idea is to use a single element list as kind of storage facility.
That enables me to copy a column, then change something in that column
and make the change turn up in the matrix and even in a previously made
copy of a row that shared the specific element.
Today's little joke: Long ago I would have solved
this by storing the data as a list of rows and _also_
a list of columns, updating each one any time the
other changed. Just goes to show you things
could always be worse...

Well have I ever ... I only thought about this last week and I
actually thought it was a *good* idea.
Sorry. If you really want to do this you could keep things
in sync automatically by writing the appropriate __setitem__
and resolving never to modify the data except through that...

def whatever.__setitem__(self, (row,col), value):
self.rows[row][col] = value
self.cols[col][row] = value

Seems to me like an, um, "untenable" waste of space. And you'd
need to be certain to never set anything except through
self[row,col]; as far as I can see anything like setrow()
would be a loop "for col in range(width): self[row,col] =".

Hmm. You could keep the data in some object that encapsulates
the two double lists and use some fancy feature (__getattribute__?)
to make certain that the only possible access to the data
was through __getitem__...
I only gave up on it because
now I would have to keep track of how far the two views are out of
sync, because some operation could use data that was modified by an
other view. Zipstar transposition is very handy for syncing, but it is
also very expensive. But probably not for you.
Expressing interest is a big mistake...

How so? ;-)
Sometime soon a thread will appear titled something
like "indexed attributes" with a stripped-down but
functional example of what I have in mind for Matrix

I can't wait! Keep up the good work.

P.
--
David C. Ullrich
Jun 27 '08 #14

P: n/a
On May 20, 10:40*am, "David C. Ullrich" <dullr...@sprynet.comwrote:
In article <20080520154802.4b5df647@hyperspace>,

*pataphor <patap...@gmail.comwrote:
On Tue, 20 May 2008 06:12:01 -0500
David C. Ullrich <dullr...@sprynet.comwrote:
Well, ok. Like I said, I never _took_ the position that it _should_
be a list of lists, I just said I didn't see the advantage to using
a single list.
I'm now thinking about a list of lists containing single element lists.
def test():
* * n = 3
* * R = range(n)
* * M = [[[i+j*n] for i in R] for j in R]
* * for L in M:
* * * * print L
* * row2 = M[1]
* * print
* * print row2
* * col3 = [L[2] for L in M]
* * print col3
* * col3[1][0]=9
print col3
* * print row2
* * print
* * for L in M:
* * * * print L
if __name__=='__main__':
* * * * test()
The idea is to use a single element list as kind of storage facility.
That enables me to copy a column, then change something in that column
and make the change turn up in the matrix and even in a previously made
copy of a row that shared the specific element.
Today's little joke: Long ago I would have solved
this by storing the data as a list of rows and _also_
a list of columns, updating each one any time the
other changed. Just goes to show you things
could always be worse...
Well have I ever ... I only thought about this last week and I
actually thought it was a *good* idea.

Sorry. If you really want to do this you could keep things
in sync automatically by writing the appropriate __setitem__
and resolving never to modify the data except through that...

def whatever.__setitem__(self, (row,col), value):
* self.rows[row][col] = value
* self.cols[col][row] = value

Seems to me like an, um, "untenable" waste of space. And you'd
need to be certain to never set anything except through
self[row,col]; as far as I can see anything like setrow()
would be a loop "for col in range(width): self[row,col] =".

Hmm. You could keep the data in some object that encapsulates
the two double lists and use some fancy feature (__getattribute__?)
to make certain that the only possible access to the data
was through __getitem__...


I only gave up on it because
now I would have to keep *track of how far the two views are out of
sync, because some operation could use data that was modified by an
other view. Zipstar transposition is very handy for syncing, but it is
also very expensive. But probably not for you.
Expressing interest is a big mistake...
How so? ;-)
Sometime soon a thread will appear titled something
like "indexed attributes" with a stripped-down but
functional example of what I have in mind for Matrix
I can't wait! Keep up the good work.
P.

--
David C. Ullrich- Hide quoted text -

- Show quoted text -- Hide quoted text -

- Show quoted text -
You can use tuples in dictionaries.

Example:
>>a= {}
a[0,1]= 2
a[2,3]= 7
a[0,1]
2
Jun 27 '08 #15

P: n/a
On Tue, 20 May 2008 10:40:17 -0500
"David C. Ullrich" <du******@sprynet.comwrote:
Today's little joke: Long ago I would have solved
this by storing the data as a list of rows and _also_
a list of columns, updating each one any time the
other changed. Just goes to show you things
could always be worse...
Well have I ever ... I only thought about this last week and I
actually thought it was a *good* idea.

Sorry. If you really want to do this you could keep things
in sync automatically by writing the appropriate __setitem__
and resolving never to modify the data except through that...

def whatever.__setitem__(self, (row,col), value):
self.rows[row][col] = value
self.cols[col][row] = value
Using the trick of encapsulating the values inside single-element lists
one can make a transposition of the matrix and get synchronicity for
the little price of doubling the number of instances. Since the views
share the data this is a lot less expensive than one would think. One
can use the row view or the column view to alter data and the changes
will automatically be visible in the other view, since the views update
the same lists. There is a little notational gotcha, instead of writing
row[0] = [1,2,3] one now must write row[0][:] = [1,2,3] or else
synchronicity is lost.

I found a nice ListMixin class at
http://aspn.activestate.com/ASPN/Coo.../Recipe/440656

Using that I wrote the following proof of concept:

class Storage(ListMixin):

def __init__(self, seq=[]):
self.L = [[x] for x in seq]

def _constructor(self, iterable):
return Storage(iterable)

def __len__(self):
return len(self.L)

def _get_element(self, i):
assert 0 <= i < len(self)
return self.L[i][0]

def _set_element(self, i, x):
assert 0 <= i < len(self)
self.L[i][0] = x

def _resize_region(self, start, end, new_size):
assert 0 <= start <= len(self)
assert 0 <= end <= len(self)
assert start <= end
self.L[start:end] = [[None] for i in range(new_size)]

def test():
n = 3
R = range(n)
it = iter(range(n*n))
MR = [Storage(it.next() for i in R) for i in R]
MC = [Storage() for i in R]
T = zip(*[x.L for x in MR])
for x,y in zip(MC,T):
x.L = y
print MR
print MC
MC[2][:] = 'abc'
print
print MR
print MC

if __name__=='__main__':
test()

Output:

[Storage([0, 1, 2]), Storage([3, 4, 5]), Storage([6, 7, 8])]
[Storage([0, 3, 6]), Storage([1, 4, 7]), Storage([2, 5, 8])]

[Storage([0, 1, 'a']), Storage([3, 4, 'b']), Storage([6, 7, 'c'])]
[Storage([0, 3, 6]), Storage([1, 4, 7]), Storage(['a', 'b', 'c'])]

P.

Jun 27 '08 #16

P: n/a
On Wed, 21 May 2008 12:47:44 +0200, pataphor <pa******@gmail.com>
wrote:
>On Tue, 20 May 2008 10:40:17 -0500
"David C. Ullrich" <du******@sprynet.comwrote:
Today's little joke: Long ago I would have solved
this by storing the data as a list of rows and _also_
a list of columns, updating each one any time the
other changed. Just goes to show you things
could always be worse...

Well have I ever ... I only thought about this last week and I
actually thought it was a *good* idea.

Sorry. If you really want to do this you could keep things
in sync automatically by writing the appropriate __setitem__
and resolving never to modify the data except through that...

def whatever.__setitem__(self, (row,col), value):
self.rows[row][col] = value
self.cols[col][row] = value

Using the trick of encapsulating the values inside single-element lists
one can make a transposition of the matrix and get synchronicity for
the little price of doubling the number of instances. Since the views
share the data this is a lot less expensive than one would think. One
can use the row view or the column view to alter data and the changes
will automatically be visible in the other view, since the views update
the same lists.
Oh - that's different. This is not what I thought you had in mind
(it's not what I had in mind in the thing I called a joke.)
>There is a little notational gotcha, instead of writing
row[0] = [1,2,3] one now must write row[0][:] = [1,2,3] or else
synchronicity is lost.

I found a nice ListMixin class at
http://aspn.activestate.com/ASPN/Coo.../Recipe/440656

Using that I wrote the following proof of concept:

class Storage(ListMixin):

def __init__(self, seq=[]):
self.L = [[x] for x in seq]

def _constructor(self, iterable):
return Storage(iterable)
Probably I'm just being dense. Why does
_constructor exist? (Oh - looking at things
below, I imagine it's something to do with
ListMixin.)
def __len__(self):
return len(self.L)

def _get_element(self, i):
assert 0 <= i < len(self)
return self.L[i][0]

def _set_element(self, i, x):
assert 0 <= i < len(self)
self.L[i][0] = x

def _resize_region(self, start, end, new_size):
assert 0 <= start <= len(self)
assert 0 <= end <= len(self)
assert start <= end
self.L[start:end] = [[None] for i in range(new_size)]

def test():
n = 3
R = range(n)
it = iter(range(n*n))
MR = [Storage(it.next() for i in R) for i in R]
MC = [Storage() for i in R]
T = zip(*[x.L for x in MR])
for x,y in zip(MC,T):
x.L = y
print MR
print MC
MC[2][:] = 'abc'
print
print MR
print MC

if __name__=='__main__':
test()

Output:

[Storage([0, 1, 2]), Storage([3, 4, 5]), Storage([6, 7, 8])]
[Storage([0, 3, 6]), Storage([1, 4, 7]), Storage([2, 5, 8])]

[Storage([0, 1, 'a']), Storage([3, 4, 'b']), Storage([6, 7, 'c'])]
[Storage([0, 3, 6]), Storage([1, 4, 7]), Storage(['a', 'b', 'c'])]

P.
David C. Ullrich
Jun 27 '08 #17

P: n/a
On Thu, 22 May 2008 06:26:41 -0500
David C. Ullrich <du******@sprynet.comwrote:
On Wed, 21 May 2008 12:47:44 +0200, pataphor <pa******@gmail.com>
wrote:
Using the trick of encapsulating the values inside single-element
lists one can make a transposition of the matrix and get
synchronicity for the little price of doubling the number of
instances. Since the views share the data this is a lot less
expensive than one would think. One can use the row view or the
column view to alter data and the changes will automatically be
visible in the other view, since the views update the same lists.

Oh - that's different. This is not what I thought you had in mind
(it's not what I had in mind in the thing I called a joke.)
The idea has two parts, the first part is to create two views and the
second part is to somehow synchronize the data. To make both views
update the same data is just not a very lazy synchronization
procedure.

Does it even have to be a list? Let's see ...

No, this works as well:

class Shared(object):

def __init__(self,value=None):
self.value = value

class Storage(ListMixin):

def __init__(self, seq=[]):
self.L = map(Shared,seq)

def _constructor(self, iterable):
return Storage(iterable)

def __len__(self):
return len(self.L)

def _get_element(self, i):
assert 0 <= i < len(self)
return self.L[i].value

def _set_element(self, i, x):
assert 0 <= i < len(self)
self.L[i].value = x

def _resize_region(self, start, end, new_size):
assert 0 <= start <= len(self)
assert 0 <= end <= len(self)
assert start <= end
self.L[start:end] = [Shared() for i in range(new_size)]
def _constructor(self, iterable):
return Storage(iterable)

Probably I'm just being dense. Why does
_constructor exist? (Oh - looking at things
below, I imagine it's something to do with
ListMixin.)
I just filled in the example class. It was very handy, one can make
changes very quickly because all the functionality is in a few lines of
code.

P.

Jun 27 '08 #18

This discussion thread is closed

Replies have been disabled for this discussion.