31天Python入门——第14天:异常处理

news2025/4/26 8:16:23

在这里插入图片描述

你好,我是安然无虞。

文章目录

    • 异常处理
      • 1. Python异常
      • 2. 异常捕获
        • try-except语句
        • 捕获所有的异常信息
        • 获取异常对象
        • finally块
      • 3. raise语句
      • 4. 自定义异常
      • 5. 函数调用里面产生的异常
      • 补充练习

在这里插入图片描述

异常处理

1. Python异常

Python异常指的是在程序执行过程中发生的错误或异常情况. 当代码遇到错误时, 会引发异常并中断程序的正常执行流程.

异常提供了一种机制来处理错误, 以便程序可以在错误发生时采取适当的行动, 而不会导致程序崩溃或产生意外结果.

在Python中, 异常是通过异常类表示的. 每个异常类都是Python内置的或自定义的, 用于表示特定类型的错误. 常见的内置异常类包括:

  • SyntaxError:语法错误, 通常是代码书写不正确.
  • NameError:名称错误, 尝试访问不存在的变量或函数.
  • TypeError:类型错误, 操作或函数应用于不兼容的数据类型.
  • ValueError:值错误, 当函数接收到不合法的值时引发.
  • ZeroDivisionError:零除错误, 尝试将一个数除以零.
  • Exception:所有内置异常类的基类, 即其他所有的异常都是基于它的.

除了这些内置异常类, Python还提供了许多其他的异常类, 用于特定的错误情况. 可以使用try-except语句来捕获和处理异常. 通过在try块中编写可能引发异常的代码, 并在except块中处理异常, 可以保护程序免受错误的影响(在程序出现异常时不会直接中断程序执行), 而是采取适当的措施来处理异常情况.

异常处理可以帮助我们提高程序的健壮性和容错能力, 使得程序能够更好地处理异常情况, 并给出恰当的反馈或采取相应的措施.

2. 异常捕获

try-except语句

在Python中, 我们使用try-except语句来捕获和处理异常. try块用于编写可能引发异常的代码, 而except块用于处理异常情况.

try:
	# 可能引发异常的代码块
except ExceptionType:
	# 处理异常的代码块
  • try块中的代码会按顺序执行, 如果发生异常, 则会跳转到匹配的except块.
  • except块中的代码会处理特定类型的异常, ExceptionType表示要捕获的异常类型.
  • 可以有多个except块, 每个块可以处理不同类型的异常, 或者使用一个块来处理多个异常类型.
def divide_numbers(a, b):
  result = a / b
  print("result", result)
  
divide_numbers(10, 2) # 5.0
divide_numbers(10, 0) # 触发除零异常, 程序中断, 后续代码都不会被执行 - 这样的话代码的容错能力很低
divide_numbers(9, 2)
divide_numbers(8, 2)
def divide_numbers(a, b):
  try:
    result = a / b
    print("result", result)
  except ZeroDivisionError:
    print("0不能作为除数.")
  except TypeError:
    print("参数传递类型错误.")
  except Exception as e:
    print(f"程序出现了意外错误: 具体出错内容: {e}.")
  finally:
    print(f"{a}/{b}的结果计算完毕.")
          
divide_numbers(10, 2) # 5.0
divide_numbers(10, 0) # 显示‘0不能作为除数’
divide_numbers(9, 2) # 4.5
divide_numbers(8, 2) # 4.0
捕获所有的异常信息
 # ---------方式一----------
 try:
  # 可能引发异常的代码块
 except:
  # 处理所有异常
 
 def divide_numbers(a, b):
 try:
  result = a / b
 except:
  print("程序出错了.")
 
 # ---------方式二----------
 # 根据基类Exception来捕获
 
 def divide_numbers(a, b):
 try:
  result = a / b
 except Exception:
  print("程序出错了.")
获取异常对象

在异常处理块中, 可以使用as关键字将异常信息赋给一个变量, 以便进一步处理或打印异常信息.

 try:
  # 可能引发异常的代码块
 except ExceptionType as e:
  # 处理异常, 并使用变量e访问异常信息
 
 
 def divide_numbers(a, b):
   try:
  	  result = a / b
   except Exception as e: # 注意: 根据基类Exception来捕获异常只能放在最后不能放在其他异常类之前
      print(f"程序出现了意外错误: 具体的出错内容: {e}")

else块

