-1

I've a class which returns the health statistics of the machine.

class HealthMonitor(object):
    """Various HealthMonitor methods."""

    @classmethod
    def get_uptime(cls):
        """Get the uptime of the system."""
        return uptime()

    @classmethod
    def detect_platform(cls):
        """Platform detection."""
        return platform.system()

    @classmethod
    def get_cpu_usage(cls):
        """Return CPU percentage of each core."""
        return psutil.cpu_percent(interval=1, percpu=True)

    @classmethod
    def get_memory_usage(cls):
        """Return current memory usage of a machine."""
        memory = psutil.virtual_memory()
        return {
            'used': memory.used,
            'total': memory.total,
            'available': memory.available,
            'free': memory.free,
            'percentage': memory.percentage
        }

    @classmethod
    def get_stats(cls):
        return {
            'memory_usage': cls.get_memory_usage(),
            'uptime': cls.uptime(),
            'cpu_usage': cls.get_cpu_usage(),
            'security_logs': cls.get_windows_security_logs()
        }

Method get_stats will be called from outside the class. Which is the correct way to defining the related functions. Using classmethods or staticmethods or make an object of the class and then call the get_stats.

I've read enough on the differences, but still want to clear my understanding with an example. Which is the more pythonic approach?

Praful Bagai
  • 16,684
  • 50
  • 136
  • 267
  • 5
    Honest question: why are you using a class at all? You don't appear to expect to ever be instantiating it. I don't see any state. Why not just a collection of functions? – glibdud Sep 28 '17 at 14:08
  • `@classmethod` and `@staticmethod` are for different things. They're not interchangeable. `@staticmethod` should be used when you want to logical group a function with a class, but the function requires no state. You can think of a `@classmethod` as an overloaded constructor in other languages. – Christian Dean Sep 28 '17 at 14:08
  • @glibdud - I prefer grouping functions of specific domain in a specific class. – Praful Bagai Sep 28 '17 at 14:09
  • @ChristianDean - So, in my case, I should use staticmethod (since it does not involves any state change). Right? – Praful Bagai Sep 28 '17 at 14:10
  • 3
    @PythonEnthusiast the more "pythonic approach" you asked for, would be to group those functions in a module - not by also putting them in a class that's never instantiated. –  Sep 28 '17 at 14:10
  • @glibdud in a number of cases, classes are used just for the purpose you suggest... to create a collection of functions >> this technique puts all the functions into a single namespace and can help prevent naming collisions. – E. Ducateme Sep 28 '17 at 14:10
  • @PythonEnthusiast Yes, you should. But as another user already said, it looks like you really don't even need a class. Just use group the functions in a module. – Christian Dean Sep 28 '17 at 14:11
  • 3
    @E.Ducateme As Bilkokuya mentions, so can a module, and that's the more common approach in Python in my experience. – glibdud Sep 28 '17 at 14:11
  • 1
    This SO question and answer provides some reasonable clarifications as to when and where to use both classmethods and staticmethods: https://stackoverflow.com/questions/136097/what-is-the-difference-between-staticmethod-and-classmethod-in-python – E. Ducateme Sep 28 '17 at 14:11
  • @E.Ducateme - Couldn't agree more. – Praful Bagai Sep 28 '17 at 14:12
  • Just for completeness sake, if namespace collisions is a worry - alias the import when you bring it in. For example `import myModule as aDifferentNameForMyModule` will prevent the collisions. –  Sep 28 '17 at 14:14
  • @glibdud True, it is very common to put such items into a module. There may be cases where `@staticmethods` could be considered useful enough to choose to put them into a class versus a module. Julien Danjou had some thoughts on this back in 2013: https://julien.danjou.info/blog/2013/guide-python-static-class-abstract-methods. As a counterpoint, Luciano Ramalho, in his book `Fluent Python` (sorry, no link to the quote) basically considers them worthless and suggests simply using a module. – E. Ducateme Sep 28 '17 at 14:22

2 Answers2

6

Use @classmethod when the method needs class information, i.e accessing class attributes. (let's say the health_monitor class had OS attribute, which would affect the command you execute)

Use @staticmethod when the method doesn't need any data of the class it is declared in; like all your functions.

I often find myself use staticmethod for functions I put inside a class for simplicity, because they are running in context with my class, but not rely on it.

As for your class: when all your methods are classmethods or staticmethods, you should consider reside the code in a module scope and not a class. Why? well, there is no reason to group them in a class if they don't share any data between them. It would be just more simple:

# health_monitor.py
def get_uptime(cls):
    """Get the uptime of the system."""
    return uptime()

# main.py
health_monitor.get_uptime()
Mads Hansen
  • 63,927
  • 12
  • 112
  • 147
Chen A.
  • 10,140
  • 3
  • 42
  • 61
2

Well, classes basically provide an encapsulation over data i.e. a set of behaviour over certain data that identifies that object. Now, none of the methods that you have defined have anything to do with the class in particular.

Therefore, as long as you don't need to share data between those methods, it doesn't make sense at all to use classmethods. Although you'd be better off using static methods instead, then again all they would do is just provide a namespace. How about just defining all the methods as simple functions in a file named health_monitor.py, and then using it as follows -

import health_monitor

uptime = health_monitor.get_uptime()

Only con of this approach is that you'd have to enforce this convention of importing by the module name and not function.

hspandher
  • 15,934
  • 2
  • 32
  • 45