Context Managers in Matlab: Invoking __enter__ in

2019-05-20 10:16发布

问题:

I have a python package and I would like to use its classes and methods in Matlab. I know that this can be done directly since Matlab 2014b. I mean all you have to do is add py. in the beginning of your statements. So far so good, however, I couldn't figure out how to deal with context managers through MATLAB, which are invoked using the with statement. For instance, assume that we have the following class in a module called app.py,

class App(object):

    def __init__(self, input):
        self._input = input
        self._is_open = False

    def __enter__(self):
        self._is_open = True
        # many other stuff going after this but not relevant to this problem

In Matlab, I can call this as

app = py.app.App(input);
py.getattr(app, '_is_open')

ans =

logical

0

and I see an instance of App in my workspace. However, as expected only __init__ is invoked this way but not __enter__.

So, is there a way to invoke __enter__ from Matlab, as if we are calling it like with App(input) as app: in Python?

Note: I am using Python 3.5.1 and Matlab 2017b

回答1:

I don't believe there is any way to invoke the __enter__ method of a Python class from MATLAB, but the __exit__ method might be implicitly called (I'll address this further below).

It's important to first consider the purpose of context managers (via the __enter__ and __exit__ methods), which is to provide a way to allocate and release resources in a scope-limited fashion, whether or not that scope is exited normally or via an error. MATLAB has a more limited means of "scoping": each function has its own workspace, and control structures like loops, conditional statements, etc. within that function all share that workspace (unlike many languages in which these control structures have their own sub-scopes).

When a workspace is exited in MATLAB, the variables it contains are cleared, but any resources that were allocated may still need to be released. This can be achieved with onCleanup objects. When they are cleared from memory, they invoke a given function for managing existing resources. An example would be opening and reading from a file:

function openFileSafely(fileName)
  fid = fopen(fileName, 'r');
  c = onCleanup(@() fclose(fid));

  s = fread(fid);
  ...
end

Here, a file is opened and subsequently read from. An onCleanup object c is created that will close the file when c is cleared from memory upon exit from the function. If the file were simply closed with fclose(fid) at the end of the function, then an error exit from the function (such as during the course of reading data) would cause the file to still remain opened. Using an onCleanup object ensures that the file will be closed regardless of how the function exits. Here's an example of how this could be handled in Python:

with open('some_file', 'w') as opened_file:
    opened_file.write('Hola!')

Since MATLAB has a different means of "context management" than Python, this may explain why it's not possible to access the __enter__ method. I tried with a class I knew had one: the io.FileIO class. I first looked for help:

>> py.help('io.FileIO.__enter__')
Help on method_descriptor in io.FileIO:

io.FileIO.__enter__ = __enter__(...)

It finds some help text. It's not particularly helpful, but it's there. However, when I create an object and look at its methods list, neither __enter__ nor __exit__ (nor a clear equivalent) is there:

>> fio = py.io.FileIO('test.txt');
>> methods(fio)

Methods for class py._io.FileIO:

FileIO      eq          ge          le          read        readinto    seek        truncate    writelines  
char        fileno      gt          lt          readable    readline    seekable    writable    
close       flush       isatty      ne          readall     readlines   tell        write       

Methods of py._io.FileIO inherited from handle.

Methods for class handle:

addlistener  eq           findprop     gt           le           ne           
delete       findobj      ge           isvalid      lt           notify

I did notice something interesting when I cleared the fio object, though. While the fio object still existed (with the file open), I couldn't delete or move the file, as expected. However, after issuing the command clear fio, without first closing the file, I was able to interact with the file normally. This implies that the file was automatically closed. This makes me wonder if the __exit__ method might be getting implicitly invoked, but I have yet to determine it for certain.