Python从入门到放弃必看:用PyCharm新建Python文件其实一点不简单,好吗!

文摘   2024-10-03 11:00   新加坡  
点击订阅公众号 | 前沿学术成果每日更新               
内容速览:
  • PyCharm和python

  • 新建Python脚本的选项

  • 简单创建Python File

  • 详细探讨Python Unit Test

  • 深入理解Python Stub

PyCharm和python

在当今的软件开发领域,Python因其强大的功能和易于学习的特性,已成为最受欢迎的编程语言之一。随着Python的普及,各种开发工具也应运而生,而PyCharm作为其中的佼佼者,被广泛用于专业的Python开发。PyCharm是由JetBrains开发的一个强大的Python IDE,提供代码自动完成、项目管理、代码质量检查、强大的调试功能等一系列功能。PyCharm支持多种python的多种应用框架,如Django和Flask,且内置了对多种Python解释器的支持。无论是初学者还是资深开发者,PyCharm都是提高开发效率、优化编码体验的优选工具。本文旨在介绍PyCharm在新建Python脚本时的几个关键选项——Python FilePython Unit TestPython Stub,帮助Python初学者和开发者更有效地利用这款强大的工具。

新建Python脚本的选项

在PyCharm中,新建Python脚本可以选择以下三种类型:

  1. Python File: 标准的Python脚本文件,用于编写普通的Python代码。
  2. Python Unit Test: 专门用于编写Python的单元测试代码,利用Python的unittest测试框架。
  3. Python Stub: 用于创建类型注解存根(.pyi)文件,提供对第三方库或自定义模块的类型支持。

简单创建Python File

创建一个Python File是日常开发中的基础操作。在PyCharm的项目窗口中右键点击,选择“New”然后是“Python File”,即可创建一个新的.py文件。这种文件类型最常用于实现应用程序逻辑、脚本任务等。

这个略有点简单,那么下面的内容,各位小伙伴们是否以前有了解呢?


详细探讨Python Unit Test

Python Unit Test是用来创建单元测试脚本的,那么什么是单元测试?

单元测试 是对软件中最小可测试单位(通常是一个函数或类方法)的测试,目的是验证该单元的行为是否符合预期。通过自动化单元测试,开发者可以在代码变化时迅速检测出潜在的问题,并且能确保应用程序的稳定性和正确性。所以单元测试是确保代码质量、预防未来错误的关键步骤。

创建一个Python Unit Test文件时,PyCharm会自动导入Python的单元测试框架unittest,并可生成类和方法的模板代码。一个示例如下:

import unittest
from my_module import add_function

class TestAddFunction(unittest.TestCase):
    def test_addition(self):
        self.assertEqual(add_function(23), 5)

if __name__ == '__main__':
    unittest.main()

但是需要注意,unittest 不是 PyCharm 的专属功能,它是 Python 标准库 中的一个内置模块,专门用于编写和运行单元测试。它基于 JUnit(Java 中的单元测试框架)的设计,并扩展了一些功能,而PyCharm 只是提供了对 unittest 框架的内置支持,简化了测试的编写、运行和调试过程,但 unittest 本质上属于 Python 生态的一部分。

unittest 的特点和功能

unittest 使用测试类来组织测试,测试类继承自 unittest.TestCase,在该类中每个以 test_ 开头的方法都被视为一个单独的测试用例,如上面示例中的class TestAddFunction以及def test_addition(self)

断言(Assertions)unittest 的最大的特点和功能。unittest提供了丰富的断言方法,用于验证测试中的某些条件,常用的断言方法包括:

  • assertEqual(a, b): 检查 a == b,如果不相等,则测试失败。
  • assertNotEqual(a, b): 检查 a != b
  • assertTrue(x): 检查 x 为 True
  • assertFalse(x): 检查 x 为 False
  • assertIsNone(x): 检查 x 是否为 None
  • assertIsNotNone(x): 检查 x 是否不为 None
  • assertIn(a, b): 检查 a 是否在 b 中。
  • assertRaises(exception, callable, *args, **kwargs): 检查是否抛出了指定的异常(exception)。

