Confused about properties, descriptors, and attributes...

R

redefined.horizons

I've been reading about Python Classes, and I'm a little confused about
how Python stores the state of an object. I was hoping for some help.

I realize that you can't create an empty place holder for a member
variable of a Python object. It has to be given a value when defined,
or set within a method.

But what is the difference between an Attribute of a Class, a
Descriptor in a Class, and a Property in a Class?

If I had a Monster Class, and I wanted to give each Monster a member
variable called ScaryFactor, how would I define it? Does a Property
simply provide methods that access an Attribute?

Thanks,

Scott Huey

P.S. - I'm speaking about the "new" Python classes, not the "old" ones.
I hope my questions make sense.
 
F

Fredrik Lundh

But what is the difference between an Attribute of a Class, a
Descriptor in a Class, and a Property in a Class?

A class has a class dictionary, which contains methods and other
class-level members for this class.

Each instance of a class has its own instance dictionary, which holds
the attributes for that instance.

An attribute is an instance variable that lives in the instance dictionary.
if you ask for the attribute value, you get the value from the instance
dictionary. If the attribute is not found in the instance, Python looks
in the class dictionary. If you assign to an attribute, Python adds it
to the instance dictionary.

A descriptor is a special kind of class attribute that implements a
descriptor protocol. if you ask for the attribute value for a descriptor
attribute, or assign to it, Python will ask the descriptor object what
to do.

A property is simply a ready-made descriptor type that maps attribute
access to getter and setter methods.

Still with me?
If I had a Monster Class, and I wanted to give each Monster a member
variable called ScaryFactor, how would I define it?

Assign to it in the initialization method:

class Monster(object):
def __init__(self):
self.ScaryFactor = 100

This gives all monsters their own ScaryFactor value.

Since Python will look in the class dictionary if an attribute is not found
in the instance dictionary, you can also do:

class AltMonster(object):
ScaryFactor = 100

If you do this, all monsters will share the same factor value. However,
if you set the factor on the instance (either by assigning to self inside
a method, or by assigning to it from the outside), that object will get
its own copy.

m1 = AltMonster()
m2 = AltMonster()
m3 = AltMonster()
print m1.ScaryFactor, m2.ScaryFactor, m3.ScaryFactor
# prints 100, 100, 100 (all three refers to the same object)

m1.ScaryFactor = 200
print m1.ScaryFactor, m2.ScaryFactor, m3.ScaryFactor
# prints 200, 100, 100 (m1 has its own attribute value)

Monster.ScaryFactor = 300 # update the class attribute
print m1.ScaryFactor, m2.ScaryFactor, m3.ScaryFactor
# prints 200, 300, 300 (!)
Does a Property simply provide methods that access an Attribute?

It maps attribute accesses to method calls, yes.

You can use it instead of a plain attribute when you need to add getter
or setter logic; there's usually no reason to use properties instead of
attributes for plain data members.

To learn more about descriptors (which are really simple, and rather mind-
boggling), see

http://users.rcn.com/python/download/Descriptor.htm

Hope this helps!

</F>
 
A

Alexis Roda

(e-mail address removed) escribió:
I've been reading about Python Classes, and I'm a little confused about
how Python stores the state of an object. I was hoping for some help.

I realize that you can't create an empty place holder for a member
variable of a Python object. It has to be given a value when defined,
or set within a method.

I don't understand what do you mean with "empty place holder". Some
pre-allocated-and-uninitialized memory to hold the attribute value (ala
C++ o java)? Maybe slots?
.... __slots__ = ['foo']
....['__class__', '__delattr__', '__doc__', '__getattribute__', '__hash__',
'__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__',
'__repr__', '__setattr__', '__slots__', '__str__', 'foo']Traceback (most recent call last):
['__class__', '__delattr__', '__doc__', '__getattribute__', '__hash__',
'__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__',
'__repr__', '__setattr__', '__slots__', '__str__', 'foo']
3

But what is the difference between an Attribute of a Class, a
Descriptor in a Class, and a Property in a Class?

I don't know what the formal definitions are. Probably "attribute"
refers to anything that can be obtained by way of getattr(). According
to this "definition" an attribute does not require to be stored in the
instance, it can be calculated or obtained from somewhere else.
.... def __getattr__(self, name) :
.... if name=='attr' :
.... return 0
.... raise AttributeError(name)
....Traceback (most recent call last):
File "<stdin>", line 1, in ?
File "<stdin>", line 5, in __getattr__
AttributeError: bar


descriptors are a mechanism/protocol for attribute access. It's intended
to solve some problems of the original __getattr__() protocol.

A property is a specialized type of descriptor which allows
customization of attribute's access methods.
If I had a Monster Class, and I wanted to give each Monster a member
variable called ScaryFactor, how would I define it?

If you're asking when to use plain attributes, descriptors or properties
the answer is that it depends on what you want/need:

* if you store monsters in a database and the ScaryFactor attribute is
computed by some complex and expensive database lookup I'll go for some
kind of lazy descriptor/property

* if you want to be able to control/limit what can be done with an
attribute may be a property is enough. This includes things like make
the attribute read only etc.

* if you don't have special requirements a plain attribute would be
probably simpler and faster

Does a Property
simply provide methods that access an Attribute?

not exactly, it allows you to assign/define the methods that will be
used to access the attribute, but you still access to the attribute in
the usual "dot-way".


For further info take a look at:

http://www.python.org/doc/newstyle/


HTH
 
B

Bruno Desthuilliers

