How to dynamically change __slots__ attribute?

后端 未结 4 891
梦毁少年i
梦毁少年i 2020-12-06 13:27

Suppose I have a class with __slots__

class A:
    __slots__ = [\'x\']

a = A()
a.x = 1   # works fine
a.y = 1   # AttributeError (as expected)
         


        
4条回答
  •  甜味超标
    2020-12-06 14:03

    Putting answers to this and related question together, I want to make an accent on a solution to this problem:

    You can kind of modify __slots__ by creating a subclass with the same name and then replacing parent class with its child. Note that you can do this for classes declared and used in any module, not just yours!


    Consider the following module which declares some classes:

    module.py:
    class A(object):
        # some class a user should import
        __slots__ = ('x', 'b')
    
        def __init__(self):
            self.b = B()
    
    class B(object):
        # let's suppose we can't use it directly,
        # it's returned as a part of another class
        __slots__ = ('z',)
    

    Here's how you can add attributes to these classes:

    >>> import module
    >>> from module import A
    >>>
    >>> # for classes imported into your module:
    >>> A = type('A', (A,), {'__slots__': ('foo',)})
    >>> # for classes which will be instantiated by the `module` itself:
    >>> module.B = type('B', (module.B,), {'__slots__': ('bar',)})
    >>>
    >>> a = A()
    >>> a.x = 1
    >>> a.foo = 2
    >>>
    >>> b = a.b
    >>> b.z = 3
    >>> b.bar = 4
    >>>
    

    But what if you receive class instances from some third-party module using the module?

    module_3rd_party.py:
    from module import A
    
    def get_instance():
        return A()
    

    No problem, it will also work! The only difference is that you may need to patch them before you import third-party module (in case it imports classes from the module):

    >>> import module
    >>>
    >>> module.A = type('A', (module.A,), {'__slots__': ('foo',)})
    >>> module.B = type('B', (module.B,), {'__slots__': ('bar',)})
    >>>
    >>> # note that we import `module_3rd_party` AFTER we patch the `module`
    >>> from module_3rd_party import get_instance
    >>>
    >>> a = get_instance()
    >>> a.x = 1
    >>> a.foo = 2
    >>>
    >>> b = a.b
    >>> b.z = 3
    >>> b.bar = 4
    >>>
    

    It works because Python imports modules only once and then shares them between all other modules, so the changes you make to modules affect all code running along yours.

提交回复
热议问题