osdir.com


[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

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


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