(warning: it's a rather long message)

En Fri, 16 May 2008 12:58:46 -0300, David C. Ullrich <[EMAIL PROTECTED]> escribió:
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ó:

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

--
http://mail.python.org/mailman/listinfo/python-list

Reply via email to