On Thu, 15 May 2008 10:59:41 -0300, "Gabriel Genellina" <[EMAIL PROTECTED]> wrote:
>En Wed, 14 May 2008 18:15:41 -0300, David C. Ullrich <[EMAIL PROTECTED]> >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? 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 -- http://mail.python.org/mailman/listinfo/python-list