@property not working after adding __slots__

3.1k views Asked by At

How can I get slots to work with @property for the class below. I have several thousand instances of below class which is causing memory issues and so I added the slots

I created instances with data and then add location information later to the instances.

After adding slots my instance creation is not working and I am getting the following error

AttributeError: 'Host' object has no attribute '_location'

class Host(object):
    __slots__ = ['data', 'location']

    def __init__(self, data, location=''):
        self.data = data
        self.location = location

    @property
    def location(self):
        return self._location

    @location.setter
    def location(self, value):
        self._location = value.lower()

    def __repr__(self):
        if self.location == '':
            self.loc = 'Not Found'
        else:
            self.loc = self.location
        return 'Host(name={}, location={})'.format(self.name, self.loc)
2

There are 2 answers

0
Martijn Pieters On

__slots__ works by creating descriptors on the class that have direct access to the in-memory data structure of your instance. You are masking the location descriptor with your property object, and you defined a new attribute _location than is not in the slots.

Make _location the slot (as that is the attribute you are actually storing):

class Host(object):
    __slots__ = ['data', '_location']

The location property (also a descriptor object) can then properly assign to self._location, an attribute backed by the slot descriptor.

Note that you do not need to use self.loc in the __repr__, just make that a local variable instead. You also are trying to use a self.name attribute which doesn't exist; it is not clear what value that is supposed to be however:

def __repr__(self):
    loc = self.location or 'Not Found'
    name = self.data['name']  # or some other expression
    return 'Host(name={}, location={})'.format(name, loc)
0
Noctis Skytower On

The definition for __slots__ should have the names of the underlying attributes that will store the data referenced by your properties. In the example below, name mangling is invoked for variables that should not be accessed outside of the class. The code is similar to yours and has no errors according to the PEP8 online website.

#! /usr/bin/env python3
def main():
    print(Host('Hello, world!', 'Earth'))
    print(Host('Hello, Xyz!'))


class Host:

    __slots__ = '__name', '__location'

    def __init__(self, name, location=''):
        self.name = name
        self.location = location

    def __repr__(self):
        return '{!s}({!r}, {!r})'.format(
            type(self).__name__,
            self.name,
            self.location
        )

    @property
    def name(self):
        return self.__name

    @name.setter
    def name(self, value):
        self.__name = value

    @property
    def location(self):
        return self.__location

    @location.setter
    def location(self, value):
        self.__location = value.casefold() if value else 'Not Found'


if __name__ == '__main__':
    main()