Why can't access the property setter using the super?

Arup Rakshit ar at zeit.io
Tue Mar 19 15:15:34 EDT 2019


Hello Ian,

That seems like too much code involved. Is this how we do write inheritance in Python. Coming from Ruby and JS world I find Python inheritance mechanism confusing so far. :/


Thanks,

Arup Rakshit
ar at zeit.io



> On 19-Mar-2019, at 9:32 PM, Ian Kelly <ian.g.kelly at gmail.com> wrote:
> 
> Here's the thing: the result of calling super() is not an instance of the
> base class. It's just a proxy object with a __getattribute__ implementation
> that looks up class attributes in the base class, and knows how to pass
> self to methods to simulate calling base class methods on an instance. This
> works fine for looking up methods and property getters. It doesn't work so
> well for simulating other behaviors, like setters, or comparisons, or
> iteration, etc.
> 
> A property is one example of what in Python is known as a descriptor, which
> is described here:
> https://docs.python.org/3/reference/datamodel.html#implementing-descriptors
> 
> A settable property has a __set__ method per the descriptor protocol. When
> you try to set an attribute with the name of the property, Python looks up
> the name in the class dict, finds the property object, and then calls its
> __set__ method. The class of the super proxy object, however, does not
> contain this property, and because you're not doing an attribute lookup,
> the super proxy's __getattribute__ does not get called. So all Python sees
> is that you tried to set an attribute that doesn't exist on the proxy
> object.
> 
> The way I would suggest to get around this would be change your super()
> call so that it looks up the property from the base class instead of trying
> to set an attribute directly. For example, this should work:
> 
>    super(HeatedRefrigeratedShippingContainer,
> self.__class__).celsius.fset(self, value)
> 
> On Tue, Mar 19, 2019 at 9:12 AM Arup Rakshit <ar at zeit.io> wrote:
> 
>> I have 3 classes which are connected to them through inheritance as you
>> see in the below code:
>> 
>> import iso6346
>> 
>> class ShippingContainer:
>>    """docstring for ShippingContainer"""
>> 
>>    HEIGHT_FT = 8.5
>>    WIDTH_FT = 8.0
>>    next_serial = 1337
>> 
>>    @classmethod
>>    def _get_next_serial(cls):
>>        result = cls.next_serial
>>        cls.next_serial += 1
>>        return result
>> 
>>    @staticmethod
>>    def _make_bic_code(owner_code, serial):
>>        return iso6346.create(owner_code=owner_code,
>>                              serial=str(serial).zfill(6))
>> 
>>    @classmethod
>>    def create_empty(cls, owner_code, length_ft, *args, **keyword_args):
>>        return cls(owner_code, length_ft, contents=None, *args,
>> **keyword_args)
>> 
>>    # ... skipped
>> 
>>    def __init__(self, owner_code, length_ft, contents):
>>        self.contents  = contents
>>        self.length_ft = length_ft
>>        self.bic = self._make_bic_code(owner_code=owner_code,
>> 
>> serial=ShippingContainer._get_next_serial())
>>        # ... skipped
>> 
>> 
>> class RefrigeratedShippingContainer(ShippingContainer):
>>    MAX_CELSIUS = 4.0
>>    FRIDGE_VOLUME_FT3 = 100
>> 
>>    def __init__(self, owner_code, length_ft, contents, celsius):
>>        super().__init__(owner_code, length_ft, contents)
>>        self.celsius = celsius
>> 
>>    # ... skipped
>> 
>>    @staticmethod
>>    def _make_bic_code(owner_code, serial):
>>        return iso6346.create(owner_code=owner_code,
>>                              serial=str(serial).zfill(6),
>>                              category='R')
>>    @property
>>    def celsius(self):
>>        return self._celsius
>> 
>>    @celsius.setter
>>    def celsius(self, value):
>>        if value > RefrigeratedShippingContainer.MAX_CELSIUS:
>>            raise ValueError("Temperature too hot!")
>>        self._celsius = value
>> 
>>    # ... skipped
>> 
>> 
>> class HeatedRefrigeratedShippingContainer(RefrigeratedShippingContainer):
>>    MIN_CELSIUS = -20.0
>> 
>>    @RefrigeratedShippingContainer.celsius.setter
>>    def celsius(self, value):
>>        if value < HeatedRefrigeratedShippingContainer.MIN_CELSIUS:
>>            raise ValueError("Temperature too cold!")
>>        super().celsius = value
>> 
>> 
>> 
>> 
>> Now when I run the code :
>> 
>> Python 3.7.2 (v3.7.2:9a3ffc0492, Dec 24 2018, 02:44:43)
>> [Clang 6.0 (clang-600.0.57)] on darwin
>> Type "help", "copyright", "credits" or "license" for more information.
>>>>> from shipping import *                                        >>> h1 =
>> HeatedRefrigeratedShippingContainer.create_empty('YML', length_ft=40,
>> celsius=-18)
>> Traceback (most recent call last):
>>  File "<stdin>", line 1, in <module>
>>  File "/Users/aruprakshit/python_playground/shipping.py", line 23, in
>> create_empty
>>    return cls(owner_code, length_ft, contents=None, *args, **keyword_args)
>>  File "/Users/aruprakshit/python_playground/shipping.py", line 47, in
>> __init__
>>    self.celsius = celsius
>>  File "/Users/aruprakshit/python_playground/shipping.py", line 92, in
>> celsius
>>    super().celsius = value
>> AttributeError: 'super' object has no attribute 'celsius'
>>>>> 
>> 
>> 
>> Why here super is denying the fact that it has no celsius setter, although
>> it has. While this code doesn’t work how can I solve this. The thing I am
>> trying here not to duplicate the validation of temperature which is already
>> in the  setter property of the RefrigeratedShippingContainer class.
>> 
>> 
>> 
>> 
>> Thanks,
>> 
>> Arup Rakshit
>> ar at zeit.io
>> 
>> 
>> 
>> --
>> https://mail.python.org/mailman/listinfo/python-list
>> 
> -- 
> https://mail.python.org/mailman/listinfo/python-list




More information about the Python-list mailing list