使用“与” Python 中 CSV 文件的语句

发布于 2024-07-11 13:51:56 字数 429 浏览 9 评论 0原文

是否可以直接对 CSV 文件使用 with 语句? 能够执行这样的操作似乎很自然:

import csv
with csv.reader(open("myfile.csv")) as reader:
    # do things with reader

但是 csv.reader 不提供 __enter__ 和 __exit__ 方法,因此这不起作用。 不过,我可以分两步完成:

import csv
with open("myfile.csv") as f:
    reader = csv.reader(f)
    # do things with reader

第二种方法是理想的方法吗? 为什么他们不让 csv.reader 直接与 with 语句兼容?

Is it possible to use the with statement directly with CSV files? It seems natural to be able to do something like this:

import csv
with csv.reader(open("myfile.csv")) as reader:
    # do things with reader

But csv.reader doesn't provide the __enter__ and __exit__ methods, so this doesn't work. I can however do it in two steps:

import csv
with open("myfile.csv") as f:
    reader = csv.reader(f)
    # do things with reader

Is this second way the ideal way to do it? Why wouldn't they make csv.reader directly compatible with the with statement?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

你的心境我的脸 2024-07-18 13:51:56

with 语句的主要用途是对语句中使用的对象进行异常安全清理。 with 确保文件已关闭、锁已释放、上下文已恢复等。

csv.reader 有异常情况需要清理吗?

我会选择:

with open("myfile.csv") as f:
    for row in csv.reader(f):
        # process row

您不需要提交补丁即可一起使用 csv.readerwith 语句。

import contextlib

模块 contextlib 中函数 contextmanager 的帮助:

contextmanager(func)
    @contextmanager decorator.

典型用法:

    @contextmanager
    def some_generator(<arguments>):
        <setup>
        try:
            yield <value>
        finally:
            <cleanup>

这使得此:

    with some_generator(<arguments>) as <variable>:
        <body>

相当于此:

    <setup>
    try:
        <variable> = <value>
        <body>
    finally:
        <cleanup>

这是我如何使用它的具体示例: 诅咒屏幕

The primary use of with statement is an exception-safe cleanup of an object used in the statement. with makes sure that files are closed, locks are released, contexts are restored, etc.

Does csv.reader have things to cleanup in case of exception?

I'd go with:

with open("myfile.csv") as f:
    for row in csv.reader(f):
        # process row

You don't need to submit the patch to use csv.reader and with statement together.

import contextlib

Help on function contextmanager in module contextlib:

contextmanager(func)
    @contextmanager decorator.

Typical usage:

    @contextmanager
    def some_generator(<arguments>):
        <setup>
        try:
            yield <value>
        finally:
            <cleanup>

This makes this:

    with some_generator(<arguments>) as <variable>:
        <body>

equivalent to this:

    <setup>
    try:
        <variable> = <value>
        <body>
    finally:
        <cleanup>

Here's a concrete example how I've used it: curses_screen.

唐婉 2024-07-18 13:51:56

是的。 第二种方法是正确的。

至于为什么? 谁知道呢。 你是对的,这可能是一个简单的改变。 它不像其他事情那么优先。

您可以轻松制作自己的补丁包并提交。

Yes. The second way is correct.

As to why? Who ever knows. You're right, it's probably an easy change. It's not as high priority as other things.

You can easily make your own patch kit and submit it.

猫烠⑼条掵仅有一顆心 2024-07-18 13:51:56

问题是 csv.reader 并没有真正管理上下文。 它可以接受任何可迭代对象,而不仅仅是文件。 因此,它不会对其输入调用 close (顺便说一句,如果这样做,您可以使用 contextlib. opening )。 因此,对 csv.reader 的上下文支持实际上会做什么并不明显。

The problem is csv.reader doesn't really manage a context. It can accept any iterable, not just a file. Therefore it doesn't call close on its input (incidentally if it did you could use contextlib.closing). So it's not obvious what context support for csv.reader would actually do.

嗼ふ静 2024-07-18 13:51:56
import csv

