Оператор Python с выражением, основанный на условии [duplicate]

NullPointerException s - исключения, возникающие при попытке использовать ссылку, которая указывает на отсутствие местоположения в памяти (null), как если бы она ссылалась на объект. Вызов метода по нулевой ссылке или попытка получить доступ к полю нулевой ссылки вызовет функцию NullPointerException. Они наиболее распространены, но другие способы перечислены на странице NullPointerException javadoc.

Вероятно, самый быстрый пример кода, который я мог бы придумать для иллюстрации NullPointerException, be:

public class Example {

    public static void main(String[] args) {
        Object obj = null;
        obj.hashCode();
    }

}

В первой строке внутри main я явно устанавливаю ссылку Object obj равной null. Это означает, что у меня есть ссылка, но она не указывает на какой-либо объект. После этого я пытаюсь обработать ссылку так, как если бы она указывала на объект, вызывая метод на нем. Это приводит к NullPointerException, потому что нет кода для выполнения в местоположении, на которое указывает ссылка.

(Это техничность, но я думаю, что она упоминает: ссылка, которая указывает на null, равна 't то же, что и указатель C, указывающий на недопустимую ячейку памяти. Нулевой указатель буквально не указывает на в любом месте , который отличается от указаний на местоположение, которое оказывается недопустимым.)

36
задан nicole 6 January 2015 в 18:26
поделиться

4 ответа

Если вы хотите избежать дублирования кода и используете версию Python до 3.3 (где contextlib.ExitStack недоступен), вы можете сделать что-то вроде:

class dummy_context_mgr():
    def __enter__(self):
        return None
    def __exit__(self, exc_type, exc_value, traceback):
        return False

или:

import contextlib

@contextlib.contextmanager
def dummy_context_mgr():
    yield None

, а затем использовать его как:

with get_stuff() if needs_with() else dummy_context_mgr() as gs:
   # do stuff involving gs or not

Вы также можете заставить get_stuff() возвращать разные вещи на основе needs_with().

33
ответ дан jamesdlin 19 August 2018 в 06:59
поделиться
  • 1
    Этот менеджер контекста должен находиться в стандартной библиотеке python, imho. Спасибо за это. – jjmontes 21 January 2016 в 19:37
  • 2
    Как насчет использования имен should ... и не . Тогда подобные утверждения будут читать with get_stuff() if should_get_stuff() else dont() as gs:? – Riaz Rizvi 25 March 2016 в 16:12
  • 3
    @RiazRizvi Я бы так не назвал его так; Я использовал имена из вопроса. – jamesdlin 25 March 2016 в 19:01
  • 4
    @jjmontes contextlib.ExitStack (новый python 3.3) может использоваться как менеджер контекстного контекста. – Bryce Guinta 14 May 2017 в 02:48

Сторонний вариант для достижения именно этого: https://pypi.python.org/pypi/conditional

from conditional import conditional

with conditional(needs_with(), get_stuff()):
    # do stuff
7
ответ дан Anentropic 19 August 2018 в 06:59
поделиться
  • 1
    Поддерживает ли это предложение as ... в конце инструкции with? – Craig McQueen 9 December 2016 в 01:40
  • 2
    глядя на источник ... да, да. with conditional(needs_with(), get_stuff()) as stuff: даст вам ссылку на менеджер контекста get_stuff() (если и только если условие выполнено, в противном случае вы получите None) – Anentropic 9 December 2016 в 12:27

Python 3.3 представил contextlib.ExitStack только для такой ситуации. Он дает вам «стек», к которому вы добавляете менеджеров контекста по мере необходимости. В вашем случае вы сделаете следующее:

from contextlib import ExitStack

with ExitStack() as stack:
    if needs_with():
        gs = stack.enter_context(get_stuff())

    # do nearly the same large block of stuff,
    # involving gs or not, depending on needs_with()

Все, что введено в stack, автоматически exit ed в конце инструкции with, как обычно. (Если ничего не введено, это не проблема.) В этом примере все, что было возвращено get_stuff(), автоматически exit.

Если вам нужно использовать более раннюю версию python, вы можете иметь возможность использовать модуль contextlib2 , хотя это не является стандартным. Он поддерживает эту и другие функции в более ранних версиях python. Вы можете даже сделать условный импорт, если вам нравится этот подход.

31
ответ дан Mike 19 August 2018 в 06:59
поделиться
  • 1
    +1, это должен быть выбранный ответ. Как указано здесь , он предназначен для решения этой проблемы. Кроме того, он может использоваться как отличный однострочный: with get_stuff() if needs_with() else ExitStack() as gs. – farsil 20 February 2017 в 15:14
  • 2
    @ AnthonySottile Я думал, это то, что я сказал. – Mike 3 July 2017 в 16:51
  • 3
    derp, я полностью пропустил это! / me удаляет – Anthony Sottile 3 July 2017 в 17:16

Вы можете использовать contextlib.nested, чтобы поставить 0 или более менеджеров контекста в один оператор with.

>>> import contextlib
>>> managers = []
>>> test_me = True
>>> if test_me:
...     managers.append(open('x.txt','w'))
... 
>>> with contextlib.nested(*managers):                                                       
...  pass                                                    
...                                                             
>>> # see if it closed
... managers[0].write('hello')                                                                                                                              
Traceback (most recent call last):                              
  File "<stdin>", line 2, in <module>                                   
ValueError: I/O operation on closed file

Это решение имеет свои причуды, и я просто заметил, что с 2,7 его устарело , Я написал свой собственный менеджер контекстов для управления несколькими контекстными менеджерами. До сих пор это работало на меня, но я не рассматривал крайние условия

class ContextGroup(object):
    """A group of context managers that all exit when the group exits."""

    def __init__(self):
        """Create a context group"""
        self._exits = []

    def add(self, ctx_obj, name=None):
        """Open a context manager on ctx_obj and add to this group. If
        name, the context manager will be available as self.name. name
        will still reference the context object after this context
        closes.
        """
        if name and hasattr(self, name):
            raise AttributeError("ContextGroup already has context %s" % name)
        self._exits.append(ctx_obj.__exit__)
        var = ctx_obj.__enter__()
        if name:
            self.__dict__[name] = var

    def exit_early(self, name):
        """Call __exit__ on named context manager and remove from group"""
        ctx_obj = getattr(self, name)
        delattr(self, name)
        del self._exits[self._exits.index(ctx_obj)]
        ctx_obj.__exit__(None, None, None)

    def __enter__(self):
        return self

    def __exit__(self, _type, value, tb):
        inner_exeptions = []
        for _exit in self._exits:
            try:
                _exit(_type, value, tb )
            except Exception, e:
                inner_exceptions.append(e)
        if inner_exceptions:
            r = RuntimeError("Errors while exiting context: %s" 
                % (','.join(str(e)) for e in inner_exceptions))

    def __setattr__(self, name, val):
        if hasattr(val, '__exit__'):
            self.add(val, name)
        else:
            self.__dict__[name] = val
4
ответ дан tdelaney 19 August 2018 в 06:59
поделиться
  • 1
    Как я упоминал в своем ответе, python 3.3 добавил contextlib.ExitStack, что, похоже, очень сильно влияет на ваш ContextGroup. Я скажу, что я немного удивлен, что он не был обращен, но если вы захотите потребовать python & gt; = 3.3, это может быть хорошей надежной альтернативой для вас. – Mike 14 January 2016 в 21:02
  • 2
    contextlib2 представляет собой пакет pypi, который передал ExitStack в python 2 – Anthony Sottile 3 July 2017 в 16:27
Другие вопросы по тегам:

Похожие вопросы: