Property with parameter...

Alex Martelli aleaxit at yahoo.com
Mon Sep 13 05:09:07 EDT 2004


kepes.krisztian <kepes.krisztian at peto.hu> wrote:

> Hi !
> 
> I want to create a property that can use parameter(s).
> In Delphi I can create same thing (exm: Canvas.Pixel[x,y] -> 
> Canvas.GetPixel(self,X,Y):integer; Canvas.SetPixel(self,X,Y,Color::integer);
> 
> class A(object):
>       def __init__(self):
>           self.__Tags={}
>       def GetTag(self,tname):
>           return self.__Tags.get(tname,None)
>       def SetTag(self,tname,value):
>           self.__Tags[tname]=Value
>       Tag=property(GetTag,SetTag)
> 
> a=A()
> print a.Tag('A')
> print a.Tag['A']
> 
> But it is seems to be not possible in this way.

Have your get method return an instance of an auxiliary class which
implements __getitem__ and __setitem__ (if you want to use square
brackets; if you want to use round parentheses, then __call__, but
beware -- you can't have a bare call on the left of an assignment!!!).

For example, a small refactoring of your attempt might be:

class A(object):

      def __init__(self):
          self.__Tags={}
          self.__TagsAccessor = None

      def getTagsAccessor(self):
          if not self.__tagsAccessor:
              def getter(__, tname):
                  return self.__Tags.get(tname, None)
              def setter(__, tname, value):
                  self.__Tags[tname] = value
              class TagAccessor: pass
              TagAccessor.__getitem__ = getter
              TagAccessor.__setitem__ = setter
              self.__TagsAccessor = TagAccessor()
          return self.__TagsAccessor
      Tag = property(getTagsAccessor)

Now, you can use such code as:

a = A()
print a.Tag['foo']
a.Tag['foo'] = 'barbaz'
print a.Tag['foo']

Note that we define no setter at all for Tag.  This means that, e.g.:

a.Tag = 23

will raise "AttributeError: can't set attribute".  The way we coded,
a.Tag MUST be indexed when used on the left of an = sign in an
assignment.  If that's not what you want -- if you do want to allow
assigning to bare a.Tag without an index -- then, and only then, write 
a setTagsAccessor and give it whatever semantics you wish, and pass it
as the second argument in the call to property.

Of course, you can refactor this basic idea in many different ways.  I
have used closures for getter and setter so as to finesse any trouble
with your use of leading double underscore, though that means that the
first argument of getter and setter CAN'T be named self (I used __ to
indicate I mean to ignore that argument...), but there are many other
possibilities, such as a more general TagAccessor class which takes
self.__Tags in its __init__, etc, etc.  You could even choose to use a
custom descriptor class instead of the built-in property, but I don't
think that's warranted if all you need is what you have expressed.


Alex



More information about the Python-list mailing list