Attribute Access and Bound Methods

PythonPythonBeginner
Practice Now

This tutorial is from open-source community. Access the source code

Introduction

Objectives:

  • Learn about attribute access
  • Learn how use getattr(), setattr(), and related functions.
  • Experiment with bound methods.

Files Created: tableformat.py


Skills Graph

%%%%{init: {'theme':'neutral'}}%%%% flowchart RL python(("`Python`")) -.-> python/BasicConceptsGroup(["`Basic Concepts`"]) python(("`Python`")) -.-> python/ControlFlowGroup(["`Control Flow`"]) python(("`Python`")) -.-> python/DataStructuresGroup(["`Data Structures`"]) python(("`Python`")) -.-> python/FunctionsGroup(["`Functions`"]) python(("`Python`")) -.-> python/ModulesandPackagesGroup(["`Modules and Packages`"]) python(("`Python`")) -.-> python/PythonStandardLibraryGroup(["`Python Standard Library`"]) python/BasicConceptsGroup -.-> python/comments("`Comments`") python/BasicConceptsGroup -.-> python/variables_data_types("`Variables and Data Types`") python/BasicConceptsGroup -.-> python/booleans("`Booleans`") python/ControlFlowGroup -.-> python/for_loops("`For Loops`") python/DataStructuresGroup -.-> python/lists("`Lists`") python/DataStructuresGroup -.-> python/tuples("`Tuples`") python/FunctionsGroup -.-> python/function_definition("`Function Definition`") python/ModulesandPackagesGroup -.-> python/importing_modules("`Importing Modules`") python/ModulesandPackagesGroup -.-> python/standard_libraries("`Common Standard Libraries`") python/PythonStandardLibraryGroup -.-> python/data_collections("`Data Collections`") python/BasicConceptsGroup -.-> python/python_shell("`Python Shell`") python/FunctionsGroup -.-> python/build_in_functions("`Build-in Functions`") subgraph Lab Skills python/comments -.-> lab-132491{{"`Attribute Access and Bound Methods`"}} python/variables_data_types -.-> lab-132491{{"`Attribute Access and Bound Methods`"}} python/booleans -.-> lab-132491{{"`Attribute Access and Bound Methods`"}} python/for_loops -.-> lab-132491{{"`Attribute Access and Bound Methods`"}} python/lists -.-> lab-132491{{"`Attribute Access and Bound Methods`"}} python/tuples -.-> lab-132491{{"`Attribute Access and Bound Methods`"}} python/function_definition -.-> lab-132491{{"`Attribute Access and Bound Methods`"}} python/importing_modules -.-> lab-132491{{"`Attribute Access and Bound Methods`"}} python/standard_libraries -.-> lab-132491{{"`Attribute Access and Bound Methods`"}} python/data_collections -.-> lab-132491{{"`Attribute Access and Bound Methods`"}} python/python_shell -.-> lab-132491{{"`Attribute Access and Bound Methods`"}} python/build_in_functions -.-> lab-132491{{"`Attribute Access and Bound Methods`"}} end

The Three Operations

The entire Python object system consists of just three core operations: getting, setting, and deleting of attributes. Normally, these are accessed via the dot (.) like this:

>>> s = Stock('GOOG', 100, 490.1)
>>> s.name    ##  get
'GOOG'
>>> s.shares = 50    ## set
>>> del s.shares     ## delete
>>>

The three operations are also available as functions. For example:

>>> getattr(s, 'name')            ## Same as s.name
'GOOG'
>>> setattr(s, 'shares', 50)      ## Same as s.shares = 50
>>> delattr(s, 'shares')          ## Same as del s.shares
>>>

An additional function hasattr() can be used to probe an object for the existence of an attribute:

>>> hasattr(s, 'name')
True
>>> hasattr(s, 'blah')
False
>>>

Using getattr()

The getattr() function is extremely useful for writing code that processes objects in an extremely generic way. To illustrate, consider this example which prints out a set of user-defined attributes:

>>> s= Stock('GOOG', 100, 490.1)
>>> fields = ['name','shares','price']
>>> for name in fields:
           print(name, getattr(s, name))

name GOOG
shares 100
price 490.1
>>>

Table Output

In Exercise 3.1, you wrote a function print_portfolio() that made a nicely formatted table. That function was custom tailored to a list of Stock objects. However, it can be completely generalized to work with any list of objects using the technique in part (b).

Create a new module called tableformat.py. In that program, write a function print_table() that takes a sequence (list) of objects, a list of attribute names, and prints a nicely formatted table. For example:

>>> import stock
>>> import tableformat
>>> portfolio = stock.read_portfolio('portfolio.csv')
>>> tableformat.print_table(portfolio, ['name','shares','price'])
      name     shares      price
---------- ---------- ----------
        AA        100       32.2
       IBM         50       91.1
       CAT        150      83.44
      MSFT        200      51.23
        GE         95      40.37
      MSFT         50       65.1
       IBM        100      70.44

>>> tableformat.print_table(portfolio,['shares','name'])
    shares       name
---------- ----------
       100         AA
        50        IBM
       150        CAT
       200       MSFT
        95         GE
        50       MSFT
       100        IBM
>>>

For simplicity, just have the print_table() function print each field in a 10-character wide column.

Bound Methods

It may be surprising, but method calls are layered onto the machinery used for simple attributes. Essentially, a method is an attribute that executes when you add the required parentheses () to call it like a function. For example:

>>> s = Stock('GOOG',100,490.10)
>>> s.cost           ## Looks up the method
<bound method Stock.cost of <__main__.Stock object at 0x409530>>
>>> s.cost()         ## Looks up and calls the method
49010.0

>>> ## Same operations using getattr()
>>> getattr(s, 'cost')
<bound method Stock.cost of <__main__.Stock object at 0x409530>>
>>> getattr(s, 'cost')()
49010.0
>>>

A bound method is attached to the object where it came from. If that object is modified, the method will see the modifications. You can view the original object by inspecting the __self__ attribute of the method.

>>> c = s.cost
>>> c()
49010.0
>>> s.shares = 75
>>> c()
36757.5
>>> c.__self__
<__main__.Stock object at 0x409530>
>>> c.__func__
<function cost at 0x37cc30>
>>> c.__func__(c.__self__)      ## This is what happens behind the scenes of calling c()
36757.5
>>>

Try it with the sell() method just to make sure you understand the mechanics:

>>> f = s.sell
>>> f.__func__(f.__self__, 25)     ## Same as s.sell(25)
>>> s.shares
50
>>>

Summary

Congratulations! You have completed the Attribute Access lab. You can practice more labs in LabEx to improve your skills.