在课堂的每一种方法中都能获得同样的期望

时间:2016-07-12 14:54:41

标签: python python-3.x exception exception-handling

我是初学者,正在开发基于卡片的简单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?

4 个答案:

答案 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一致的方式编写的内容。数据模型。