class CSV(object):
    def __init__(self,path,mode):
        self.path = path
        self.mode = mode
        self.file = None

    def __enter__(self):
        self.file = open(self.path,self.mode)
        if self.mode == 'r':
            return csv.reader(self.file)
        elif self.mode == 'w':
            return csv.writer(self.file)

    def __exit__(self, exc_type, exc_val, exc_tb):
        self.file.close()   

with CSV('data.csv','r') as reader:
    for row in reader:
        print row
import csv

class CSV(object):
    def __init__(self,path,mode):
        self.path = path
        self.mode = mode
        self.file = None

    def __enter__(self):
        self.file = open(self.path,self.mode)
        if self.mode == 'r':
            return csv.reader(self.file)
        elif self.mode == 'w':
            return csv.writer(self.file)

    def __exit__(self, exc_type, exc_val, exc_tb):
        self.file.close()   

with CSV('data.csv','r') as reader:
    for row in reader:
        print row
薄情伤 2024-07-18 13:51:56

使用生成器函数可以轻松创建您想要的内容:


import csv
from contextlib import contextmanager

@contextmanager
def opencsv(path):
   yield csv.reader(open(path))

with opencsv("myfile.csv") as reader:
   # do stuff with your csvreader

It's easy to create what you want using a generator function:


import csv
from contextlib import contextmanager

@contextmanager
def opencsv(path):
   yield csv.reader(open(path))

with opencsv("myfile.csv") as reader:
   # do stuff with your csvreader
独﹏钓一江月 2024-07-18 13:51:56

正如其他答案所指出的,第二种方法是更好的选择。 而且我认为 FileContextHandle 将来不会出现,因为 csv 模块估计的是 Iterator[str],而不是 Typing.IO。

然而,缺少提供完整功能和正确文件上下文处理的 csv_open。

class csv_open:
"""
A in most cases useless class, but Stackoverflow threads have triggered me.
"""

def __init__(
    self,
    file: str | Path,
    mode: str = "rt",
    buffering: Optional[int] = 1,
    encoding: Optional[str] = None,
    errors: Optional[str] = None,
    newline: Optional[typing.Literal["", "\n", "\r", "\n\r"]] = None,
):
    """

    Notes:
        if you do not specify a reader or writer a normal csv.reader is
        created for mode r and a csv.writer is created for modes w,
        a and x.

        If you want dict-reader or dict-writer or open the file in
        reading and writing mode you have to specify the reader or writer
        explicitly.

    Examples:
        >>> with csv_open('names.csv', mode='rt') as reader:
        ...    for row in reader:
        ...        print(row)

        >>> with csv_open("names.csv", mode="rt").dict_reader(
        ...     fieldnames=['firstname', 'surname']) as reader:
        ...     for row in reader:
        ...         print(f"{row['firstname']} {row['surname']}")


    Args:
        mode: r-reading, w-writing, a-appending, x-create-write
        buffering: 0 is forbidden, 1 is a per line buffering,
            greater 1 is byte-block buffering.
        encoding: any supported python encoding, defaults to archive
            specific encoding, or system-specific if there is no archive
            specific encoding.
        errors: Indicates the error handling for encoding errors run
            help(codecs.Codec) for more information.
        newline: indicates if line endings should be converted
    """
    self._newline = newline
    self._errors = errors
    self._encoding = encoding
    self._buffering = buffering
    self._mode = mode
    self._file = path(file)
    self._fd = None
    self._reader = None
    self._writer = None

def reader(self, dialect: str = "excel", **kwargs):
    """
    indicates that a csv-reader should be created. This method is for
    setting parameters explicit. The method is called implicitly if no
    other reader or writer is set and file opening mode is `r`

    Args:
        dialect: the dialect that is used by the reader
        kwargs: used to overwrite variables set in dialect

    Returns:
        Self
    """
    self._reader = csv.reader(self._open(), dialect=dialect, **kwargs)
    return self

