What is the purpose of python's inner classes?

前端 未结 9 1197
心在旅途
心在旅途 2020-11-28 02:12

Python\'s inner/nested classes confuse me. Is there something that can\'t be accomplished without them? If so, what is that thing?

9条回答
  •  忘掉有多难
    2020-11-28 02:59

    The main use case I use this for is the prevent proliferation of small modules and to prevent namespace pollution when separate modules are not needed. If I am extending an existing class, but that existing class must reference another subclass that should always be coupled to it. For example, I may have a utils.py module that has many helper classes in it, that aren't necessarily coupled together, but I want to reinforce coupling for some of those helper classes. For example, when I implement https://stackoverflow.com/a/8274307/2718295

    :utils.py:

    import json, decimal
    
    class Helper1(object):
        pass
    
    class Helper2(object):
        pass
    
    # Here is the notorious JSONEncoder extension to serialize Decimals to JSON floats
    class DecimalJSONEncoder(json.JSONEncoder):
    
        class _repr_decimal(float): # Because float.__repr__ cannot be monkey patched
            def __init__(self, obj):
                self._obj = obj
            def __repr__(self):
                return '{:f}'.format(self._obj)
    
        def default(self, obj): # override JSONEncoder.default
            if isinstance(obj, decimal.Decimal):
                return self._repr_decimal(obj)
            # else
            super(self.__class__, self).default(obj)
            # could also have inherited from object and used return json.JSONEncoder.default(self, obj) 
    

    Then we can:

    >>> from utils import DecimalJSONEncoder
    >>> import json, decimal
    >>> json.dumps({'key1': decimal.Decimal('1.12345678901234'), 
    ... 'key2':'strKey2Value'}, cls=DecimalJSONEncoder)
    {"key2": "key2_value", "key_1": 1.12345678901234}
    

    Of course, we could have eschewed inheriting json.JSONEnocder altogether and just override default():

    :

    import decimal, json
    
    class Helper1(object):
        pass
    
    def json_encoder_decimal(obj):
        class _repr_decimal(float):
            ...
    
        if isinstance(obj, decimal.Decimal):
            return _repr_decimal(obj)
    
        return json.JSONEncoder(obj)
    
    
    >>> json.dumps({'key1': decimal.Decimal('1.12345678901234')}, default=json_decimal_encoder)
    '{"key1": 1.12345678901234}'
    

    But sometimes just for convention, you want utils to be composed of classes for extensibility.

    Here's another use-case: I want a factory for mutables in my OuterClass without having to invoke copy:

    class OuterClass(object):
    
        class DTemplate(dict):
            def __init__(self):
                self.update({'key1': [1,2,3],
                    'key2': {'subkey': [4,5,6]})
    
    
        def __init__(self):
            self.outerclass_dict = {
                'outerkey1': self.DTemplate(),
                'outerkey2': self.DTemplate()}
    
    
    
    obj = OuterClass()
    obj.outerclass_dict['outerkey1']['key2']['subkey'].append(4)
    assert obj.outerclass_dict['outerkey2']['key2']['subkey'] == [4,5,6]
    

    I prefer this pattern over the @staticmethod decorator you would otherwise use for a factory function.

提交回复
热议问题