Python 入门指南(五)(4)

简介: Python 入门指南(五)

Python 入门指南(五)(3)https://developer.aliyun.com/article/1507422

异常层次结构

我们已经看到了几个最常见的内置异常,你可能会在你的常规 Python 开发过程中遇到其余的异常。正如我们之前注意到的,大多数异常都是Exception类的子类。但并非所有异常都是如此。Exception本身实际上是继承自一个叫做BaseException的类。事实上,所有异常都必须扩展BaseException类或其子类之一。

有两个关键的内置异常类,SystemExitKeyboardInterrupt,它们直接从BaseException而不是Exception派生。SystemExit异常是在程序自然退出时引发的,通常是因为我们在代码中的某个地方调用了sys.exit函数(例如,当用户选择退出菜单项,单击窗口上的关闭按钮,或输入命令关闭服务器时)。该异常旨在允许我们在程序最终退出之前清理代码。但是,我们通常不需要显式处理它,因为清理代码可以发生在finally子句中。

如果我们处理它,我们通常会重新引发异常,因为捕获它会阻止程序退出。当然,也有一些情况下,我们可能希望阻止程序退出;例如,如果有未保存的更改,我们希望提示用户是否真的要退出。通常,如果我们处理SystemExit,那是因为我们想对其进行特殊处理,或者直接预期它。我们尤其不希望它在捕获所有正常异常的通用子句中被意外捕获。这就是它直接从BaseException派生的原因。

KeyboardInterrupt异常在命令行程序中很常见。当用户使用与操作系统相关的组合键(通常是Ctrl + C)明确中断程序执行时,就会抛出该异常。这是用户有意中断运行中程序的标准方式,与SystemExit一样,它几乎总是应该通过终止程序来响应。同样,像SystemExit一样,它应该在finally块中处理任何清理任务。

这是一个完全说明了层次结构的类图:


当我们使用except:子句而没有指定任何异常类型时,它将捕获BaseException的所有子类;也就是说,它将捕获所有异常,包括这两个特殊的异常。由于我们几乎总是希望这些得到特殊处理,因此不明智地使用except:语句而不带参数。如果你想捕获除SystemExitKeyboardInterrupt之外的所有异常,明确地捕获Exception。大多数 Python 开发人员认为没有指定类型的except:是一个错误,并会在代码审查中标记它。如果你真的想捕获所有异常,只需明确使用except BaseException:

定义我们自己的异常

偶尔,当我们想要引发一个异常时,我们发现没有一个内置的异常适合。幸运的是,定义我们自己的新异常是微不足道的。类的名称通常设计为传达出了什么问题,我们可以在初始化程序中提供任意参数以包含额外的信息。

我们所要做的就是继承Exception类。我们甚至不必向类中添加任何内容!当然,我们可以直接扩展BaseException,但我从未遇到过这种情况。

这是我们在银行应用程序中可能使用的一个简单的异常:

class InvalidWithdrawal(Exception): 
    pass 
raise InvalidWithdrawal("You don't have $50 in your account") 

最后一行说明了如何引发新定义的异常。我们能够将任意数量的参数传递给异常。通常使用字符串消息,但可以存储任何在以后的异常处理程序中可能有用的对象。Exception.__init__方法设计为接受任何参数并将它们存储为名为args的属性中的元组。这使得异常更容易定义,而无需覆盖__init__

当然,如果我们确实想要自定义初始化程序,我们是可以自由这样做的。这里有一个异常,它的初始化程序接受当前余额和用户想要提取的金额。此外,它添加了一个方法来计算请求透支了多少。

class InvalidWithdrawal(Exception): 
    def __init__(self, balance, amount): 
        super().__init__(f"account doesn't have ${amount}") 
        self.amount = amount 
        self.balance = balance 
    def overage(self): 
        return self.amount - self.balance 
raise InvalidWithdrawal(25, 50) 

结尾的raise语句说明了如何构造这个异常。正如你所看到的,我们可以对异常做任何其他对象可以做的事情。

这是我们如何处理InvalidWithdrawal异常的方法,如果有异常被引发:

try: 
    raise InvalidWithdrawal(25, 50) 
except InvalidWithdrawal as e: 
    print("I'm sorry, but your withdrawal is " 
            "more than your balance by " 
            f"${e.overage()}") 

在这里,我们看到了as关键字的有效使用。按照惯例,大多数 Python 程序员将异常命名为eex变量,尽管通常情况下,你可以自由地将其命名为exception,或者如果你愿意的话,可以称之为aunt_sally

定义自己的异常有很多原因。通常,向异常中添加信息或以某种方式记录异常是很有用的。但是,自定义异常的实用性在创建面向其他程序员访问的框架、库或 API 时才真正显现出来。在这种情况下,要小心确保代码引发的异常对客户程序员有意义。它们应该易于处理,并清楚地描述发生了什么。客户程序员应该很容易看到如何修复错误(如果它反映了他们代码中的错误)或处理异常(如果这是他们需要知道的情况)。

异常并不是异常的。新手程序员倾向于认为异常只对异常情况有用。然而,异常情况的定义可能模糊不清,而且可能会有不同的解释。考虑以下两个函数:

def divide_with_exception(number, divisor): 
    try: 
        print(f"{number} / {divisor} = {number / divisor}") 
    except ZeroDivisionError: 
        print("You can't divide by zero") 
def divide_with_if(number, divisor): 
    if divisor == 0: 
        print("You can't divide by zero") 
    else: 
        print(f"{number} / {divisor} = {number / divisor}") 

这两个函数的行为是相同的。如果divisor为零,则打印错误消息;否则,显示除法结果的消息。我们可以通过使用if语句来避免抛出ZeroDivisionError。同样,我们可以通过明确检查参数是否在列表范围内来避免IndexError,并通过检查键是否在字典中来避免KeyError

但我们不应该这样做。首先,我们可能会编写一个if语句,检查索引是否低于列表的参数,但忘记检查负值。

记住,Python 列表支持负索引;-1指的是列表中的最后一个元素。

最终,我们会发现这一点,并不得不找到我们检查代码的所有地方。但如果我们简单地捕获IndexError并处理它,我们的代码就可以正常工作。

Python 程序员倾向于遵循“宁可请求原谅,而不是事先征得许可”的模式,也就是说,他们执行代码,然后处理任何出现的问题。相反,先“三思而后行”的做法通常不太受欢迎。这样做的原因有几个,但主要原因是不应该需要消耗 CPU 周期来寻找在正常代码路径中不会出现的异常情况。因此,明智的做法是将异常用于异常情况,即使这些情况只是稍微异常。进一步地,我们实际上可以看到异常语法对于流程控制也是有效的。与if语句一样,异常可以用于决策、分支和消息传递。

想象一家销售小部件和小工具的公司的库存应用程序。当客户购买商品时,商品可以是有库存的,这种情况下商品会从库存中移除并返回剩余商品数量,或者可能是缺货的。现在,缺货在库存应用程序中是一件完全正常的事情。这绝对不是一个异常情况。但如果缺货了,我们应该返回什么呢?一个显示缺货的字符串?一个负数?在这两种情况下,调用方法都必须检查返回值是正整数还是其他值,以确定是否缺货。这似乎有点混乱,特别是如果我们在代码中忘记做这个检查。

相反,我们可以引发OutOfStock并使用try语句来控制程序流程。有道理吗?此外,我们还要确保不会将同一商品卖给两个不同的客户,或者出售还未备货的商品。促进这一点的一种方法是锁定每种商品,以确保一次只有一个人可以更新它。用户必须锁定商品,操作商品(购买、补充库存、计算剩余商品数量…),然后解锁商品。以下是一个带有描述部分方法应该做什么的文档字符串的不完整的Inventory示例:

class Inventory:
    def lock(self, item_type):
        """Select the type of item that is going to
        be manipulated. This method will lock the
        item so nobody else can manipulate the
        inventory until it's returned. This prevents
        selling the same item to two different
        customers."""
        pass
    def unlock(self, item_type):
        """Release the given type so that other
        customers can access it."""
        pass
    def purchase(self, item_type):
        """If the item is not locked, raise an
        exception. If the item_type does not exist,
        raise an exception. If the item is currently
        out of stock, raise an exception. If the item
        is available, subtract one item and return
        the number of items left."""
        pass

我们可以将这个对象原型交给开发人员,并让他们实现方法,确保它们按照我们说的那样工作,而我们则可以继续编写需要进行购买的代码。我们将使用 Python 强大的异常处理来考虑不同的分支,具体取决于购买是如何进行的。

item_type = "widget"
inv = Inventory()
inv.lock(item_type)
try:
    num_left = inv.purchase(item_type)
except InvalidItemType:
    print("Sorry, we don't sell {}".format(item_type))
except OutOfStock:
    print("Sorry, that item is out of stock.")
else:
    print("Purchase complete. There are {num_left} {item_type}s left")
finally:
    inv.unlock(item_type)

注意所有可能的异常处理子句是如何用来确保在正确的时间发生正确的操作。尽管OutOfStock并不是一个非常异常的情况,但我们能够使用异常来适当地处理它。这段代码也可以用if...elif...else结构来编写,但这样不容易阅读和维护。

我们还可以使用异常来在不同的方法之间传递消息。例如,如果我们想要告知客户商品预计何时会再次有货,我们可以确保我们的OutOfStock对象在构造时需要一个back_in_stock参数。然后,当我们处理异常时,我们可以检查该值并向客户提供额外的信息。附加到对象的信息可以很容易地在程序的两个不同部分之间传递。异常甚至可以提供一个方法,指示库存对象重新订购或预订商品。

使用异常来进行流程控制可以设计出一些方便的程序。从这次讨论中要记住的重要事情是异常并不是我们应该尽量避免的坏事。发生异常并不意味着你应该阻止这种异常情况的发生。相反,这只是一种在两个可能不直接调用彼此的代码部分之间传递信息的强大方式。

案例研究

我们一直在比较低级的细节层面上看异常的使用和处理——语法和定义。这个案例研究将帮助我们将这一切与之前的章节联系起来,这样我们就能看到异常在对象、继承和模块的更大背景下是如何使用的。

今天,我们将设计一个简单的中央认证和授权系统。整个系统将放置在一个模块中,其他代码将能够查询该模块对象以进行认证和授权。我们应该承认,从一开始,我们并不是安全专家,我们设计的系统可能存在许多安全漏洞。

我们的目的是研究异常,而不是保护系统。然而,对于其他代码可以与之交互的基本登录和权限系统来说,这是足够的。以后,如果其他代码需要更安全,我们可以请安全或密码专家审查或重写我们的模块,最好不要改变 API。

认证是确保用户确实是他们所说的人的过程。我们将遵循当今常见的网络系统的做法,使用用户名和私人密码组合。其他的认证方法包括语音识别、指纹或视网膜扫描仪以及身份证。

授权,另一方面,完全取决于确定特定(经过身份验证的)用户是否被允许执行特定操作。我们将创建一个基本的权限列表系统,该系统存储了允许执行每个操作的特定人员的列表。

此外,我们将添加一些管理功能,以允许新用户加入系统。为简洁起见,我们将省略密码编辑或一旦添加后更改权限,但是这些(非常必要的)功能当然可以在将来添加。

这是一个简单的分析;现在让我们继续设计。显然,我们需要一个存储用户名和加密密码的User类。这个类还将允许用户通过检查提供的密码是否有效来登录。我们可能不需要一个Permission类,因为可以将这些类别映射到使用字典的用户列表。我们应该有一个中央的Authenticator类,负责用户管理和登录或注销。拼图的最后一块是一个Authorizor类,处理权限和检查用户是否能执行某项活动。我们将在auth模块中提供这些类的单个实例,以便其他模块可以使用这个中央机制来满足其所有的身份验证和授权需求。当然,如果它们想要实例化这些类的私有实例,用于非中央授权活动,它们是可以自由这样做的。

随着我们的进行,我们还将定义几个异常。我们将从一个特殊的AuthException基类开始,它接受username和可选的user对象作为参数;我们自定义的大多数异常将继承自这个类。

让我们首先构建User类;这似乎足够简单。可以使用用户名和密码初始化一个新用户。密码将被加密存储,以减少被盗的可能性。我们还需要一个check_password方法来测试提供的密码是否正确。以下是完整的类:

import hashlib
class User:
    def __init__(self, username, password):
        """Create a new user object. The password
        will be encrypted before storing."""
        self.username = username
        self.password = self._encrypt_pw(password)
        self.is_logged_in = False
    def _encrypt_pw(self, password):
        """Encrypt the password with the username and return
        the sha digest."""
        hash_string = self.username + password
        hash_string = hash_string.encode("utf8")
        return hashlib.sha256(hash_string).hexdigest()
    def check_password(self, password):
        """Return True if the password is valid for this
        user, false otherwise."""
        encrypted = self._encrypt_pw(password)
        return encrypted == self.password

