【CS61A 2024秋】Python入门课,全过程记录P5(Week8 Inheritance开始,更新于2025/2/3)
文章目录
- 关于
- 基本介绍👋
- 新的问题
- 更好的解决方案
- Week8
- Mon Inheritance
- 阅读材料
- Lab 06: Object-Oriented Programming
- Q1: Bank Account
- Q2: Email
- Q3: Mint
- Wed Representation
- 阅读材料
- Disc 07: OOP
- Q1: Draw
- Q2: Keyboard
- Q3: Bear
- Fri Composition
- 阅读材料
- HW 06: Object-Oriented Programming, Linked Lists
- Q1: Mid-Semester Feedback
- Q2: Vending Machine
- Q3: Store Digits
- Q4: Mutable Mapping
- Q5: Two List
- Week9
- Mon Efficiency
- 阅读材料
- Lab 07: Linked Lists, Inheritance
- Q1: WWPD: Inheritance ABCs
- Q2: Retirement
关于
个人博客,里面偶尔更新,最近比较忙。发一些总结的帖子和思考。
江湖有缘相见🤝。如果读者想和我交个朋友可以加我好友(见主页or个人博客),共同学习。笔者是学生,课业还是比较繁重的,可能回复不及时。笔者也正在四处寻找一些可以兼职锻炼知识并且补贴一些生活的工作,如果读者需要一些详细的辅导,或者帮助完成一些简易的lab也可以找我,笔者还是学生,自以为才学有限,也没有高价的理由📖。
基本介绍👋
CS61A是加州大学伯克利分校(UC Berkeley)计算机科学专业的入门课程,全名为Structure and Interpretation of Computer Programs。
这是入门的Python课程,因此这篇博客会默认读者没有很多的编程背景,讲的东西会比较基础,有编程经验的读者可以快速浏览。
首先贴上课程网址。
CS61A课程主页
新的问题
可以发现,cs61a归档了,而这个网站是需要Berkeley账号的,还是没法登录。这时候笔者决定使用档案馆网站,去翻网页的归档了。虽然有点难受,但是还能用orz。
对了,textbook是可以直接访问的,在这里
更好的解决方案
我在GitHub上找到了cs61a 2024 fall的归档,这里给出连接link
Week8
Mon Inheritance
阅读材料
Ants里面已经写了很多类了,这里才开始具体讲面向对象编程orz。
Lab 06: Object-Oriented Programming
关键是要搞清楚实例属性,类属性的区别。前者修改只会影响当前实例,后者会影响当前类的全部实例。
Q1: Bank Account
这题注意交易失败也要记录下来。
class Transaction:
def __init__(self, id, before, after):
self.id = id
self.before = before
self.after = after
def changed(self):
"""Return whether the transaction resulted in a changed balance."""
"*** YOUR CODE HERE ***"
return self.before != self.after
def report(self):
"""Return a string describing the transaction.
>>> Transaction(3, 20, 10).report()
'3: decreased 20->10'
>>> Transaction(4, 20, 50).report()
'4: increased 20->50'
>>> Transaction(5, 50, 50).report()
'5: no change'
"""
msg = 'no change'
if self.changed():
"*** YOUR CODE HERE ***"
if self.after > self.before:
msg = f'increased {self.before}->{self.after}'
else:
msg = f'decreased {self.before}->{self.after}'
return str(self.id) + ': ' + msg
class BankAccount:
"""A bank account that tracks its transaction history.
>>> a = BankAccount('Eric')
>>> a.deposit(100) # Transaction 0 for a
100
>>> b = BankAccount('Erica')
>>> a.withdraw(30) # Transaction 1 for a
70
>>> a.deposit(10) # Transaction 2 for a
80
>>> b.deposit(50) # Transaction 0 for b
50
>>> b.withdraw(10) # Transaction 1 for b
40
>>> a.withdraw(100) # Transaction 3 for a
'Insufficient funds'
>>> len(a.transactions)
4
>>> len([t for t in a.transactions if t.changed()])
3
>>> for t in a.transactions:
... print(t.report())
0: increased 0->100
1: decreased 100->70
2: increased 70->80
3: no change
>>> b.withdraw(100) # Transaction 2 for b
'Insufficient funds'
>>> b.withdraw(30) # Transaction 3 for b
10
>>> for t in b.transactions:
... print(t.report())
0: increased 0->50
1: decreased 50->40
2: no change
3: decreased 40->10
"""
# *** YOU NEED TO MAKE CHANGES IN SEVERAL PLACES IN THIS CLASS ***
def __init__(self, account_holder):
self.balance = 0
self.holder = account_holder
self.id = 0
self.transactions = []
def deposit(self, amount):
"""Increase the account balance by amount, add the deposit
to the transaction history, and return the new balance.
"""
self.transactions.append(Transaction(self.id, self.balance, self.balance + amount))
self.id += 1
self.balance = self.balance + amount
return self.balance
def withdraw(self, amount):
"""Decrease the account balance by amount, add the withdraw
to the transaction history, and return the new balance.
"""
if amount > self.balance:
self.transactions.append(Transaction(self.id, self.balance, self.balance))
self.id += 1
return 'Insufficient funds'
self.transactions.append(Transaction(self.id, self.balance, self.balance - amount))
self.id += 1
self.balance = self.balance - amount
return self.balance
Q2: Email
填完邮件类。注意一下即可。
class Email:
"""An email has the following instance attributes:
msg (str): the contents of the message
sender (Client): the client that sent the email
recipient_name (str): the name of the recipient (another client)
"""
def __init__(self, msg, sender, recipient_name):
self.msg = msg
self.sender = sender
self.recipient_name = recipient_name
class Server:
"""Each Server has one instance attribute called clients that is a
dictionary from client names to client objects.
"""
def __init__(self):
self.clients = {}
def send(self, email):
"""Append the email to the inbox of the client it is addressed to.
email is an instance of the Email class.
"""
self.clients[email.recipient_name].inbox.append(email)
def register_client(self, client):
"""Add a client to the clients mapping (which is a
dictionary from client names to client instances).
client is an instance of the Client class.
"""
self.clients[client.name] = client
class Client:
"""A client has a server, a name (str), and an inbox (list).
>>> s = Server()
>>> a = Client(s, 'Alice')
>>> b = Client(s, 'Bob')
>>> a.compose('Hello, World!', 'Bob')
>>> b.inbox[0].msg
'Hello, World!'
>>> a.compose('CS 61A Rocks!', 'Bob')
>>> len(b.inbox)
2
>>> b.inbox[1].msg
'CS 61A Rocks!'
>>> b.inbox[1].sender.name
'Alice'
"""
def __init__(self, server, name):
self.inbox = []
self.server = server
self.name = name
server.register_client(self)
def compose(self, message, recipient_name):
"""Send an email with the given message to the recipient."""
email = Email(message, self, recipient_name)
self.server.send(email)
Q3: Mint
可以通过类名直接获取类属性。
class Mint:
"""A mint creates coins by stamping on years.
The update method sets the mint's stamp to Mint.present_year.
>>> mint = Mint()
>>> mint.year
2024
>>> dime = mint.create(Dime)
>>> dime.year
2024
>>> Mint.present_year = 2104 # Time passes
>>> nickel = mint.create(Nickel)
>>> nickel.year # The mint has not updated its stamp yet
2024
>>> nickel.worth() # 5 cents + (80 - 50 years)
35
>>> mint.update() # The mint's year is updated to 2104
>>> Mint.present_year = 2179 # More time passes
>>> mint.create(Dime).worth() # 10 cents + (75 - 50 years)
35
>>> Mint().create(Dime).worth() # A new mint has the current year
10
>>> dime.worth() # 10 cents + (155 - 50 years)
115
>>> Dime.cents = 20 # Upgrade all dimes!
>>> dime.worth() # 20 cents + (155 - 50 years)
125
"""
present_year = 2024
def __init__(self):
self.update()
def create(self, coin):
"*** YOUR CODE HERE ***"
return coin(self.year)
def update(self):
"*** YOUR CODE HERE ***"
self.year = self.present_year
class Coin:
cents = None # will be provided by subclasses, but not by Coin itself
def __init__(self, year):
self.year = year
def worth(self):
"*** YOUR CODE HERE ***"
return self.cents + max(Mint.present_year - self.year - 50, 0)
class Nickel(Coin):
cents = 5
class Dime(Coin):
cents = 10
Wed Representation
阅读材料
主要讲了一些例子。略。
Disc 07: OOP
Q1: Draw
这里的pop要注意,必须从大到小pop,因为pop前面的数会影响后面的数的下标。注意后面还需要翻转回来,保持在原来序列的顺序。
def draw(hand, positions):
"""Remove and return the items at positions from hand.
>>> hand = ['A', 'K', 'Q', 'J', 10, 9]
>>> draw(hand, [2, 1, 4])
['K', 'Q', 10]
>>> hand
['A', 'J', 9]
"""
return list(reversed( [hand.pop(i) for i in reversed(sorted(positions))] ))
Q2: Keyboard
根据文档测试很容易写出,Button的press要返回self,文档测试里有.press.press。
LOWERCASE_LETTERS = 'abcdefghijklmnopqrstuvwxyz'
class CapsLock:
def __init__(self):
self.pressed = 0
def press(self):
self.pressed += 1
class Button:
"""A button on a keyboard.
>>> f = lambda c: print(c, end='') # The end='' argument avoids going to a new line
>>> k, e, y = Button('k', f), Button('e', f), Button('y', f)
>>> s = e.press().press().press()
eee
>>> caps = Button.caps_lock
>>> t = [x.press() for x in [k, e, y, caps, e, e, k, caps, e, y, e, caps, y, e, e]]
keyEEKeyeYEE
>>> u = Button('a', print).press().press().press()
A
A
A
"""
caps_lock = CapsLock()
def __init__(self, letter, output):
assert letter in LOWERCASE_LETTERS
self.letter = letter
self.output = output
self.pressed = 0
def press(self):
"""Call output on letter (maybe uppercased), then return the button that was pressed."""
self.pressed += 1
"*** YOUR CODE HERE ***"
if self.caps_lock.pressed % 2 == 1:
self.output(self.letter.upper())
else:
self.output(self.letter)
return self
Q3: Bear
根据文档测试写即可。
class SleepyBear(Bear):
"""A bear with closed eyes.
>>> SleepyBear().print()
? -o-?
"""
"*** YOUR CODE HERE ***"
def next_eye(self):
return Eye(True)
class WinkingBear(Bear):
"""A bear whose left eye is different from its right eye.
>>> WinkingBear().print()
? -o0?
"""
def __init__(self):
"*** YOUR CODE HERE ***"
self.cur = 0
self.nose_and_mouth = 'o'
def next_eye(self):
"*** YOUR CODE HERE ***"
if self.cur == 0:
self.cur += 1
return Eye(True)
else:
self.cur += 1
return Eye(False)
Fri Composition
阅读材料
讲解链表,链表不管用什么语言都是非常的繁琐,据说Rust的链表更是地狱级难度。
HW 06: Object-Oriented Programming, Linked Lists
Q1: Mid-Semester Feedback
略。期中评价。
Q2: Vending Machine
售货机,比较简单。
class VendingMachine:
"""A vending machine that vends some product for some price.
>>> v = VendingMachine('candy', 10)
>>> v.vend()
'Nothing left to vend. Please restock.'
>>> v.add_funds(15)
'Nothing left to vend. Please restock. Here is your $15.'
>>> v.restock(2)
'Current candy stock: 2'
>>> v.vend()
'Please add $10 more funds.'
>>> v.add_funds(7)
'Current balance: $7'
>>> v.vend()
'Please add $3 more funds.'
>>> v.add_funds(5)
'Current balance: $12'
>>> v.vend()
'Here is your candy and $2 change.'
>>> v.add_funds(10)
'Current balance: $10'
>>> v.vend()
'Here is your candy.'
>>> v.add_funds(15)
'Nothing left to vend. Please restock. Here is your $15.'
>>> w = VendingMachine('soda', 2)
>>> w.restock(3)
'Current soda stock: 3'
>>> w.restock(3)
'Current soda stock: 6'
>>> w.add_funds(2)
'Current balance: $2'
>>> w.vend()
'Here is your soda.'
"""
def __init__(self, product, price):
"""Set the product and its price, as well as other instance attributes."""
"*** YOUR CODE HERE ***"
self.product = product
self.price = price
self.balance = 0
self.stock = 0
def restock(self, n):
"""Add n to the stock and return a message about the updated stock level.
E.g., Current candy stock: 3
"""
"*** YOUR CODE HERE ***"
self.stock += n
return f'Current {self.product} stock: {self.stock}'
def add_funds(self, n):
"""If the machine is out of stock, return a message informing the user to restock
(and return their n dollars).
E.g., Nothing left to vend. Please restock. Here is your $4.
Otherwise, add n to the balance and return a message about the updated balance.
E.g., Current balance: $4
"""
"*** YOUR CODE HERE ***"
self.balance += n
if self.stock == 0:
cur = self.balance
self.balance = 0
return f'Nothing left to vend. Please restock. Here is your ${cur}.'
return f'Current balance: ${self.balance}'
def vend(self):
"""Dispense the product if there is sufficient stock and funds and
return a message. Update the stock and balance accordingly.
E.g., Here is your candy and $2 change.
If not, return a message suggesting how to correct the problem.
E.g., Nothing left to vend. Please restock.
Please add $3 more funds.
"""
"*** YOUR CODE HERE ***"
if self.stock == 0:
return f'Nothing left to vend. Please restock.'
if self.balance < self.price:
return f'Please add ${self.price - self.balance} more funds.'
if self.balance > self.price:
change = self.balance - self.price
self.stock -= 1
self.balance = 0
return f'Here is your {self.product} and ${change} change.'
self.stock -= 1
self.balance = 0
return f'Here is your {self.product}.'
Q3: Store Digits
使用链表存储数字。
def store_digits(n):
"""Stores the digits of a positive number n in a linked list.
>>> s = store_digits(1)
>>> s
Link(1)
>>> store_digits(2345)
Link(2, Link(3, Link(4, Link(5))))
>>> store_digits(876)
Link(8, Link(7, Link(6)))
>>> store_digits(2450)
Link(2, Link(4, Link(5, Link(0))))
>>> store_digits(20105)
Link(2, Link(0, Link(1, Link(0, Link(5)))))
>>> # a check for restricted functions
>>> import inspect, re
>>> cleaned = re.sub(r"#.*\\n", '', re.sub(r'"{3}[\s\S]*?"{3}', '', inspect.getsource(store_digits)))
>>> print("Do not use str or reversed!") if any([r in cleaned for r in ["str", "reversed"]]) else None
"""
"*** YOUR CODE HERE ***"
def f(n, link):
if n < 10:
return Link(n, link)
return f(n // 10, Link(n % 10, link))
return f(n, Link.empty)
Q4: Mutable Mapping
设计一个map,映射链表内全部的元素。
def deep_map_mut(func, s):
"""Mutates a deep link s by replacing each item found with the
result of calling func on the item. Does NOT create new Links (so
no use of Link's constructor).
Does not return the modified Link object.
>>> link1 = Link(3, Link(Link(4), Link(5, Link(6))))
>>> print(link1)
<3 <4> 5 6>
>>> # Disallow the use of making new Links before calling deep_map_mut
>>> Link.__init__, hold = lambda *args: print("Do not create any new Links."), Link.__init__
>>> try:
... deep_map_mut(lambda x: x * x, link1)
... finally:
... Link.__init__ = hold
>>> print(link1)
<9 <16> 25 36>
"""
"*** YOUR CODE HERE ***"
if isinstance(s.first, Link):
deep_map_mut(func, s.first)
else:
s.first = func(s.first)
if isinstance(s.rest, Link):
deep_map_mut(func, s.rest)
Q5: Two List
直接暴力递归。
def two_list(vals, counts):
"""
Returns a linked list according to the two lists that were passed in. Assume
vals and counts are the same size. Elements in vals represent the value, and the
corresponding element in counts represents the number of this value desired in the
final linked list. Assume all elements in counts are greater than 0. Assume both
lists have at least one element.
>>> a = [1, 3]
>>> b = [1, 1]
>>> c = two_list(a, b)
>>> c
Link(1, Link(3))
>>> a = [1, 3, 2]
>>> b = [2, 2, 1]
>>> c = two_list(a, b)
>>> c
Link(1, Link(1, Link(3, Link(3, Link(2)))))
"""
"*** YOUR CODE HERE ***"
if counts:
if counts[0] > 1:
counts[0] -= 1
return Link(vals[0], two_list(vals, counts))
else:
return Link(vals[0], two_list(vals[1:], counts[1:]))
else:
return Link.empty
Week9
Mon Efficiency
阅读材料
比较简单,略。
Lab 07: Linked Lists, Inheritance
Q1: WWPD: Inheritance ABCs
略。