Difference between revisions of "Attribute"

From CS 61A Wiki
Jump to: navigation, search
[checked revision][checked revision]
(Sources)
(copyedit; add examples)
 
(8 intermediate revisions by 2 users not shown)
Line 1: Line 1:
An '''attribute''' of an object is a piece of data that describes the object, stored within the object as a key-value pair. Given an object, one can ask for the value of a particular key using ''dot notation''.
+
{{Sufficient-class}}
 +
{{OOP sidebar}}
 +
An '''attribute''' of an object is a piece of data that describes the object, stored within the object as a key-value pair. Given an object, one can ask for the value of an attribute using ''dot notation''.
 
<syntaxhighlight lang="python">
 
<syntaxhighlight lang="python">
>>> object.key
+
>>> object.attribute
 
value
 
value
 
</syntaxhighlight>
 
</syntaxhighlight>
To access an object's list of attributes, just call the <code>dir()</code> function on the object.
 
  
== Example of handling attributes ==
+
==Class attribute==
 +
A ''class attribute'' is shared by all instances of a class. Class attributes are defined within the class but outside of methods.
 +
 
 +
They can be accessed by the class or by instances of the class: <code>A.attr</code> or <code>obj.attr</code>, where <code>A</code> is the class and <code>obj</code> is an instance of the class. Example:
 
<syntaxhighlight lang="python">
 
<syntaxhighlight lang="python">
>>> import datetime
+
>>> class A:
>>> date_object = datetime.date
+
...    class_attribute = 5
>>> dir(date_object)
+
...
['__add__', '__class__', '__delattr__', '__doc__', '__eq__', ..., 'today', 'toordinal', 'weekday', 'year']
+
>>> A.class_attribute
>>> date.year
+
5
2014
+
>>> inst = A()
 +
>>> inst.class_attribute
 +
5
 
</syntaxhighlight>
 
</syntaxhighlight>
== Class vs. instance attributes ==
+
<!--
 +
<syntaxhighlight lang="python">
 +
>>> class Test():
 +
        num = 0
 +
        def do_nothing(self):
 +
            return
 +
>>> Test.do_nothing
 +
<function do_nothing at 0x17d6270>
 +
>>> Test.do_nothing = 1
 +
>>> Test.do_nothing
 +
1
 +
>>> inst = Test()
 +
>>> inst.do_nothing()
 +
Traceback (most recent call last):
 +
  File "<stdin>", line 1, in <module>
 +
TypeError: 'str' object is not callable
 +
>>> inst.do_nothing
 +
1
 +
>>> Test.num
 +
0
 +
>>> inst.num
 +
0
 +
</syntaxhighlight>
 +
-->
  
