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: 
A leading principle is that *there is no such thing as a declaration*. That is, you never declare "this class has a method foo" or "instances of this class have an attribute bar", let alone making a statement about the types of objects to be stored there. You simply define a method, attribute, class, etc. and it's added. As JBernardo points out, any `__init__` method does the very same thing. It wouldn't make a lot of sense to arbitrarily restrict creation of new attributes to methods with the name `__init__`. And it's sometimes useful to store a function as `__init__` which don't actually have that name (e.g. decorators), and such a restriction would break that.

Now, this isn't universally true. Builtin types omit this capability as an optimization. Via [`__slots__`](http://docs.python.org/reference/datamodel.html#slots), you can also prevent this on user-defined classes. But this is merely a space optimization (no need for a dictionary for every object), not a correctness thing.

If you want a safety net, well, too bad. Python does not offer one, and you cannot reasonably add one, and most importantly, it would be shunned by Python programmers who embrace the language (read: almost all of those you want to work with). Testing and discipline, still go a long way to ensuring correctness. Don't use the liberty to make up attributes outside of `__init__` *if it can be avoided*, and do automated testing. I very rarely have an `AttributeError` or a logical error due to trickery like this, and of those that happen, almost all are caught by tests.
There are two types of attributes in Python - `Class Data Attributes` and `Instance Data Attributes`.

Python gives you flexibility of creating `Data Attributes` on the fly. 

Since an instance data attribute is related to an instance, you can also do that in `__init__` method or you can do it after you have created your instance..

```
class Demo(object):
    classAttr = 30
    def __init__(self):
         self.inInit = 10

demo = Demo()
demo.outInit = 20
Demo.new_class_attr = 45; # You can also create class attribute here.

print demo.classAttr  # Can access it 

del demo.classAttr         # Cannot do this.. Should delete only through class

demo.classAttr = 67  # creates an instance attribute for this instance.
del demo.classAttr   # Now OK.
print Demo.classAttr  

```

So, you see that we have created two instance attributes, one inside `__init__` and one outside, after instance is created..

But a difference is that, the instance attribute created inside `__init__` will be set for all the instances, while if created outside, you can have different instance attributes for different isntances..

This is unlike Java, where each Instance of a Class have same set of Instance Variables..

* NOTE: - While you can access a class attribute through an instance, you cannot delete it..
Also, if you try to modify a class attribute through an instance, you actually create an instance attribute which shadows the class attribute..