由于在__init__check_password中需要加密密码的代码,我们将其提取到自己的方法中。这样,如果有人意识到它不安全并需要改进,它只需要在一个地方进行更改。这个类可以很容易地扩展到包括强制或可选的个人详细信息,比如姓名、联系信息和出生日期。

在编写代码添加用户之前(这将在尚未定义的Authenticator类中进行),我们应该检查一些用例。如果一切顺利,我们可以添加一个带有用户名和密码的用户;User对象被创建并插入到字典中。但是,有哪些情况可能不顺利呢?显然,我们不希望添加一个已经存在于字典中的用户名的用户。

如果这样做,我们将覆盖现有用户的数据,新用户可能会访问该用户的权限。因此,我们需要一个UsernameAlreadyExists异常。另外,出于安全考虑,如果密码太短,我们可能应该引发一个异常。这两个异常都将扩展AuthException,我们之前提到过。因此,在编写Authenticator类之前,让我们定义这三个异常类:

class AuthException(Exception): 
    def __init__(self, username, user=None): 
        super().__init__(username, user) 
        self.username = username 
        self.user = user 
class UsernameAlreadyExists(AuthException): 
    pass 
class PasswordTooShort(AuthException): 
    pass 

AuthException需要用户名,并且有一个可选的用户参数。第二个参数应该是与该用户名关联的User类的实例。我们正在定义的两个具体异常只需要通知调用类发生了异常情况,因此我们不需要为它们添加任何额外的方法。

现在让我们开始Authenticator类。它可以简单地是用户名到用户对象的映射,因此我们将从初始化函数中的字典开始。添加用户的方法需要在将新的User实例添加到字典之前检查两个条件(密码长度和先前存在的用户):

class Authenticator:
    def __init__(self):
        """Construct an authenticator to manage
        users logging in and out."""
        self.users = {}
    def add_user(self, username, password):
        if username in self.users:
            raise UsernameAlreadyExists(username)
        if len(password) < 6:
            raise PasswordTooShort(username)
        self.users[username] = User(username, password)

当然,如果需要,我们可以扩展密码验证以引发其他方式太容易破解的密码的异常。现在让我们准备login方法。如果我们现在不考虑异常,我们可能只希望该方法根据登录是否成功返回TrueFalse。但我们正在考虑异常,这可能是一个不那么异常的情况使用它们的好地方。我们可以引发不同的异常,例如,如果用户名不存在或密码不匹配。这将允许尝试登录用户的任何人使用try/except/else子句优雅地处理情况。因此,首先我们添加这些新的异常:

class InvalidUsername(AuthException): 
    pass 
class InvalidPassword(AuthException): 
    pass 

然后我们可以为我们的Authenticator类定义一个简单的login方法,如果必要的话引发这些异常。如果不是,它会标记user已登录并返回以下内容:

def login(self, username, password): 
        try: 
            user = self.users[username] 
        except KeyError: 
            raise InvalidUsername(username) 
        if not user.check_password(password): 
            raise InvalidPassword(username, user) 
        user.is_logged_in = True 
        return True 

请注意KeyError的处理方式。这可以使用if username not in self.users:来处理,但我们选择直接处理异常。我们最终吞掉了这个第一个异常,并引发了一个更适合用户界面 API 的全新异常。

我们还可以添加一个方法来检查特定用户名是否已登录。在这里决定是否使用异常更加棘手。如果用户名不存在,我们应该引发异常吗?如果用户未登录,我们应该引发异常吗?

要回答这些问题,我们需要考虑该方法如何被访问。大多数情况下,这种方法将用于回答是/否的问题,*我应该允许他们访问吗?*答案要么是,是的,用户名有效且他们已登录,要么是,不,用户名无效或他们未登录。因此,布尔返回值就足够了。这里没有必要使用异常,只是为了使用异常:

def is_logged_in(self, username): 
        if username in self.users: 
            return self.users[username].is_logged_in 
        return False 

最后,我们可以向我们的模块添加一个默认的认证实例,以便客户端代码可以使用auth.authenticator轻松访问它:

authenticator = Authenticator() 

这一行放在模块级别,不在任何类定义之外,因此可以通过auth.authenticator访问authenticator变量。现在我们可以开始Authorizor类,它将权限映射到用户。Authorizor类不应允许用户访问权限,如果他们未登录,因此它们将需要引用特定的认证实例。我们还需要在初始化时设置权限字典:

class Authorizor: 
    def __init__(self, authenticator): 
        self.authenticator = authenticator 
        self.permissions = {} 

