Python OO program structure planning
Asked Answered
I

3

10

I'm a beginner in OOP and I want to create a program with three classes, A, B, and C. Each instance of the class is defined by a set of characteristics, Achar1, Achar2 etc.

The program is supposed to create uses comprising of element of A, element of B and element of C with begin and end date. There are subclasses of A and B, so that some elements of A can only be connected to certain elements of B. The main functionality of the program is to list elements of A, B and C with their aributes, list uses and suggest new uses that would emply the instances of classes used the least to avoid repetition.

My first instinct is to use three dictionaries A, B and C and a dictionary for uses. This seems intuitive and easy to import/export into json or similar. I tried to rewrite it to employ classes, but I can't see any gain in doing so: it's hard (?) to iterate over instances of classes and also they basically are dictionaries since this data doesn't need much else.

What am I doing wrong? What can I gain from switching from dictionaries to objects?

Edit: the code in question (dict-based) is here. The first version, where I tried to make it object oriented is here.

Izy answered 15/1, 2017 at 12:9 Comment(2)
What can I gain from switching from dictionaries to objects? Business logic contained in methods. Dictionaries are only data. But does not mean you have to use objects, functional programming is a perfectly fine paradigm. If you feel like you can do everything with just dictionaries and lists and some functions, FP is a way to go.Flump
@ŁukaszRogalski the only thing is that I'm used to data-oriented programming with very ugly code (I have written a fair share of FORTRAN77 code...) and try to learn something different, clearer, easier?Izy
P
11

What can you gain from switching from dictionaries to objects?

This is a pretty vast question.

It is right, in Python, that an object is semantically equivalent to a dictionary, since an object is almost equivalent to its __dict__ attribute (I will not detail this "almost" here, since it is far off the topic).

I see two main benefits from using classes instead of dictionaries: abstraction, and comfort.

Abstraction

When you are in the design phase, especially for short to medium length programs, you generally want to write the skeleton of your code while thinking.

In a situation where you need to model interactions, it's more natural to think in classes, because it's halfway between speaking and programming.

This makes it easier to understand your own problematic. In addition, it greatly improves the readability of your code, because it seems natural, even when the reader does not know anything about your code.

This brings you concepts such as inheritance and polymorphism, that enrich the abstraction offered by OOP.

Comfort

One of Python's many strengths is its data model. The plenty magic methods and attributes allow you to use very simple syntaxes. In addition, it can take care of some operations in your place.

Here are some comparisons between imperative and object-oriented programming in Python.

Of course, everything in Python is an object, so I will use dot calls (foo.bar()) even in imperative examples.

Files reading

Imperative way

f1 = open(in_path, 'r')
f2 = open(out_path, 'w')
for line in f1:
    processed = process(line)
    f2.write(processed)

# Oops, I forgot to close my files...

Object-oriented way

with open(in_path, 'r') as f1, open(out_path, 'w') as f2:    
    for line in f1:
        processed = process(line)
        f2.write(processed)

# I don't have to close my files, Python did it for me

Note that for line in f is an extensive use of Python's object-oriented data model. Imagine the pain if this syntax did not exist (well, just try it in C).

Emptiness test

Imperative way

if len(some_list) == 0:
    print("empty list")

Object-oriented way

if some_list:
    print("empty list")

Iteration over a sequence

Imperative way

i = 0
while i < len(some_sequence):
    print(some_sequence[i])
    i += 1

Object-oriented way

for elt in some_sequence:
    print(elt)

But the actual strength of this data model is that it lets you redefine a lot of magic attributes. For instance, you can make complex things comparable just by implementing __lt__, __le__ and so on, which will redefine the behaviour of <, <=, and so on. Thenceforth, built-in functions like min, max or sort will understand how to compare your objects.

This said, the use of mere dictionaries can be more than enough in a large variety of cases.

And at the end of the day, OOP is only one paradigm, and imperative programming just works as fine.

Panic answered 24/1, 2017 at 16:59 Comment(2)
It's not that I don't think OOP is not for me, I'm just not used to it and don't understand what I can gain from it. Importing data into a class is less obvious than just loading the json, in fact loading is the intermediate step. Exporting is the same. I need to create my own separate list of objects so that I can iterate over them which is given by just iterating over a dictionary etc.Izy
It is not true that importing data into a class is less obvious than "just" loading th json. Well, it varies according to the person. I personally prefer putting such complex methods behind a class, because once it's written, I can forget about it. Then, I'll just write d = DataManager(), and then d.load(data), comfortably relying on my past self. Code might be longer or heavier in OOP (might), but it is way easier to use, and to maintain, too.Panic
A
4

tl;dr The answer is Polymorphism

Classes are not inherently better than dicts, just applicable to different problem domains. If you want to create 3 objects that behave like dicts, then just use dicts. So, if A, B, and C are dicts and that solves your problem, then you are done: dicts are the right choice.

If they are, actually, different things with different data structures and perhaps radically different implementations, then dicts aren't the right answer.

In this case, however, (and this is where classes help you), classes provide polymorphism to help you with your stated problem. This is where multiple objects, though belonging to different classes, can each respond to the same method, each in their own way.

Thus, you can implement a "use" method for classes A, B, and C. OBjects of those classes will now respond to that method, even though the rest of their implementations are wildly different.

This is most easily represented in the __str__ function of basic classes.

>>> x = 1
>>> x.__str__()
'1'
>>> x = "hello"
>>> x.__str__()
'hello'

No one will ever confuse the function of an integer (i.e. doing math) with the function of a string (i.e. storing text), but both of them respond to the same message __str__.Which is why the following works:

>>> y = 1
>>> z = "hello"
>>> y.__str__() + z.__str__()
'1hello'

So in your case, you can have three classes A, B, and C with wildly different implementations, but give them each one similar method "use". Then when you ask them all their "use", they will all answer the same way, and you can compile your list of uses.

Your code is replete with long "case" statements, here is a great discussion on how to use polymorphism to remove them:

Ways to eliminate switch in code

You should be able to drastically reduce the amount of code you have, especially all of the repetitive parts, by eliminating all of the control statements (ifs) and replacing them with sending messages to objects and letting the objects figure out what to do. Your objects should have methods such as "import", "export" and "add".

Aland answered 30/1, 2017 at 18:7 Comment(0)
C
0

What if you just extend each class from dict and personalize their behavior?

class classA(dict):
    '''Some personalization here...'''
    def __init__(self, **kwargs):
        pass

I mean... they will still be dictionaries, will work as dictionaries, but you can define a few methods of your own.

Edit:

Ok... I read your code and tried to do some OOP from it. Not really the best OOP ever, but you can get the idea. Code is cleaner, easier to debug, easier to extend, easier maintenance, and so on...

Here is the dropbox folder: https://www.dropbox.com/sh/8clyys2gcnodzkq/AACXhPnCW5d9fx0XY6Y-y_6ca?dl=0

What do you think?

Coconut answered 24/1, 2017 at 16:40 Comment(1)
Well this is fine and I have done something very similar, but found that this gave me nothing compared to pure dicts. Why would one want to use classes in a problem like the one I have put forward?Izy

© 2022 - 2024 — McMap. All rights reserved.