完整的unittest的使用示例

假设我们有一个简单的 Python 函数 add,我们要为它编写单元测试:

# main.py

def add(a, b):
    return a + b

现在我们为这个函数创建一个测试类,使用 unittest 进行测试:

# test_main.py

import unittest
from main import add

class TestAddFunction(unittest.TestCase):

    # 测试 add 函数
    def test_add_positive(self):
        self.assertEqual(add(12), 3)

    def test_add_negative(self):
        self.assertEqual(add(-1-1), -2)

    def test_add_zero(self):
        self.assertEqual(add(00), 0)

# 让这个文件可以通过命令行直接运行
if __name__ == '__main__':
    unittest.main()

在终端(命令行/CMD)中,进入包含 test_main.py 文件的目录并运行python -m unittest test_main.py,你会看到类似这样的输出:

...
----------------------------------------------------------------------
Ran 3 tests in 0.000s

OK

这里表示所有测试通过

如果我们修改代码第10行为self.assertEqual(add(1, 2), 4),重新运行就会报错:

FAIL: test_add_positive (__main__.TestAddFunction)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "test_main.py", line 10, in test_add_positive
    self.assertEqual(add(1, 2), 4)
AssertionError: 3 != 4

----------------------------------------------------------------------
Ran 3 tests in 0.001s

FAILED (failures=1)

PyCharm 提供了对 unittest 的内置支持,PyCharm 可以自动识别以 test_ 开头的测试文件、测试类和测试方法,并自动运行这些测试。选中测试脚本,右键选择 **Run 'Unittest in test_main'**,PyCharm 会自动使用 unittest 框架运行测试,并在底部显示测试结果。

  • 如果测试通过,会显示绿色对勾。
  • 如果测试失败,会显示红色的叉号,点击可以看到详细的错误信息。

运行示例:

  • 测试成功的窗口:


  • 测试发现错误时的运行窗口:

unittest的高级功能

setUp 和 tearDown

unittest 允许使用 setUp() 和 tearDown() 方法在测试前后执行某些初始化和清理操作,一个示例如下:

import unittest
import tempfile
import os

