Property with parameter...

Bengt Richter bokr at oz.net
Mon Sep 13 13:56:04 EDT 2004


On Mon, 13 Sep 2004 11:09:07 +0200, aleaxit at yahoo.com (Alex Martelli) wrote:

>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
Why a get method when a.Tag can return the aux class instance as a plain attribute?
(other than that the OP mentioned 'property' and might want to protect
against a.Tag = 23 ;-) E.g. See below.

>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.
>
If the OP doesn't need to protect against a.Tag = 23 etc., seems like a
separate class for Tag might be simplest for him? I.e.,

 >>> class TagClass(object):
 ...     def __init__(self): self.__Tags = {}
 ...     def __getitem__(self, k): return self.__Tags.get(k, None) # per OP
 ...     def __setitem__(self, k, v): self.__Tags[k] = v
 ...
 >>> class A(object):
 ...     def __init__(self): self.Tag = TagClass()
 ...

Then
 >>> a=A()
 >>> a.Tag[2,3] = 'two, three'
 >>> a.Tag[2,3]
 'two, three'
 >>> a.Tag
 <__main__.TagClass object at 0x00901210>
 >>> vars(a)
 {'Tag': <__main__.TagClass object at 0x00901210>}
 >>> vars(a.Tag)
 {'_TagClass__Tags': {(2, 3): 'two, three'}}

For me, capitalized attributes kind of grate on the convention nerve though ;-)

Regards,
Bengt Richter



More information about the Python-list mailing list