3

I come from a Matlab background. In matlab I can create a class definition and then create an array of objects. I can easily dereference each object with an index. In addition, when I call a method from the object array (without an index), I have access to all the objects in the array. E.g, say that myNewClass has the properties .data and .text, it also has the method .clob. I can:

% init
a(1) = myNewClass;
a(2) = myNewClass;

a(1).data = [0;0;0];
a(2).data = [1;1;1];

So that now, if I call a.clob (not a(1).clob or a(2).clob), I can do something like

% this is a function inside the methods definition of my class definition
function clob(self)
   % self here is my object array, "a", from above
   for i=1:length(self)
       % deref a single object
       self(i).goClobYourself;
   end
end

How do I do something like this in Python? Please note, I want my class to be an indexed collection, kind of like a list. But, I don't want my "class-list" to accept any class, just myNewClass. If I inherit from "list," will my class just be a "list" class with the attributes .data, .text, and the function .clob? Also note, I don't want a "list" that contains my objects, I want my objects to be list so that I can deref them from an index: a[1].clob() or a(1).clob() (?? or something like that). Or, I want to pass the whole array to self: a.clob() gives me access to the list. I may have the terminology a little cloudy.

Best regards

bgporter
  • 35,114
  • 8
  • 59
  • 65
userJohn
  • 157
  • 1
  • 1
  • 6
  • "But, I don't want my "class-list" to accept any class, just myNewClass." The simple answer is "don't do this". There is practically never a good reason to do this. It may be common in MATLAB, but it is strongly frowned-upon in Python. – TheBlackCat Jul 22 '15 at 12:59

2 Answers2

3

When programming in Python, performing type checks isn't incredibly common. Are you positive you need your list to only accept one type (and its subtypes), or will you trust the programmer to read your docs and not put something in that's not supposed to be there?

If so, here's an example generic class that inherits from collections.MutableSequence that would probably do what you want:

from collections import MutableSequence
class VerifierList(MutableSequence):
    _list = None
    def __init__(self, allowedClasses, *args, **kwargs):
        super(VerifierList, self).__init__()
        self._list = list(*args, **kwargs)
        self.allowedClasses = tuple(allowedClasses)
    def __repr__(self):
        return repr(self._list)
    def __str__(self):
        return str(self._list)
    def __len__(self):
        return len(self._list)
    def __getitem__(self, index):
        return self._list[index]
    def __setitem__(self, index, value):
        if not isinstance(value, self.allowedClasses):
            raise TypeError('Value of type %s not allowed!' % value.__class__.__name__)
        self._list[index] = value
    def __delitem__(self, index):
        del self._list[index]
    def insert(self, index, value):
        if not isinstance(value, self.allowedClasses):
            raise TypeError('Value of type %s not allowed!' % value.__class__.__name__)
        self._list.insert(index, value)

Use it as follows:

>>> class A(object): pass

>>> class B(object): pass

>>> l = VerifierList((A,))
>>> l.append(A())
>>> print(l)
>>> [<__main__.A object at 0x000000000311F278>]
>>> l.append(B())

Traceback (most recent call last):
  File "<pyshell#228>", line 1, in <module>
    l.append(B())
  File "C:\Python27\lib\_abcoll.py", line 661, in append
    self.insert(len(self), value)
  File "<pyshell#204>", line 23, in insert
    raise TypeError('Value of type %s not allowed!' % value.__class__.__name__)
TypeError: Value of type B not allowed!
Alyssa Haroldsen
  • 3,652
  • 1
  • 20
  • 35
  • Thank you for the information. I'm trying to understand this. For the collection part, the objects are stored on _list and served/administered from the object reference (name), "[" and "]" are just operators that get over-rided by this definition, correct? Nothing personal (to you) but I don't like the VerifierList((A,)), mainly because I don't naturally understand it -- other than that's the way the class type is loaded into .allowedClass; but I don't like it. I can remove the verify portion, and have a regular "listable" class, correct? Best regards – userJohn Jul 21 '15 at 23:45
  • If you want a regular "listable" class, just use the builtin `list`. This just adds type "safety", which I thought was what you wanted. – Alyssa Haroldsen Jul 22 '15 at 04:43
  • Thanks for the comments. You are correct but I decided I didn't like the how it was mechanized in the language. I'm going with you suggestion and inherit from the "list" my class def is `class myTest(list): def __init__(self): list.__init__(self) self.data = None self.test = None` But I don't understand the results. when `a=myTest a.data=[1,2,3,4] a.text = 'hello bob'`. I assume that this is element a[0]. But when I `a.append(myTest())` that element is actually a[0]. Also, my debugger doesn't show any of the attributes. Best regards and thanks in advance. – userJohn Jul 23 '15 at 01:14
  • 1
    Why do you not like how it is mechanized in the language? Do you need an extra feature? What is it that makes the built-in list so undesirable? Changing a pretty fundamental part of the language is almost always the wrong way to do things, and you'll simply irritate other people who have to look at your code. – Alyssa Haroldsen Jul 23 '15 at 06:56
1

All such capabilities in Python use "Special method names" as described in the Language Reference section 3.3. Section 3.3.6 describes how to emulate container types, which in general is what you're asking for here. You need to define and implement methods __getitem__, __setitem__,__delitem__ and perhaps also __iter__, __reversed__ and __contains__. Python is quite good for this and the approach is very flexible.

Paul Cornelius
  • 9,245
  • 1
  • 15
  • 24