现在我们可以编写方法来添加新的权限,并设置哪些用户与每个权限相关联:

def add_permission(self, perm_name): 
        '''Create a new permission that users 
        can be added to''' 
        try: 
            perm_set = self.permissions[perm_name] 
        except KeyError: 
            self.permissions[perm_name] = set() 
        else: 
            raise PermissionError("Permission Exists") 
    def permit_user(self, perm_name, username): 
        '''Grant the given permission to the user''' 
        try: 
            perm_set = self.permissions[perm_name] 
        except KeyError: 
            raise PermissionError("Permission does not exist") 
        else: 
            if username not in self.authenticator.users: 
                raise InvalidUsername(username) 
            perm_set.add(username) 

第一个方法允许我们创建一个新的权限,除非它已经存在,否则会引发异常。第二个方法允许我们将用户名添加到权限中,除非权限或用户名尚不存在。

我们使用set而不是list来存储用户名,这样即使您多次授予用户权限,集合的性质意味着用户只会在集合中出现一次。

这两种方法都引发了PermissionError错误。这个新错误不需要用户名,所以我们将它直接扩展为Exception,而不是我们自定义的AuthException

class PermissionError(Exception): 
    pass 

最后,我们可以添加一个方法来检查用户是否具有特定的permission。为了让他们获得访问权限,他们必须同时登录到认证器并在被授予该特权访问的人员集合中。如果这两个条件中有一个不满足,就会引发异常:

def check_permission(self, perm_name, username): 
        if not self.authenticator.is_logged_in(username): 
            raise NotLoggedInError(username) 
        try: 
            perm_set = self.permissions[perm_name] 
        except KeyError: 
            raise PermissionError("Permission does not exist") 
        else: 
            if username not in perm_set: 
                raise NotPermittedError(username) 
            else: 
                return True 

这里有两个新的异常;它们都使用用户名,所以我们将它们定义为AuthException的子类:

class NotLoggedInError(AuthException): 
    pass 
class NotPermittedError(AuthException): 
    pass 

最后,我们可以添加一个默认的authorizor来与我们的默认认证器配对:

authorizor = Authorizor(authenticator) 

这完成了一个基本的身份验证/授权系统。我们可以在 Python 提示符下测试系统,检查用户joe是否被允许在油漆部门执行任务:

>>> import auth
>>> auth.authenticator.add_user("joe", "joepassword")
>>> auth.authorizor.add_permission("paint")
>>> auth.authorizor.check_permission("paint", "joe")
Traceback (most recent call last):
 File "<stdin>", line 1, in <module>
 File "auth.py", line 109, in check_permission
 raise NotLoggedInError(username)
auth.NotLoggedInError: joe
>>> auth.authenticator.is_logged_in("joe")
False
>>> auth.authenticator.login("joe", "joepassword")
True
>>> auth.authorizor.check_permission("paint", "joe")
Traceback (most recent call last):
 File "<stdin>", line 1, in <module>
 File "auth.py", line 116, in check_permission
    raise NotPermittedError(username)
auth.NotPermittedError: joe
>>> auth.authorizor.check_permission("mix", "joe")
Traceback (most recent call last):
 File "auth.py", line 111, in check_permission
 perm_set = self.permissions[perm_name]
KeyError: 'mix'
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
 File "<stdin>", line 1, in <module>
 File "auth.py", line 113, in check_permission
 raise PermissionError("Permission does not exist")
auth.PermissionError: Permission does not exist
>>> auth.authorizor.permit_user("mix", "joe")
Traceback (most recent call last):
 File "auth.py", line 99, in permit_user
 perm_set = self.permissions[perm_name]
KeyError: 'mix'
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
 File "<stdin>", line 1, in <module>
 File "auth.py", line 101, in permit_user
 raise PermissionError("Permission does not exist")
auth.PermissionError: Permission does not exist
>>> auth.authorizor.permit_user("paint", "joe")
>>> auth.authorizor.check_permission("paint", "joe")
True  

虽然冗长,前面的输出显示了我们所有的代码和大部分异常的运行情况,但要真正理解我们定义的 API,我们应该编写一些实际使用它的异常处理代码。这里有一个基本的菜单界面,允许特定用户更改或测试程序:

import auth
# Set up a test user and permission
auth.authenticator.add_user("joe", "joepassword")
auth.authorizor.add_permission("test program")
auth.authorizor.add_permission("change program")
auth.authorizor.permit_user("test program", "joe")
class Editor:
    def __init__(self):
        self.username = None
        self.menu_map = {
            "login": self.login,
            "test": self.test,
            "change": self.change,
            "quit": self.quit,
        }
    def login(self):
        logged_in = False
        while not logged_in:
            username = input("username: ")
            password = input("password: ")
            try:
                logged_in = auth.authenticator.login(username, password)
            except auth.InvalidUsername:
                print("Sorry, that username does not exist")
            except auth.InvalidPassword:
                print("Sorry, incorrect password")
            else:
                self.username = username
    def is_permitted(self, permission):
        try:
            auth.authorizor.check_permission(permission, self.username)
        except auth.NotLoggedInError as e:
            print("{} is not logged in".format(e.username))
            return False
        except auth.NotPermittedError as e:
            print("{} cannot {}".format(e.username, permission))
            return False
        else:
            return True
    def test(self):
        if self.is_permitted("test program"):
            print("Testing program now...")
    def change(self):
        if self.is_permitted("change program"):
            print("Changing program now...")
    def quit(self):
        raise SystemExit()
    def menu(self):
        try:
            answer = ""
            while True:
                print(
                    """
Please enter a command:
\tlogin\tLogin
\ttest\tTest the program
\tchange\tChange the program
\tquit\tQuit
"""
                )
                answer = input("enter a command: ").lower()
                try:
                    func = self.menu_map[answer]
                except KeyError:
                    print("{} is not a valid option".format(answer))
                else:
                    func()
        finally:
            print("Thank you for testing the auth module")
Editor().menu()

这个相当长的例子在概念上非常简单。 is_permitted 方法可能是最有趣的;这是一个主要是内部方法,被testchange调用,以确保用户在继续之前被允许访问。当然,这两种方法都是存根,但我们这里不是在写编辑器;我们是通过测试身份验证和授权框架来说明异常和异常处理的使用。

练习

如果你以前从未处理过异常,你需要做的第一件事是查看你写过的任何旧的 Python 代码,并注意是否有应该处理异常的地方。你会如何处理它们?你需要完全处理它们吗?有时,让异常传播到控制台是与用户沟通的最佳方式,特别是如果用户也是脚本的编码者。有时,你可以从错误中恢复并允许程序继续。有时,你只能将错误重新格式化为用户可以理解的内容并显示给他们。

一些常见的查找地方是文件 I/O(你的代码是否可能尝试读取一个不存在的文件?),数学表达式(你要除以的值是否可能为零?),列表索引(列表是否为空?)和字典(键是否存在?)。问问自己是否应该忽略问题,通过先检查值来处理它,还是通过异常来处理它。特别注意可能使用finallyelse来确保在所有条件下执行正确代码的地方。

现在写一些新代码。想想一个需要身份验证和授权的程序,并尝试编写一些使用我们在案例研究中构建的auth模块的代码。如果模块不够灵活,可以随意修改模块。尝试处理

以明智的方式处理所有异常。如果你在想出需要身份验证的东西时遇到麻烦,可以尝试在第十六章的记事本示例中添加授权,Python 中的对象,或者在auth模块本身添加授权——如果任何人都可以开始添加权限,这个模块就不是一个非常有用的模块!也许在允许添加或更改权限之前需要管理员用户名和密码。

最后,试着想想你的代码中可以引发异常的地方。可以是你写过或正在处理的代码;或者你可以编写一个新的项目作为练习。你可能最容易设计一个小型框架或 API,供其他人使用;异常是你的代码和别人之间的绝妙沟通工具。记得设计和记录任何自引发的异常作为 API 的一部分,否则他们将不知道是否以及如何处理它们!

总结

在这一章中,我们深入讨论了引发、处理、定义和操纵异常的细节。异常是一种强大的方式,可以在不要求调用函数显式检查返回值的情况下,传达异常情况或错误条件。有许多内置的异常,引发它们非常容易。处理不同异常事件有几种不同的语法。

在下一章中,我们将讨论到目前为止所学的一切如何结合在一起,讨论面向对象编程原则和结构在 Python 应用程序中应该如何最好地应用。

