Classes Answers¶
Class Exercises¶
Record Transactions¶
Modify our class to record transactions for deposits and withdrawals.
A transactions attribute should be added to our object that maintains a list of all transactions in our account. Each transaction should be a tuple containing an action name (as a string), the amount the account changed, and the account balance after the change.
Example usage:
>>> from bank_account import BankAccount
>>> my_account = BankAccount()
>>> my_account.deposit(100)
>>> my_account.withdraw(40)
>>> my_account.deposit(95)
>>> my_account.transactions
[('OPEN', 0, 0), ('DEPOSIT', 100, 100), ('WITHDRAWAL', -40, 60), ('DEPOSIT', 95, 155)]
Answers
class BankAccount:
def __init__(self, balance=0):
self.balance = balance
self.transactions = [("OPEN", balance, balance)]
# print("Account opened.")
self.print_balance()
def deposit(self, amount):
self.balance += amount
self.transactions.append(("DEPOSIT", amount, self.balance))
# print("${} deposited.".format(amount))
self.print_balance()
def withdraw(self, amount):
self.balance -= amount
self.transactions.append(("WITHDRAWAL", -amount, self.balance))
# print("${} withdrawn.".format(amount))
self.print_balance()
# ...
Points¶
Create a Point class that has the following methods:
distance: accepts anotherPointas an argument and returns the distance between the two points (using the Pythagorean Theorem)shift: accepts anotherPointan argument and returns a new point that is the sum of the two pointsscale: accepts a number as an argument and returns a new point that is scaled by that number
>>> p1 = Point(1, 2, 3)
>>> p2 = Point(4, 5, 6)
>>> p2.shift(p1)
Point(5, 7, 9)
>>> p1.scale(2)
Point(2, 4, 6)
>>> p1.distance(p2)
5.196152422706632
Answers
import math
class Point:
"""Three-dimensional point."""
def __init__(self, x, y, z):
self.x = x
self.y = y
self.z = z
def shift(self, other):
"""Return copy of our point, shifted by other."""
return Point(self.x+other.x, self.y+other.y, self.z+other.z)
def scale(self, value):
"""Return new copy of our point, scaled by given value."""
return Point(value*self.x, value*self.y, value*self.z)
def distance(self, other):
"""Return distance between two points."""
x, y, z = (self.x-other.x), (self.y-other.y), (self.z-other.z)
return math.sqrt(x**2 + y**2 + z**2)
def __repr__(self):
"""Return dev-readable representation of Point."""
return "Point({}, {}, {})".format(self.x, self.y, self.z)
Pythonic Points¶
Modify the Point class you implemented in the last exercise:
- remove
shiftand add support for using the+operator between two points (hint: use__add__) - remove
scaleand add support for using the*operator between two points (hint: use__mul__)
>>> p1 = Point(1, 2, 3)
>>> p2 = Point(4, 5, 6)
>>> p1 + p2
Point(5, 7, 9)
>>> p1 * 2
Point(2, 4, 6)
Answers
import math
class Point:
"""Three-dimensional point."""
def __init__(self, x, y, z):
self.x = x
self.y = y
self.z = z
def distance(self, other):
"""Return distance between two points."""
x, y, z = (self.x-other.x), (self.y-other.y), (self.z-other.z)
return math.sqrt(x**2 + y**2 + z**2)
def __add__(self, other):
"""Return copy of our point, shifted by other."""
cls = self.__class__
return cls(self.x+other.x, self.y+other.y, self.z+other.z)
def __mul__(self, value):
"""Return new copy of our point, scaled by given value."""
cls = self.__class__
return cls(value*self.x, value*self.y, value*self.z)
def __rmul__(self, value):
"""Return new copy of our point, scaled by given value."""
return self.__mul__(value)
def __repr__(self):
"""Return dev-readable representation of Point."""
return "{cls}({x}, {y}, {z})".format(
cls=self.__class__.__name__,
x=self.x,
y=self.y,
z=self.z,
)
Object Exercises¶
Inverse Filter¶
Create an exclude function that only keeps items which fail a given predicate test. The function should accept a function and an iterable as its arguments and should return an iterable containing all items which yielded a falsey return value from the predicate function. This is basically the opposite of the built-in filter function.
>>> exclude(bool, [False, True, False])
[False, False]
>>> exclude(lambda x: len(x) > 3, ["red", "blue", "green"])
['red']
Answers
def exclude(condition, iterable):
return [var for var in iterable if not condition(var)]
Call¶
Write a function call which calls a given function with any given positional and keyword arguments and returns the value returned by the function call.
>>> call(int)
0
>>> call(int, "5")
5
>>> call(len, "hello")
5
>>> list(call(zip, [1, 2], [3, 4]))
[(1, 3), (2, 4)]
Answers
def call(func, *args):
return func(*args)
Call Later¶
Write a function call_later which accepts a function and a list of arguments and returns a new function that, when called, will call the function with the given arguments.
>>> names = []
>>> append_name = call_later(names.append, "Trey")
>>> append_name()
>>> names
['Trey']
>>> append_name()
>>> names
['Trey', 'Trey']
>>> call_zip = call_later(zip, [1, 2], [3, 4])
>>> list(call_zip())
[(1, 3), (2, 4)]
Answers
def call_later(func, *args):
def new_func():
return func(*args)
return new_func
Call Again¶
Write a function call_again which accepts a function and a list of arguments and returns a tuple. The first item in the tuple should be the return value from calling the given function with the given arguments. The second item in the tuple should be a function that, when called, will call the function again with the given arguments.
>>> names = []
>>> response, names_as_str = call_again(str, names)
>>> response
'[]'
>>> names.append("Diane")
>>> names_as_str()
"['Diane']"
Answers
def call_again(func, *args):
def new_func():
return func(*args)
return func(*args), new_func
Only Once¶
Make a function only_once that accepts a function as an argument and returns a new function. The returned function should be identical to the original function except that when you try to call the function more than once, it shouldn’t let you.
>>> def do_something(x, y):
... print("doing something with {} and {}".format(x, y))
... return x * 2 + y ** 2
...
>>> do_something_once = only_once(do_something)
>>> do_something_once(1, 2)
doing something with 1 and 2
6
>>> do_something_once(1, 2)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 5, in wrapped
ValueError: You can't call this function twice!
Answers
def only_once(func):
called_before = set()
def new_func(*args):
if args in called_before:
raise ValueError("You can't call this function twice!")
called_before.add(args)
return func(*args)
return new_func
Cache¶
Write a cache function which accepts takes a function as its argument and returns a function that is identical to the original function except that it caches its return values based on any positional arguments given.
>>> def do_something(x, y):
... print("doing something with {} and {}".format(x, y))
... return x * 2 + y ** 2
...
>>> do_something_cached = cache(do_something)
>>> do_something_cached(1, 2)
doing something with 1 and 2
6
>>> do_something_cached(1, 2)
6
Answers
def cache(func):
cached_calls = {}
def new_func(*args):
if args in cached_calls:
return cached_calls[args]
response = func(*args)
cached_calls[args] = response
return response
return new_func
Partial¶
Make a partial function (like the one we already made) which accepts positional arguments and keyword arguments.
Answers
def partial(func, *first_args, **first_kwargs):
def new_func(*args, **kwargs):
new_args = first_args + args
new_kwargs = first_kwargs.copy()
new_kwargs.update(kwargs)
return func(*new_args, **new_kwargs)
return new_func
Inheritance Exercises¶
Minimum Balance¶
Create a class MinimumBalanceAccount which subclasses BankAccount. This new class should raise an exception whenever the user attempts to withdraw so much money that their account goes below 0.
>>> from bank_account import MinimumBalanceAccount
>>> my_account = MinimumBalanceAccount()
Account opened.
Account balance is $0.
>>> my_account.deposit(100)
$100 deposited.
Account balance is $100.
>>> my_account.withdraw(200)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "bank_account.py", line 45, in withdraw
raise ValueError("Balance cannot be less than $0!")
ValueError: Balance cannot be less than $0!
Answers
class MinimumBalanceAccount(BankAccount):
"""Bank account which does not allow balance to drop below zero."""
def withdraw(self, amount):
if self.balance - amount < 0:
raise ValueError("Balance cannot be less than $0!")
return super().withdraw(amount)