Question: I am studying python, and although I think I get the whole concept and notion of Python, today I stumbled upon a piece of code that I did not fully understand:

Say I have a class that is supposed to define Circles but lacks a body:

```
class Circle():
    pass

```

Since I have not defined any attributes, how can I do this:

```
my_circle = Circle()
my_circle.radius = 12

```

The weird part is that Python accepts the above statement. I don't understand why Python doesn't raise an `undefined name error`. I do understand that via *dynamic typing* I just bind variables to objects whenever I want, but shouldn't an attribute `radius` exist in the `Circle` class to allow me to do this?

**EDIT**: Lots of wonderful information in your answers! **Thank you everyone for all those fantastic answers!** It's a pity I only get to mark one as an answer.

Answer: 
To add to [Conchylicultor's answer](https://stackoverflow.com/a/59711821/1719931), Python 3.10 added a new parameter to `dataclass`.

The `slots` parameter will create the `__slots__` attribute in the class, preventing creation of new attributes outside of `__init__`, but allowing assignments to existing attributes.

If `slots=True`, assigning to an attribute that was not defined will throw an `AttributeError`.

Here is an example with `slots` and with `frozen`:

```py
from dataclasses import dataclass

@dataclass
class Data:
    x:float=0
    y:float=0

@dataclass(frozen=True)
class DataFrozen:
    x:float=0
    y:float=0

@dataclass(slots=True)
class DataSlots:
    x:float=0
    y:float=0

p = Data(1,2)
p.x = 5 # ok
p.z = 8 # ok

p = DataFrozen(1,2)
p.x = 5 # FrozenInstanceError
p.z = 8 # FrozenInstanceError

p = DataSlots(1,2)
p.x = 5 # ok
p.z = 8 # AttributeError

```
As delnan said, you can obtain this behavior with the `__slots__` attribute. But the fact that it is a way to save memory space and access type does not discard the fact that it is (also) a/the mean to disable dynamic attributes.

Disabling dynamic attributes is a reasonable thing to do, if only to prevent subtle bugs due to spelling mistakes. "Testing and discipline" is fine but relying on automated validation is certainly not wrong either – and not necessarily unpythonic either.

Also, since the `attrs` library reached version 16 in 2016 (obviously way after the original question and answers), creating a closed class with slots has never been easier.

```
>>> import attr
...
... @attr.s(slots=True)
... class Circle:
...   radius = attr.ib()
...
... f = Circle(radius=2)
... f.color = 'red'
AttributeError: 'Circle' object has no attribute 'color'

```