跳至内容

文件

除了 Path CLI 参数,您还可以声明一些类型的“文件”。

提示

在大多数情况下,您可能只需要使用 Path 就可以了。

您可以使用 Path 以相同的方式读取和写入数据。

区别在于这些类型将为您提供一个 Python 类文件对象,而不是一个 Python Path

“类文件对象”与 open() 返回的相同类型的对象相同,例如

with open('file.txt') as f:
    # Here f is the file-like object
    read_data = f.read()
    print(read_data)

但在某些特殊情况下,您可能希望使用这些特殊类型。例如,如果您正在迁移现有应用程序。

FileText 读取

typer.FileText 为您提供了一个用于读取文本的类文件对象,您将从中获得 str 数据。

这意味着即使您的文件包含以非英语语言编写的文本,例如一个包含以下内容的 text.txt 文件:

la cigüeña trae al niño

您将拥有一个包含文本的 str,例如:

content = "la cigüeña trae al niño"

而不是拥有 bytes,例如:

content = b"la cig\xc3\xbce\xc3\xb1a trae al ni\xc3\xb1o"

您将获得所有正确的编辑器支持、属性、方法等,用于类文件对象:

import typer
from typing_extensions import Annotated


def main(config: Annotated[typer.FileText, typer.Option()]):
    for line in config:
        print(f"Config line: {line}")


if __name__ == "__main__":
    typer.run(main)

提示

如果可能,请优先使用 Annotated 版本。

import typer


def main(config: typer.FileText = typer.Option(...)):
    for line in config:
        print(f"Config line: {line}")


if __name__ == "__main__":
    typer.run(main)

检查它

// Create a quick text config
$ echo "some settings" > config.txt

// Add another line to the config to test it
$ echo "some more settings" >> config.txt

// Now run your program
$ python main.py --config config.txt

Config line: some settings

Config line: some more settings

FileTextWrite

要写入文本,您可以使用 typer.FileTextWrite

import typer
from typing_extensions import Annotated


def main(config: Annotated[typer.FileTextWrite, typer.Option()]):
    config.write("Some config written by the app")
    print("Config written")


if __name__ == "__main__":
    typer.run(main)

提示

如果可能,请优先使用 Annotated 版本。

import typer


def main(config: typer.FileTextWrite = typer.Option(...)):
    config.write("Some config written by the app")
    print("Config written")


if __name__ == "__main__":
    typer.run(main)

这将用于写入人类文本,例如

some settings
la cigüeña trae al niño

...而不是写入二进制 bytes

检查它

$ python main.py --config text.txt

Config written

// Check the contents of the file
$ cat text.txt

Some config written by the app

技术细节

typer.FileTextWrite 只是一个便利类。

它与使用 typer.FileText 并设置 mode="w" 相同。您将在下面了解有关 mode 的更多信息。

FileBinaryRead

要读取二进制数据,您可以使用 typer.FileBinaryRead

您将从中收到 bytes

它对于读取二进制文件(如图像)很有用

import typer
from typing_extensions import Annotated


def main(file: Annotated[typer.FileBinaryRead, typer.Option()]):
    processed_total = 0
    for bytes_chunk in file:
        # Process the bytes in bytes_chunk
        processed_total += len(bytes_chunk)
        print(f"Processed bytes total: {processed_total}")


if __name__ == "__main__":
    typer.run(main)

提示

如果可能,请优先使用 Annotated 版本。

import typer


def main(file: typer.FileBinaryRead = typer.Option(...)):
    processed_total = 0
    for bytes_chunk in file:
        # Process the bytes in bytes_chunk
        processed_total += len(bytes_chunk)
        print(f"Processed bytes total: {processed_total}")


if __name__ == "__main__":
    typer.run(main)

检查它

$ python main.py --file lena.jpg

Processed bytes total: 512
Processed bytes total: 1024
Processed bytes total: 1536
Processed bytes total: 2048

FileBinaryWrite

要写入二进制数据,您可以使用 typer.FileBinaryWrite

您将向其写入 bytes

它对于写入二进制文件(如图像)很有用。

请记住,您必须将 bytes 传递给它的 .write() 方法,而不是 str

如果您有 str,您必须先对其进行编码以获得 bytes

import typer
from typing_extensions import Annotated


def main(file: Annotated[typer.FileBinaryWrite, typer.Option()]):
    first_line_str = "some settings\n"
    # You cannot write str directly to a binary file, you have to encode it to get bytes
    first_line_bytes = first_line_str.encode("utf-8")
    # Then you can write the bytes
    file.write(first_line_bytes)
    # This is already bytes, it starts with b"
    second_line = b"la cig\xc3\xbce\xc3\xb1a trae al ni\xc3\xb1o"
    file.write(second_line)
    print("Binary file written")


