我是初学者,正在开发基于卡片的简单GUI。用Python编写。除了其他之外,还有一个基类包含所有卡片的词汇表,例如_cards = {'card1_ID': card1, 'card2_ID': card2}
。 GUI上的卡由其唯一ID引用。
由于我计划为其他初学者制作代码avabile,我想明确告知他们是否提供了不存在的卡ID(而不是抛出KeyError)。现在我有一大堆重复的try-expect条款让我怀疑:
部分代码,没有尝试捕获的单行方法:
def shift(self, card_ID, amount):
"""Moves the card by the given amount of pixels.
:param amount: the horizontal and vertical amount of shifting in pixels; tuple"""
try:
self._cards[card_ID].shift(amount)
except KeyError:
raise ValueError("Invaild card ID")
def align(self, card_ID, horizontal, vertical):
"""Aligns the card to the given position."""
try:
card = self._cards[card_ID]
card.align(horizontal, vertical)
except KeyError:
raise ValueError("Invaild card ID")
def invert(self, card_ID):
"""Inverts the card's colour"""
try:
self._cards[card_ID].invert()
except KeyError:
raise ValueError("Invaild card ID")
这是一种公认的做法吗?有没有更好的方法在类的每个方法中捕获此KeyError?
答案 0 :(得分:8)
从id中将卡的实际获取提取到一个单独的方法中,使用try / except,并从其他地方调用该方法。
def get_card(self, card_id):
try:
return self._cards[card_ID]
except KeyError:
raise ValueError("Invaild card ID")
def invert(self, card_id):
return self.get_card(card_id).invert()
...
答案 1 :(得分:7)
您可以使用装饰器去除一些重复的锅炉板。
from functools import wraps
def replace_keyerror(func):
"""Catches KeyError and replaces it with ValueError"""
@wraps(func)
def inner(*args, **kwargs):
try:
func(*args, **kwargs)
except KeyError:
raise ValueError("Invaild card ID")
return inner
然后你会像这样使用它:
@replace_keyerror
def align(self, card_ID, horizontal, vertical):
"""Aligns the card to the given position."""
card = self._cards[card_ID]
card.align(horizontal, vertical)
@replace_keyerror
def invert(self, card_ID):
"""Inverts the card's colour"""
self._cards[card_ID].invert()
答案 2 :(得分:1)
您始终可以使用装饰器功能来获得所需内容。 This link是一个很好的教程,可以了解装饰器是什么以及如何使用它们。我将在您的案例中给出一个使用装饰器的示例解决方案。
基本上,您只需创建一个函数,该函数将函数作为参数并返回对其执行特殊操作的包装器。一个可能适合您的解决方案的方式如下:
def catch_invalid_card_exception(func):
def wrapper(*args, **kwargs):
try:
return func(*args, **kwargs)
except KeyError:
raise ValueError("Invalid card ID") # Not "invaild" ;)
return wrapper
...然后你可以像这样装饰你的功能/方法:
@catch_invalid_card_exception
def shift(self, card_ID, amount):
"""Moves the card by the given amount of pixels.
:param amount: the horizontal and vertical amount of shifting in pixels; tuple"""
self._cards[card_ID].shift(amount)
@catch_invalid_card_exception
def align(self, card_ID, horizontal, vertical):
"""Aligns the card to the given position."""
card = self._cards[card_ID]
card.align(horizontal, vertical)
@catch_invalid_card_exception
def invert(self, card_ID):
"""Inverts the card's colour"""
self._cards[card_ID].invert()
......这真的只是语法糖:
def shift(self, card_ID, amount):
# ...
shift = catch_invalid_card_exception(shift)
def align(self, card_ID, horizontal, vertical):
# ...
align = catch_invalid_card_exception(align)
def invert(self, card_ID):
# ...
invert = catch_invalid_card_exception(invert)
答案 3 :(得分:1)
您可以考虑将您的卡片收集到您自己的收藏类型中,以便提供您需要的自定义例外。如果您使用dict
进行卡片收集,则可以使用以下自定义行为制作自己的dict
:
class CardsCollection(dict):
'''A dict-like collection of cards'''
def __getitem__(self, key):
try:
# first try default behavior
super().__self__(key)
except KeyError:
# it didn't work!
raise ValueError("Invalid card ID: {!r}".format(key))
现在你可以这样做各种各样的方法:
def align(self, card_ID, horizontal, vertical):
"""Aligns the card to the given position."""
card = self._cards[card_ID]
card.align(horizontal, vertical)
etc. etc.
...只需确保将您的班级用于._cards
属性。
_cards = CardsCollection(card1_ID = card1, card2_ID = card2)
或者:
_cards = CardsCollection({'card1_ID': card1, 'card2_ID': card2})
关于这一点的好处是你的UI类没有提交给持有数据的对象的某种定制或不寻常的接口(即getcard()
)。此处的界面与Python data model一致。因此,如果由于某种原因您决定要将同一个UI用于另一个对象类,那么您已经使用了一个经过验证的API,它将翻译几乎所有以与Python一致的方式编写的内容。数据模型。