Reputation: 385
Can someone explain me this behavior as to why 1) doesn't work while 2) and 3) works
1)
class bm(object):
def __init__(self,val):
self.a=val
def get(self):
return self.a
def set(self,val):
self.a=val
a=property(get,set)
In [43]: ob1=bm('vin')
gives me Recursive error ,while the below code works fine
2)
class bm(object):
def __init__(self,val):
self._a=val
def get(self):
return self._a
def set(self,val):
self._a=val
a=property(get,set)
In [43]: ob1=bm('vin')
Works fine.I can access ob.a and do ob.a=''
Even this works fine
3)
class bm(object):
def __init__(self,val):
self.a=val
def get(self):
return self._a
def set(self,val):
self._a=val
a=property(get,set)
In [43]: ob1=bm('vin')
Works fine.I can access ob.a and do ob.a=''
Upvotes: 0
Views: 1496
Reputation: 77942
The point here is that in Python, everything (including functions, methods, "properties" - or any other descriptor - classes and even modules) is an object, and that there's no distinct namespaces for "data" and "functions or methods". IOW, in Python, an object has attributes, period - no "member data" nor "member functions". Even the base classes are attributes (and are themselves objects so they have attributes too).
The attribute lookup rules are (quite simplified - I won't mention some special cases like slots etc):
for reading:
__get__
method.__dict__
__getattr__
method, call itfor setting:
__set__
method.__dict__
I mentionned but did not explain the descriptor protocol. This protocol (if you come from Java, a protocol is kind of an "implied" interface - you don't have to declare it, just to implement it) says that if an object has a __get__
method and eventually a __set__
method (here again I overly simplify by not mentionning the __del__
part) AND is a class attribute then when looked up on an instance of the class it's __get__
method will be called (with the instance and class as arguments) on "read" lookup and it's __set__
method will be called (with the instance and value) on "write".
IOW, the descriptor protocol is the basis for computed attributes in Python.
Now about the property
type (yes,it is a class, not a function): it does implement the descriptor protocol, in a very simple way. Here's a simplified version of how the property type would be implemented in pure Python (not taking the __del__
part into account):
class property(object):
def __init__(self, fget, fset=None, doc=None):
self.fget = fget
self.fset = fset
self.doc = doc
def __get__(self, instance, cls=None):
if not instance:
return self
return self.fget(instance)
def __set__(self, instance, value):
if not self.fset:
raise AttributeError("attribute is read-only")
self.fset(instance, value)
Back to the question - given the class statement :
class Something(object):
def __init__(self,val):
self.a=val
def get(self):
return self.a
def set(self,val):
self.a=val
a=property(get,set)
We have a class object Something
with a (class) attribute a
which is a property with fget=Something.get
and fset=Something.set
. Now what happens when we instanciate Something
? The initializer is called with a val
argument, and try to bind self.a
to that argument. The attribute lookup rule (for "writing" - we should really say 'binding') kicks in and notice that the Something
class object has an attribute a
which - as an instance of the property
type - implements the __set__
part of the protocol descriptor. So the lookup rule calls Something.a.__set__(theinstance, val)
, which resolves to Something.a.fset(theinstance, val)
, which is implemented as self.a = val
. New attribute lookup, finds a class attribute a
implementing the binding part of the descriptor protocol, invoke it, etc..., bang, infinite recursion.
To make a long story short: an attribute is an attribute is an attribute ;)
Note that in your third example, the set
method try to set self._a
and not self.a
. Since the class has no descriptor named _a
, this just create an instance attribute by that name, so no recursion here.
For more on the descriptor protocol, cf - http://docs.python.org/reference/datamodel.html#implementing-descriptors - http://wiki.python.org/moin/ComputedAttributesUsingPropertyObjects
and if you want to understand what Python "methods" really are (hint: the function
type implement the descriptor protocol) and why the 'self' argument is required, you can read this too:
- http://wiki.python.org/moin/FromFunctionToMethod
Upvotes: 3
Reputation: 411310
In the first example, you're creating the property a
, which lets you do things like:
self.a # Get the value
self.a = some_obj # Set the value
But within the a
property, you're referring to the a
property again, via self.a
! This will create a problem with recursion.
In the next examples, the property a
is backed by the variable self._a
, avoiding this recursion issue.
Upvotes: 5