Edits
This commit is contained in:
@@ -291,10 +291,8 @@ The `if` statement is used to execute a conditional:
|
|||||||
|
|
||||||
```python
|
```python
|
||||||
if a > b:
|
if a > b:
|
||||||
# a is greater than b
|
|
||||||
print('Computer says no')
|
print('Computer says no')
|
||||||
else:
|
else:
|
||||||
# a is lower or equal to b
|
|
||||||
print('Computer says yes')
|
print('Computer says yes')
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -302,13 +300,10 @@ You can check for multiple conditions by adding extra checks using `elif`.
|
|||||||
|
|
||||||
```python
|
```python
|
||||||
if a > b:
|
if a > b:
|
||||||
# a is greater than b
|
|
||||||
print('Computer says no')
|
print('Computer says no')
|
||||||
elif a == b:
|
elif a == b:
|
||||||
# a is equal to b
|
|
||||||
print('Computer says yes')
|
print('Computer says yes')
|
||||||
else:
|
else:
|
||||||
# a is lower to b
|
|
||||||
print('Computer says maybe')
|
print('Computer says maybe')
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ with open(filename, 'rt') as file:
|
|||||||
# Process the line
|
# Process the line
|
||||||
```
|
```
|
||||||
|
|
||||||
### Common Idioms for Write to a File
|
### Common Idioms for Writing to a File
|
||||||
|
|
||||||
Write string data.
|
Write string data.
|
||||||
|
|
||||||
|
|||||||
@@ -139,7 +139,7 @@ True
|
|||||||
>>>
|
>>>
|
||||||
```
|
```
|
||||||
|
|
||||||
For example, the inner list `[100, 101]` is being shared.
|
For example, the inner list `[100, 101, 102]` is being shared.
|
||||||
This is known as a shallow copy. Here is a picture.
|
This is known as a shallow copy. Here is a picture.
|
||||||
|
|
||||||

|