def writer(self, dialect: str = "excel", **kwargs):
    """
    indicates that a csv-writer should be createad. This method is for
    setting parameters explicit. The method is called implicitly if no
    other reader or writer is set and file opening mode is `w`, `a` or `x`

    Args:
        dialect: the dialect that is used by the writer
        **kwargs: used to overwrite variables set in dialect

    Returns:
        Self
    """
    self._writer = csv.writer(self._open(), dialect=dialect, **kwargs)
    return self

def dict_reader(
    self,
    fieldnames: list[str],
    restkey: typing.Any = None,
    restval: typing.Any = None,
    dialect: str = "excel",
    **kwargs,
):
    """
    Indicates that a csv-DictReader should be created. This method is
    never called implicit.

    Args:
        fieldnames: the fieldnames of the dict, if None the first line
            of the csv-file is used.
        restkey: the key name for values that has no field-name mapping
        restval: the value used for field-names without a value
        dialect: the dialect that is used by the reader
        **kwargs: used to overwrite dialect variables

    Returns:
        Self
    """
    self._reader = csv.DictReader(
        self._open(),
        fieldnames=fieldnames,
        restkey=restkey,
        restval=restval,
        dialect=dialect,
        **kwargs,
    )
    return self

def dict_writer(
    self,
    fieldnames: list[str],
    restval: typing.Any = None,
    dialect: str = "excel",
    extrasection: typing.Literal["raise", "ignore"] = "raise",
    **kwargs,
) -> typing.Self:
    """
    Indicates that a csv-DictWriter should be created. This method is
    never called implicit.

    Args:
        fieldnames: the fieldnames of the dict, if None the first line of
            the csv file is used.
        restval: the value used for fieldnames that do not appear in the
            dict.
        dialect: the dialect that is used by the writer
        extrasection: failure strategie dict-keys that are no fieldnames
        **kwargs: used to overwrite variables set in dialect

    Returns:
        Self
    """
    self._writer = csv.DictWriter(
        self._open(),
        fieldnames=fieldnames,
        restval=restval,
        extrasaction=extrasection,
        dialect=dialect,
        **kwargs,
    )
    return self

def _open(self):
    if self._fd is not None:
        raise ValueError("Only one reader or writer is allowed.")

    self._fd = self._file.open(
        mode=self._mode,
        buffering=self._buffering,
        encoding=self._encoding,
        errors=self._errors,
        newline=self._newline,
    )
    return self._fd

def __enter__(
    self,
) -> csv.reader | csv.writer | csv.DictReader | csv.DictWriter:
    if self._reader is not None:
        return self._reader
    elif self._writer is not None:
        return self._writer
    elif "w" in self._mode or "a" in self._mode or "x" in self._mode:
        self.writer()
        return self._writer
    elif "r" in self._mode:
        self.reader()
        return self._reader
    # handle any forgotten or invalid modes
    raise RuntimeError("Please call a reader or writer constructor")

def __exit__(self, exc_type, exc_val, exc_tb):
    self._fd.close()

As pointed out by other answers, the second way is the better option. And I think that a FileContextHandle will not come in the future, because the csv-module estimates Iterator[str], not typing.IO.

However there is a lack for a csv_open, that provides full functionality and correct file-context-handling.

class csv_open:
"""
A in most cases useless class, but Stackoverflow threads have triggered me.
"""

def __init__(
    self,
    file: str | Path,
    mode: str = "rt",
    buffering: Optional[int] = 1,
    encoding: Optional[str] = None,
    errors: Optional[str] = None,
    newline: Optional[typing.Literal["", "\n", "\r", "\n\r"]] = None,
):
    """

    Notes:
        if you do not specify a reader or writer a normal csv.reader is
        created for mode r and a csv.writer is created for modes w,
        a and x.

        If you want dict-reader or dict-writer or open the file in
        reading and writing mode you have to specify the reader or writer
        explicitly.

    Examples:
        >>> with csv_open('names.csv', mode='rt') as reader:
        ...    for row in reader:
        ...        print(row)

        >>> with csv_open("names.csv", mode="rt").dict_reader(
        ...     fieldnames=['firstname', 'surname']) as reader:
        ...     for row in reader:
        ...         print(f"{row['firstname']} {row['surname']}")


    Args:
        mode: r-reading, w-writing, a-appending, x-create-write
        buffering: 0 is forbidden, 1 is a per line buffering,
            greater 1 is byte-block buffering.
        encoding: any supported python encoding, defaults to archive
            specific encoding, or system-specific if there is no archive
            specific encoding.
        errors: Indicates the error handling for encoding errors run
            help(codecs.Codec) for more information.
        newline: indicates if line endings should be converted
    """
    self._newline = newline
    self._errors = errors
    self._encoding = encoding
    self._buffering = buffering
    self._mode = mode
    self._file = path(file)
    self._fd = None
    self._reader = None
    self._writer = None