相关文章
|
2天前
|
数据采集 运维 API
适合所有编程初学者,豆瓣评分8.6的Python入门手册开放下载!
Python是一种跨平台的计算机程序设计语言,它可以用来完成Web开发、数据科学、网络爬虫、自动化运维、嵌入式应用开发、游戏开发和桌面应用开发。 Python上手很容易,基本有其他语言编程经验的人可以在1周内学会Python最基本的内容(PS:没有基础的人也可以直接学习,速度会慢一点) 今天给小伙伴们分享一份Python语言及其应用的手册,这份手册主要介绍 Python 语言的基础知识及其在各个领域的具体应用,基于最新版本 3.x。
|
5天前
|
数据可视化 API Python
Python零基础“圣经”!300W小白从入门到精通首选!
今天分享的这本书在让你尽快学会 Python基础知识的同时,能够编写并正确的运行程序(游戏、数据可视化、Web应用程序) 最大的特色在于,在为初学者构建完整的 Python 语言知识体系的同时,面向实际应用情境编写代码样例,而且许多样例还是 后续实践项目部分的伏笔。实践项目部分的选题经过精心设计,生动详尽 又面面俱到。相信这本书能够得到更多 Python 初学者的喜爱。
|
6天前
|
Python
小白入门必备!计科教授的Python精要参考PDF开放下载!
随着互联网产业的高速发展,在网络上早已积累了极其丰富的Python学习资料,任何人都可以基于这些资源,自学掌握 Python。 但实际上,网络上充斥的资源太多、太杂且不成体系,在没有足够的编程/工程经验之前,仅靠“看”线上资源自学,的确是一件非常困难的事。
|
6天前
|
数据可视化 API Python
豆瓣评分9.4!堪称经典的Python入门圣经,你还没看过吗?
最理想的新人入门书应该满足两个特点:第一就是内容通俗易懂;第二就是要有实战,能够让读者在学完之后知道具体怎么用。 今天给小伙伴们分享的这份Python入门手册,在为初学者构建完整的Python语言知识体系的同时,面向实际应用情境编写代码样例,而且许多样例还是后续实践项目部分的伏笔。实践项目部分的选题经过精心设计,生动详尽又面面俱到。
|
8天前
|
数据采集 运维 API
适合所有编程初学者,豆瓣评分8.6的Python入门手册开放下载!
Python是一种跨平台的计算机程序设计语言,它可以用来完成Web开发、数据科学、网络爬虫、自动化运维、嵌入式应用开发、游戏开发和桌面应用开发。 Python上手很容易,基本有其他语言编程经验的人可以在1周内学会Python最基本的内容(PS:没有基础的人也可以直接学习,速度会慢一点)
|
9天前
|
数据采集 SQL 数据可视化
使用Python和Pandas库进行数据分析的入门指南
使用Python和Pandas库进行数据分析的入门指南
72 0
|
9天前
|
Linux iOS开发 MacOS
Python入门指南
Python入门指南
32 0
|
10天前
|
数据采集 前端开发 JavaScript
Python爬虫入门
网络爬虫是自动抓取网页数据的程序,通过URL获取网页源代码并用正则表达式提取所需信息。反爬机制是网站为防止爬取数据设置的障碍,而反反爬是对这些机制的对策。`robots.txt`文件规定了网站可爬取的数据。基础爬虫示例使用Python的`urllib.request`模块。HTTP协议涉及请求和响应,包括状态码、头部和主体。`Requests`模块是Python中常用的HTTP库,能方便地进行GET和POST请求。POST请求常用于隐式提交表单数据,适用于需要发送复杂数据的情况。
16 1
|
13天前
|
机器学习/深度学习 人工智能 数据可视化
Python编程入门:从零开始探索编程的奇妙世界
这篇教程引导初学者入门Python编程,从安装Python开始,逐步讲解基本语法,如`print()`、变量、条件判断、循环以及自定义函数。文章强调了Python在数据处理、数据分析、人工智能和机器学习等领域的重要性,并鼓励学习者探索Python的广泛应用,开启编程之旅。
|
14天前
|
数据可视化 API Python
Python零基础“圣经”!300W小白从入门到精通首选!
今天分享的这本书在让你尽快学会 Python基础知识的同时,能够编写并正确的运行程序(游戏、数据可视化、Web应用程序) 最大的特色在于,在为初学者构建完整的 Python 语言知识体系的同时,面向实际应用情境编写代码样例,而且许多样例还是 后续实践项目部分的伏笔。实践项目部分的选题经过精心设计,生动详尽 又面面俱到。相信这本书能够得到更多 Python 初学者的喜爱。