Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What does calling a python class without parenthesis do?

Tags:

python

I don't understand what happens when a class is called without parenthesis. My question stems from reading from this Python HOWTO: Logging Cookbook. Specifically I'm trying to understand a recipe that I've placed at the end of this question. The purpose of the recipe is to instruct a logging formatter to use UTC time from within a logging configuration dictionary.

A class is defined,

class UTCFormatter(logging.Formatter):
    converter = time.gmtime

and then later used by name only; without parenthesis.

'formatters': {
            'utc': {
                '()': UTCFormatter,   # <-------------------Class used here
                'format': '%(asctime)s %(message)s',
            },

What happens here and why is calling the class in this way a benefit over creating an instance of the class? What is this way of doing things called? That is, what keywords (other than 'class') could I search for that would help me find more information about this in the python documentation?

Full Recipe:

import logging
import logging.config
import time

class UTCFormatter(logging.Formatter):
    converter = time.gmtime

LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'formatters': {
        'utc': {
            '()': UTCFormatter,
            'format': '%(asctime)s %(message)s',
        },
        'local': {
            'format': '%(asctime)s %(message)s',
        }
    },
    'handlers': {
        'console1': {
            'class': 'logging.StreamHandler',
            'formatter': 'utc',
        },
        'console2': {
            'class': 'logging.StreamHandler',
            'formatter': 'local',
        },
    },
    'root': {
        'handlers': ['console1', 'console2'],
   }
}

if __name__ == '__main__':
    logging.config.dictConfig(LOGGING)
    logging.warning('The local time is %s', time.asctime())
like image 474
jlsecrest Avatar asked May 14 '26 21:05

jlsecrest


1 Answers

Using just the name of a class (UTCFormatter in your case) does not instantiate the class. Instead, UTCFormatter is a variable that points to a value of type type:

In [1]: class Foo:
   ...:     pass
   ...: 

In [2]: type(Foo)
Out[2]: type

That variable can be used to create new instances via UTCFormatter(), but it can also be used like any other variable. In particular, it can be passed to functions and bound to other names. For example, you can do the following:

In [3]: def my_function(some_class):
   ...:     print('Creating an instance of {}'.format(some_class.__name__))
   ...:     return some_class()
   ...: 

In [4]: foo = my_function(Foo)
Creating an instance of Foo

In [5]: foo
Out[5]: <__main__.Foo at 0x7f017451e438>

This is useful in many situations. In your recipe, you define a formatter once by specifying a class (UTCFormatter) and a name ('utc'). That definition is then re-used when you configure different handlers to use that formatter, e.g.

'console1': {
    'class': 'logging.StreamHandler',
    'formatter': 'utc',
},

To construct the actual logger hierarchy from your configuration, logging.config.dictConfig doesn't need one instance of UTCFormatter (which it would get if you had passed UTCFormatter() instead) but one for each handler that you configure to use that formatter (two in your case). Passing the class itself instead of an instance allows dictConfig to create new instances as required.

like image 78
Florian Brucker Avatar answered May 16 '26 10:05

Florian Brucker



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!