else块是try-except语句的可选部分, 用于指定在try块中的代码执行完毕且没有引发异常时要执行的代码. 如果在try块中发生了异常并被捕获, 则不会执行else块中的代码.

 try:
  # 可能引发异常的代码块
 except ExceptionType:
  # 处理异常的代码块
 else:
 # 在没有发生异常时执行的代码块, 发生异常时就不会执行

注意点: 在try里如果return了, 即使没有报错, 也不会执行else里的代码

 # 示例代码, 大家自己跑到pycharm里运行的试试.
 def test():
  try:
      # 执行一些操作
      result = 3
      return result  # 在这里返回结果,函数立即退出
  except ValueError:
     # 处理异常
      pass
  else:
      # 这里的代码不会执行
     print("This code will not be executed.")
 
 # 调用函数
 result = test()
 print(result)

使用 else 块可以提高代码的可读性和可维护性, 但并不是所有人都认同这种做法.有些开发者更倾向于只使用 try-except 块, 将异常处理的逻辑放在一起, 使代码更加紧凑.总之, 使用 try-else 结构是一种代码组织的选择, 而不是强制性的规范.在实际开发中, 我们应根据代码的需求、可读性和一致性等因素来决定是否使用 else

很多大牛在开发中几乎不使用 else, 因为对代码的可读性并没有多大的提升.

所以了解即可.

finally块

除了try-except语句, 还可以使用finally(可选的)块来执行无论是否发生异常都需要执行的代码. finally块中的代码在try块中的代码执行完毕后无论是否发生异常都会执行.

 try:
  # 可能引发异常的代码块
 except ExceptionType:
  # 处理异常的代码块
 finally:
 # 无论是否发生异常, 都会执行的代码块
 
 
  def divide_numbers(a, b):
  try:
     result = a / b
     print("result", result)
  except ZeroDivisionError:
      print("0不能作为除数.")
  except TypeError:
      print("参数传递类型错误.")
  except Exception as e:
      print(f"程序出现了意外错误: 具体出错内容: {e}.")
  finally:
      print(f"{a}/{b}的结果计算完毕.")

由于finally块来执行无论是否发生异常都会执行的代码块, 所以经常用来处理资源回收.

try:
  # 没有使用with语句打开文件的话, 需要手动关闭文件
  f = open('./contact1.txt', 'r')
  f.close()
except FileNotFoundError:
  print("不存在此文件")
  
# 上面的代码是有问题的
# 如果打开文件的时候出错了, 比如当前目录下没有这个文件就会导致异常
# 打开文件导致异常后就不会执行f.close()关闭文件
# 但是有一点还需要格外注意就是如果是由于没有找到这个文件触发异常就会在打开文件的时候触发异常然后直接跳到except FileNotFoundError执行后面的代码, 由于表达式的执行是先计算右边的值然后再将计算结果赋值给左边, 这里由于打开文件触发异常导致没有执行赋值语句, 所以此时f变量是不存在的, 后续调用f.close()是没有意义的.

# 还有一种情况是, 如果打开文件没有问题, 但是在后续处理异常时出现问题
# 这个时候就可以使用finally来关闭文件
try:
  f = open('./contact.txt', 'r')
  content = f.read()
  # 后续处理
  # ...
except FileNotFoundError:
  print("不存在此文件")
except Exception:
  print("后续处理异常")
finally:
  f.close()
# 如果在函数体的try代码块中有return语句和finally, 它们的执行顺序是怎样的:
def divide_numbers(a, b):
    try:
        result = a / b
        return result
    except ZeroDivisionError:
        print("0不能作为除数.")
    except TypeError:
        print("参数传递类型错误.")
    except Exception as e:
        print(f"程序出现了意外错误: 具体出错内容: {e}.")
    finally:
        print(f"{a}/{b}的结果计算完毕.")


res1 = divide_numbers(10, 2)

# 执行结果
# 10/2的结果计算完毕.
# 5.0

# 从上面的执行结果说明了:
# 即使在函数定义里的try代码块中使用了return语句还是会执行后面finally代码块中的内容

总结

异常通常用于处理意外或异常的情况, 即那些无法在代码中预测和处理的情况.
当你无法准确预测可能出现的错误或无法在当前上下文中处理错误时, 抛出异常是一种更合适的方式.
异常提供了一种机制, 让你能够在错误发生时终止当前的代码执行, 并将控制权交给上层代码或异常处理机制

3. raise语句

raise语句是 Python 中的一个关键字, 用于手动引发异常.(前面都是自动触发异常)
通过 raise 语句, 我们可以选择性地在代码中引发特定类型的异常, 并提供相应的错误信息.这样, 我们就能够在需要的时候中断程序的正常执行流程, 并进行适当的异常处理.

