Monkey patching a @property

后端 未结 7 2031
慢半拍i
慢半拍i 2021-02-03 18:33

Is it at all possible to monkey patch the value of a @property of an instance of a class that I do not control?

class Foo:
    @property
    def bar         


        
7条回答
  •  感动是毒
    2021-02-03 19:03

    It looks like you need to move on from properties to the realms of data descriptors and non-data descriptors. Properties are just a specialised version of data descriptors. Functions are an example of non-data descriptors -- when you retrieve them from an instance they return a method rather than the function itself.

    A non-data descriptor is just an instance of a class that has a __get__ method. The only difference with a data descriptor is that it has a __set__ method as well. Properties initially have a __set__ method that throws an error unless you provide a setter function.

    You can achieve what you want really easily just by writing your own trivial non-data descriptor.

    class nondatadescriptor:
        """generic nondata descriptor decorator to replace @property with"""
        def __init__(self, func):
            self.func = func
    
        def __get__(self, obj, objclass):
            if obj is not None:
                # instance based access
                return self.func(obj)
            else:
                # class based access
                return self
    
    class Foo:
        @nondatadescriptor
        def bar(self):
            return "baz"
    
    foo = Foo()
    another_foo = Foo()
    
    assert foo.bar == "baz"
    foo.bar = 42
    assert foo.bar == 42
    assert another_foo.bar == "baz"
    del foo.bar
    assert foo.bar == "baz"
    print(Foo.bar)
    

    What makes all this work is that logic under the hood __getattribute__. I can't find the appropriate documentation at the moment, but order of retrieval is:

    1. Data descriptors defined on the class are given the highest priority (objects with both __get__ and __set__), and their __get__ method is invoked.
    2. Any attribute of the object itself.
    3. Non-data descriptors defined on the class (objects with only a __get__ method).
    4. All other attributes defined on the class.
    5. Finally the __getattr__ method of the object is invoked as a last resort (if defined).

提交回复
热议问题