Hi guys,

This is more a general discussion of methodology than a specific question.

I have been working on a, the primary purpose of which is to store an oddly formatted data file (aka not you classic CSV). At the end of the day, the data is stored in a custom dictionary (thanks for help with this previously guys). In defining many of the methods, I realized that it's not always clear to me if I should write an instance method, or if I should write general methods and pass objects into them.

def testmethod(self, args...)

def testmethod(args...)

There are certain methods, like getters and setters, which I think intrinsically should be instance methods; however, many methods could easily operate on any object passed into them. I guess at the end of the day, my question is this.

Since I can define methods outside of a class and then pass an object into them, why bother writing instance methods at all? When should I say "yes this method is going to be in the class" as opposed to "no this method is just going to accept objects as its input". In the case of the latter, what's the best way to make sure the user passes the correct object?

Looking forward to your insights. Thanks.

There is few views you can have in OOP and Python. This is from a guy more familiar with structured/modular programming than object oriented.

  1. Python lets you use objects when you need, otherwise do not bother. That means generally that when you have many instances you want to create, which become clearer by using classes. It means you do not write slow pure Python code to reinvent list or dict but use builtins or inherit from them. Generally in Python you use builtin dict a lot. You would also use OOP to avoid need of global variables.

  2. Actually many things you do not think so are an object instance like your script module or function. So you can create instance variables for them.

  3. If the operation would make sense assuming as little as possible of the type of parameters ('duck typed'), it make sense to use general function of the module instead of putting it in class.

You do not however use getter or setter methods in Python, if needed attribute is later easy to change as calculated property, but only if needed so.

The classic http://dirtsimple.org/2004/12/python-is-not-java.html

Edited 4 Years Ago by pyTony

Thanks PyTony. Let me present a question just to better understand how you would approach the problem under the guidelines you presented.

You have a custom dictionary. You want to write a function the datafield to a given key. Do you make this its own method? You said you don't use getters. Would this be a getter?

I still not get what you want to do, I only mean you would use

car.weight

not

car.get_weight()

So, in my dictionary, let's say I have a list of values. They each entry represents a datafield. Something like this:

class Test(object)
    mydict={key:[height, width, length]}

The class is built to manage various facets of the dataset, and predominately manipulates the mydict object. It also does things like populate the dictionary from a user-supplied file, for example. If I wanted to return the second column, or "width" entry, for a user-supplied key, or a list of all the values at this index, isn't it neccessary to write a method to do so. Something trivial like:

def get_width(self, key):
    return self.mydict[key][1]

There is many alternatives, for example, you coud define values as namedtuple allowing field access, or using dictionary of dictionaries.

Edited 4 Years Ago by pyTony

Thanks for the heads up. The named tuple is a nice alternative. Would you say that the get_width() function that I presented above is non-pythonic then?

get_width is so so. Here some experiments:

from collections import namedtuple

class Test(object):
    def __init__(self, **mydict):
        self.mydict = mydict

    def __str__(self):
        return '\n'.join('%s = %s' % v for v in self.mydict.items())

Info = namedtuple("Info", "height, width, length")

my_data = Test(tony = Info(183,23, 12), shoe = Info(175, 12, 45))
print my_data

#named tuple field can be accessed, but can not change
print my_data.mydict['shoe'].width
print

class Info(object):
    __slots__ = 'height', 'width', 'length'
    def __init__(self, height, width, length):
        self.height, self.width, self.length = height, width, length

    def __str__(self):
        return 'Info(%s)' % ','.join('%s = %s' % (v, getattr(self, v)) for v in self.__slots__)

my_data = Test(tony = Info(183,23, 12), shoe = Info(175, 12, 45))
print my_data
print

#with Info class the field is writable
my_data.mydict['tony'].width = 44
print my_data

Tweaking the field access and dropping mydict and inheriting from dict:

from collections import namedtuple

class Test(dict):
    def __init__(self, **mydict):
        self.update(mydict)

    __getattr__ = dict.__getitem__

    def __setattr__(self, key, value):
        self.__dict__[key] = value

    def __str__(self):
        return '\n'.join('%s = %s' % v for v in self.items())

Info = namedtuple("Info", "height, width, length")

my_data = Test(tony = Info(183,23, 12), shoe = Info(175, 12, 45))
print my_data

#named tuple field can be accessed, but can not change
print my_data.shoe.width
print

class Info(object):
    __slots__ = 'height', 'width', 'length'
    def __init__(self, height, width, length):
        self.height, self.width, self.length = height, width, length

    def __str__(self):
        return 'Info(%s)' % ','.join('%s = %s' % (v, getattr(self, v)) for v in self.__slots__)



my_data = Test(tony = Info(183,23, 12), shoe = Info(175, 12, 45))
print my_data
print

#with Info class the field is writable
my_data.tony.width = 44
print my_data

Hi Pytony,

I am finally having some time to go back to this project and implement your suggestions. After reading for hours about named tuples and custom dicts, I see that your implmentation is really intelligent. I would like to adapt it and have a few questions:

First, in terms of validation and default values... I would like to make sure users don't enter incorrect data types. How would I put a stringent requirement on the fields so that users, for example, can't enter string input for a tuple field that I expect to be an integer? I assume I would do the validation at the tuple level. How would you implement this behavior? Also, would you enforce defaults at teh tuple level?

Secondly, can you elaborate a bit on what these methods are doing?

getattr = dict.getitem
def setattr(self, key, value):
self.dict[key] = value

def str(self):
return '\n'.join('%s = %s' % v for v in self.items())

I'm having some trouble finding good information on custom dictionaries in the literature. Like, what is getattr=dict.getitem doing?

Third, let's say I have my dictionary constructed and I'd like to return all values in a field. For example:

def get_widths():
    return [value.width for value in my_data.values]

Is there a better way to do this for all attributes in the dictionary, or should I just make a simple method like this for every field?

Hmm,

So I think I'm going to adopt this custom "records" class.

http://code.activestate.com/recipes/576555/

Which is effectively a namedtuple with mutability and default values. This seems easier than completely creating my own info object; although, now I understand how to do that thanks to PyTony. This will take care of default values on its own, and I will overwrite set_items and/or init methods to have my own validations in place.

Good that you found a suitable data structure for your use, and thank you for inspiring one of my entries to the code snippet competition!

Hey PyTony,

I hope you win the contest. I started another thread on this, but maybe it isn't really clear as it follows from some info we talked about in here.

In your example with the Info() object, is there a simple way to mandate that certain fields do have a specified type. For example, one of my fields has to be a float? I know that type checking is not always necessary; however, you can imagine certain fields are very prone to errors, so its best to make sure they have the correct type from the start.

As it is right now, I feel like I have to declare the types explicitly in botht he init method and then if I wanted to do more validation, I'd have to overwrite the setattr method. For example, if I wanted to enforce one of my fields is a float:

class Info(object):  
    __slots__ = 'height', 'width'
    def __init__(self, height, width):
        self.height, self.width, = float(height), width

That's no problem, but if the user sets "height", how do I let the program know that this should be a float? Aka, the user wants to do this:

myinfo.width='Sally'

I'd like the program to catch this and barf before any further happenings in the code.

I run into this same issue when I try to use namedTuple as well, namely I don't know how to restrict the field types for a couple of the fields that I know are going to problematic in real use cases.

Edited 4 Years Ago by hughesadam_87

Geez man, I completely forgot I already had started a thread pre-dating this one on that exact same topic. That's what I get for putting this project down for so long. Sorry, and thanks a lot. I will certainly implement the solution you outlined in that response.

This question has already been answered. Start a new discussion instead.