我能在python类方法中“检测”切片表达式吗?

2024-04-26 12:25:39 发布

您现在位置:Python中文网/ 问答频道 /正文

我正在开发一个应用程序,其中我定义了一个“variable”对象,其中包含numpy数组形式的数据。这些变量链接到(netcdf)数据文件,我希望在需要时动态加载变量值,而不是在开始时从有时很大的文件中加载所有数据。在

下面的代码片段演示了该原理,并且运行良好,包括使用切片访问数据部分。例如,您可以写下:

a = var()   # empty variable
print a.values[7]   # values have been automatically "loaded"

甚至:

^{2}$

但是,这段代码仍然迫使我一次加载整个变量数据。Netcdf(使用netCDF4库)将允许我直接访问文件中的数据片。示例:

f = netCDF4.Dataset(filename, "r")
print f.variables["a"][7]

我不能直接使用netcdf变量对象,因为我的应用程序绑定到一个不能记住netcdf文件处理程序的web服务上,而且还因为变量数据并不总是来自netcdf文件,而是可能来自其他来源,如ogcweb服务。在

有没有办法“捕捉”属性或setter方法中的切片表达式并使用它们?我们的想法是写一些类似的东西:

    @property
    def values(self):
        if self._values is None:
            self._values = np.arange(10.)[slice]  # load from file ...
        return self._values

而不是下面的代码。在

工作演示:

import numpy as np

class var(object):

    def __init__(self, values=None, metadata=None):
        if values is None:
            self._values = None
        else:
            self._values = np.array(values)
        self.metadata = metadata  # just to demonstrate that var has mor than just values

    @property
    def values(self):
        if self._values is None:
            self._values = np.arange(10.)  # load from file ...
        return self._values

    @values.setter
    def values(self, values):
        self._values = values

第一个想法:我是否应该将值作为一个单独的类创建,然后使用__getitem__?见In python, how do I create two index slicing for my own matrix class?


Tags: 文件数据代码selfnone应用程序ifis
2条回答

多亏了Martijn Pieters的指导和更多的阅读,我想出了以下代码作为演示。注意Reader类使用netcdf文件和netCDF4库。如果您想自己尝试这段代码,您需要一个带有变量“a”和“b”的netcdf文件,或者将Reader替换为将返回数据数组或数据数组切片的其他文件。在

这个解决方案定义了三个类:Reader执行实际的文件I/O处理,Values管理数据访问部分,并在内存中没有存储数据时调用Reader实例,var是最终的“变量”,在实际生活中它将包含更多的元数据。代码包含两个额外的打印语句,用于教育目的。在

"""Implementation of a dynamic variable class which can read data from file when needed or
return the data values from memory if they were read already. This concepts supports
slicing for both memory and file access.""" 

import numpy as np
import netCDF4 as nc

FILENAME = r"C:\Users\m.schultz\Downloads\data\tmp\MACC_20141224_0001.nc"
VARNAME = "a"


class Reader(object):
    """Implements the actual data access to variable values. Here reading a
    slice from a netcdf file.
    """

    def __init__(self, filename, varname):
        """Final implementation will also have to take groups into account...
        """
        self.filename = filename
        self.varname = varname

    def read(self, args=slice(None, None, None)):
        """Read a data slice. Args is a tuple of slice objects (e.g.
        numpy.index_exp). The default corresponds to [:], i.e. all data
        will be read.
        """
        with nc.Dataset(self.filename, "r") as f:
            values = f.variables[self.varname][args]
        return values


class Values(object):

    def __init__(self, values=None, reader=None):
        """Initialize Values. You can either pass numerical (or other) values,
        preferrably as numpy array, or a reader instance which will read the
        values on demand. The reader must have a read(args) method, where
        args is a tuple of slices. If no args are given, all data should be
        returned.
        """
        if values is not None:
            self._values = np.array(values)
        self.reader = reader

    def __getattr__(self, name):
        """This is only be called if attribute name is not present.
        Here, the only attribute we care about is _values.
        Self.reader should always be defined.
        This method is necessary to allow access to variable.values without
        a slicing index. If only __getitem__ were defined, one would always
        have to write variable.values[:] in order to make sure that something
        is returned.
        """
        print ">>> in __getattr__, trying to access ", name
        if name == "_values":
            print ">>> calling reader and reading all values..."
            self._values = self.reader.read()
        return self._values

    def __getitem__(self, args):
        print "in __getitem__"
        if not "_values" in self.__dict__:
            values = self.reader.read(args)
            print ">>> read from file. Shape = ", values.shape
            if args == slice(None, None, None):
                self._values = values  # all data read, store in memory
            return values
        else:
            print ">>> read from memory. Shape = ", self._values[args].shape
            return self._values[args]

    def __repr__(self):
        return self._values.__repr__()

    def __str__(self):
        return self._values.__str__()


class var(object):

    def __init__(self, name=VARNAME, filename=FILENAME, values=None):
        self.name = name
        self.values = Values(values, Reader(filename, name))


if __name__ == "__main__":
    # define a variable and access all data first
    # this will read the entire array and save it in memory, so that
    # subsequent access with or without index returns data from memory
    a = var("a", filename=FILENAME)
    print "1: a.values = ", a.values
    print "2: a.values[-1] = ", a.values[-1]
    print "3: a.values = ", a.values
    # define a second variable, where we access a data slice first
    # In this case the Reader only reads the slice and no data are stored
    # in memory. The second access indexes the complete array, so Reader
    # will read everything and the data will be stored in memory.
    # The last access will then use the data from memory.
    b = var("b", filename=FILENAME)
    print "4: b.values[0:3] = ", b.values[0:3]
    print "5: b.values[:] = ", b.values[:]
    print "6: b.values[5:8] = ",b.values[5:8]

不,您无法检测从.values返回后将对对象执行的操作。结果可以存储在一个变量中,并且只能(稍后)切片,或者在不同的位置切片,或者整体使用等等

实际上,您应该返回一个包装器对象并钩住^{};这样可以根据需要检测切片并加载数据。切片时,Python传递一个^{} object。在

相关问题 更多 >