I am trying to fill in a gap in my understanding of how Python objects and classes work.
A bare object
instance does not support attribute assignment in any way:
> object().a = 5
# or
> setattr(object(), 'a', 5)
AttributeError: 'object' object has no attribute 'a'
I assume that this is because a bare object
instance does not possess an __dict__
attribute:
> '__dict__' in dir(object())
False
However a bare object
instance does have a defined __setattr__
attribute, so this is a bit confusing to me:
> '__setattr__' in dir(object())
True
An instance of a regular empty class on the other hand has full ability of attribute assignment:
class B(object):
pass
> B().a = 5
> setattr(B(), 'a', 5)
My question is: what inherent difference between an object
instance and a class B(object)
instance allows the latter to have assignable attributes, if B
inherits directly from object
?
while you can access class attributes using an instance it's not safe to do so. In python, the instance of a class is referred to by the keyword self. Using this keyword you can access not only all instance attributes but also the class attributes.
Class attributes are the variables defined directly in the class that are shared by all objects of the class. Instance attributes are attributes or properties attached to an instance of a class. Instance attributes are defined in the constructor.
Classes contain characteristics called Attributes. We make a distinction between instance attributes and class attributes. Instance Attributes are unique to each object, (an instance is another name for an object). Here, any Dog object we create will be able to store its name and age.
Accessing the attributes of a class getattr() − A python method used to access the attribute of a class. hasattr() − A python method used to verify the presence of an attribute in a class. setattr() − A python method used to set an additional attribute in a class.
Instance Attributes. Unlike class attributes, instance attributes are not shared by objects. Every object has its own copy of the instance attribute (In case of class attributes all object refer to single copy). To list the attributes of an instance/object, we have two functions:-.
Class Attributes are unique to each class. Each instance of the class will have this attribute. It’s sometimes used to specify a defualt value that all objects should have after they’ve been instantiated. Here, our class attribute is species
02:59 There are two types of attributes: instance attributes and class attributes. Instance attributes are what we’ve seen before. These are the attributes that are independent to each object, like the door color or the height, in the previous example. In this example, our Dog class has two instance attributes: .name and .age.
These are the attributes that are independent to each object, like the door color or the height, in the previous example. In this example, our Dog class has two instance attributes: .name and .age. 03:21 This def __init__ () is a very special function that belongs to our class.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With