语法:

 raise [ExceptionType([args])]
 # ExceptionType 是要引发的异常类型.
 # args 是一个可选参数, 用于向异常类传递附加的信息.
 # 如果只写一个raise, 则会默认引发RuntimeError.
 # raise语句后的代码不会执行.(类似return)
def check_age(age):
    if age < 18:
        raise ValueError('年龄小于18岁, 不允许注册.') # 手动抛出异常
    else:
        # 注册代码如下:
        pass
        print('恭喜, 注册成功')

try:
    age = int(input("请输入年龄:"))
    check_age(age)
except ValueError as e:
    print(e) # 可以把在上面的raise中指定的抛出异常的信息打印出来, 请细品raise的使用 - 可以Chat一下
    
# 执行结果:
# 请输入年龄:17
# 年龄小于18岁, 不允许注册.

使用注意事项:

  1. 选择适当的异常类型:根据具体的情况, 选择合适的异常类型来反映错误的性质. Python 提供了许多内置的异常类, 如 ValueErrorTypeErrorFileNotFoundError 等, 可以根据需要选择合适的异常类或自定义异常类.
  2. 提供明确的错误信息:在引发异常时, 尽量提供清晰、明确的错误信息, 以便在程序出错时能够准确地定位和理解错误的原因.
  3. 在适当的位置引发异常:raise语句应该放置在程序逻辑中合适的位置, 以便在需要时引发异常.根据代码的要求, 可以在函数、方法或其他控制流结构中使用 raise 语句.
  4. 捕获和处理异常:使用 try-except 块捕获和处理引发的异常.根据具体的异常类型, 编写相应的错误处理代码, 以便优雅地处理异常情况.
  5. 避免滥用 raise 语句:raise 语句应该用于合适的情况, 不应该滥用.只有在必要的时候才使用 raise语句, 以避免引发不必要的异常.

4. 自定义异常

异常类型都是 继承自Exception的类,表示各种类型的错误.

我们也可以自己定义异常,比如我们写一个用户注册的函数, 要求用户输入的电话号码只能是中国的电话号码,并且电话号码中不能有非数字字符.

可以定义下面这两种异常类型:

# 异常对象,代表电话号码有非法字符
class InvalidCharError(Exception):
    pass

# 异常对象,代表电话号码非中国号码
class NotChinaTelError(Exception):
    pass

定义了上面的异常,当用户输入电话号码时,出现相应错误的时候,我们就可以使用raise 关键字来抛出对应的自定义异常.

def  register():
    tel = input('请注册您的电话号码:')

    # 如果有非数字字符
    if not tel.isdigit(): 
        raise InvalidCharError()

    # 如果不是以86开头,则不是中国号码
    if not tel.startswith('86'): 
        raise NotChinaTelError()

    return tel

try:
    ret = register()
except InvalidCharError:
    print('电话号码中有错误的字符')
except NotChinaTelError:
    print('非中国手机号码')

5. 函数调用里面产生的异常

请看下面这段代码:

def level_3():
    print ('进入 level_3')
    a = [0]
    b = a[1]
    print ('离开 level_3')

def level_2():
    print ('进入 level_2')
    level_3()
    print ('离开 level_2')

def level_1():
    print ('进入 level_1')
    level_2()
    print ('离开 level_1')


level_1()

print('程序正常退出')

运行该代码会得到类似下面的结果:

进入 level_1
进入 level_2
进入 level_3
Traceback (most recent call last):
  File "E:\err.py", line 18, in <module>
    level_1()
  File "E:\err.py", line 14, in level_1
    level_2()
  File "E:\err.py", line 9, in level_2
    level_3()
  File "E:\err.py", line 4, in level_3
    b = a[1]
IndexError: list index out of range

函数调用次序是这样的

主体部分调用 函数 level_1

函数level_1调用 函数level_2

函数level_2调用 函数level_3

大家注意:函数 level_3 中有个 列表索引越界的错误.

所以执行到该函数的时候,解释器报错了。它在终端上显示了错误代码的具体位置. 也就是:

File "E:\err.py", line 4, in level_3
    b = a[1]

大家可以发现,上面还有输出的信息,说明了这行引起异常的代码, 是怎样被 一层层 的调用进来的.

这就是函数调用栈的信息.

