Python: how to implement __getattr__()?

后端 未结 8 1879
北海茫月
北海茫月 2020-12-07 22:57

My class has a dict, for example:

class MyClass(object):
    def __init__(self):
        self.data = {\'a\': \'v1\', \'b\': \'v2\'}

Then I

8条回答
  •  眼角桃花
    2020-12-07 23:16

    class MyClass(object):
    
        def __init__(self):
            self.data = {'a': 'v1', 'b': 'v2'}
    
        def __getattr__(self, attr):
            return self.data[attr]
    

    >>> ob = MyClass()
    >>> v = ob.a
    >>> v
    'v1'
    

    Be careful when implementing __setattr__ though, you will need to make a few modifications:

    class MyClass(object):
    
        def __init__(self):
            # prevents infinite recursion from self.data = {'a': 'v1', 'b': 'v2'}
            # as now we have __setattr__, which will call __getattr__ when the line
            # self.data[k] tries to access self.data, won't find it in the instance 
            # dictionary and return self.data[k] will in turn call __getattr__
            # for the same reason and so on.... so we manually set data initially
            super(MyClass, self).__setattr__('data', {'a': 'v1', 'b': 'v2'})
    
        def __setattr__(self, k, v):
            self.data[k] = v
    
        def __getattr__(self, k):
            # we don't need a special call to super here because getattr is only 
            # called when an attribute is NOT found in the instance's dictionary
            try:
                return self.data[k]
            except KeyError:
                raise AttributeError
    

    >>> ob = MyClass()
    >>> ob.c = 1
    >>> ob.c
    1
    

    If you don't need to set attributes just use a namedtuple eg.

    >>> from collections import namedtuple
    >>> MyClass = namedtuple("MyClass", ["a", "b"])
    >>> ob = MyClass(a=1, b=2)
    >>> ob.a
    1
    

    If you want the default arguments you can just write a wrapper class around it:

    class MyClass(namedtuple("MyClass", ["a", "b"])):
    
        def __new__(cls, a="v1", b="v2"):
            return super(MyClass, cls).__new__(cls, a, b)
    

    or maybe it looks nicer as a function:

    def MyClass(a="v1", b="v2", cls=namedtuple("MyClass", ["a", "b"])):
        return cls(a, b)
    

    >>> ob = MyClass()
    >>> ob.a
    'v1'
    

提交回复
热议问题