views:

2460

answers:

6

Let's say I have a class that has a member called data which is a list.

I want to be able to initialize the class with, for example, a filename (which contains data to initialize the list) or with an actual list.

What's your technique for doing this?

Do you just check the type by looking at __class__?

Is there some trick I might be missing?

I'm used to C++ where overloading by argument type is easy.

Thanks.

+5  A: 

Use backticks to escape double underscores: __class__.

A better way would be to use isinstance and type conversion. If I'm understanding you right, you want this:

def __init__ (self, filename):
    if isinstance (filename, basestring):
        # filename is a string
    else:
        # try to convert to a list
        self.path = list (filename)
John Millikin
nice use of basestring!
Blair Conrad
+3  A: 

You probably want the isinstance builtin function:

self.data = data if isinstance(data, list) else self.parse(data)
Eli Courtwright
the if else expression will only work in python 2.5 (and later)
Moe
+4  A: 

You should use isinstace

isinstance(...)
    isinstance(object, class-or-type-or-tuple) -> bool

    Return whether an object is an instance of a class or of a subclass thereof.
    With a type as second argument, return whether that is the object's type.
    The form using a tuple, isinstance(x, (A, B, ...)), is a shortcut for
    isinstance(x, A) or isinstance(x, B) or ... (etc.).
Moe
A: 

OK, great. I just tossed together this example with a tuple, not a filename, but that's easy. Thanks all.

class MyData:
    def __init__(self, data):
        self.myList = []
        if isinstance(data, tuple):
            for i in data:
                self.myList.append(i)
        else:
            self.myList = data

    def GetData(self):
        print self.myList

a = [1,2]

b = (2,3)

c = MyData(a)

d = MyData(b)

c.GetData()

d.GetData()

[1, 2]

[2, 3]

Baltimark
There's no need for all that code in __init__ -- I shortened it down to just a type conversion, which does the same thing and is more flexible.
John Millikin
In Python, the getter is also mostly unnecessary. Just use direct attribute access. If you ever need to do more, you can use property() to hide the getter/setter behind normal attribute access.
Thomas Wouters
I know that, but that defeats the purpose of the example; I was just trying to show how to use two different input types. It might not be necessary with tuple/list, but it would be if that was a filename. I guess that just echoes what others said, though. My example would have been instructive to me
Baltimark
comment was @Millikin
Baltimark
+19  A: 

A much neater way to get 'alternate constructors' is to use classmethods. For instance:

>>> class MyData:
...     def __init__(self, data):
...         "Initialize MyData from a sequence"
...         self.data = data
...     
...     @classmethod
...     def fromfilename(cls, filename):
...         "Initialize MyData from a file"
...         data = open(filename).readlines()
...         return cls(data)
...     
...     @classmethod
...     def fromdict(cls, datadict):
...         "Initialize MyData from a dict's items"
...         return cls(datadict.items())
... 
>>> MyData([1, 2, 3]).data
[1, 2, 3]
>>> MyData.fromfilename("/tmp/foobar").data
['foo\n', 'bar\n', 'baz\n']
>>> MyData.fromdict({"spam": "ham"}).data
[('spam', 'ham')]

The reason it's neater is that there is no doubt about what type is expected, and you aren't forced to guess at what the caller intended for you to do with the datatype it gave you. The problem with isinstance(x, basestring) is that there is no way for the caller to tell you, for instance, that even though the type is not a basestring, you should treat it as a string (and not another sequence.) And perhaps the caller would like to use the same type for different purposes, sometimes as a single item, and sometimes as a sequence of items. Being explicit takes all doubt away and leads to more robust and clearer code.

Thomas Wouters
Cool! Where can I read about what exactly @classmethod does under the hood?
Hamish Grubijan
http://www.python.org/download/releases/2.2.3/descrintro/ is a good source.
Thomas Wouters
+4  A: 

Excellent question. I've tackled this problem as well, and while I agree that "factories" (class-method constructors) are a good method, I would like to suggest another, which I've also found very useful:

Here's a sample (this is a read method and not a constructor, but the idea is the same):

def read(self, str=None, filename=None, addr=0):
    """ Read binary data and return a store object. The data
        store is also saved in the interal 'data' attribute.

        The data can either be taken from a string (str 
        argument) or a file (provide a filename, which will 
        be read in binary mode). If both are provided, the str 
        will be used. If neither is provided, an ArgumentError 
        is raised.
    """
    if str is None:
        if filename is None:
            raise ArgumentError('Please supply a string or a filename')

        file = open(filename, 'rb')
        str = file.read()
        file.close()
    ...
    ... # rest of code

The key idea is here is using Python's excellent support for named arguments to implement this. Now, if I want to read the data from a file, I say:

obj.read(filename="blob.txt")

And to read it from a string, I say:

obj.read(str="\x34\x55")

This way the user has just a single method to call. Handling it inside, as you saw, is not overly complex

Eli Bendersky