当异常在函数中产生的时候,解释器会终止当前代码的执行, 查看当前函数是否 声明了该类型异常的 except 处理,如果有,就执行, 随后继续执行代码.

如果当前函数没有 声明了该类型异常的处理, 就会中止当前函数的执行,退出到调用该函数的上层函数中, 查看上层是否有 声明了该类型异常的 except 处理. 如果有,就执行该异常匹配处理. 随后继续执行代码.

如果上层函数也没有 该类型异常的匹配处理, 就会到继续到再上层的函数查看是否有 该类型异常的匹配处理.

如此这般,直到到了最外层的代码. 如果依然没有 声明了该类型异常处理,就终止当前代码的执行.

补充练习

案例: 模拟用户名校验.

def check_username(username):
    """
    1. 长度不能小于5.
    2. 只能包含字符.
    3. 禁止使用系统用户名. admin, root.
    :param username: 传入的用户名.
    :return: None.
    """
    if username in ['admin', 'root']:
        raise ValueError('禁止使用系统用户名')
    if len(username) < 5:
        raise ValueError('用户名长度小于5')
    if not username.isalpha():
        raise ValueError('用户名只能包含字符')
    # 如果上面的3个判断都没进, 就会走到这里.
    print(f'{username}校验成功.')


try:
    username = input("请输入用户名:")
    check_username(username)
except ValueError as e:
    print(e)
"""
对之前写的通讯录加上异常处理机制 使用异常捕获完善. 使其健壮性更强.
1. 如果用户输入了我们规则之外的指令. '1 2 3 4 5'以外的. 应该如何处理.
2. 读写我们上节课使用的'r+', 实际上读跟写要分离开, 才是最好的.
3. 若是我们本地没有通讯录文件的时候, 第一次读取, 会出错, 如何处理.
4. 删除联系人时, 如果用户输入错了想要删除的人, 如想要删除'张', 输入成了'刘', 容易出现误删, 而且会出现删除通讯录里不存在的人. 应该如何处理.
5. 手机号是不存在字母的, 如果用户输入有字母的话, 应该如何处理.
"""
import json


def read_file():
    try:
        with open("./contact.txt", 'r', encoding='utf8') as f:
            content = f.read()
            return content
    except FileNotFoundError:
        return False


def write_file(content):
    with open("./contact.txt", 'w', encoding='utf8') as f:
        f.write(content)


def query_all():
    content = read_file()
    if content:
        # 如果存在, 就要展示所有的联系人.
        try:
            json_data = json.loads(content)
            if not json_data:
                print(f'该通讯录目前没有联系人, 请先添加联系人')
                return
        except json.decoder.JSONDecodeError:
            print('系统出错, 稍后在尝试.')
            # 给开发人员发出提醒. 赶紧去修复.
            return
        else:
            print('为您查询到的所有联系人如下:')
        for key, val in json_data.items():
            print(f'{key}: {val}')
    else:
        print(f'该通讯录目前没有联系人, 请先添加联系人')


def query_contact():
    name = input("请输入想要查询的联系人姓名:")
    content = read_file()
    if content:
        # 如果存在, 就要做查询操作.
        json_data = json.loads(content)
        phone = json_data.get(name)
        print(f'为您查询到的指定联系人{name}的电话是: {phone}')
    else:
        print(f'未找到指定联系人: {name}')


def del_contact():
    name = input("请输入想要删除的联系人姓名:")
    content = read_file()
    if content:
        # 如果存在, 就要做删除操作.
        res = input(f"是否确认删除联系人; {name}. y/n?")
        if res.lower() == 'y':
            json_data = json.loads(content)
            # pop_value = json_data.pop(name, None)
            try:
                json_data.pop(name)
            except KeyError:
                print('没有找到指定的联系人.')
                return
            write_file(json.dumps(json_data, ensure_ascii=False))
            print(f'删除{name}成功')
        else:
            print('已取消删除操作')
    else:
        # 如果不存在, 就没办法删了.
        print(f'未找到指定联系人: {name}')


def add_contact():
    name = input("请输入联系人姓名:")
    phone = input("请输入联系人电话:")
    try:
        int(phone)
    except ValueError:
        print('您输入的手机号并不是纯数字的. 请重新添加')
        return
    contact_dict = {
        name: phone
    }
    content = read_file()
    if content:
        json_data = json.loads(content)
        json_data.update(contact_dict)
    else:
        json_data = contact_dict
    write_file(json.dumps(json_data, ensure_ascii=False))
    print(f'添加{name}成功')


