Confused about properties, descriptors, and attributes...

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • redefined.horizons@gmail.com

    Confused about properties, descriptors, and attributes...

    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.

  • Fredrik Lundh

    #2
    Re: Confused about properties, descriptors, and attributes...

    redefined.horiz ons@gmail.com wrote:
    [color=blue]
    > But what is the difference between an Attribute of a Class, a
    > Descriptor in a Class, and a Property in a Class?[/color]

    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?
    [color=blue]
    > If I had a Monster Class, and I wanted to give each Monster a member
    > variable called ScaryFactor, how would I define it?[/color]

    Assign to it in the initialization method:

    class Monster(object) :
    def __init__(self):
    self.ScaryFacto r = 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(obje ct):
    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.ScaryFa ctor = 300 # update the class attribute
    print m1.ScaryFactor, m2.ScaryFactor, m3.ScaryFactor
    # prints 200, 300, 300 (!)
    [color=blue]
    > Does a Property simply provide methods that access an Attribute?[/color]

    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



    Hope this helps!

    </F>



    Comment

    • Bruno Desthuilliers

      #3
      Re: Confused about properties, descriptors, and attributes...

      redefined.horiz ons@gmail.com a écrit :[color=blue]
      > I've been reading about Python Classes, and I'm a little confused about
      > how Python stores the state of an object.[/color]

      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.
      [color=blue]
      > 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.[/color]

      There's a special Python object named None.
      [color=blue]
      > But what is the difference between an Attribute of a Class, a
      > Descriptor in a Class, and a Property in a Class?[/color]

      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=l ambda self: self._name, fset=_set_name)
      # we don't want to pollute the class's namespace:
      del _set_name

      and some musing with it:
      [color=blue][color=green][color=darkred]
      >>> MyObject.__dict __.keys()[/color][/color][/color]
      ['__module__', 'name', 'classattr', '__dict__', '__weakref__',
      '__doc__', '__init__'][color=blue][color=green][color=darkred]
      >>> MyObject.__dict __['classattr'][/color][/color][/color]
      'I am a class attribute'[color=blue][color=green][color=darkred]
      >>> MyObject.__dict __['__init__'][/color][/color][/color]
      <function __init__ at 0x404134fc>[color=blue][color=green][color=darkred]
      >>> MyObject.__init __[/color][/color][/color]
      <unbound method MyObject.__init __>[color=blue][color=green][color=darkred]
      >>> MyObject.name[/color][/color][/color]
      <property object at 0x4041661c>[color=blue][color=green][color=darkred]
      >>> MyObject.name._ _class__.__dict __.keys()[/color][/color][/color]
      ['fset', '__new__', '__set__', '__getattribute __', '__doc__', 'fget',
      '__get__', 'fdel', '__init__', '__delete__'][color=blue][color=green][color=darkred]
      >>>> MyObject.name._ _class__.__dict __['fget'][/color][/color][/color]
      <member 'fget' of 'property' objects>[color=blue][color=green][color=darkred]
      >>> MyObject.name._ _class__.fget[/color][/color][/color]
      <member 'fget' of 'property' objects>[color=blue][color=green][color=darkred]
      >>> MyObject.name.f get[/color][/color][/color]
      <function <lambda> at 0x4041356c>[color=blue][color=green][color=darkred]
      >>> MyObject.name.f set[/color][/color][/color]
      <function _set_name at 0x40413534>[color=blue][color=green][color=darkred]
      >>> MyObject.__clas s__[/color][/color][/color]
      <type 'type'>[color=blue][color=green][color=darkred]
      >>> m = MyObject('parro t')
      >>> m[/color][/color][/color]
      <__main__.MyObj ect object at 0x40418dcc>[color=blue][color=green][color=darkred]
      >>> m.name[/color][/color][/color]
      'parrot'[color=blue][color=green][color=darkred]
      >>> MyObject.name.f get(m)[/color][/color][/color]
      'parrot'[color=blue][color=green][color=darkred]
      >>> dir(m)[/color][/color][/color]
      ['__class__', '__delattr__', '__dict__', '__doc__', '__getattribute __',
      '__hash__', '__init__', '__module__', '__new__', '__reduce__',
      '__reduce_ex__' , '__repr__', '__setattr__', '__str__', '__weakref__',
      '_name', 'classattr', 'name'][color=blue][color=green][color=darkred]
      >>> m.__dict__.keys ()[/color][/color][/color]
      ['_name'][color=blue][color=green][color=darkred]
      >>> m.__dict__['_name'][/color][/color][/color]
      'parrot'[color=blue][color=green][color=darkred]
      >>> m.classattr[/color][/color][/color]
      'I am a class attribute'[color=blue][color=green][color=darkred]
      >>> MyObject.classa ttr[/color][/color][/color]
      'I am a class attribute'[color=blue][color=green][color=darkred]
      >>> MyObject.classa ttr = 'yes I am'
      >>> m.classattr[/color][/color][/color]
      'yes I am'[color=blue][color=green][color=darkred]
      >>> m.classattr = "and I am in fact an instance attribute shadowing[/color][/color][/color]
      MyObject.classa ttr"[color=blue][color=green][color=darkred]
      >>> m.classattr[/color][/color][/color]
      'and I am in fact an instance attribute shadowing MyObject.classa ttr'[color=blue][color=green][color=darkred]
      >>> MyObject.classa ttr[/color][/color][/color]
      'yes I am'[color=blue][color=green][color=darkred]
      >>> m.__dict__[/color][/color][/color]
      {'classattr': 'and I am in fact an instance attribute shadowing
      MyObject.classa ttr', '_name': 'parrot'}[color=blue][color=green][color=darkred]
      >>> del m.classattr
      >>> m.classattr[/color][/color][/color]
      'yes I am'[color=blue][color=green][color=darkred]
      >>> def sayHello(obj, who):[/color][/color][/color]
      .... return "hello, %s, my name is %s" % (who, obj.name)
      ....[color=blue][color=green][color=darkred]
      >>> sayHello(m, 'Scott')[/color][/color][/color]
      'hello, Scott, my name is parrot'[color=blue][color=green][color=darkred]
      >>> import types
      >>> m.greet = types.MethodTyp e(sayHello, m, m.__class__)
      >>> m.greet[/color][/color][/color]
      <bound method MyObject.sayHel lo of <__main__.MyObj ect object at 0x40418dcc>>[color=blue][color=green][color=darkred]
      >>> m.__dict__[/color][/color][/color]
      {'greet': <bound method MyObject.sayHel lo of <__main__.MyObj ect object
      at 0x40418dcc>>, '_name': 'parrot'}[color=blue][color=green][color=darkred]
      >>> >>> m.greet('Joe')[/color][/color][/color]
      'hello, Joe, my name is parrot'[color=blue][color=green][color=darkred]
      >>> MyObject.greet[/color][/color][/color]
      Traceback (most recent call last):
      File "<stdin>", line 1, in ?
      AttributeError: type object 'MyObject' has no attribute 'greet'[color=blue][color=green][color=darkred]
      >>>[/color][/color][/color]


      If I had a Monster Class, and I wanted to give each Monster a member[color=blue]
      > variable called ScaryFactor, how would I define it?[/color]

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

      [color=blue]
      > Does a Property
      > simply provide methods that access an Attribute?[/color]

      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
      [color=blue]
      > P.S. - I'm speaking about the "new" Python classes, not the "old" ones.[/color]

      Hopefully. I don't see much reason to use old-style classes
      [color=blue]
      > I hope my questions make sense.[/color]

      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.

      Comment

      • Alexis Roda

        #4
        Re: Confused about properties, descriptors, and attributes...

        redefined.horiz ons@gmail.com escribió:[color=blue]
        > 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.[/color]

        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?
        [color=blue][color=green][color=darkred]
        >>> class Foo(object) :[/color][/color][/color]
        .... __slots__ = ['foo']
        ....[color=blue][color=green][color=darkred]
        >>> f=Foo()
        >>> dir(f)[/color][/color][/color]
        ['__class__', '__delattr__', '__doc__', '__getattribute __', '__hash__',
        '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__' ,
        '__repr__', '__setattr__', '__slots__', '__str__', 'foo'][color=blue][color=green][color=darkred]
        >>> f.foo[/color][/color][/color]
        Traceback (most recent call last):
        File "<stdin>", line 1, in ?
        AttributeError: foo[color=blue][color=green][color=darkred]
        >>> f.foo = 3
        >>> dir(f)[/color][/color][/color]
        ['__class__', '__delattr__', '__doc__', '__getattribute __', '__hash__',
        '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__' ,
        '__repr__', '__setattr__', '__slots__', '__str__', 'foo'][color=blue][color=green][color=darkred]
        >>> f.foo[/color][/color][/color]
        3
        [color=blue]
        > But what is the difference between an Attribute of a Class, a
        > Descriptor in a Class, and a Property in a Class?[/color]

        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.
        [color=blue][color=green][color=darkred]
        >>> class SimpleExample :[/color][/color][/color]
        .... def __getattr__(sel f, name) :
        .... if name=='attr' :
        .... return 0
        .... raise AttributeError( name)
        ....[color=blue][color=green][color=darkred]
        >>> s = SimpleExample()
        >>> s.attr[/color][/color][/color]
        0[color=blue][color=green][color=darkred]
        >>> s.bar[/color][/color][/color]
        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.
        [color=blue]
        > If I had a Monster Class, and I wanted to give each Monster a member
        > variable called ScaryFactor, how would I define it?[/color]

        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

        [color=blue]
        > Does a Property
        > simply provide methods that access an Attribute?[/color]

        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:

        The official home of the Python Programming Language



        HTH

        Comment

        Working...