Приобретение ресурсов - это инициализация, в Python - PullRequest
5 голосов
/ 09 мая 2019

Я новичок в Python.Я пришел из C ++.

В некоторых обзорах кода у меня было несколько пиров, которые хотели, чтобы я переместил вещи из init и del в метод start и stop.В большинстве случаев это идет вразрез с RAII, который был вбит в мою голову десятилетиями C ++.

https://en.wikipedia.org/wiki/Resource_acquisition_is_initialization

Разве RAII не вещь в Python?Разве это не должно быть?

В конце концов, мы можем выбросить исключения, и мы бы хотели освободить ресурсы, когда мы это сделаем, нет?

Если это не так.Может ли кто-то дать некоторое представление о том, почему все по-другому?Есть ли какая-то языковая функция, которую я не понимаю?

, если у меня есть:

class Poop:
    def __init__:
        # Get some Windows Resource
    def __del__:
        #Release some Windows Resource

def foo():
    poop = Poop()
    raise Exception("Poop happens")

Ресурс Windows выпущен, верно?

Ответы [ 2 ]

8 голосов
/ 09 мая 2019

RAII работает в C ++, потому что уничтожение детерминировано.

В таких языках, как Python, ваш объект может теоретически никогда не уничтожиться, даже если вы вызовете del для него .

В любом случае, идиоматический способ обработки ресурсов в Python - не с RAII и не с start / stop, а с менеджерами контекста .

Простейшим примеромс файловым объектом:

with open('this_file.txt') as f:
    #  ... do stuff with f ...

# ... back to code that doesn't touch f ...

Оператор with является более или менее блоком try-finally, который создает ресурс и обеспечивает его очистку по окончании блока;что-то вроде этого:

try:
    f = open('this_file.txt')
    #  ... do stuff with f ...

finally:
    f.close()

# ... back to code that doesn't touch f ...

Я не знаю Java, но я считаю, что JVM также использует сборку мусора, и аналогично try-finally - это идиома для управления ресурсами в Java.

В любом случае, оператор with принимает менеджер контекста , который является экземпляром класса, определяющего методы __enter__ и __exit__ (см. docs ).

Для полноты картины могут быть случаи, когда вам нужен менеджер контекста, но вы не хотите определять целый класс только для этого.В этом случае contextlib может помочь .

Рабочий пример;скажем, у вас есть ресурс:

class Resource:

    def method(self):
        pass

get_resource = Resource
release_resource = lambda x: None

RAII-подобный класс может выглядеть примерно так:

class RAIILike:

    def __init__(self):
        self.resource = get_resource()

    def __del__(self):
        release_resource(self.resource)

    def do_complex_thing(self):
        #  do something complex with resource
        pass

raii_thingy = RAIILike()

И вы будете использовать этот ресурс следующим образом:

raii_thingy.resource.method()

С другой стороны, управляемый контекстом ресурс может выглядеть следующим образом ...

class ContextManagedResource:

    def __enter__(self):
        self._resource = get_resource()
        return self._resource

    def __exit__(self, exc_type, exc_value, traceback):
        if exc_type is not None:
            #  handle exception here
            pass

        else:
            pass

        release_resource(self._resource)
        return True

... и использоваться следующим образом:

with ContextManagedResource() as res:
    res.method()

Однажды with блок заканчивается, ресурс будет автоматически освобожден, независимо от того, был ли объект, который его получил, был сборщиком мусора .

0 голосов
/ 09 мая 2019

Ваша собственная ссылка на википедию гласит:

Perl, Python (в реализации CPython) и PHP управляют временем жизни объекта путем подсчета ссылок, что позволяет использовать RAII.Объекты, на которые больше нет ссылок, немедленно уничтожаются или завершаются и освобождаются, поэтому деструктор или финализатор могут освободить ресурс в это время.Тем не менее, это не всегда идиоматично в таких языках, и особенно не рекомендуется в Python (в пользу менеджеров контекста и финализаторов из пакета слабых ссылок).

...