【问题标题】:Python: None and Try ExceptPython:无和尝试除外
【发布时间】:2020-08-03 23:21:49
【问题描述】:

我正在为预算计划制作测试工具。个人购买由 Item 类表示。如果类初始化不正确,我想返回 None。但是,当我运行我的测试工具时,它不会将变量检测为无。为什么测试工具没有将 Item 检测为 None?

def __init__(self, nameItem, costItem):

    try: 

        if costItem < 0.00 or type(nameItem) is not str:
            self = None
        else:

            if len(nameItem) < 1:
                self = None
            else:
                self.name = nameItem
                self.cost = round(costItem,2)

                if self.name == None or self.cost == None:
                    self = None
    except:
        self = None
#Test Case

currentTest = currentTest + 1
    print("Test" ,currentTest,":Create invalid item using negative cost.")
    try:
        testItem = Item("Coffee",-1.00)
        if testItem is None:
            print("Made None value when given incorrect data:TEST PASSED")
            passTests = passTests + 1
        else:
            raise TypeError
    except TypeError:
        print("Error when creating item, created item instead of detecing error:TEST FAILED")
        failedTests.insert(failedCount,currentTest)
        failedCount = failedCount + 1
    except:
        print("Error when conducting test:TEST FAILED")
        failedTests.insert(failedCount,currentTest)
        failedCount = failedCount + 1

【问题讨论】:

    标签: python unit-testing testing try-catch


    【解决方案1】:

    __init__()(或任何类方法)内部,self是一个本地变量,绑定到调用此方法的对象。分配给它只会影响该局部变量的绑定,它不会改变对存在于方法之外的对象的实际引用。

    当您到达__init__ 时,该对象已经被分配 - 您也许可以通过一些“诡计”来扭转它,但我不确定这是否是个好主意,即使如果可能。

    正常 (a) 处理初始化问题的方法只是抛出一个异常,该异常可以被捕获以适当地重新绑定变量(或根据需要执行其他操作) :

    try:
        item = className()
    except:
        item = None
    

    您甚至可以提供一个辅助函数来为您完成繁重的工作,因此构建仍然只需要一行,例如:

    class ClassName:
        def __init__(self, makeFail):
            if makeFail: raise Exception("ouch")
    
    def tryMake(cls, *args):
        try:
            return cls(*args)
        except:
            return None
    
    print(tryMake(ClassName, False)) # equiv: print(ClassName(False))
    print(tryMake(ClassName, True))
    

    从输出中可以看出,它将失败的构造转换为None

    <__main__.ClassName object at 0x7f7ed1e38c70>
    None
    

    但是,如果您真的想要执行您建议的操作但不想捕获异常或通过辅助函数重新绑定,则存在另一种可能性。

    另一个魔术函数__new____init__之前被调用,并且能够返回不同的类型。只有当它返回请求的类型时,Python 才会对其调用 __init__

    因此,您可以定义 __new__ 调用来检查价格,并在其无效时拒绝实例化类型,如以下示例代码所示:

    class Item:
        def __new__(cls, item, price):
            if price < 0:
                return None
            return super(Item, cls).__new__(cls)
    
        def __init__(self, item, price):
            self.item = item
            self.price = price
    
        def __str__(self):
            return f"({self.item}, {self.price})"
    
    print(Item("Coffee", 4.50))
    print(Item("Tea", -1))
    print(Item("Biscuit", 2.75))
    

    这个输出似乎是你想要的:

    (Coffee, 4.5)
    None
    (Biscuit, 2.75)
    

    现在这可能不是 会做的事情,而是宁愿在__init__() 中抛出异常并让调用者以他们选择的任何方式处理它(包括使用辅助函数如果需要)。


    (a) 定义为:

    正常 (n) - 不会让经验丰富的 Python 开发人员质疑你的理智 :-)

    【讨论】:

    • Returning None 的目的是表示输入错误。尝试冒泡错误处理时,使用 New 是否比使用 Init 更好?
    • @Ehedges,我自己可能会避免它,只是在__init__ 中抛出一个异常,让调用者处理它。使用__new__ 只是提供了一种没有异常或辅助函数的方法。我修改了答案以更强烈地显示我的偏好。
    • None 是尝试指示错误输入。我来自 C 背景,所以我试图将其用作正确的返回值,例如 NULL 来指示问题。
    【解决方案2】:

    可能与您的值 -1.00 有关,请尝试将其分配为函数或 init 中的浮点数。例如:

    def init(self, nameItem, float(costItem)):

    如果 costItem

    testItem = Item("咖啡",float(-1.00))

    【讨论】:

      【解决方案3】:

      在您的 __init__ 函数中存在概念错误:将 self 设置为 None 您正在创建一个名为 self 的新标签,其中 None 值,您没有将原始对象设置为 None

      __init__被调用时,它刚刚创建的对象。

      如果你想禁止对象创建,你可以使用__new__,但是你必须使用异常,所以不能取消对象值。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2021-03-07
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-05-17
        • 2019-12-09
        • 2018-07-04
        相关资源
        最近更新 更多