2

I have a class like this:

class Metrics:
 meters = {}
 for subscription in SUBSCRIPTION_TYPES:
 meters[subscription] = metrics.new_meter(subscription)
 @staticmethod
 def get_meter_data(field, key):
 return Metrics.meters[field].get()[key]
 @staticmethod
 def track(subscription, value):
 Metrics.meters[subscription].notify(value)
 gauge_for_passport.set_function(lambda: Metrics.meters[subscription].get()["count"])

basically, I only want the meters dictionary to be initialized once since it's going to be keeping track of certain events in my app so I have the for loop right in the class body. Is this the right way to do this?

should I also use a classmethod so I can use cls and pass the class itself to the method instead of calling Metrics directly?

asked Aug 7, 2019 at 15:44
9
  • Take a look at singletons. Commented Aug 7, 2019 at 15:44
  • What's wrong with just using a class? Isn't there only one of them? Commented Aug 7, 2019 at 15:46
  • A singleton is an implementation of a class that restricts usage to a single instance. That is to say, no matter how many times it's instantiated, all instances are using the same underlying data. Commented Aug 7, 2019 at 15:49
  • but I'm not instantiating a class here into an object. Commented Aug 7, 2019 at 15:49
  • 1
    I would consider putting it in a module, and eliminating the class body. When you need it initialized, you import it. You can only have 1 instance of a given module, so it sounds like what you are wanting. Commented Aug 7, 2019 at 15:50

1 Answer 1

3

Classes are "initialized" at the point in time the module is run. So when you write this:

# my_module.py
class Metrics:
 meters = {}
 for subscription in SUBSCRIPTION_TYPES:
 meters[subscription] = metrics.new_meter(subscription)

...the for loop executes when my_module is imported.

It sounds to me like what you really want is a module, not a class. There is only one instance of any given module.

# metrics.py module
meters = {}
for subscription in SUBSCRIPTION_TYPES:
 meters[subscription] = new_meter(subscription)
def get_meter_data(field, key):
 return meters[field].get()[key]
def track(subscription, value):
 meters[subscription].notify(value)
 gauge_for_passport.set_function(lambda: meters[subscription].get()["count"])

When you want it "instantiated", you just import it:

# other_module.py
import metrics

If you really don't want this module in a separate file, there are ways of dynamically creating a module inside of another module. But honestly, it probably isn't worth the trouble, and I'm not even sure how you would write the module but also delay its execution.

One other option to delay class creation might be to write a class factory, which is a pythonic approach and might make sense depending on your use case:

def get_metrics():
 class Metrics:
 meters = {}
 for subscription in SUBSCRIPTION_TYPES:
 meters[subscription] = metrics.new_meter(subscription)
 @staticmethod
 def get_meter_data(field, key):
 return Metrics.meters[field].get()[key]
 @staticmethod
 def track(subscription, value):
 Metrics.meters[subscription].notify(value)
 gauge_for_passport.set_function(lambda: Metrics.meters[subscription].get()["count"])
 return Metrics

In this way, you can control when the class is run. But your example code really doesn't look like much of a class to me; it looks like a bunch of tools that belong in a namespace together.


Let's Get a Little Ridiculous

Back to the idea of dynamic module creation: you could pair these two ideas together and do something like this:

from types import ModuleType
def init_metrics_module():
 """This is a function to delay creation of the module below."""
 meters = {}
 for subscription in SUBSCRIPTION_TYPES:
 meters[subscription] = new_meter(subscription)
 def get_meter_data(field, key):
 return meters[field].get()[key]
 def track(subscription, value):
 meters[subscription].notify(value)
 gauge_for_passport.set_function(lambda: meters[subscription].get()["count"])
 # create module
 module_dict = locals().copy()
 metrics = ModuleType("metrics")
 metrics.__dict__.update(module_dict)
 return metrics

Then when you want it:

metrics = init_metrics_module()
type(metrics) # <class 'module'>
answered Aug 7, 2019 at 16:15
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.