def menu():
    print("欢迎使用通讯录管理系统")
    print("------welcome-----")
    print("菜单选项:")
    print("1. 添加联系人.")
    print("2. 删除联系人.")
    print("3. 查询指定联系人.")
    print("4. 查看所有的联系人.")
    print("5. 退出通讯录.")
    while True:
        try:
            choice = int(input("请输入您想要操作的选项:"))
        except ValueError:
            print('请输入1-2-3-4-5的对应指令.')
            continue
        if choice == 1:
            add_contact()
        elif choice == 2:
            del_contact()
        elif choice == 3:
            query_contact()
        elif choice == 4:
            query_all()
        elif choice == 5:
            print('已退出通讯录管理系统')
            break
        else:
            print('请输入1-2-3-4-5的对应指令.')

menu()
遇见安然遇见你,不负代码不负卿。
谢谢老铁的时间,咱们下篇再见~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2325561.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

浅析Android Jetpack ACC之LiveData

一、Android Jetpack简介 Android官网对Jetpack的介绍如下&#xff1a; Jetpack is a suite of libraries to help developers follow best practices, reduce boilerplate code, and write code that works consistently across Android versions and devices so that develo…

【区块链安全 | 第十五篇】类型之值类型(二)

文章目录 值类型有理数和整数字面量&#xff08;Rational and Integer Literals&#xff09;字符串字面量和类型&#xff08;String Literals and Types&#xff09;Unicode 字面量&#xff08;Unicode Literals&#xff09;十六进制字面量&#xff08;Hexadecimal Literals&am…

Ubuntu修改用户名

修改用户名&#xff1a; 1.CTRL ALT T 快捷键打开终端&#xff0c;输入‘sudo su’ 转为root用户。 2.输入‘ gredit /etc/passwd ’&#xff0c;修改用户名&#xff0c;只修改用户名&#xff0c;后面的全名、目录等不修改。 3.输入 ‘ gedit /etc/shadow ’ 和 ‘ gedit /etc/…

Windows 系统下多功能免费 PDF 编辑工具详解

IceCream PDF Editor是一款极为实用且操作简便的PDF文件编辑工具&#xff0c;它完美适配Windows操作系统。其用户界面设计得十分直观&#xff0c;哪怕是初次接触的用户也能快速上手。更为重要的是&#xff0c;该软件具备丰富多样的强大功能&#xff0c;能全方位满足各类PDF编辑…

UE学习记录part11

第14节 breakable actors 147 destructible meshes a geometry collection is basically a set of static meshes that we get after we fracture a mesh. 几何体集合基本上是我们在断开网格后获得的一组静态网格。 选中要破碎的网格物品&#xff0c;创建集合 可以选择不同的…

Redis-07.Redis常用命令-集合操作命令

一.集合操作命令 SADD key member1 [member2]&#xff1a; sadd set1 a b c d sadd set1 a 0表示没有添加成功&#xff0c;因为集合中已经有了这个元素了&#xff0c;因此无法重复添加。 SMEMBERS key: smembers set1 SCARD key&#xff1a; scard set1 SADD key member1 …

vscode 源代码管理

https://code.visualstudio.com/updates/v1_92#_source-control 您可以通过切换 scm.showHistoryGraph 设置来禁用传入/传出更改的图形可视化。

iOS审核被拒:Missing privacy manifest 第三方库添加隐私声明文件

问题&#xff1a; iOS提交APP审核被拒&#xff0c;苹果开发者网页显示二进制错误&#xff0c;收到的邮件显示的详细信息如下图: 分析&#xff1a; 从上面信息能看出第三方SDK库必须要包含一个隐私文件&#xff0c;去第三方库更新版本。 几经查询资料得知&#xff0c;苹果在…

【LeetCode Solutions】LeetCode 101 ~ 105 题解

CONTENTS LeetCode 101. 对称二叉树&#xff08;简单&#xff09;LeetCode 102. 二叉树的层序遍历&#xff08;中等&#xff09;LeetCode 103. 二叉树的锯齿形层序遍历&#xff08;中等&#xff09;LeetCode 104. 二叉树的最大深度&#xff08;简单&#xff09;LeetCode 105. 从…

Orpheus-TTS 介绍,新一代开源文本转语音

Orpheus-TTS 是由 Canopy Labs 团队于2025年3月19日发布的开源文本转语音&#xff08;TTS&#xff09;模型&#xff0c;其技术突破集中在超低延迟、拟人化情感表达与实时流式生成三大领域。以下从技术架构、核心优势、应用场景、对比分析、开发背景及最新进展等多维度展开深入解…