def reader(self, dialect: str = "excel", **kwargs):
    """
    indicates that a csv-reader should be created. This method is for
    setting parameters explicit. The method is called implicitly if no
    other reader or writer is set and file opening mode is `r`

    Args:
        dialect: the dialect that is used by the reader
        kwargs: used to overwrite variables set in dialect

    Returns:
        Self
    """
    self._reader = csv.reader(self._open(), dialect=dialect, **kwargs)
    return self

def writer(self, dialect: str = "excel", **kwargs):
    """
    indicates that a csv-writer should be createad. This method is for
    setting parameters explicit. The method is called implicitly if no
    other reader or writer is set and file opening mode is `w`, `a` or `x`

    Args:
        dialect: the dialect that is used by the writer
        **kwargs: used to overwrite variables set in dialect

    Returns:
        Self
    """
    self._writer = csv.writer(self._open(), dialect=dialect, **kwargs)
    return self

def dict_reader(
    self,
    fieldnames: list[str],
    restkey: typing.Any = None,
    restval: typing.Any = None,
    dialect: str = "excel",
    **kwargs,
):
    """
    Indicates that a csv-DictReader should be created. This method is
    never called implicit.

    Args:
        fieldnames: the fieldnames of the dict, if None the first line
            of the csv-file is used.
        restkey: the key name for values that has no field-name mapping
        restval: the value used for field-names without a value
        dialect: the dialect that is used by the reader
        **kwargs: used to overwrite dialect variables

    Returns:
        Self
    """
    self._reader = csv.DictReader(
        self._open(),
        fieldnames=fieldnames,
        restkey=restkey,
        restval=restval,
        dialect=dialect,
        **kwargs,
    )
    return self

def dict_writer(
    self,
    fieldnames: list[str],
    restval: typing.Any = None,
    dialect: str = "excel",
    extrasection: typing.Literal["raise", "ignore"] = "raise",
    **kwargs,
) -> typing.Self:
    """
    Indicates that a csv-DictWriter should be created. This method is
    never called implicit.

    Args:
        fieldnames: the fieldnames of the dict, if None the first line of
            the csv file is used.
        restval: the value used for fieldnames that do not appear in the
            dict.
        dialect: the dialect that is used by the writer
        extrasection: failure strategie dict-keys that are no fieldnames
        **kwargs: used to overwrite variables set in dialect

    Returns:
        Self
    """
    self._writer = csv.DictWriter(
        self._open(),
        fieldnames=fieldnames,
        restval=restval,
        extrasaction=extrasection,
        dialect=dialect,
        **kwargs,
    )
    return self

def _open(self):
    if self._fd is not None:
        raise ValueError("Only one reader or writer is allowed.")

    self._fd = self._file.open(
        mode=self._mode,
        buffering=self._buffering,
        encoding=self._encoding,
        errors=self._errors,
        newline=self._newline,
    )
    return self._fd

def __enter__(
    self,
) -> csv.reader | csv.writer | csv.DictReader | csv.DictWriter:
    if self._reader is not None:
        return self._reader
    elif self._writer is not None:
        return self._writer
    elif "w" in self._mode or "a" in self._mode or "x" in self._mode:
        self.writer()
        return self._writer
    elif "r" in self._mode:
        self.reader()
        return self._reader
    # handle any forgotten or invalid modes
    raise RuntimeError("Please call a reader or writer constructor")

def __exit__(self, exc_type, exc_val, exc_tb):
    self._fd.close()
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文