== @property ==
+
==Instance attribute==
The property object is, simply put, another way of having a getter and setter method for a defined class. It has three methods a [[Getter|getter]], a [[Setter|setter]], and a deleter (but don't worry about deleters. They are outside the scope of this course and you will not be tested on them.)  
+
An ''instance attribute'' is specific to one instance of a class. Instance attributes are typically defined inside methods. Example:
 +
<syntaxhighlight lang="python">
 +
class A:
 +
    def __init__(self):
 +
        self.x = 1 # x is an instance attribute
  
Here's an example:
+
>>> a = A()
 +
>>> a.x
 +
1
 +
</syntaxhighlight>
  
 +
If a class attribute and instance attribute have the same name, then the instance attribute will hide the class attribute. The instance can access only the instance attribute, although the class can still access the class attribute. Example:
 
<syntaxhighlight lang="python">
 
<syntaxhighlight lang="python">
class ImAClass(object):
+
>>> class A:
 +
        num = 0
  
 +
        def __init__(self):
 +
            self.num = 1
 +
>>> A.num
 +
0
 +
>>> inst1 = A()
 +
>>> inst1.num
 +
1
 +
</syntaxhighlight>
 +
 +
=== Method ===
 +
A ''method'' is an instance attribute that is a function that manipulates the object. A method can be invoked on an object in two ways:
 +
* <code>A.method(obj, arg)</code>, where <code>A</code> is a class and <code>obj</code> is the object. <code>A.method</code> is a normal function.
 +
* <code>obj.method(arg)</code>, where <code>obj</code> is the object. <code>obj.method</code> is called a ''bound method'' because it has an object attached to it. The object is automatically passed into the method as the first argument (usually <code>self</code>).
 +
<syntaxhighlight lang="python">
 +
class A:
 +
    def __init__(self, id):
 +
        self.id = id
 +
 +
    def get_id(self):
 +
        return self.id
 +
 +
>>> a = A(6)
 +
>>> A.get_id
 +
<function A.get_id at 0x...>
 +
>>> a.get_id
 +
<bound method A.get_id of <__main__.A object at 0x...>>
 +
>>> A.get_id(a)
 +
6
 +
>>> a.get_id()
 +
6
 +
</syntaxhighlight>
 +
 +
The first argument of a method is always the current object. This is conventionally called <code>[[self]]</code>.
 +
 +
== Attribute assignment ==
 +
Attributes can be assigned to by using dot notation and an equals sign.
 +
 +
=== From inside the class ===
 +
Inside the class definition, class attributes are assigned to by using dot notation on the class name. Example:
 +
<syntaxhighlight lang="python">
 +
class A:
 +
    num = 0
 
     def __init__(self):
 
     def __init__(self):
         self._Property = None
+
         A.num += 1
  
 +
>>> A()
 +
<__main__.A object at 0x...>
 +
>>> A()
 +
<__main__.A object at 0x...>
 +
>>> A.num
 +
2
 +
</syntaxhighlight>
  
    @property
+
Inside the class definition, instance attributes are assigned to by using dot notation on <code>self</code>. Example:
     def Property(self):
+
<syntaxhighlight lang="python">
         """I am a property."""
+
class A:
        return self._Property
+
     def __init__(self):
 +
         self.id = 1
  
 +
>>> a = A()
 +
>>> a.id
 +
1
 +
</syntaxhighlight>
  
     @Property.setter
+
=== From outside the class ===
    def Property(self, value):
+
Example of assigning a class attribute:
        self._Property = value
+
<syntaxhighlight lang="python">
 +
>>> class A:
 +
...     class_attribute = 5
 +
...  
 +
>>> A.class_attribute
 +
5
 +
>>> A.class_attribute = 6
 +
>>> A.class_attribute
 +
6
 +
</syntaxhighlight>
  
 +
Example of assigning an instance attribute:
 +
<syntaxhighlight lang="python">
 +
class A:
 +
    def __init__(self):
 +
        self.x = 1
  
    @Property.deleter
+
>>> a = A()
    def Property(self):
+
>>> a.x
        del self._Property
+
1
 +
>>> a.x = 2
 +
>>> a.x
 +
2
 
</syntaxhighlight>
 
</syntaxhighlight>
  
 +
=== Assigning to methods ===
 +
To assign to a method for the entire class:
 +
<syntaxhighlight lang="python">
 +
A.method = lambda self: ret_value
 +
</syntaxhighlight>
 +
 +
To assign to a method for an instance:
 +
<syntaxhighlight lang="python">
 +
inst.method = lambda: ret_value
 +
</syntaxhighlight>
 +
 +
Examples:
 +
<syntaxhighlight lang="python">
 +
>>> class A:
 +
...    def __init__(self, x):
 +
...        self.x = x
 +
...
 +
>>> A.prev = lambda self: self.x - 1 # add a method for the class
 +
>>> a = A(1)
 +
>>> a.prev()
 +
0
 +
>>> a.next = lambda: a.x + 1 # add a method for object 'a'
 +
>>> a.next()
 +
2
 +
</syntaxhighlight>
 +
 +
== <code>@property</code> ==
 +
<code>@property</code> is a shorthand way to create getter and setter methods. Putting <code>@property</code> above a method allows it to be accessed as if it were an instance variable.
 +
 +
Sometimes we have a value that can be computed by accessing other instance variables. Instead of defining a new instance variable (which would then have to be modified each time we change an existing instance variable), we simply make a property.<ref>http://markmiyashita.com/cs61a/object_oriented_programming/property/</ref>
 +
 +
Example:
 +
<syntaxhighlight lang="python">
 +
class Square:
 +
    def __init__(self, side_length):
 +
        self._length = side_length # leading underscore indicates that this attribute is only for internal use
 +
 +
    @property
 +
    def area(self):
 +
        return self._length ** 2
 +
 +
    @area.setter
 +
    def area(self, value):
 +
        self._length = value ** (1/2)
 +
 +
>>> sq = Square(5.0)
 +
</syntaxhighlight>
 +
 +
Because <code>area</code> is a <code>property</code> method, it can be accessed by <code>sq.area</code> instead of <code>sq.area()</code>:
 +
<syntaxhighlight lang="python">
 +
>>> sq.area
 +
25.0
 +
</syntaxhighlight>
 +
 +
The second <code>area</code> method is marked as a setter method for the property <code>area</code>, so it is called when we use attribute assignment:
 +
<syntaxhighlight lang="python">
 +
>>> sq.area = 9.0
 +
>>> sq.area
 +
9.0
 +
>>> sq._length
 +
3.0
 +
</syntaxhighlight>
 +
<!--
 
There is little to no difference between implementing a property vs implementing separate getters and setters, and the difference in the amount of code is often negligible.  
 
There is little to no difference between implementing a property vs implementing separate getters and setters, and the difference in the amount of code is often negligible.  
  
Here is an example of implementation using getters and setters:
+
Here is an example of implementation using getters and setters<ref>http://blaag.haard.se/What-s-the-point-of-properties-in-Python/</ref>:
 
<syntaxhighlight lang="python">
 
<syntaxhighlight lang="python">
 
>>> class Get_set_imp(object):
 
>>> class Get_set_imp(object):
Line 62: Line 234:
 
3
 
3
 
</syntaxhighlight>
 
</syntaxhighlight>
<ref>http://blaag.haard.se/What-s-the-point-of-properties-in-Python/</ref>
+
 
  
 
And here is the same implementation, this time using properties:
 
And here is the same implementation, this time using properties:
Line 82: Line 254:
  
 
Both of these examples essentially do the same thing in roughly the same amount of code. The advantage of having properties however, is that you may switch from the two whenver you want. Simply add or remove or add the property tags from the getters and setters.
 
Both of these examples essentially do the same thing in roughly the same amount of code. The advantage of having properties however, is that you may switch from the two whenver you want. Simply add or remove or add the property tags from the getters and setters.
 +
-->
 +
== <code>AttributeError</code> ==
 +
When one tries to access a nonexistent attribute of an object, Python will raise an <code>AttributeError: 'type_of_obj' object has no attribute 'key'</code>.
  
 
+
This error commonly occurs when the object to the left of the dot is <code>None</code>. Example:
== AttributeErrors ==
+
When one tries to access an attribute of the object that does not exist, Python will raise an AttributeError: 'type_of_obj' object has no attribute 'key'.
+
=== 'Nonetype' has no attribute ===
+
This error occurs when the object to the left of the dot notation is <code>None</code>.
+
 
<syntaxhighlight lang="python">
 
<syntaxhighlight lang="python">
>>> class Foo():
+
>>> lst = None
...    def bar(self):
+
>>> lst.append()
...        print(3)
+
...
+
>>> a = Foo()
+
>>> b = a.bar()
+
3
+
>>> b.bar()
+
 
Traceback (most recent call last):
 
Traceback (most recent call last):
   File "<stdin>", line 1, in <module>
+
   ...
AttributeError: 'NoneType' object has no attribute 'bar'
+
AttributeError: 'NoneType' object has no attribute 'append'
 
</syntaxhighlight>
 
</syntaxhighlight>
== Naming conventions ==
 
* _single_leading_underscore indicates that the attribute should be only be used internally by the object.
 
* __double_leading_and_trailing__underscores__ denote [[Magic method|"magic" attributes]], built-in attributes in Python that have a particular meaning beyond just holding a key-value pair.
 
  
 +
== Naming conventions<ref>http://legacy.python.org/dev/peps/pep-0008 PEP 8 Style Guide for Python Code</ref> ==
 +
* <code>_single_leading_underscore</code> indicates that the attribute should be only be used internally by the object.
 +
* <code>__double_leading_and_trailing__underscores__</code> denote [[magic method]]s, built-in attributes in Python that have a particular meaning beyond just holding a key-value pair.
 +
 +
== <code>dir</code>, <code>hasattr</code>, <code>getattr</code> ==
 +
* The <code>dir</code> function returns an object's list of attributes. Example:
 +
<blockquote style="margin: 1em 1.6em;"><syntaxhighlight lang="python">
 +
>>> import datetime
 +
>>> date_object = datetime.date
 +
>>> dir(date_object)
 +
['__add__', '__class__', '__delattr__', '__doc__', '__eq__', ..., 'today', 'toordinal', 'weekday', 'year']
 +
</syntaxhighlight></blockquote>
 +
* <code>hasattr</code> takes in a string and checks if the object has that attribute. <code>getattr</code> takes in a string and returns that attribute of the object. Example:
 +
<blockquote style="margin: 1em 1.6em;"><syntaxhighlight lang="python">
 +
>>> class A:
 +
...    def __init__(self, x):
 +
...        self.x = x
 +
...
 +
>>> a = A(1)
 +
>>> hasattr(a, 'x')
 +
True
 +
>>> getattr(a, 'x')
 +
1
 +
</syntaxhighlight></blockquote>
 +
 +
== Implementation ==
 +
In a Python functional implementation of object-oriented programming, attributes can be modeled as a local dictionary within the object frame<ref>http://composingprograms.com/pages/26-implementing-classes-and-objects.html#instances Implementation of attributes in Python</ref>, because attributes are just key-value pairs belonging to the object.
 +
<syntaxhighlight lang=Python>
 +
>>> # From Section 2.6.1 of Composing Programs by John DeNero (see citation above)
 +
>>> def make_instance(cls):
 +
        """Return a new object instance, which is a dispatch dictionary."""
 +
        def get_value(name):
 +
            if name in attributes:
 +
                return attributes[name]
 +
            else:
 +
                value = cls['get'](name)
 +
                return bind_method(value, instance)
 +
        def set_value(name, value):
 +
            attributes[name] = value
 +
        attributes = {}
 +
        instance = {'get': get_value, 'set': set_value}
 +
        return instance
 +
</syntaxhighlight>
  
== Sources ==
+
== References ==
* [http://legacy.python.org/dev/peps/pep-0008 PEP 8 Style Guide for Python Code]
+
<references>
* [http://blaag.haard.se/What-s-the-point-of-properties-in-Python/]
+

Latest revision as of 14:55, 24 July 2014

An attribute of an object is a piece of data that describes the object, stored within the object as a key-value pair. Given an object, one can ask for the value of an attribute using dot notation.

>>> object.attribute
value

Class attribute

A class attribute is shared by all instances of a class. Class attributes are defined within the class but outside of methods.

They can be accessed by the class or by instances of the class: A.attr or obj.attr, where A is the class and obj is an instance of the class. Example:

>>> class A:
...     class_attribute = 5
... 
>>> A.class_attribute
5
>>> inst = A()
>>> inst.class_attribute
5

Instance attribute

An instance attribute is specific to one instance of a class. Instance attributes are typically defined inside methods. Example:

class A:
    def __init__(self):
        self.x = 1 # x is an instance attribute
 
>>> a = A()
>>> a.x
1

If a class attribute and instance attribute have the same name, then the instance attribute will hide the class attribute. The instance can access only the instance attribute, although the class can still access the class attribute. Example:

>>> class A:
        num = 0
 
        def __init__(self):
            self.num = 1
>>> A.num
0
>>> inst1 = A()
>>> inst1.num
1

Method

A method is an instance attribute that is a function that manipulates the object. A method can be invoked on an object in two ways:

  • A.method(obj, arg), where A is a class and obj is the object. A.method is a normal function.
  • obj.method(arg), where obj is the object. obj.method is called a bound method because it has an object attached to it. The object is automatically passed into the method as the first argument (usually self).
class A:
    def __init__(self, id):
        self.id = id
 
    def get_id(self):
        return self.id
 
>>> a = A(6)
>>> A.get_id
<function A.get_id at 0x...>
>>> a.get_id
<bound method A.get_id of <__main__.A object at 0x...>>
>>> A.get_id(a)
6
>>> a.get_id()
6

The first argument of a method is always the current object. This is conventionally called self.

Attribute assignment

Attributes can be assigned to by using dot notation and an equals sign.

From inside the class

Inside the class definition, class attributes are assigned to by using dot notation on the class name. Example:

class A:
    num = 0
    def __init__(self):
        A.num += 1
 
>>> A()
<__main__.A object at 0x...>
>>> A()
<__main__.A object at 0x...>
>>> A.num
2

Inside the class definition, instance attributes are assigned to by using dot notation on self. Example:

class A:
    def __init__(self):
        self.id = 1
 
>>> a = A()
>>> a.id
1

From outside the class

Example of assigning a class attribute:

>>> class A:
...     class_attribute = 5
... 
>>> A.class_attribute
5
>>> A.class_attribute = 6
>>> A.class_attribute
6

Example of assigning an instance attribute:

class A:
    def __init__(self):
        self.x = 1
 
>>> a = A()
>>> a.x
1
>>> a.x = 2
>>> a.x
2

Assigning to methods

To assign to a method for the entire class:

A.method = lambda self: ret_value

To assign to a method for an instance:

inst.method = lambda: ret_value

Examples:

>>> class A:
...     def __init__(self, x):
...         self.x = x
... 
>>> A.prev = lambda self: self.x - 1 # add a method for the class
>>> a = A(1)
>>> a.prev()
0
>>> a.next = lambda: a.x + 1 # add a method for object 'a'
>>> a.next()
2

@property

@property is a shorthand way to create getter and setter methods. Putting @property above a method allows it to be accessed as if it were an instance variable.

Sometimes we have a value that can be computed by accessing other instance variables. Instead of defining a new instance variable (which would then have to be modified each time we change an existing instance variable), we simply make a property.[1]

Example:

class Square:
    def __init__(self, side_length):
        self._length = side_length # leading underscore indicates that this attribute is only for internal use
 
    @property
    def area(self):
        return self._length ** 2
 
    @area.setter
    def area(self, value):
        self._length = value ** (1/2)
 
>>> sq = Square(5.0)

Because area is a property method, it can be accessed by sq.area instead of sq.area():

>>> sq.area
25.0

The second area method is marked as a setter method for the property area, so it is called when we use attribute assignment:

>>> sq.area = 9.0
>>> sq.area
9.0
>>> sq._length
3.0

AttributeError

When one tries to access a nonexistent attribute of an object, Python will raise an AttributeError: 'type_of_obj' object has no attribute 'key'.

This error commonly occurs when the object to the left of the dot is None. Example:

>>> lst = None
>>> lst.append()
Traceback (most recent call last):
  ...
AttributeError: 'NoneType' object has no attribute 'append'

Naming conventions[2]

  • _single_leading_underscore indicates that the attribute should be only be used internally by the object.
  • __double_leading_and_trailing__underscores__ denote magic methods, built-in attributes in Python that have a particular meaning beyond just holding a key-value pair.

dir, hasattr, getattr

  • The dir function returns an object's list of attributes. Example:
>>> import datetime
>>> date_object = datetime.date
>>> dir(date_object)
['__add__', '__class__', '__delattr__', '__doc__', '__eq__', ..., 'today', 'toordinal', 'weekday', 'year']
  • hasattr takes in a string and checks if the object has that attribute. getattr takes in a string and returns that attribute of the object. Example:
>>> class A:
...     def __init__(self, x):
...         self.x = x
...
>>> a = A(1)
>>> hasattr(a, 'x')
True
>>> getattr(a, 'x')
1

Implementation

In a Python functional implementation of object-oriented programming, attributes can be modeled as a local dictionary within the object frame[3], because attributes are just key-value pairs belonging to the object.

>>> # From Section 2.6.1 of Composing Programs by John DeNero (see citation above)
>>> def make_instance(cls):
        """Return a new object instance, which is a dispatch dictionary."""
        def get_value(name):
            if name in attributes:
                return attributes[name]
            else:
                value = cls['get'](name)
                return bind_method(value, instance)
        def set_value(name, value):
            attributes[name] = value
        attributes = {}
        instance = {'get': get_value, 'set': set_value}
        return instance

References

  1. http://markmiyashita.com/cs61a/object_oriented_programming/property/
  2. http://legacy.python.org/dev/peps/pep-0008 PEP 8 Style Guide for Python Code
  3. http://composingprograms.com/pages/26-implementing-classes-and-objects.html#instances Implementation of attributes in Python