3

Before applying __slots__, I can do this:

class C(object):

    def __init__(self):
        self.m_A = 1

    def __getattr__(self, name):
        if name in ("A"):
            return self.__dict__["m_%s" % name]
        raise AttributeError(name)

a = C()
a.A

Now, I want to use __slots__; however, that disables __dict__ causing my code to break.

So, my questions are:

  1. Is there any other way to do this?(except property

  2. How Python do I access an object attribute when using __slots__?

Raymond Hettinger
  • 216,523
  • 63
  • 388
  • 485
John He
  • 317
  • 2
  • 12
  • Possible duplicate of [Python \_\_Slots\_\_ (Making and Using)](http://stackoverflow.com/questions/14666138/python-slots-making-and-using) – OJFord Apr 20 '17 at 02:10
  • Also consider revising the title to summarise the *question* - at the moment it appears to be a *statement* that `__slots__` and by extension this question are not worth anybody's time. – OJFord Apr 20 '17 at 02:12
  • I have seen it, but it is not what I want. – John He Apr 20 '17 at 02:31

1 Answers1

7

Just change:

return self.__dict__["m_%s" % name]

to:

return getattr(self, "m_%s" % name)

The former is using implementation details that don't apply, the latter is explicitly looking up a new attribute by name.

Note: For this specific case, I'd avoid __getattr__ entirely as involving too much overhead, and just do:

@property
def A(self):
    return self.m_A

That will behave the same, and run faster (generic dispatch through __getattr__ and a getattr call is more expensive than targeted lookup through a property descriptor). If you have a dozen such slots to make read-only accessors for, you can still use this pattern fairly cheaply by adding the accessors after making the class:

from operator import attrgetter

class Foo(object):
     __slots__ = 'm_XXX', 'm_YYY', etc.

     ... rest of class body ...

# Add readonly accessors sans m_ prefix for each slot with an m_ prefix:
for membername in Foo.__slots__:
    if membername.startswith('m_'):
        setattr(Foo, membername.replace('m_', '', 1), property(attrgetter(membername)))
ShadowRanger
  • 143,180
  • 12
  • 188
  • 271
  • I have a lot of attributes like `m_A`,so `property` can not meet my needs. – John He Apr 20 '17 at 02:21
  • @JohnHe: If you're using `__slots__`, your attribute list is finite, and if they're all simple read-only accessors, a bunch of two line accessor functions (one for decorator, one for function and return) isn't unreasonable, and it will be faster. You can actually generalize this particular pattern too; I'll edit it into my answer. – ShadowRanger Apr 20 '17 at 02:25
  • @JohnHe: Added the example that generalizes creating the `property`s, substituting the runtime overhead of `__getattr__` with trivial definition time overhead. – ShadowRanger Apr 20 '17 at 02:32
  • Is there some builtin variable like `__dict__` to this? – John He Apr 20 '17 at 02:34
  • @JohnHe: Not sure what you mean by "builtin variable ... to this". The class itself will (after the class definition is exited) have the descriptors that implement the slots on the class `__dict__`, but the values themselves are stored in the object internals, sort of like a `tuple` stores its contents, but without exposing indexing to the programmer. `dir()` will give you the string names of all the slots (and a ton of other special internal things), but there is no direct equivalent to `__dict__` that provides a complete mapping for you. – ShadowRanger Apr 20 '17 at 02:37