Can't set attributes on instance of object class
To support arbitrary attribute assignment, an object needs a __dict__
: a dict associated with the object, where arbitrary attributes can be stored. Otherwise, there's nowhere to put new attributes.
An instance of object
does not carry around a __dict__
-- if it did, before the horrible circular dependence problem (since dict
, like most everything else, inherits from object
;-), this would saddle every object in Python with a dict, which would mean an overhead of many bytes per object that currently doesn't have or need a dict (essentially, all objects that don't have arbitrarily assignable attributes don't have or need a dict).
For example, using the excellent pympler
project (you can get it via svn from here), we can do some measurements...:
>>> from pympler import asizeof
>>> asizeof.asizeof({})
144
>>> asizeof.asizeof(23)
16
You wouldn't want every int
to take up 144 bytes instead of just 16, right?-)
Now, when you make a class (inheriting from whatever), things change...:
>>> class dint(int): pass
...
>>> asizeof.asizeof(dint(23))
184
...the __dict__
is now added (plus, a little more overhead) -- so a dint
instance can have arbitrary attributes, but you pay quite a space cost for that flexibility.
So what if you wanted int
s with just one extra attribute foobar
...? It's a rare need, but Python does offer a special mechanism for the purpose...
>>> class fint(int):
... __slots__ = 'foobar',
... def __init__(self, x): self.foobar=x+100
...
>>> asizeof.asizeof(fint(23))
80
...not quite as tiny as an int
, mind you! (or even the two int
s, one the self
and one the self.foobar
-- the second one can be reassigned), but surely much better than a dint
.
When the class has the __slots__
special attribute (a sequence of strings), then the class
statement (more precisely, the default metaclass, type
) does not equip every instance of that class with a __dict__
(and therefore the ability to have arbitrary attributes), just a finite, rigid set of "slots" (basically places which can each hold one reference to some object) with the given names.
In exchange for the lost flexibility, you gain a lot of bytes per instance (probably meaningful only if you have zillions of instances gallivanting around, but, there are use cases for that).
Why can't you add attributes to object in python?
Notice that an object
instance has no __dict__
attribute:
>>> dir(object())
['__class__', '__delattr__', '__doc__', '__getattribute__', '__hash__', '__init__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__str__']
An example to illustrate this behavior in a derived class:
>>> class Foo(object):
... __slots__ = {}
...
>>> f = Foo()
>>> f.bar = 42
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'Foo' object has no attribute 'bar'
Quoting from the docs on slots
:
[...] The
__slots__
declaration takes a sequence of instance variables and reserves just enough space in each instance to hold a value for each variable. Space is saved because__dict__
is not created for each instance.
EDIT: To answer ThomasH from the comments, OP's test class is an "old-style" class. Try:
>>> class test: pass
...
>>> getattr(test(), '__dict__')
{}
>>> getattr(object(), '__dict__')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'object' object has no attribute '__dict__'
and you'll notice there is a __dict__
instance. The object class may not have a __slots__
defined, but the result is the same: lack of a __dict__
, which is what prevents dynamic assignment of an attribute. I've reorganized my answer to make this clearer (move the second paragraph to the top).
Can't set an arbitrary attribute in an instance of an object
I don't think there's a better way than subclassing Surface
if you really need to be able to assign arbitrary attributes to an instance. The subclass can be empty if you don't need to add any other features (the instance __dict__
will be created automatically).
class MySurface(pygame.Surface):
pass
However, I'm not sure you really need that ability.
A better design is generally to encapsulate the Surface
object and whatever other data you have as attributes of some other object type. In Pygame you'll often use a subclass of pygame.sprite.Sprite
to bundle a Surface
(as the image
attribute) with coordinates (as part of the rect
attribute). If you have any other data you need to bundle, the Sprite
can take it too. Sprite
instances have a __dict__
by default, even if you don't write your own subclass.
AttributeError: can't set attribute in python
items[node.ind] = items[node.ind]._replace(v=node.v)
(Note: Don't be discouraged to use this solution because of the leading underscore in the function _replace. Specifically for namedtuple some functions have leading underscore which is not for indicating they are meant to be "private")
What allows bare class instances to have assignable attributes?
The object()
class is like a fundamental particle of the python universe, and is the base class (or building block) for all objects (read everything) in Python. As such, the stated behavior is logical, for not all objects can (or should) have arbitrary attributes set. For example, it wouldn't make sense if a NoneType
object could have attributes set, and, just like object()
, a None
object also does not have a __dict__
attribute. In fact, the only difference in the two is that a None
object has a __bool__
attribute. For example:
n = None
o = object()
type(n)
>>> <class 'NoneType'>
set(dir(n)) - set(dir(o))
>>> {'__bool__'}
isinstance(n, object)
>>> True
bool(n)
>>> False
Inheritance from object
is automatic, and, just like any other means of inheriting, one can add their own class methods and attributes to the child. Python automatically adds the __dict__
attribute for custom data types as you already showed.
In short, it is much easier to add an object's __dict__
attribute than to take it away for objects that do not have custom writable attributes (i.e. the NoneType
).
Update based on comment:
Original comment:
Would it then be safe to assume that it is
__setattr__
that checks for the existence of__dict__
and raises an exception accordingly? –
bool3max
In CPython, the logic behind object.__setattr__(self, name, value)
is implemented by Objects/object.c _PyObject_GenericSetAttrWithDict
(see CPython source code). Specifically, it looks like if the name
argument is a string, then the object is checked for a __dict__
object in one of its slots and makes sure it is "ready" (see this line).
The readiness state of the object is determined by the PyType_Ready()
, briefly described and quoted from here:
Defining a Python type in C involves populating the fields of a PyTypeObject struct with the values you care about. We call each of those fields a “slot”.
On[c]e the definition is ready, we pass it into the PyType_Ready() function, which does a number of things, inclulding exposing most of the type definition to Python’s attribute lookup mechanism.
Attribute assignment to built-in object
Python stores attributes in a dict. You can add attributes to MyClass
, see it has a __dict__
:
>>> class MyClass(object):
>>> pass
>>> dir(MyClass)
['__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__']
The important difference is that object
has no __dict__
attribute.
>>> dir(object)
['__class__', '__delattr__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__']
More detailed explanations:
- Can't set attributes of object class
- Why can't you add attributes to object in python?
AttributeError: can't set attribute - Python instance attribute assignment
If you look at the source code (or even if you just print(scrapy.Spider.logger)
you can see that Spider.logger
is a property
, and in particular without a setter defined, so you can't easily assign to it.
You don't necessarily need to create your own logger if you want to add additional handlers though, so I'm not sure what you're trying to achieve beyond that. Though if you ''really'' wanted to override the default self.logger
, since you're subclassing Spider
there's nothing stopping you from adding something like:
@property
def logger(self):
return logging.getLogger('my_logger')
to your class.
Related Topics
Unpacking, Extended Unpacking and Nested Extended Unpacking
Remove Unwanted Parts from Strings in a Column
Using 'Try' VS. 'If' in Python
Find All Occurrences of a Key in Nested Dictionaries and Lists
Sending "User-Agent" Using Requests Library in Python
Add a Prefix to All Flask Routes
How to Merge a List of Dicts into a Single Dict
How to Pass Another Entire Column as Argument to Pandas Fillna()
Python - Pip Install Trouble Shooting - Permissionerror: [Winerror 5] Access Is Denied
Why Is the Id of a Python Class Not Unique When Called Quickly
Python Windows Authentication Username and Password Is Not Working
What Is the Best Project Structure for a Python Application
Correct Way to Try/Except Using Python Requests Module
How to Cross Compile Python Interpreter for Windows Under Linux
How to Create a Constant in Python