Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python classes, how to use them style-wise, and the Single Responsibility Principle [closed]

I've been programming in Python for some time and have covered some knowledge in Python style but still have a problem on how to use classes properly. When reading object oriented lecture I often find rules like Single Responsibility Principle that state

"The Single Responsibility Principle says that a class should have one, and only one, reason to change"

Reading this, I might think of breaking one class into two, like:

class ComplicatedOperations(object):
    def __init__(self, item):
        pass

    def do(self):
        ...

    ## lots of other functions

class CreateOption(object):
    def __init__(self, simple_list):
        self.simple_list = simple_list

    def to_options(self):
        operated_data = self.transform_data(self.simple_list)
        return self.default_option() + operated_data

    def default_option(self):
        return [('', '')]

    def transform_data(self, simple_list):
        return [self.make_complicated_operations_that_requires_losts_of_manipulation(item)
                        for item in simple_list]

    def make_complicated_operations_that_requires_losts_of_manipulation(self, item):
            return ComplicatedOperations(item).do()

This, for me, raises lots of different questions; like:

  • When should I use class variables or pass arguments in class functions?
  • Should the ComplicatedOperations class be a class or just a bunch of functions?
  • Should the __init__ method be used to calculate the final result. Does that makes that class hard to test.
  • What are the rules for the pythonists?

Edited after answers:

So, reading Augusto theory, I would end up with something like this:

class ComplicatedOperations(object):
    def __init__(self):
        pass

    def do(self, item):
        ...

    ## lots of other functions

def default_option():
    return [('', '')]

def complicate_data(item):
    return ComplicatedOperations().do(item)

def transform_data_to_options(simple_list):
    return default_option() + [self.complicate_data(item)
                    for item in simple_list]

(Also corrected a small bug with default_option.)

like image 273
user1634074 Avatar asked Feb 11 '13 14:02

user1634074


People also ask

What is single responsibility principle in Python?

The single-responsibility principle (SRP) is a computer programming principle that states that "A module should be responsible to one, and only one, actor." The term actor refers to a group (consisting of one or more stakeholders or users) that requires a change in the module.

How do I use single responsibility principle?

The single responsibility principle (SRP) states that every class or module in a program should have responsibility for just a single piece of that program's functionality. Further, the elements of that responsibility should be encapsulated by the responsible class rather than spread out in unrelated classes.


4 Answers

When should I use class variables or pass arguments in class functions

In your example I would pass item into the do method. Also, this is related to programming in any language, give a class only the information it needs (Least Authority), and pass everything that is not internal to you algorithm via parameters (Depedency Injection), so, if the ComplicatedOperations does not need item to initialize itself, do not give it as a init parameter, and if it needs item to do it's job, give it as a parameter.

Should the ComplicatedOperations class be a class or just a bunch of functions

I'd say, depends. If you're using various kinds of operations, and they share some sort of interface or contract, absolutely. If the operation reflects some concept and all the methods are related to the class, sure. But if they are loose and unrelated, you might just use functions or think again about the Single Responsability and split the methods up into other classes

Should the init method be used to calculate the final result. Does that makes that class hard to test.

No, the init method is for initialization, you should do its work on a separated method.

As a side note, because of the lack of context, I did not understand what is CreateOption's role. If it is only used as show above, you might as well just remove it ...

like image 110
Augusto Hack Avatar answered Nov 14 '22 23:11

Augusto Hack


I personally think of classes as of concepts. I'd define a Operation class which behaves like an operation, so contains a do() method, and every other method/property that may make it unique.

As mgilson correctly says, if you cannot define and isolate any concept, maybe a simple functional approach would be better.

To answer your questions:

  • you should use class attributes when a certain property is shared among the instances (in Python class attributes are initialized at compile time, so different object will see the same value. Usually class attributes should be constants). Use instance attributes to have object-specific properties to use in its methods without passing them. This doesn't mean you should put everything in self, but just what you consider characterising for your object. Use passed variables to have values that do not regard your object and may depend from the state of external objects (or on the execution of the program).
  • As said above, I'd keep one single class Operation and use a list of Operation objects to do your computations.
  • the init method would just instantiate the object and make all the processing needed for the proper behaviour of the object (in other words make it read to use).
  • Just think about the ideas you're trying to model.
like image 42
drekyn Avatar answered Nov 14 '22 22:11

drekyn


A class generally represents a type of object. Class instances are specific objects of that type. A classic example is an Animal class. a cat would be an instance of Animal. class variables (I assume you mean those that belong to the instance rather than the class object itself), should be used for attributes of the instance. In this case, for example, colour could be a class attribute, which would be set as cat.colour = "white" or bear.colour = "brown". Arguments should be used where the value could come from some source outside the class. If the Animal class has a sleep method, it might need to know the duration of the sleep and posture that the animal sleeps in. duration would be an argument of the method, since it has no relation on the animal, but posture would be a class variable since it is determined by the animal.

In python, a class is typically used to group together a set of functions and variables which share a state. Continuing with the above example, a specific animal has a state which is shared across its methods and is defined by its attributes. If your class is just a group of functions which don't in any way depend on the state of the class, then they could just as easily be separate functions.

If __init__ is used to calculate the final result (which would have to be stored in an attribute of the class since __init__ cannot return a result), then you might as well use a function. A common pattern, however, is to do a lot of processing in __init__ via several other, sometimes private, methods of the class. The reason for this is that large complicated functions are often easier to test if they are broken down into smaller, distinct tasks, each of which can then be tested individually. However, this is usually only done when a class is needed anyway.

One approach to the whole business is to start out by deciding what functionality you need. When you have a group of functions or variables which all act on or apply to the same object, then it is time to move them into a class. Remember that Object Oriented Programming (OOP) is a design method suited to some tasks, but is not inherently superiour to functional programming (in fact, some programmers would argue the opposite!), so there's no need to use classes unless there is actually a need.

like image 38
aquavitae Avatar answered Nov 14 '22 22:11

aquavitae


Classes are an organizational structure. So, if you are not using them to organize, you are doing it wrong. :)

There are several different things you can use them for organizing:

  1. Bundle data with methods that use said data, defines one spot that the code will interact with this data
  2. Bundle like functions together, provides understandable api since 'everyone knows' that all math functions are in the math object
  3. Provide defined communications between methods, sets up a 'conveyor belt' of operations with a defined interface. Each operation is a black box, and can change arbitrarily, so long as it keeps to the standard
  4. Abstract a concept. This can include sub classes, data, methods, so on and so forth all around some central idea like database access. This class then becomes a component you can use in other projects with a minimal amount of retooling

If you don't need to do some organizational thing like the above, then you should go for simplicity and program in a procedural/functional style. Python is about having a toolbox, not a hammer.

like image 24
Spencer Rathbun Avatar answered Nov 14 '22 22:11

Spencer Rathbun