|
||||||
|
|||||||
@@ -1,17 +1,22 @@
|
|||||||
|
[Contents](../Contents) \| [Previous (3.6 Design discussion)](../03_Program_organization/06_Design_discussion) \| [Next (4.2 Inheritance)](02_Inheritance)
|
||||||
|
|
||||||
# 4.1 Classes
|
# 4.1 Classes
|
||||||
|
|
||||||
|
This section introduces the class statement and the idea of creating new objects.
|
||||||
|
|
||||||
### Object Oriented (OO) programming
|
### Object Oriented (OO) programming
|
||||||
|
|
||||||
A Programming technique where code is organized as a collection of *objects*.
|
A Programming technique where code is organized as a collection of
|
||||||
|
*objects*.
|
||||||
|
|
||||||
An *object* consists of:
|
An *object* consists of:
|
||||||
|
|
||||||
* Data. Attributes
|
* Data. Attributes
|
||||||
* Behavior. Methods, functions applied to the object.
|
* Behavior. Methods which are functions applied to the object.
|
||||||
|
|
||||||
You have already been using some OO during this course.
|
You have already been using some OO during this course.
|
||||||
|
|
||||||
For example with Lists.
|
For example, manipulating a list.
|
||||||
|
|
||||||
```python
|
```python
|
||||||
>>> nums = [1, 2, 3]
|
>>> nums = [1, 2, 3]
|
||||||
@@ -24,14 +29,14 @@ For example with Lists.
|
|||||||
|
|
||||||
`nums` is an *instance* of a list.
|
`nums` is an *instance* of a list.
|
||||||
|
|
||||||
Methods (`append` and `insert`) are attached to the instance (`nums`).
|
Methods (`append()` and `insert()`) are attached to the instance (`nums`).
|
||||||
|
|
||||||
### The `class` statement
|
### The `class` statement
|
||||||
|
|
||||||
Use the `class` statement to define a new object.
|
Use the `class` statement to define a new object.
|
||||||
|
|
||||||
```python
|
```python
|
||||||
class Player(object):
|
class Player:
|
||||||
def __init__(self, x, y):
|
def __init__(self, x, y):
|
||||||
self.x = x
|
self.x = x
|
||||||
self.y = y
|
self.y = y
|
||||||
@@ -59,9 +64,10 @@ They are created by calling the class as a function.
|
|||||||
>>>
|
>>>
|
||||||
```
|
```
|
||||||
|
|
||||||
`a` anb `b` are instances of `Player`.
|
`a` and `b` are instances of `Player`.
|
||||||
|
|
||||||
*Emphasize: The class statement is just the definition (it does nothing by itself). Similar to a function definition.*
|
*Emphasize: The class statement is just the definition (it does
|
||||||
|
nothing by itself). Similar to a function definition.*
|
||||||
|
|
||||||
### Instance Data
|
### Instance Data
|
||||||
|
|
||||||
@@ -77,7 +83,7 @@ Each instance has its own local data.
|
|||||||
This data is initialized by the `__init__()`.
|
This data is initialized by the `__init__()`.
|
||||||
|
|
||||||
```python
|
```python
|
||||||
class Player(object):
|
class Player:
|
||||||
def __init__(self, x, y):
|
def __init__(self, x, y):
|
||||||
# Any value stored on `self` is instance data
|
# Any value stored on `self` is instance data
|
||||||
self.x = x
|
self.x = x
|
||||||
@@ -92,7 +98,7 @@ There are no restrictions on the total number or type of attributes stored.
|
|||||||
Instance methods are functions applied to instances of an object.
|
Instance methods are functions applied to instances of an object.
|
||||||
|
|
||||||
```python
|
```python
|
||||||
class Player(object):
|
class Player:
|
||||||
...
|
...
|
||||||
# `move` is a method
|
# `move` is a method
|
||||||
def move(self, dx, dy):
|
def move(self, dx, dy):
|
||||||
@@ -113,15 +119,15 @@ def move(self, dx, dy):
|
|||||||
|
|
||||||
By convention, the instance is called `self`. However, the actual name
|
By convention, the instance is called `self`. However, the actual name
|
||||||
used is unimportant. The object is always passed as the first
|
used is unimportant. The object is always passed as the first
|
||||||
argument. It is simply Python programming style to call this argument
|
argument. It is merely Python programming style to call this argument
|
||||||
`self`.
|
`self`.
|
||||||
|
|
||||||
### Class Scoping
|
### Class Scoping
|
||||||
|
|
||||||
Classes do not define a scope.
|
Classes do not define a scope of names.
|
||||||
|
|
||||||
```python
|
```python
|
||||||
class Player(object):
|
class Player:
|
||||||
...
|
...
|
||||||
def move(self, dx, dy):
|
def move(self, dx, dy):
|
||||||
self.x += dx
|
self.x += dx
|
||||||
@@ -132,13 +138,15 @@ class Player(object):
|
|||||||
self.move(-amt, 0) # YES. Calls method `move` from above.
|
self.move(-amt, 0) # YES. Calls method `move` from above.
|
||||||
```
|
```
|
||||||
|
|
||||||
If you want to operate on an instance, you always have to refer too it explicitly (e.g., `self`).
|
If you want to operate on an instance, you always refer to it explicitly (e.g., `self`).
|
||||||
|
|
||||||
## Exercises
|
## Exercises
|
||||||
|
|
||||||
Note: For this exercise you want to have fully working code from earlier
|
Starting with this set of exercises, we start to make a series of
|
||||||
exercises. If things are broken look at the solution code for Exercise 3.18.
|
changes to existing code from previous sctions. It is critical that
|
||||||
You can find this code in the `Solutions/3_18` directory.
|
you have a working version of Exercise 3.18 to start. If you don't
|
||||||
|
have that, please work from the solution code found in the
|
||||||
|
`Solutions/3_18` directory. It's fine to copy it.
|
||||||
|
|
||||||
### Exercise 4.1: Objects as Data Structures
|
### Exercise 4.1: Objects as Data Structures
|
||||||
|
|
||||||
@@ -206,8 +214,8 @@ Create a few more `Stock` objects and manipulate them. For example:
|
|||||||
|
|
||||||
One thing to emphasize here is that the class `Stock` acts like a
|
One thing to emphasize here is that the class `Stock` acts like a
|
||||||
factory for creating instances of objects. Basically, you call
|
factory for creating instances of objects. Basically, you call
|
||||||
it as a function and it creates a new object for you. Also, it needs
|
it as a function and it creates a new object for you. Also, it must
|
||||||
to be emphasized that each object is distinct---they each have their
|
be emphasized that each object is distinct---they each have their
|
||||||
own data that is separate from other objects that have been created.
|
own data that is separate from other objects that have been created.
|
||||||
|
|
||||||
An object defined by a class is somewhat similar to a dictionary--just
|
An object defined by a class is somewhat similar to a dictionary--just
|
||||||
@@ -238,8 +246,8 @@ stored inside an object. Add a `cost()` and `sell()` method to your
|
|||||||
|
|
||||||
### Exercise 4.3: Creating a list of instances
|
### Exercise 4.3: Creating a list of instances
|
||||||
|
|
||||||
Try these steps to make a list of Stock instances and compute the total
|
Try these steps to make a list of Stock instances from a list of
|
||||||
cost:
|
dictionaries. Then compute the total cost:
|
||||||
|
|
||||||
```python
|
```python
|
||||||
>>> import fileparse
|
>>> import fileparse
|
||||||
@@ -258,10 +266,11 @@ cost:
|
|||||||
|
|
||||||
### Exercise 4.4: Using your class
|
### Exercise 4.4: Using your class
|
||||||
|
|
||||||
Modify the `read_portfolio()` function in the `report.py` program so that it
|
Modify the `read_portfolio()` function in the `report.py` program so
|
||||||
reads a portfolio into a list of `Stock` instances. Once you have done that,
|
that it reads a portfolio into a list of `Stock` instances as just
|
||||||
fix all of the code in `report.py` and `pcost.py` so that it works with
|
shown in Exercise 4.3. Once you have done that, fix all of the code
|
||||||
`Stock` instances instead of dictionaries.
|
in `report.py` and `pcost.py` so that it works with `Stock` instances
|
||||||
|
instead of dictionaries.
|
||||||
|
|
||||||
Hint: You should not have to make major changes to the code. You will mainly
|
Hint: You should not have to make major changes to the code. You will mainly
|
||||||
be changing dictionary access such as `s['shares']` into `s.shares`.
|
be changing dictionary access such as `s['shares']` into `s.shares`.
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
|
[Contents](../Contents) \| [Previous (4.1 Classes)](01_Class) \| [Next (4.3 Special methods)](03_Special_methods)
|
||||||
|
|
||||||
# 4.2 Inheritance
|
# 4.2 Inheritance
|
||||||
|
|
||||||
Inheritance is a commonly used tool for writing extensible programs. This section explores that idea.
|
Inheritance is a commonly used tool for writing extensible programs.
|
||||||
|
This section explores that idea.
|
||||||
|
|
||||||
### Introduction
|
### Introduction
|
||||||
|
|
||||||
@@ -10,13 +13,13 @@ Inheritance is used to specialize existing objects:
|
|||||||
class Parent:
|
class Parent:
|
||||||
...
|
...
|
||||||
|
|
||||||
class Child(Parent): # Check how `Parent` is between the parenthesis
|
class Child(Parent):
|
||||||
...
|
...
|
||||||
```
|
```
|
||||||
|
|
||||||
The new class `Child` is called a derived class or subclass.
|
The new class `Child` is called a derived class or subclass. The
|
||||||
The `Parent` class is known as base class or superclass.
|
`Parent` class is known as base class or superclass. `Parent` is
|
||||||
`Parent` is specified in `()` after the class name, `class Child(Parent):`.
|
specified in `()` after the class name, `class Child(Parent):`.
|
||||||
|
|
||||||
### Extending
|
### Extending
|
||||||
|
|
||||||
@@ -33,7 +36,7 @@ In the end you are **extending existing code**.
|
|||||||
Suppose that this is your starting class:
|
Suppose that this is your starting class:
|
||||||
|
|
||||||
```python
|
```python
|
||||||
class Stock(object):
|
class Stock:
|
||||||
def __init__(self, name, shares, price):
|
def __init__(self, name, shares, price):
|
||||||
self.name = name
|
self.name = name
|
||||||
self.shares = shares
|
self.shares = shares
|
||||||
@@ -61,9 +64,11 @@ Usage example.
|
|||||||
```python
|
```python
|
||||||
>>> s = MyStock('GOOG', 100, 490.1)
|
>>> s = MyStock('GOOG', 100, 490.1)
|
||||||
>>> s.sell(25)
|
>>> s.sell(25)
|
||||||
>>> s.shares 75
|
>>> s.shares
|
||||||
|
75
|
||||||
>>> s.panic()
|
>>> s.panic()
|
||||||
>>> s.shares 0
|
>>> s.shares
|
||||||
|
0
|
||||||
>>>
|
>>>
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -84,15 +89,15 @@ Usage example.
|
|||||||
>>>
|
>>>
|
||||||
```
|
```
|
||||||
|
|
||||||
The new method takes the place of the old one. The other methods are unaffected.
|
The new method takes the place of the old one. The other methods are unaffected. It's tremendous.
|
||||||
|
|
||||||
## Overriding
|
## Overriding
|
||||||
|
|
||||||
Sometimes a class extends an existing method, but it wants to use the original implementation.
|
Sometimes a class extends an existing method, but it wants to use the
|
||||||
For this, use `super()`:
|
original implementation inside the redefinition. For this, use `super()`:
|
||||||
|
|
||||||
```python
|
```python
|
||||||
class Stock(object):
|
class Stock:
|
||||||
...
|
...
|
||||||
def cost(self):
|
def cost(self):
|
||||||
return self.shares * self.price
|
return self.shares * self.price
|
||||||
@@ -107,7 +112,7 @@ class MyStock(Stock):
|
|||||||
|
|
||||||
Use `super()` to call the previous version.
|
Use `super()` to call the previous version.
|
||||||
|
|
||||||
*Caution: Python 2 is different.*
|
*Caution: In Python 2, the syntax was more verbose.*
|
||||||
|
|
||||||
```python
|
```python
|
||||||
actual_cost = super(MyStock, self).cost()
|
actual_cost = super(MyStock, self).cost()
|
||||||
@@ -115,10 +120,10 @@ actual_cost = super(MyStock, self).cost()
|
|||||||
|
|
||||||
### `__init__` and inheritance
|
### `__init__` and inheritance
|
||||||
|
|
||||||
If `__init__` is redefined, it is mandatory to initialize the parent.
|
If `__init__` is redefined, it is essential to initialize the parent.
|
||||||
|
|
||||||
```python
|
```python
|
||||||
class Stock(object):
|
class Stock:
|
||||||
def __init__(self, name, shares, price):
|
def __init__(self, name, shares, price):
|
||||||
self.name = name
|
self.name = name
|
||||||
self.shares = shares
|
self.shares = shares
|
||||||
@@ -134,14 +139,15 @@ class MyStock(Stock):
|
|||||||
return self.factor * super().cost()
|
return self.factor * super().cost()
|
||||||
```
|
```
|
||||||
|
|
||||||
You should call the `init` on the `super` which is the way to call the previous version as shown previously.
|
You should call the `__init__()` method on the `super` which is the
|
||||||
|
way to call the previous version as shown previously.
|
||||||
|
|
||||||
### Using Inheritance
|
### Using Inheritance
|
||||||
|
|
||||||
Inheritance is sometimes used to organize related objects.
|
Inheritance is sometimes used to organize related objects.
|
||||||
|
|
||||||
```python
|
```python
|
||||||
class Shape(object):
|
class Shape:
|
||||||
...
|
...
|
||||||
|
|
||||||
class Circle(Shape):
|
class Circle(Shape):
|
||||||
@@ -151,8 +157,10 @@ class Rectangle(Shape):
|
|||||||
...
|
...
|
||||||
```
|
```
|
||||||
|
|
||||||
Think of a logical hierarchy or taxonomy. However, a more common usage is
|
Think of a logical hierarchy or taxonomy. However, a more common (and
|
||||||
related to making reusable or extensible code:
|
practical) usage is related to making reusable or extensible code.
|
||||||
|
For example, a framework might define a base class and instruct you
|
||||||
|
to customize it.
|
||||||
|
|
||||||
```python
|
```python
|
||||||
class CustomHandler(TCPHandler):
|
class CustomHandler(TCPHandler):
|
||||||
@@ -162,14 +170,14 @@ class CustomHandler(TCPHandler):
|
|||||||
```
|
```
|
||||||
|
|
||||||
The base class contains some general purpose code.
|
The base class contains some general purpose code.
|
||||||
Your class inherits and customized specific parts. Maybe it plugs into a framework.
|
Your class inherits and customized specific parts.
|
||||||
|
|
||||||
### "is a" relationship
|
### "is a" relationship
|
||||||
|
|
||||||
Inheritance establishes a type relationship.
|
Inheritance establishes a type relationship.
|
||||||
|
|
||||||
```python
|
```python
|
||||||
class Shape(object):
|
class Shape:
|
||||||
...
|
...
|
||||||
|
|
||||||
class Circle(Shape):
|
class Circle(Shape):
|
||||||
@@ -185,7 +193,8 @@ True
|
|||||||
>>>
|
>>>
|
||||||
```
|
```
|
||||||
|
|
||||||
*Important: Code that works with the parent is also supposed to work with the child.*
|
*Important: Ideally, any code that worked with instances of the parent
|
||||||
|
class will also work with instances of the child class.*
|
||||||
|
|
||||||
### `object` base class
|
### `object` base class
|
||||||
|
|
||||||
@@ -198,25 +207,29 @@ class Shape(object):
|
|||||||
|
|
||||||
`object` is the parent of all objects in Python.
|
`object` is the parent of all objects in Python.
|
||||||
|
|
||||||
*Note: it's not technically required in Python 3. If omitted in Python 2, it results in an "old style class" which should be avoided.*
|
*Note: it's not technically required, but you often see it specified
|
||||||
|
as a hold-over from it's required use in Python 2. If omitted, the
|
||||||
|
class still implicitly inherits from `object`.
|
||||||
|
|
||||||
### Multiple Inheritance
|
### Multiple Inheritance
|
||||||
|
|
||||||
You can inherit from multiple classes by specifying them in the definition of the class.
|
You can inherit from multiple classes by specifying them in the definition of the class.
|
||||||
|
|
||||||
```python
|
```python
|
||||||
class Mother(object):
|
class Mother:
|
||||||
...
|
...
|
||||||
|
|
||||||
class Father(object):
|
class Father:
|
||||||
...
|
...
|
||||||
|
|
||||||
class Child(Mother, Father):
|
class Child(Mother, Father):
|
||||||
...
|
...
|
||||||
```
|
```
|
||||||
|
|
||||||
The class `Child` inherits features from both parents. There are some rather tricky details. Don't do it unless you know what you are doing.
|
The class `Child` inherits features from both parents. There are some
|
||||||
We're not going to explore multiple inheritance further in this course.
|
rather tricky details. Don't do it unless you know what you are doing.
|
||||||
|
Some further information will be given in the next section, but we're not
|
||||||
|
going to utilize multiple inheritance further in this course.
|
||||||
|
|
||||||
## Exercises
|
## Exercises
|
||||||
|
|
||||||
@@ -271,7 +284,7 @@ following class:
|
|||||||
```python
|
```python
|
||||||
# tableformat.py
|
# tableformat.py
|
||||||
|
|
||||||
class TableFormatter(object):
|
class TableFormatter:
|
||||||
def headings(self, headers):
|
def headings(self, headers):
|
||||||
'''
|
'''
|
||||||
Emit the table headings.
|
Emit the table headings.
|
||||||
@@ -286,10 +299,12 @@ class TableFormatter(object):
|
|||||||
```
|
```
|
||||||
|
|
||||||
This class does nothing, but it serves as a kind of design specification for
|
This class does nothing, but it serves as a kind of design specification for
|
||||||
additional classes that will be defined shortly.
|
additional classes that will be defined shortly. A class like this is
|
||||||
|
sometimes called an "abstract base class."
|
||||||
|
|
||||||
Modify the `print_report()` function so that it accepts a `TableFormatter` object
|
Modify the `print_report()` function so that it accepts a
|
||||||
as input and invokes methods on it to produce the output. For example, like this:
|
`TableFormatter` object as input and invokes methods on it to produce
|
||||||
|
the output. For example, like this:
|
||||||
|
|
||||||
```python
|
```python
|
||||||
# report.py
|
# report.py
|
||||||
@@ -341,12 +356,13 @@ Run this new code:
|
|||||||
```
|
```
|
||||||
|
|
||||||
It should immediately crash with a `NotImplementedError` exception. That's not
|
It should immediately crash with a `NotImplementedError` exception. That's not
|
||||||
too exciting, but continue to the next part.
|
too exciting, but it's exactly what we expected. Continue to the next part.
|
||||||
|
|
||||||
### Exercise 4.6: Using Inheritance to Produce Different Output
|
### Exercise 4.6: Using Inheritance to Produce Different Output
|
||||||
|
|
||||||
The `TableFormatter` class you defined in part (a) is meant to be extended via inheritance.
|
The `TableFormatter` class you defined in part (a) is meant to be
|
||||||
In fact, that's the whole idea. To illustrate, define a class `TextTableFormatter` like this:
|
extended via inheritance. In fact, that's the whole idea. To
|
||||||
|
illustrate, define a class `TextTableFormatter` like this:
|
||||||
|
|
||||||
```python
|
```python
|
||||||
# tableformat.py
|
# tableformat.py
|
||||||
@@ -485,11 +501,11 @@ that expected to use a `TableFormatter` object, it would work no
|
|||||||
matter what kind of `TableFormatter` you actually gave it. This
|
matter what kind of `TableFormatter` you actually gave it. This
|
||||||
behavior is sometimes referred to as "polymorphism."
|
behavior is sometimes referred to as "polymorphism."
|
||||||
|
|
||||||
One potential problem is figuring out how to allow a user to pick
|
One potential problem is figuring out how to allow a user to pick out
|
||||||
out the formatter that they want. Direct use of the class names
|
the formatter that they want. Direct use of the class names such as
|
||||||
such as `TextTableFormatter` is often annoying. Thus, you
|
`TextTableFormatter` is often annoying. Thus, you might consider some
|
||||||
might consider some simplified approach. Perhaps you embed an `if-`statement
|
simplified approach. Perhaps you embed an `if-`statement into the
|
||||||
into the code like this:
|
code like this:
|
||||||
|
|
||||||
```python
|
```python
|
||||||
def portfolio_report(portfoliofile, pricefile, fmt='txt'):
|
def portfolio_report(portfoliofile, pricefile, fmt='txt'):
|
||||||
@@ -521,10 +537,10 @@ the `portfolio_report()` function like that the best idea? It might
|
|||||||
be better to move that code to a general purpose function somewhere
|
be better to move that code to a general purpose function somewhere
|
||||||
else.
|
else.
|
||||||
|
|
||||||
In the `tableformat.py` file, add a
|
In the `tableformat.py` file, add a function `create_formatter(name)`
|
||||||
function `create_formatter(name)` that allows a user to create a
|
that allows a user to create a formatter given an output name such as
|
||||||
formatter given an output name such as `'txt'`, `'csv'`, or `'html'`.
|
`'txt'`, `'csv'`, or `'html'`. Modify `portfolio_report()` so that it
|
||||||
Modify `portfolio_report()` so that it looks like this:
|
looks like this:
|
||||||
|
|
||||||
```python
|
```python
|
||||||
def portfolio_report(portfoliofile, pricefile, fmt='txt'):
|
def portfolio_report(portfoliofile, pricefile, fmt='txt'):
|
||||||
@@ -587,9 +603,25 @@ you to define your own object that inherits from a provided base
|
|||||||
class. You're then told to fill in various methods that implement
|
class. You're then told to fill in various methods that implement
|
||||||
various bits of functionality.
|
various bits of functionality.
|
||||||
|
|
||||||
That said, designing object oriented programs can be extremely difficult.
|
Another somewhat deeper concept is the idea of "owning your
|
||||||
For more information, you should probably look for books on the topic of
|
abstractions." In the exercises, we defined *our own class* for
|
||||||
design patterns (although understanding what happened in this exercise
|
formatting a table. You may look at your code and tell yourself "I should
|
||||||
will take you pretty far in terms of using most library modules).
|
just use a formatting library or something that someone else already
|
||||||
|
made instead!" No, you should use BOTH your class and a library.
|
||||||
|
Using your own class promotes loose coupling and is more flexible.
|
||||||
|
As long as your application uses the programming interface of your class,
|
||||||
|
you can change the internal implementation to work in any way that you
|
||||||
|
want. You can write all-custom code. You can use someone's third
|
||||||
|
party package. You swap out one third-party package for a different
|
||||||
|
package when you find a better one. It doesn't matter--none of
|
||||||
|
your application code will break as long as you preserve keep the
|
||||||
|
interface. That's a powerful idea and it's one of the reasons why
|
||||||
|
you might consider inheritance for something like this.
|
||||||
|
|
||||||
|
That said, designing object oriented programs can be extremely
|
||||||
|
difficult. For more information, you should probably look for books
|
||||||
|
on the topic of design patterns (although understanding what happened
|
||||||
|
in this exercise will take you pretty far in terms of using objects in
|
||||||
|
a practically useful way).
|
||||||
|
|
||||||
[Contents](../Contents) \| [Previous (4.1 Classes)](01_Class) \| [Next (4.3 Special methods)](03_Special_methods)
|
[Contents](../Contents) \| [Previous (4.1 Classes)](01_Class) \| [Next (4.3 Special methods)](03_Special_methods)
|
||||||
|
|||||||
@@ -1,12 +1,16 @@
|
|||||||
|
[Contents](../Contents) \| [Previous (4.2 Inheritance)](02_Inheritance) \| [Next (4.4 Exceptions)](04_Defining_exceptions)
|
||||||
|
|
||||||
# 4.3 Special Methods
|
# 4.3 Special Methods
|
||||||
|
|
||||||
Various parts of Python's behavior can be customized via special or magic methods.
|
Various parts of Python's behavior can be customized via special or so-called "magic" methods.
|
||||||
This section introduces that idea.
|
This section introduces that idea. In addition dynamic attribute access and bound methods
|
||||||
|
are discussed.
|
||||||
|
|
||||||
### Introduction
|
### Introduction
|
||||||
|
|
||||||
Classes may define special methods. These have special meaning to the Python interpreter.
|
Classes may define special methods. These have special meaning to the
|
||||||
They are always preceded and followed by `__`. For example `__init__`.
|
Python interpreter. They are always preceded and followed by
|
||||||
|
`__`. For example `__init__`.
|
||||||
|
|
||||||
```python
|
```python
|
||||||
class Stock(object):
|
class Stock(object):
|
||||||
@@ -49,7 +53,8 @@ for programmers.
|
|||||||
>>>
|
>>>
|
||||||
```
|
```
|
||||||
|
|
||||||
Those functions, `str()` and `repr()`, use a pair of special methods in the class to get the string to be printed.
|
Those functions, `str()` and `repr()`, use a pair of special methods
|
||||||
|
in the class to produce the string to be displayed.
|
||||||
|
|
||||||
```python
|
```python
|
||||||
class Date(object):
|
class Date(object):
|
||||||
@@ -68,19 +73,19 @@ class Date(object):
|
|||||||
```
|
```
|
||||||
|
|
||||||
*Note: The convention for `__repr__()` is to return a string that,
|
*Note: The convention for `__repr__()` is to return a string that,
|
||||||
when fed to `eval()`., will recreate the underlying object. If this
|
when fed to `eval()`, will recreate the underlying object. If this
|
||||||
is not possible, some kind of easily readable representation is used
|
is not possible, some kind of easily readable representation is used
|
||||||
instead.*
|
instead.*
|
||||||
|
|
||||||
### Special Methods for Mathematics
|
### Special Methods for Mathematics
|
||||||
|
|
||||||
Mathematical operators are just calls to special methods.
|
Mathematical operators involve calls to the following methods.
|
||||||
|
|
||||||
```python
|
```python
|
||||||
a + b a.__add__(b)
|
a + b a.__add__(b)
|
||||||
a - b a.__sub__(b)
|
a - b a.__sub__(b)
|
||||||
a * b a.__mul__(b)
|
a * b a.__mul__(b)
|
||||||
a / b a.__div__(b)
|
a / b a.__truediv__(b)
|
||||||
a // b a.__floordiv__(b)
|
a // b a.__floordiv__(b)
|
||||||
a % b a.__mod__(b)
|
a % b a.__mod__(b)
|
||||||
a << b a.__lshift__(b)
|
a << b a.__lshift__(b)
|
||||||
@@ -108,7 +113,7 @@ del x[a] x.__delitem__(a)
|
|||||||
You can use them in your classes.
|
You can use them in your classes.
|
||||||
|
|
||||||
```python
|
```python
|
||||||
class Sequence(object):
|
class Sequence:
|
||||||
def __len__(self):
|
def __len__(self):
|
||||||
...
|
...
|
||||||
def __getitem__(self,a):
|
def __getitem__(self,a):
|
||||||
@@ -204,33 +209,6 @@ x = getattr(obj, 'x', None)
|
|||||||
|
|
||||||
### Exercise 4.9: Better output for printing objects
|
### Exercise 4.9: Better output for printing objects
|
||||||
|
|
||||||
All Python objects have two string representations. The first
|
|
||||||
representation is created by string conversion via `str()`
|
|
||||||
(which is called by `print`). The string representation is
|
|
||||||
usually a nicely formatted version of the object meant for humans.
|
|
||||||
The second representation is a code representation of the object
|
|
||||||
created by `repr()` (or by viewing a value in the
|
|
||||||
interactive shell). The code representation typically shows you the
|
|
||||||
code that you have to type to get the object.
|
|
||||||
|
|
||||||
The two representations of an object are often different. For example,
|
|
||||||
you can see the difference by trying the following:
|
|
||||||
|
|
||||||
```python
|
|
||||||
>>> from datetime import date
|
|
||||||
>>> d = date(2017, 4, 9)
|
|
||||||
>>> print(d) # Nice output
|
|
||||||
2017-04-09
|
|
||||||
>>> print(repr(d)) # Representation output
|
|
||||||
datetime.date(2017, 4, 9)
|
|
||||||
>>> print(f'{d!r}') # Alternate way to get repr() string
|
|
||||||
datetime.date(2017, 4, 9)
|
|
||||||
>>>
|
|
||||||
```
|
|
||||||
|
|
||||||
Both kinds of string conversions can be redefined in a class if it
|
|
||||||
defines the `__str__()` and `__repr__()` methods.
|
|
||||||
|
|
||||||
Modify the `Stock` object that you defined in `stock.py`
|
Modify the `Stock` object that you defined in `stock.py`
|
||||||
so that the `__repr__()` method produces more useful output. For
|
so that the `__repr__()` method produces more useful output. For
|
||||||
example:
|
example:
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
[Contents](../Contents) \| [Previous (4.3 Special methods)](03_Special_methods) \| [Next (5 Object Model)](../05_Object_model/00_Overview)
|
||||||
|
|
||||||
# 4.4 Defining Exceptions
|
# 4.4 Defining Exceptions
|
||||||
|
|
||||||
User defined exceptions are defined by classes.
|
User defined exceptions are defined by classes.
|
||||||
@@ -8,6 +10,7 @@ class NetworkError(Exception):
|
|||||||
```
|
```
|
||||||
|
|
||||||
**Exceptions always inherit from `Exception`.**
|
**Exceptions always inherit from `Exception`.**
|
||||||
|
|
||||||
Usually they are empty classes. Use `pass` for the body.
|
Usually they are empty classes. Use `pass` for the body.
|
||||||
|
|
||||||
You can also make a hierarchy of your exceptions.
|
You can also make a hierarchy of your exceptions.
|
||||||
|
|||||||
Reference in New Issue
Block a user