Java数据结构-栈和队列

目录 1. 栈(Stack) 1.1 概念 1.2 栈的使用 1.3 栈的模拟实现 1.4 栈的应用场景 1. 改变元素的序列 2. 将递归转化为循环 3. 括号匹配 4. 逆波兰表达式求值 5. 出栈入栈次序匹配 6. 最小栈 1.5 概念区分 2. 队列(Queue) 2.1 概念 2.2 队列的使用 2.3 队列模拟实…

权重衰减-笔记

《动手学深度学习》-4.5-笔记 权重衰减就像给模型“勒紧裤腰带”&#xff0c;不让它太贪心、不让它学太多。 你在学英语单词&#xff0c;别背太多冷门单词&#xff0c;只背常见的就行&#xff0c;这样考试时更容易拿分。” —— 这其实就是在“限制你学的内容复杂度”。 在…

Hyperliquid 遇袭「拔网线」、Polymarket 遭治理攻击「不作为」,从双平台危机看去中心化治理的进化阵痛

作者&#xff1a;Techub 热点速递 撰文&#xff1a;Glendon&#xff0c;Techub News 继 3 月 12 日「Hyperliquid 50 倍杠杆巨鲸」引发的 Hyperliquid 清算事件之后&#xff0c;3 月 26 日 晚间&#xff0c;Hyperliquid 再次遭遇了一场针对其流动性和治理模式的「闪电狙击」。…

软考笔记6——结构化开发方法

第六章节——结构化开发方法 结构化开发方法 第六章节——结构化开发方法一、系统分析与设计概述1. 系统分析概述2. 系统设计的基本原理3. 系统总体结构设计 二、结构化分析方法1. 结构化分析方法概述2. 数据流图(DFD)3. 数据字典 三、结构化设计方法&#xff08;了解&#xff…

一种C# Winform的UI处理

效果 圆角 阴影 突出按钮 说明 这是一种另类的处理&#xff0c;不是多层窗口 也不是WPF 。这种方式的特点是比较简单&#xff0c;例如圆角、阴影、按钮等特别容易修改过。其实就是html css DirectXForm。 在VS中如下 圆角和阴影 然后编辑这个窗体的Html模板&#xff0c…

为什么视频文件需要压缩?怎样压缩视频体积即小又清晰?

在日常生活中&#xff0c;无论是为了节省存储空间、便于分享还是提升上传速度&#xff0c;我们常常会遇到需要压缩视频的情况。本文将介绍为什么视频需要压缩&#xff0c;压缩视频的好处与坏处&#xff0c;并教你如何使用简鹿视频格式转换器轻松完成MP4视频文件的压缩。 为什么…

Nginx — Nginx处理Web请求机制解析

一、Nginx请求默认页面资源 1、配置文件详解 修改端口号为8080并重启服务&#xff1a; 二、Nginx进程模型 1、nginx常用命令解析 master进程&#xff1a;主进程&#xff08;只有一个&#xff09; worker进程&#xff1a;工作进程&#xff08;可以有多个&#xff0c;默认只有一…

5.0 WPF的基础介绍1-Grid,Stack,button

WPF: Window Presentation Foundation. WPF与WinForms的对比如下&#xff1a; 特性WinFormsWPF技术基础基于传统的GDI&#xff08;图形设备接口&#xff09;基于DirectX&#xff0c;支持硬件加速的矢量渲染UI设计方式拖拽控件事件驱动代码&#xff08;简单但局限&#xff09;…

Docker 端口映射原理

在 Docker 中&#xff0c;默认情况下容器无法直接与外部网络通信。 为了使外部网络能够访问容器内的服务&#xff0c;Docker 提供了端口映射功能&#xff0c;通过将宿主机的端口映射到容器内的端口&#xff0c;外部可以通过宿主机的IP和端口访问容器内的服务 以下通过动手演示…

SDL —— 将sdl渲染画面嵌入Qt窗口显示(附:源码)

🔔 SDL/SDL2 相关技术、疑难杂症文章合集(掌握后可自封大侠 ⓿_⓿)(记得收藏,持续更新中…) 效果 使用QWidget加载了SDL的窗口,渲染器使用硬件加速跑GPU的。支持Qt窗口缩放或显示隐藏均不影响SDL的图像刷新。   操作步骤 1、在创建C++空工程时加入SDL,引入头文件时需…