Review: Lab06 & Hw06
lab06
p1
def __init__(self, product, price):
self.product = product
self.price = price
self.stock = 0
self.balance = 0
def vend(self):
if self.stock == 0:
return 'Machine is out of stock.'
elif self.balance < self.price:
return 'You must add ${0} more funds.'.format(self.price - self.balance)
else:
change = self.balance - self.price
self.stock -= 1
self.balance = 0
if change == 0:
return 'Here is your {0}.'.format(self.product)
else:
return 'Here is your {0} and ${1} change.'.format(self.product, change)
def add_funds(self, amount):
if self.stock == 0:
return 'Machine is out of stock. Here is your ${0}.'.format(amount)
else:
self.balance += amount
return 'Current balance: ${0}'.format(self.balance)
def restock(self, amount):
self.stock += amount
return 'Current {0} stock: {1}'.format(self.product, self.stock)
本题难点就是要从给定的输入输出推理出每个方法要做的事情:
__init__
:存储商品名称和单价vend
:购买 1 份商品,要处理 4 种情况:没货,没余额,余额超出单价,余额刚好,其中没货优先级最高add_funds
:添加余额,注意处理没货的情况restock
:补货
代码很简单,理清楚要做什么就可以了 qwq
p2
class Cat(Pet):
def __init__(self, name, owner, lives=9):
super().__init__(name, owner)
self.lives = lives
def talk(self):
print(self.name + " says meow!")
def lose_life(self):
if self.lives == 0:
print(self.name + " has no more lives to lose.")
else:
self.lives -= 1
if self.lives == 0:
self.is_alive = False
和上题一样理清楚每个方法要做什么就行,代码不复杂 qwq。注意 python 不会帮你执行父类的生成函数,因此需要添加一行 super().__init__(name, owner)
,并且不需要再手动处理 name
owner
了。
p3
只有 talk
的行为发生了改变,因此只需要覆写 talk
方法。复用代码只需要调用两次 super().talk()
。
p4
class PrintModule:
def pp(self):
pretty_print(self)
class Pet(PrintModule):
...
def pretty_print(obj):
print(f'{Colors.OKBLUE}{type(obj).__name__}{Colors.ENDC}{Colors.OKCYAN}{obj.to_str()}{Colors.ENDC}')
这题一定要注意看 Note 和 Hint 以及样例。注意以下几点:
- 在一个颜色的区块结束时一定要加
Colors.ENDC
- Hint 要求你利用
PrintModule
,不要自己在Pet
里添加pp
函数,只需要让Pet
继承PrintModule
即可 self
就可以代表完整的这个实例本身
p5
def __init__(self, year, month, day):
self.year = year
self.month = month
self.day = day
self.format = "yy.mm.dd"
def setyear(self, year):
if not isinstance(year, int) or year < 0:
print("Parameter Error!")
else:
self.year = year
def setmonth(self, month):
if not isinstance(month, int) or month < 1 or month > 12:
print("Parameter Error!")
else:
self.month = month
def setday(self, day):
if not isinstance(day, int) or day < 1 or day > 31:
print("Parameter Error!")
else:
self.day = day
def setformat(self, format):
self.format = format
def to_str(self):
year = str(self.year).zfill(2)[-2:]
month = str(self.month).zfill(2)
day = str(self.day).zfill(2)
return self.format.replace("yy", year).replace("mm", month).replace("dd", day)
这题的初衷是想让大家体验一下 OOP 实用的地方,本身并没有什么太难的地方,大家不要想复杂了哦
__init__
不用对输入参数做判断,我们保证输入合法,题目里也只让你对 set 系列方法做判断,这是为了简化题目,不然还要设定默认的年月日判断一个年、月、日是否合法前首先要判断其是不是数字,使用
isinstance(x, int)
即可,不过一定要放在判断语句的最前面,不然非数字没法和数字比较to_str
注意理解题目,拿年举例:对于 2024 年,y
是 y,yy
是 24,yyy
是 24y,yyyy
是 2424 …注意不足两位要补0,哪怕年也有可能,公元 1 ~ 9 年是存在的
使用
replace
和zfill
可以让这题变得很简单。其中zfill
就是个补 0 函数,哪怕大家自己实现也不难。replace
可以不用吗?当然可以,而且哪怕不用也可以写的很优雅:def to_str(self): dict = { "yy": str(self.year).zfill(2)[-2:], "mm": str(self.month).zfill(2), "dd": str(self.day).zfill(2) } res = self.format for i in range(len(res) - 1): now = res[i] + res[i + 1] res = res[:i] + dict.get(now, now) + res[i + 2:] return res
不要逃避没见过的题目,要将其简化为已知的内容。有同学可能想用
str[index] = char
修改字符串,这是不可以的,因为 python 的字符串是不可变量
最后给大家几个忠告:
- 每行没有仔细测试的代码都有可能是错的
- 要考虑在题目指定的合法输入范围内所有可能的输入情况,包括你能想到的所有的边界情况
- 没有满分应该去仔细调试,而不是直接索要答案 (¬_¬ )
hw06
p1
class shiftcipher(methods):
def __init__(self, shift):
self.shift = shift % 26
def encrypt(self, message):
return ''.join([chr((ord(char) - 97 + self.shift) % 26 + 97) for char in message])
class dictionarycipher(methods):
def __init__(self, dictionary):
self.dictionary = dictionary
def encrypt(self, message):
return ''.join([self.dictionary.get(char, char) for char in message])
class fencecipher(methods):
def __init__(self, rails):
self.rails = rails
def encrypt(self, message):
mp = [[] for _ in range(self.rails)]
for i in range(len(message)):
mp[i % self.rails].append(message[i])
return ''.join([''.join(rail) for rail in mp])
- 不要害怕!题目其实一点都不难,大家要相信自己的实力>﹏<
- 要学会 RTFM STFW RTFSC,本题对前两种加密方法的介绍一带而过,像很多同学有疑问的当 字母 +
shift
超出了字母表怎么办,其实都可以在互联网上找到答案,这种情况也肯定是要处理的,RTFM 可以发现 Problem 1: Encryption (200pts) - Homework 06: OOP and Inheritance 只规定了输入由小写字母组成,所以肯定是可以出现字母 +shift
超出了字母表的情况的。同时,chr
ord
这两个在 ASCII 码和字符之间相互转换函数的用法也要依赖大家自己 RTFM STFW(ASCII 码是什么?请 STFW)。除了这两个函数之外的内容都可以用已经学过的知识做出来,''.join(list)
其实就是把一个 list 里的所有元素拼成一个大的字符串,但其实 join 的对象可以是空字符串之外的字符串,这时候 join 的行为是什么样的?欢迎 RTFM。 - 栅栏加密有很多种不同的版本,这里其实非常简单:就是弄 rails 行,竖着填横着读,这么描述是不是很清楚啦,大家要学会自己总结概括题目
- 题目只规定了输入是由小写字母组成的字符串,那么空字符串肯定是合法输入。字典加密只规定了输入是双射(大家可以想想,为什么一定要是双射?)字典,那么空字典肯定是合法输入
p2
class multipleencryption(extensions):
def __init__(self, counts=1):
self.counts = counts
def decorator(self, function, message):
for _ in range(self.counts):
message = function(message)
return message
class splitencryption(extensions):
def __init__(self, x):
self.x = x
def decorator(self, function, message):
l1 = []
l2 = []
for i in range(len(message)):
if i % self.x == 0 and i != 0:
l2.append(message[i])
else:
l1.append(message[i])
return function(''.join(l1) + ''.join(l2))
class encryption:
def __init__(self, method, extension):
self.method = method
self.extension = extension
def encrypt(self, message):
return self.extension.decorator(self.method.encrypt, message)
- 理解分裂加密的意思:对一个字符串的下标从 0 开始编号,对给定的 x,所有下标为 x 的倍数(除了0)的字母会被提取(拿出并删除)之后拼接到字符串后面,然后再进行加密(注意顺序)
counts
可能等于 0,x
可能等于 1,x = 0
没有考察,因为我们没有给出这种情况的规定 awa,0 是否为任何数的倍数有歧义- 目前加密方法和插件其实很少,大家感受不出来。但如果我们有 100 个加密方法,100 个加密插件,只需要写 100 + 100 = 200 个类,就可以实现 100 * 100 = 10000 种加密方法!是不是很神奇呀~
p3
def __init__(self, name, attack, defense):
self.name = name
self.attack = attack
self.defense = defense
def power(self, other_card):
return self.attack - other_card.defense / 2
认真读题就好啦,可以经常去 Problem 2: The Lambda-ing (350pts) - Homework 06: OOP and Inheritance 看看 (*^-^*)。另外,不用管没让你实现的部分,把它们交给抽象吧~
p4
def __init__(self, deck, name):
self.deck = deck
self.name = name
self.hand = []
for _ in range(5):
self.draw()
def draw(self):
assert not self.deck.is_empty(), 'Deck is empty!'
self.hand.append(self.deck.draw())
def play(self, card_index):
card = self.hand[card_index]
self.hand.pop(card_index)
return card
- 注意起手要抽 5 张牌
- 抽牌记得用
deck.draw()
- 打出牌不要忘了删牌!
p5
class TutorCard(Card):
cardtype = 'Tutor'
def effect(self, other_card, player, opponent):
opponent.play(0)
opponent.play(0)
opponent.play(0)
opponent.draw()
opponent.draw()
opponent.draw()
print('{} discarded and re-drew 3 cards!'.format(opponent.name))
class TACard(Card):
cardtype = 'TA'
def effect(self, other_card, player, opponent):
other_card.attack, other_card.defense = other_card.defense, other_card.attack
class ProfessorCard(Card):
cardtype = 'Professor'
def effect(self, other_card, player, opponent):
orig_opponent_deck_length = len(opponent.deck.cards)
for card in player.deck.cards:
card.attack += other_card.attack
card.defense += other_card.defense
list_copy = opponent.deck.cards[:]
opponent.deck.cards = []
for i, card in enumerate(list_copy):
if card.attack == other_card.attack or card.defense == other_card.defense:
continue
else:
opponent.deck.cards.append(card)
discarded = orig_opponent_deck_length - len(opponent.deck.cards)
if discarded:
print('{} cards were discarded from {}\'s deck!'.format(discarded, opponent.name))
return
def copy(self):
return ProfessorCard(self.name, self.attack, self.defense)
- 记得仔细读题!
Tutor
是弃三抽三,可以用你已经实现的play
来打出(在这里其实是弃牌)Professor
在删牌的时候如果用循环一定要注意删牌会搞乱循环对象!你可以像题解这样写来解决,但我觉得更优雅的写法是opponent.deck.cards.filter(lambda card: card.attack != other_card.attack and card.defense != other_card.defense)
或者使用列表递推式的for if
哦
Just For Fun Problems
p6
class dmethods:
def decrypt(self, message):
pass
class shiftdecipher(dmethods):
def __init__(self, shift):
self.shift = shift % 26
def decrypt(self, message):
return ''.join([chr((ord(char) - 97 - self.shift) % 26 + 97) for char in message])
class dictionarydecipher(dmethods):
def __init__(self, dictionary):
self.dictionary = dictionary
def decrypt(self, message):
return ''.join([list(self.dictionary.keys())[list(self.dictionary.values()).index(char)] if char in self.dictionary.values() else char for char in message])
class fencedecipher(dmethods):
def __init__(self, rails):
self.rails = rails
def decrypt(self, message):
mp = [[] for _ in range(self.rails)]
message = list(message)
lm = len(message)
t = lm % self.rails
for i in range(self.rails):
if t == 0:
mp[i].extend(message[:lm // self.rails])
message = message[len(message) // self.rails:]
elif i < t:
mp[i].extend(message[:lm // self.rails + 1])
message = message[len(message) // self.rails + 1:]
else:
mp[i].extend(message[:lm // self.rails])
message = message[lm // self.rails:]
res = []
j = 0
while len(res) != lm:
res.append(mp[j][0])
mp[j].pop(0)
j += 1
if j == self.rails:
j = 0
return ''.join(res)
class dextensions:
def decorator(self, function, message):
pass
class multipledecryption(dextensions):
def __init__(self, counts=1):
self.counts = counts
def decorator(self, function, message):
for _ in range(self.counts):
message = function(message)
return message
class splitdecryption(dextensions):
def __init__(self, x):
self.x = x
def decorator(self, function, message):
message = function(message)
line = len(message) // self.x
l1 = list(message[:-line])
l2 = list(message[-line:])
now = self.x - 1
while l2:
l1.insert(now + 1, l2.pop(0))
now += self.x
return ''.join(l1)
class decryption:
def __init__(self, encryption_instance):
if isinstance(encryption_instance.method, shiftcipher):
self.method = shiftdecipher(encryption_instance.method.shift)
elif isinstance(encryption_instance.method, dictionarycipher):
self.method = dictionarydecipher(encryption_instance.method.dictionary)
elif isinstance(encryption_instance.method, fencecipher):
self.method = fencedecipher(encryption_instance.method.rails)
if isinstance(encryption_instance.extension, multipleencryption):
self.extension = multipledecryption(encryption_instance.extension.counts)
elif isinstance(encryption_instance.extension, splitencryption):
self.extension = splitdecryption(encryption_instance.extension.x)
- 本题是让大家尝试解密,大部分结构都和加密差不多,但是这里的
decryption
构造时不是接受方法和插件,而是接受一个encryption
类的对象,我们要使用isinstance
判断并获得类型 - 移位加密反向移位即可,字典加密交换 key 和 value 即可,栅栏加密横着填竖着读即可,但要判断哪些行填的多哪些填的少,jpgg 可以给大家画一下 qwq
- 多次加密多次解密即可,分裂加密要注意这次的顺序是先解密再填回去,
insert
函数可以帮助大家做到。这里还是有点难,jpgg 可以给大家画一下
p7
class Fib:
def __init__(self, value=0):
self.value = value
def next(self):
if self.value == 0:
res = Fib(1)
res.previous = 0
return res
res = Fib(self.value + self.previous)
res.previous = self.value
return res
- 这题就很简单了,用一个 previous 记录一下上一个数就行,注意特判第 0 项!