Dynamic attribute assignment [duplicate] - python

This question already has answers here:
Why can't you add attributes to object in python? [duplicate]
(2 answers)
Closed 9 years ago.
I was trying out dynamic attribute assignment for testing purposes and discovered following behavior:
>>> class Foo(object): pass
...
>>> bar = Spam()
>>> bar.a = 1
>>> spam = object()
>>> spam.a = 2
Traceback (most recent call last):
File "<input>", line 1, in <module>
AttributeError: 'object' object has no attribute 'a'
Why is the first version with a derived class legit, but the second direct usage of object not? It seems a bit strange to me because deriving hasn't changed anything that has obviously something to do with how variable assignment is handled.

That's because object is a native type, meaning that it's implemented in C code and does not support dynamic attribute assignment, for performance reasons. The same can be said for most Python native classes, such as str or int.
But Python allows you to subclass any native type and your subclasses do support dynamic assignment.
You can disable it for performance reasons on your classes too, using the __slots__ special attribute.

object instances don't have a __dict__.
>>> hasattr(object(), '__dict__')
False
And therefore can't have any attributes added to them.

Related

What happens if you assign a value to `type` in python? [duplicate]

This question already has an answer here:
Why can you assign values to built-in functions in Python?
(1 answer)
Closed 1 year ago.
What happens in these two scenarios?
# Scenario 1
type(3)
# <class 'int'>
type = "A random value"
# This will throw an error
type(3)
# Traceback (most recent call last):
# File "<stdin>", line 1, in <module>
# TypeError: 'str' object is not callable
Did we loose reference to type for the rest of the application?
# Scenario 2
class Foo:
type = 3
bar = Foo()
bar.type
# 3
type(bar.type)
# <class 'int'>
What does that mean when using type as a django model field?
class FooBar(models.Model):
baz = models.IntegerField(...)
type = models.CharField(...)
And why does python allow assigning a value to type?
type is just a built-in function, you can override it like any other function
str = 5
print = "hello"
And why does python allow assigning a value to type?
Python in general almost never not allow you to do something, just like accessing private variables in classes, one of the principles of the language is trusting the dev to do whatever he needs

Class variables declared but not instantiated [duplicate]

This question already has answers here:
What are type hints in Python 3.5?
(5 answers)
Closed 2 years ago.
I'm trying to understand the following Python code
class MyClass():
aa:int
What is happening here? It seems to me that the variable aa is a class variable which is declared but not initialized. The :int seems to be a typing hint. Am I correct? I can instantiate the class but I cannot access aa. Which makes me think that my understanding is wrong. See below
mm = MyClass()
mm.aa
Traceback (most recent call last):
File "<ipython-input-15-cfce603dd5e0>", line 1, in <module>
mm.aa
AttributeError: 'MyClass' object has no attribute 'aa'
Indeed, this only creates an annotation for the attribute, it does not create the attribute itself. Attributes and variables are only created by assignment, and nothing's being assigned here, so it doesn't exist (not even with an implicit None or such).
This pattern is useful to satisfy type checkers if the attribute is initialised outside of __init__, e.g.:
class MyClass(SomeParentClass):
aa: int
def initialize(self):
self.aa = 'foo'
Let's say that SomeParentClass will call initialize at some defined point during its instantiation process and it wants subclasses to use initialize to do their initialisations, instead of overriding __init__. A type checker might complain here that aa is created outside of __init__ and is therefore not safe to access. The aa: int annotation explicitly says that aa should be expected to exist as an int at any time, so is safe to access (taking care that that'll actually be the case is your responsibility then). An example of this kind of pattern can be found in Tornado, for instance.
Another use of these annotations of course are classes where those annotations are explicitly used at runtime, like Python's own dataclasses do.

Why are instances of the `object` class immutable in Python?

>>> a = object()
>>> a.x = 5
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'object' object has no attribute 'x'
>>> b = lambda:0
>>> b.x = 5
>>> b.x
5
Why do instances of the object class not have a __dict__, causing it to behave as semantically immutable? What were the reasons for choosing this design?
Specifically, why:
instances of types defined in C don't have a __dict__ attribute by
default.
As noted in this question.
The documentation for Python 2 is not very helpful in giving an explanation as to why you cannot assign attributes to an object(), but the documentation for Python 3 provides a bit more information:
Return a new featureless object. object is a base for all classes. It has the methods that are common to all instances of Python classes. This function does not accept any arguments.
Note: object does not have a __dict__, so you can’t assign arbitrary attributes to an instance of the object class.
Thus, the reason you cannot add arbitrary attributes to your object() appears to be because of the fact that object() instances do not have an implementation of the __dict__ attribute, not because object() instances are immutable:
>>> hasattr(object(), '__dict__')
False
>>>
Another interesting thing, but perhaps not relevant to the discussion at hand, is that while an instance of object may not have a __dict__ implementation, the object class itself does:
>>> hasattr(object, '__dict__')
True
As for the why part of the question, I cannot find any exact reasons for why object() doesn't have a __dict__. Is is probably because - as #tdelany has already mentioned on in the comments - an implementation detail. If you really want a definitive answer, you should ask Guido himself.

what makes user defined objects not throw AttributeError in Python? [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Why can't I directly add attributes to any python object?
Why can't you add attributes to object in python?
The following code does not throw AttributeError
class MyClass():
def __init__(self):
self.a = 'A'
self.b = 'B'
my_obj = MyClass()
my_obj.c = 'C'
That contrasts with
>>> {}.a = 'A'
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'dict' object has no attribute 'a'
What makes such difference? Is it about dict being a built-in class while MyClass being user defined?
The difference is that instances of user-defined classes have a dictionary of attributes associated with them by default. You can access this dictionary using vars(my_obj) or my_obj.__dict__. You can prevent the creation of an attribute dictionary by defining __slots__:
class MyClass(object):
__slots__ = []
Built-in types could also provide an attribute dictionary, but usually they don't. An example of a built-in type that does support attributes is the type of a function.

Adding attributes to python objects

It's a thing that bugged me for a while. Why can't I do:
>>> a = ""
>>> a.foo = 2
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'str' object has no attribute 'foo'
...while I can do the following?
>>> class Bar():
... pass
...
>>> a = Bar()
>>> a.foo = 10 #ok!
What's the rule here? Could you please point me to some description?
You can add attributes to any object that has a __dict__.
x = object() doesn't have it, for example.
Strings and other simple builtin objects also don't have it.
Classes using __slots__ also do not have it.
Classes defined with class have it unless the previous statement applies.
If an object is using __slots__ / doesn't have a __dict__, it's usually to save space. For example, in a str it would be overkill to have a dict - imagine the amount of bloat for a very short string.
If you want to test if a given object has a __dict__, you can use hasattr(obj, '__dict__').
This might also be interesting to read:
Some objects, such as built-in types and their instances (lists, tuples, etc.) do not have a __dict__. Consequently user-defined attributes cannot be set on them.
Another interesting article about Python's data model including __dict__, __slots__, etc. is this from the python reference.

Categories