if __name__ == "__main__":
    typer.run(main)

提示

如果可能,请优先使用 Annotated 版本。

import typer


def main(file: typer.FileBinaryWrite = typer.Option(...)):
    first_line_str = "some settings\n"
    # You cannot write str directly to a binary file, you have to encode it to get bytes
    first_line_bytes = first_line_str.encode("utf-8")
    # Then you can write the bytes
    file.write(first_line_bytes)
    # This is already bytes, it starts with b"
    second_line = b"la cig\xc3\xbce\xc3\xb1a trae al ni\xc3\xb1o"
    file.write(second_line)
    print("Binary file written")


if __name__ == "__main__":
    typer.run(main)
$ python main.py --file binary.dat

Binary file written

// Check the binary file was created
$ ls ./binary.dat

./binary.dat

文件CLI 参数配置

您可以在 typer.Option()typer.Argument() 中为这些类型(类)使用多个配置参数

  • mode:控制用于打开文件的 "模式"。
    • 通过使用上面的类,它会自动为您设置。
    • 阅读下面有关它的更多信息。
  • encoding:强制使用特定编码,例如 "utf-8"
  • lazy:延迟 I/O 操作。默认情况下自动执行。
    • 默认情况下,在写入文件时,Click 将生成一个尚未成为实际文件的类文件对象。一旦您开始写入,它将打开文件并开始写入,但不会在此之前。这主要用于避免在开始写入之前创建文件。通常将此设置为自动是安全的。但是,您可以通过设置 lazy=False 来覆盖它。默认情况下,对于写入,它是 lazy=True,对于读取,它是 lazy=False
  • atomic: 如果为真,所有写入操作实际上都会写入一个临时文件,并在完成后移动到最终目标。这对于由多个用户/程序频繁修改的文件很有用。

高级 mode

默认情况下,Typer 会为您配置 mode

  • typer.FileText: mode="r",用于读取文本。
  • typer.FileTextWrite: mode="w",用于写入文本。
  • typer.FileBinaryRead: mode="rb",用于读取二进制数据。
  • typer.FileBinaryWrite: mode="wb",用于写入二进制数据。

关于 FileTextWrite 的说明

typer.FileTextWrite 实际上只是一个方便类。它与使用 typer.FileTextmode="w" 相同。

但它可能更短、更直观,因为您可以在编辑器中通过只开始输入 typer.File 来获得它...就像其他类一样。

自定义 mode

您可以覆盖上述默认值中的 mode

例如,您可以使用 mode="a" 将内容“追加”到同一个文件。

import typer
from typing_extensions import Annotated


def main(config: Annotated[typer.FileText, typer.Option(mode="a")]):
    config.write("This is a single line\n")
    print("Config line written")


if __name__ == "__main__":
    typer.run(main)

提示

如果可能,请优先使用 Annotated 版本。

import typer


def main(config: typer.FileText = typer.Option(..., mode="a")):
    config.write("This is a single line\n")
    print("Config line written")


if __name__ == "__main__":
    typer.run(main)

提示

由于您手动设置了 mode="a",您可以使用 typer.FileTexttyper.FileTextWrite,两者都可以工作。

检查它

$ python main.py --config config.txt

Config line written

// Run your program a couple more times to see how it appends instead of overwriting
$ python main.py --config config.txt

Config line written

$ python main.py --config config.txt

Config line written

// Check the contents of the file, it should have each of the 3 lines appended
$ cat config.txt

This is a single line
This is a single line
This is a single line

关于不同类型

信息

这些是关于 **Typer** 提供的不同类型/类的技术细节。

但您不需要这些信息就能使用它们。您可以跳过它。

**Typer** 为您提供这些不同的类型(类),因为它们直接继承自实际的 Python 实现,这些实现将在每种情况下提供。

这样,您的编辑器将为每种类型提供正确的类型检查和完成。

即使您使用lazy。当您使用lazy时,Click 会创建一个特殊的对象来延迟写入,并充当将要写入的实际文件的“代理”。但这个特殊的代理对象不公开编辑器类型检查和自动完成所需的属性和方法。如果您访问这些属性或调用这些方法,“代理”延迟对象将在最终对象中调用它们,并且一切都会正常工作。但您不会获得它们的自动完成。

但由于这些Typer类继承自将在其下方提供的实际实现(而不是延迟对象),因此您将在编辑器中获得所有自动完成和类型检查。