class TestFileOperations(unittest.TestCase):
    def setUp(self):
        # 在每个测试开始前创建一个临时文件
        self.temp_file = tempfile.NamedTemporaryFile(delete=False)
        print(f"创建临时文件: {self.temp_file.name}")

    def test_write_to_file(self):
        # 测试写入文件
        with open(self.temp_file.name, 'w'as f:
            f.write("Hello, world!")
        with open(self.temp_file.name, 'r'as f:
            self.assertEqual(f.read(), "Hello, world!")

    def test_delete_file(self):
        # 确认文件存在
        self.assertTrue(os.path.isfile(self.temp_file.name))

    def tearDown(self):
        # 在每个测试结束后删除文件
        os.remove(self.temp_file.name)
        print(f"删除临时文件: {self.temp_file.name}")

if __name__ == '__main__':
    unittest.main()

在这个示例中,setUp() 创建一个临时文件,该文件用于后续的测试。tearDown() 确保无论测试结果如何,这个文件都会被删除,不会产生垃圾文件。

suite和unittest.TextTestRunner()

你可以将多个测试用例组织在一起在suite函数中,然后然后使用测试套件(unittest.TextTestRunner())以便于批量执行测试。示例:

# suite.py
import unittest
from test_main import TestAddFunction

def suite():
    suite = unittest.TestSuite()
    suite.addTest(TestAddFunction('test_add_positive'))
    suite.addTest(TestAddFunction('test_add_negative'))
    return suite

if __name__ == '__main__':
    runner = unittest.TextTestRunner()
    runner.run(suite())

运行结果如下:

一个更强大的测试场景就是,当我们进行有关数据库的测试,首先要先连接数据库再测试其他的数据逻辑。然而unittest 框架默认的测试行为是按照字母排序的,那么就需要suite来自定义的测试逻辑,一个代码示例如下:
import unittest

class TestDatabaseOperations(unittest.TestCase):
    def setUp(self):
        # 假设这里创建数据库连接
        self.connection = self.create_database_connection()
        print("数据库连接已创建")

    def tearDown(self):
        # 假设这里关闭数据库连接
        self.connection.close()
        print("数据库连接已关闭")

    def test_insert_data(self):
        # 测试插入数据
        self.assertTrue(self.insert_data(self.connection, data="Sample Data"))

    def test_read_data(self):
        # 测试读取数据
        self.assertEqual(self.read_data(self.connection, key="Sample Key"), "Sample Data")

    def create_database_connection(self):
        # 创建假的数据库连接
        return "Connection"

    def insert_data(self, connection, data):
        # 假设插入数据的方法
        print(f"插入数据: {data}")
        return True

    def read_data(self, connection, key):
        # 假设读取数据的方法
        print(f"读取数据键: {key}")
        return "Sample Data"

def suite():
    suite = unittest.TestSuite()
    suite.addTest(TestDatabaseOperations('test_insert_data'))
    suite.addTest(TestDatabaseOperations('test_read_data'))
    return suite

if __name__ == '__main__':
    runner = unittest.TextTestRunner()
    runner.run(suite())

代码解读:通过定义一个 suite() 函数来创建一个 TestSuite 实例。可以向这个套件中添加测试方法,按照添加的顺序来执行测试。这里先添加 test_insert_data,再添加 test_read_data,确保插入数据的测试先于读取数据的测试执行。最后使用 unittest.TextTestRunner() 运行自定义的测试套件。这种方式可以确保测试在一个控制和可预见的顺序中运行,适合测试需要遵循特定执行逻辑的场景。

自动测试发现

pycharm可以支持自动发现unittest 脚本,这对于大项目非常有用。在 PyCharm 中使用 unittest 框架的自动发现功能意味着 PyCharm 可以自动识别项目中所有符合标准的 unittest 测试用例,并运行它们。这一特性依赖于 Python 的 unittest 模块的测试发现机制,它查找所有命名为 test*.py 的文件,从这些文件中自动发现并运行继承自 unittest.TestCase 的类的所有方法,这些方法以 test 开头。

这样的机制简化了测试管理,因为你不需要手动指定每个测试文件或测试类来运行你的测试。它特别适用于大型项目,其中可能包含多个测试文件和测试类。

如何在 PyCharm 中使用 unittest 的自动发现功能的步骤:

  1. 创建测试用例

  • 确保测试文件以 test 开头,例如 test_example.py
  • 确保测试类继承自 unittest.TestCase
  • 确保测试方法以 test 开头。
  • 配置测试运行器

    • 在 PyCharm 中,打开你想运行的项目。
    • 从顶部菜单选择 "Run" -> "Edit Configurations"。
    • 点击左上角的 "+",选择 "Python tests" -> "Unittests"。
    • 指定包含测试文件的目录。
    • 确保 "Pattern" 是 test*.py(这是默认设置,通常不需要改变)。
    • 应用并关闭配置窗口。
  • 运行测试

    • 选择你刚刚创建的配置,然后点击 "Run"。
    • PyCharm 会自动发现并运行指定文件夹中所有符合条件的测试。

    以下是一个简单的 PyCharm unittest 项目结构和示例代码:

    项目结构

    my_project/
    |-- test/
    | |-- __init__.py
    | |-- test_math_functions.py

    test_math_functions.py

    import unittest

    class TestMathFunctions(unittest.TestCase):
        def test_addition(self):
            self.assertEqual(1 + 12)

        def test_subtraction(self):
            self.assertEqual(2 - 11)

    if __name__ == '__main__':
        unittest.main()

    注意,这里的测试文件中不需要显式引入要测试的脚本文件。在这个项目中,test目录中可以包含多个以 test*.py 命名的文件,每个文件可以包含一个或多个测试类。这种方式非常方便地管理和执行大量的测试用例。通过这种设置,PyCharm 提供了一种高效的方法来自动化和简化测试执行过程,特别适用于进行持续集成和持续测试的开发环境。

    深入理解Python Stub

    Python Stub 文件是一种用于类型提示(type hinting)的文件,通常用于没有明确类型注解的代码(例如,老代码库或第三方库)中,为动态类型语言(如 Python)提供静态类型检查支持。这些文件的扩展名是 .pyi,它们与 .py 文件对应,但只包含函数、类的声明和类型注解,而不包含具体的实现。Stub 文件的主要作用是:

    • 类型检查:提供函数和类的签名及其类型提示,帮助类型检查器(如 mypy)更准确地推断代码的类型正确性。

    • 提高 IDE 支持:提高 IDE(如 PyCharm)的代码补全、错误检测和代码导航功能,尤其在处理未明确标注类型的库时。

    • 与现有代码兼容:不需要修改源代码,只需提供 .pyi 文件,就可以为现有代码库添加类型注解。

    虽然在初学阶段一般接触不到调类型提示,但是在成长为一位python大手子的过程,并不可少的需要类型提示,来让自己的代码的类型提示更加优雅。

    ==如果您希望需要类型提示的详细说明,请在评论区告知。如果反馈较多的话,我会写篇文章单独介绍类型提示的使用方法。==

    一个简单的stub文件的使用案例如下:

    假设有一个 Python 模块 math_operations.py,其中没有类型注解:

    # math_operations.py
    def add(a, b):
        return a + b

    def multiply(a, b):
        return a * b

    我们希望为这些函数提供类型提示,但不想修改原始代码。此时,可以创建一个 Stub 文件 math_operations.pyi,并在其中为函数添加类型注解。在 math_operations.py 同目录下创建 math_operations.pyi 文件:

    # math_operations.pyi
    def add(a: int, b: int) -> int: ...
    def multiply(a: int, b: int) -> int: ...

    在 PyCharm 中,Stub 文件会自动与源代码文件关联。PyCharm 会使用 .pyi 文件中的类型注解为代码提供类型检查和智能提示,例如:

    from math_operations import add

    result = add(23)  # PyCharm 能够自动识别 add() 期望的参数类型是 int,并返回 int

    如果你尝试传递错误的类型,例如:

    add(2"3")  # PyCharm 会警告参数类型错误

    总结一下:

    • Stub 文件的主要目的是为没有类型注解的代码(例如,老代码库或第三方库)添加类型提示,而不需要修改原始的源代码。
    • PyCharm 对 Stub 文件有很好的支持,它可以读取 .pyi 文件中的类型注解,增强代码补全和错误检测的功能。
    • Python 标准库自带部分 .pyi 文件,这些文件位于安装的 Python 库目录中,并为标准库中的模块提供类型提示。
    • 很多第三方库(例如 requestsnumpy)也会提供自己的 Stub 文件。这些可以直接安装,通常通过 typing 或 types- 前缀的包提供。例如,pip install types-requests 可以安装 requests 的类型注解。
    • 对于自己的项目或没有类型提示的第三方库,可以编写 .pyi 文件,为这些库提供类型提示。
                   

                   

    声明:本公众号分享的前沿学术成果来源于各学术网站,不依法享有其所有权。若原作者发现本次分享中的文字及图片涉及侵权,请立刻联系公众号后台或发送邮件,我们将及时修改或删除!         

    邮箱:environmodel@sina.com         

    若您认为有用,欢迎

    Environmodel设为星标,或

    点击“在看”或“分享”给他人


    Environmodel
    Environmodel(环境模型)专注于环境科学与工程领域的建模及模型研究进展,并分享涵盖机器学习、深度学习以及人工智能等相关领域的理论知识、主流工具和Python编程技巧。
     最新文章