"indexed properties"...

Gabriel Genellina gagsl-py2 at yahoo.com.ar
Thu May 15 09:59:41 EDT 2008


En Wed, 14 May 2008 18:15:41 -0300, David C. Ullrich <dullrich at sprynet.com> escribió:

> 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




More information about the Python-list mailing list