(e-mail address removed) a écrit :
I've been reading about Python Classes, and I'm a little confused about
how Python stores the state of an object.

Simply put: a Python object is mostly a hashtable, with attribute names
as keys and references to other objects as values - one of these
name/ref pairs pointing to the class object.
I was hoping for some help.

I realize that you can't create an empty place holder for a member
variable of a Python object. It has to be given a value when defined,
or set within a method.

There's a special Python object named None.
But what is the difference between an Attribute of a Class, a
Descriptor in a Class, and a Property in a Class?

Everything is an attribute, one way or another. Let's start simple: an
object has a special attribute named __dict__, in which (references to)
instance attributes - those you set with 'self.attr=val' - are stored.
As I mentioned above, our object has another special attribute named
__class__, that points to the class object - which, being an object,
has itself a __dict__ attribute in which class attributes are stored,
and a __class__ attribute pointing to the metaclass - the class of the
class.

Note BTW that Python functions being objects, the term 'attribute'
include methods... (or more exactly, functions that will be turned into
methods when looked up)

Ok, now how is this used. <overly-simplified> When a name is looked up
on an object (via the lookup operator '.'), it is first looked up in the
object's __dict__. If this fails, it's then looked up in the class's
__dict__, then in parent classes __dict__s. If the attribute object
found is a callable, it is wrapped in a method object before. If the
object is a descriptor (and has been found in a class __dict__), then
the __get__ method of the descriptor is called.</overly-simplified>

NB : a descriptor is an object that implements at least a __get__ method
that takes an instance (the one on which the lookup occurs) and a class
as params. Properties are descriptors. methods too.

The __dict__ of a class is populated with the names and functions
defined in the class: block. The __dict__ of an instance is populated
with the attributes bound to the instance (usually inside method, and
mostly inside the __init__() method, which is called just after object's
creation) - with the exception that, if the name already refers to a
descriptor having a __set__ method, then this will take precedence.

All this is a 1000 feet high, overly simplified view of Python's object
model - the lookup rules are a bit more complex (cf the
__getattr__/__setattr__/__getattribute___ methods, and the complete
definition of the dexcriptor protocol), and I didn't even talked about
slots nor metaclasses.

To summarize: a sample class

class MyObject(object):
classattr = "I am a class attribute"

# the __init__ function will also be
# an attribute of class MyObject
def __init__(self, name):
# _name will be an instance attribute
self._name = name

def _set_name(self, value):
self._name = name
# name will be an attribute of MyClass.
# it's a property - which is a descriptor
name = property(fget=lambda self: self._name, fset=_set_name)
# we don't want to pollute the class's namespace:
del _set_name

and some musing with it:
['__module__', 'name', 'classattr', '__dict__', '__weakref__',
'__doc__', '__init__']
>>> MyObject.__dict__['classattr'] 'I am a class attribute'
>>> MyObject.__dict__['__init__']
>>> MyObject.__init__
>>> MyObject.name
>>> MyObject.name.__class__.__dict__.keys()
['fset', '__new__', '__set__', '__getattribute__', '__doc__', 'fget',
'__get__', 'fdel', '__init__', '__delete__']
MyObject.name.__class__.__dict__['fget']
MyObject.name.__class__.fget
MyObject.name.fget
>>> MyObject.name.fset
>>> MyObject.__class__
>>> m = MyObject('parrot')
>>> m
>>> m.name 'parrot'
>>> MyObject.name.fget(m) 'parrot'
>>> dir(m)
['__class__', '__delattr__', '__dict__', '__doc__', '__getattribute__',
'__hash__', '__init__', '__module__', '__new__', '__reduce__',
'__reduce_ex__', '__repr__', '__setattr__', '__str__', '__weakref__',
'_name', 'classattr', 'name']
>>> m.__dict__.keys() ['_name']
>>> m.__dict__['_name'] 'parrot'
>>> m.classattr 'I am a class attribute'
>>> MyObject.classattr 'I am a class attribute'
>>> MyObject.classattr = 'yes I am'
>>> m.classattr 'yes I am'
>>> m.classattr = "and I am in fact an instance attribute shadowing MyObject.classattr"
>>> m.classattr 'and I am in fact an instance attribute shadowing MyObject.classattr'
>>> MyObject.classattr 'yes I am'
>>> m.__dict__
{'classattr': 'and I am in fact an instance attribute shadowing
MyObject.classattr', '_name': 'parrot'}.... return "hello, %s, my name is %s" % (who, obj.name)
....{'greet': <bound method MyObject.sayHello of <__main__.MyObject object
at 0x40418dcc>>, '_name': 'parrot'}Traceback (most recent call last):


If I had a Monster Class, and I wanted to give each Monster a member
variable called ScaryFactor, how would I define it?

class Monster(object):
def __init__(self, scary_factor):
self.scary_factor = scary_factor

Does a Property
simply provide methods that access an Attribute?

A property is mean to have simple computed attributes. The two main uses
are to restrict access to an existing attribute (ie: make it readonly)
or to compute something from existing attributes. Now you can use custom
descriptors for more advanced computed attributes (that may have nothing
to do with the object on which there are called).

HTH
P.S. - I'm speaking about the "new" Python classes, not the "old" ones.

Hopefully. I don't see much reason to use old-style classes
I hope my questions make sense.

It does. Python's object model is much more complex than it seems at
first sight, and learning how to use it is worth the time spent if you
want to really take advantage of Python's power.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Similar Threads


Members online

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,014
Latest member
BiancaFix3

Latest Threads

Top