-1

I'm trying to sort out how to organize a lot of code that iterates on data stored in python class instances. In most cases, the code reads the instance data and produces some result (a number, a list, etc).

Essentially, I'm just trying to keep everything maintainable and easier to read by creating top-level instance methods that delegate the grunt work to otherwise "dumb" methods that need the instance data, but don't need to modify the instance itself.

Given the read-only nature of these other methods, am I better off declaring them as static methods and passing the needed data as separate parameters, or leaving them all as instance methods?

I prefer the static method approach as a "self" parameter signals that the method modifies instance data, but I don't know if I'm over-using static methods to adopt that approach...

I realize this makes for a broad question that invites opinion-based responses, but I can't find anything information which suggests over-using static methods creates any real hardships / performance issues (or vice-versa). That is, I see suggestions that static methods should be used sparingly, but I don't see any justification for that.

For example, in the code snippet below, assuming the static methods represent significant, but separate, operations on the instance data, is it better to tag them as static methods (which tells me right away they only return a result, not modify the instance or class), or make them all instance-level methods and access the data directly? Is there any real problem this approach creates?

class Test(object):

    def __init__(self):
        self.var1 = 10
        self.var2 = ["my", "list", "of", "strings"]
        self.var3 = [1,2,3,4,5]

    @staticmethod
    def processFloat(inst_float):
        pass

    @staticmethod
    def processList(inst_list):
        pass

    @staticmethod
    def processArray(inst_array):
        pass

    def doStuff(self):
        processFloat(self.var1)
        processList(self.var2)
        processArray(self.var3)
Joel Graff
  • 167
  • 15
  • 2
    ' "self" parameter signals that the method modifies instance data' — no it doesn't – khelwood Oct 24 '18 at 13:07
  • I recognize that it's not guaranteed, but if the "self" parameter is in the signature, then it signals that it's an instance method which has the ability to modify instance data. Thus, at a glance, I can know if the method is capable of modifying the instance or not. – Joel Graff Oct 24 '18 at 13:16
  • When you say "static methods", do you mean "class methods"? or perhaps do you mean "functions"? because actual static methods are rarely that helpful in Python – khelwood Oct 24 '18 at 13:18
  • Since static methods cannot access instance data, why have them in a class at all? If it's because you wish to namespace your methods, you may consider a module instead of a class. – Mario Camilleri Oct 24 '18 at 13:21
  • @mkam - It's mostly about keeping methods short and readable and being able to follow flow... See my edited response that, hopefully, makes things clearer. – Joel Graff Oct 24 '18 at 13:36
  • @khelwood - I don't mean "functions" as in lambda functions... and I don't mean "class" methods, because there's no class-level state to manipulate. It is simply code that is relevant to the class itself, but too large to build as a lambda function – Joel Graff Oct 24 '18 at 13:36
  • @JoelGraff "Functions" doesn't mean just lambda functions. It means normal functions that are not part of a class. If your functions don't need to be part of a class, usually they shouldn't be – khelwood Oct 24 '18 at 13:39
  • (Just a minor note, this "self" parameter does not signal that it is an instance method, khelwood's comment is correct. The fact it is in a class is what makes it an instance method. You could call self "badger" and it would still work as an instance method. You must explicitly anotate the method to declare that it is not an instance method using @staticmethod for example) – Rob Bricheno Oct 24 '18 at 13:48
  • @rbricheno - Understood. I guess I'm focused more on convention than technical correctness. :) – Joel Graff Oct 24 '18 at 13:55
  • @JoelGraff I would still consider a module. Python doesn't really need static methods because it can do the same just using modules. You can call your module **test**, put functions in it using def, import it into other modules, and call those functions **test.processFloat()**, **test.processList()** etc. just as if they were static methods in a class. – Mario Camilleri Oct 24 '18 at 13:57
  • @rbricheno - Ok. That makes sense, too. That actually clarifies things a bit more, that I can look at modules and static methods as serving the same purpose. Thanks! – Joel Graff Oct 24 '18 at 14:07

1 Answers1

1

I think you must have misunderstood the use of the self parameter. It is used to refer to the instance of the class you are using. You must use instance methods to access instance data (regardless of whether or not you are modifying it), you can't get at the data at all using static methods*.

Consider the following:

class Test(object):
    def __init__(self):
        self.my_instance_variable = 7

    def get_variable(self):
        return self.my_instance_variable

    @staticmethod
    def try_and_get_stuff():
        return self.my_instance_variable
        # This is not valid! "self" is undefined!
        # What other variable could we use here instead of self?
        # None! Because we don't have a reference to any instance!

a = Test()
# This is fine:
print(a.get_variable())
# This is fine too:
print(a.my_instance_variable)
# These are not fine:
print(Test.get_variable())
print(Test.my_instance_variable)
# Test has no variable called my_instance_variable until an instance of Test
# is instantiated!

Now, what you may well want to do is write some kind of helper method related to Test, and a static method might be a good place to put it. But you will need to explicitly pass it instances of Test for it to handle:

class Test(object):
    def __init__(self, start_value):
        self.my_instance_variable = start_value

    def get_variable(self):
        return self.my_instance_variable

    @staticmethod
    def process_some_instances(list_of_test_instances):
        score = 0
        for my_test in list_of_test_instances:
            score = score + my_test.get_variable()
        return score

a = Test(8)
b = Test(9)
instance_list = [a, b]
print(Test.process_some_instances(instance_list)

You could also just declare these methods at the module level as functions. I usually prefer to put them in the class as static or class methods if they are related to a specific class, but it's a matter of personal preference. If you come from a Java background or similar then you probably understand static methods more easily than module level functions.

(* unless you do some horrible things outside the scope of this question which you should not consider here)

Rob Bricheno
  • 4,467
  • 15
  • 29
  • I edited my question above. I'm really just trying to manage large amounts of code by splitting it into separate methods that are useful only in the class itself. I think partly what I'm dealing with is the lack of privacy in Python, as I come for a C++ background. – Joel Graff Oct 24 '18 at 13:30
  • I think you've hit on what I needed to know. It doesn't really matter if I tag them as static methods or leave them as instance methods. I want to keep them in the class because they are, in fact, useful only to the class. I just wondered if I was overthinking / abusing the staticmethod tag with this approach. – Joel Graff Oct 24 '18 at 13:40
  • Yep I think you've pretty much got it, and I understand your question now, thanks for the edit. It is fine or even prefereable to use static methods in this way, in my opinion. Do make sure you also understand how immutable types in Python are passed by assignment, e.g. see here https://stackoverflow.com/questions/986006/how-do-i-pass-a-variable-by-reference – Rob Bricheno Oct 24 '18 at 13:43