So I'm trying to make a class that extends list, with the extra ability that certain special attributes are mapped to refer to certain parts of the list. Using this Py3k doc page, I created the following code. The idea is that (say I have a sequence
instance of this class) sequence.seq
should act exactly like sequence[0]
, and sequence.index
should act exactly like sequence[2]
, etc.
It seems to work great, except that I can't seem to access the class variable mapping attributes to the list.
I found this SO question, but either the answer there is wrong, or something is different within methods. I could also use self.__class__.__map__
, but since I need the class variable inside __getattribute__
, that sends me into an infinite recursion loop.
>>> class Sequence(list):
... __map__ = {'seq': 0,
... 'size': 1,
... 'index': 2,
... 'fdbid': 3,
... 'guide': 4,
... 'factors': 5,
... 'clas': 6,
... 'sorttime': 7,
... 'time': 8,
... 'res': 9,
... 'driver': 10 }
...
... def __setattr__(self, name, value): # "Black magic" meta programming to make certain attributes access the list
... print('Setting atr', name, 'with val', value)
... try:
... self[__map__[name]] = value
... except KeyError:
... object.__setattr__(self, name, value)
...
... def __getattribute__(self, name):
... print('Getting atr', name)
... try:
... return self[__map__[name]]
... except KeyError:
... return object.__getattribute__(self, name)
...
... def __init__(self, seq=0, size=0, index=0, fdbid=0, guide=None, factors=None,
... sorttime=None, time=None):
... super().__init__([None for i in range(11)]) # Be sure the list has the necessary length
... self.seq = seq
... self.index = index
... self.size = size
... self.fdbid = fdbid
... self.guide = ''
... self.time = time
... self.sorttime = sorttime
... self.factors = factors
... self.res = ''
... self.driver = ''
...
>>> a = Sequence()
Setting atr seq with val 0
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 31, in __init__
File "<stdin>", line 17, in __setattr__
NameError: global name '__map__' is not defined
2 Answers 2
Since none of the methods are called until after Sequence
is fully defined, you can refer to Sequence.__map__
without any trouble. For example:
def __setattr(self, name, value):
print('Setting atr', name, 'with val', value)
try:
self[Sequence.__map__[name]] = value
except KeyError:
object.__setattr__(self, name, value)
As an aside, here's a demonstration that class attributes may be accessed via objects as long as an instance attribute with the same name does not also exist:
class Foo:
i = 3
def __init__(self, overwrite):
if overwrite:
self.i = 4
f = Foo(False)
id(f.i) == id(Foo.i) # Should be True
f = Foo(True)
id(f.i) == id(Foo.i) # Should be False
-
I was trying to do it without using the literal class name in the code... but at least it's a solution. I forgot about the methods-not-called-until-after-definition, thanks for the reminder ;)Dubslow– Dubslow2012年07月27日 02:40:38 +00:00Commented Jul 27, 2012 at 2:40
-
So the summary is use
var
outside method definitions, but use__class__.var
inside method definitions. Cool.Dubslow– Dubslow2012年07月27日 02:41:57 +00:00Commented Jul 27, 2012 at 2:41 -
Hmm... I just tried using
self._map
"in the field" and it didn't work. It sent me into infinite recursion in__getattribute__
trying to getself._map
. I reverted toSequence._map
and that worked fine.Dubslow– Dubslow2012年07月27日 06:23:11 +00:00Commented Jul 27, 2012 at 6:23 -
Oh, right. Sorry, I forgot that you were redefining getattribute, which gets called when you access an attribute of your object (but not the class).chepner– chepner2012年07月27日 12:24:31 +00:00Commented Jul 27, 2012 at 12:24
You access attributes with a dot (.
), not with []
. Python doesn't allow you to omit the self
reference, so you need to access the class variable with self.__map__
. So if you want to access the element at that position, you need self[self.__map__[name]]
.
Note that it's not a good idea to use double-underscore-sandwiched names for your own purposes. Even two leading underscores (which does name-mangling) is usually more than you need. If you just want to indicate to users that the __map__
attribute isn't part of the public API, call it _map
.
-
Since
Sequence
extends thelist
type,self
is also an instance oflist
, and can be indexed as such. I believe there is no difference betweenself.__map__
andSequence.__map__
, the style I used in my answer. And good point about preferring_map
to__map__
.chepner– chepner2012年07月27日 02:35:36 +00:00Commented Jul 27, 2012 at 2:35 -
self.__map__
is not the same asSequence.__map__
. Instances do not have the class variables, see here. Thanks for the tip about hidden variables though, I'll be sure to change that. (And chepner's right, since Sequence subclasses list, as long as I run super().__init__(), self is in fact a list.)Dubslow– Dubslow2012年07月27日 02:39:29 +00:00Commented Jul 27, 2012 at 2:39 -
@Dubslow:
self.__map__
andSequence.__map__
refer to the same object until you assign toself.__map__
. Thenself.__map__
is created as an instance attribute separate from the class attribute.chepner– chepner2012年07月27日 02:50:43 +00:00Commented Jul 27, 2012 at 2:50 -
@chepner Then accessing
self.__map__
(without assigning it) won't cause it to be added to the instance attribute dict? So I really could useself.__map__
instead ofSequence.__map__
without fear of adding an attribute to the instance? Neat! Sorry about being wrong, I think perhaps that link I posted needs some editing...Dubslow– Dubslow2012年07月27日 02:56:29 +00:00Commented Jul 27, 2012 at 2:56
Explore related questions
See similar questions with these tags.