0

Consider this code:

class testobj( object ): ...

x = testobj()
x.toast = 'toast'
print( x.toast )   # <-- toast

y = object()
y.toast = 'toast'

The last line produces the error

AttributeError                            Traceback (most recent call last)
<ipython-input-24-873470c47cb3> in <module>()
----> 1 y.toast = 'toast'

AttributeError: 'object' object has no attribute 'toast'

I also tried

class testobj2( object ):
    def __init__( self ):
        super().__init__()

which behaves the same way, allowing arbitrary attributes to be set.

Based on my understanding of inheritance in Python, I would expect testobj to have all the same behavior (all same methods, including __setattr__ ) as object, since it's a subclass and no new methods have been defined. However, it is not the same because the above code allows me to set an arbitrary attribute. Why does this happen and how can I disallow setting of arbitrary attributes?

jacob
  • 39
  • 4
  • 2
    What is *toast* (from `y.toast = toast`)? – CristiFati Aug 30 '19 at 22:06
  • 2
    The error you are seeing is irrelevant to what you are describing. If you change it to `y.toast = "toast" `, the error becomes `object object has no attribute toast`, which is what the question is about. – Junuxx Aug 30 '19 at 22:10
  • sorry, that was supposed to be a string. – jacob Aug 30 '19 at 22:10
  • Objects won't shate dynamically created attributes. No matter how they are related (unless you do some meta magic). – Bayleef Aug 30 '19 at 22:10
  • @BaileyKocin what do you mean by "shate"? Is this the expected behavior in python? – jacob Aug 30 '19 at 22:15
  • @jacob Looks like a typo for "share". – Barmar Aug 30 '19 at 22:22
  • 1
    @BaileyKocin There's no intent to share anything. He's just adding a new attribute to two objects. When the class is `testobj` it's allowed, when the class is `object` it's not allowed. – Barmar Aug 30 '19 at 22:23
  • You can't (easily) prevent new attribute from being added to an object, whether they're instances of subclasses or not—that's just not how Python works. You _might_ be able to override `__setattr__()`, but doing so would be very tricky... – martineau Aug 30 '19 at 22:29
  • @martineau Then how does the `object` class do it? – Barmar Aug 30 '19 at 22:33
  • `object` is a built-in type, and you generally can't add attributes to instances of them — because they're part of the interpreter and written in C (or possibly some other language). – martineau Aug 30 '19 at 22:36
  • https://stackoverflow.com/questions/14594120/python-read-only-property has examples of using `__setattr__` to create read-only attributes. – Barmar Aug 30 '19 at 22:36
  • @Barmar: I don't think creating read-only attributes/properties is the same thing as preventing new ones from being added. – martineau Aug 30 '19 at 22:41
  • I know, but it's an example of the kind of thing you can do with `__setattr__`. You can prevent creating arbitrary attributes by simply signalling an error if the attribute name isn't in a whitelist. – Barmar Aug 30 '19 at 22:43
  • If you want a class to not allow dynamic attribute's, and only to allow form a predefined set, use `__slots__` – juanpa.arrivillaga Aug 30 '19 at 23:20

1 Answers1

3

An easy way to prevent creation of attributes is to define __slots__:

class A:
  __slots__ = ('a', 'b')

a = A()
a.a = 1
a.b = 2
a.c = 3 # Raises

--

object() doesn't allow arbitrary attributes, but this is a special rule of instances of object, that is lost on inheritance.

Rémi Bonnet
  • 793
  • 5
  • 16
  • Thanks. Is it more pythonic to define \__slots__ for all classes, or give the user the opportunity to assign arbitrary attributes? – jacob Sep 03 '19 at 06:06
  • 1
    The dominant point of view in python devs is that you should trust your users, and `__slots__` is only provided as an optimization. Personally, i don't trust my users. – Rémi Bonnet Sep 03 '19 at 15:23