1

I just started learning python and am very confused at why I am able to do this. Will someone please explain why this works:

class foo(): @property def hello(self): return "hello world" if __name__ == '__main__': cli = foo() cli.foo2 = 3 print cli.hello #prints hello world print cli.foo2 #prints 3 

It doesn't make sense to me that I am able to create a new attribute and assign a value when it doesn't exist in the original class. Is there a way to prevent this behavior or can someone at least explain the logic behind this?

-edit- I am glad to know this is normal behavior for objects in python and I wasn't just doing something wrong. It just didn't feel right as I normally work in VBA and C#. From what others have said in answers below I thought adding slots would prevent anything from being added to the class in this way but maybe I didn't understand.

class foo(): @property def hello(self): return "hello world" __slots__ = [] if __name__ == '__main__': cli = foo() cli.foo2 = 3 print cli.hello #prints hello world print cli.foo2 #prints 
3
  • I tried this, and it doesn't work for me (as expected) - neither cli.foo nor cli.foo2 is defined. Commented Jun 27, 2013 at 4:22
  • @AMADANON: That's a little odd. It works for me, as well as the OP and others. Commented Jun 27, 2013 at 4:29
  • it's been edited/fixed - I still have the cut & paste in my terminal window :) Commented Jun 27, 2013 at 4:31

2 Answers 2

3

From a smarter person than me:

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__, 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.

Sign up to request clarification or add additional context in comments.

1 Comment

I guess I'll just accept this as normal behavior and move on then.
0

That is intended behavior for a python object, you are able to add attributes and the like whenever you create them. You can, however, restrict that behavior by doing this:

class foo(): __slots__ = [] @property def hello(self): return "hello world" 

This __slots__ variable is one way to make your class immutable, so that you cannot add properties to it otherwise python class typically behave like you described in your question

Another option is to override the methods for setting and deleting attributes, like this

def __setattr__(self, *args): raise TypeError def __delattr__(self, *args): raise TypeError 

2 Comments

Even after adding __slots__ = [] I was able to run the code without error. Not sure if I misunderstood how slots is supposed to work?
@Ripster it should be working as is, so should my additional solutions that I just posted

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.