programing

Python의 조건부 문장 포함)

muds 2023. 7. 25. 21:22
반응형

Python의 조건부 문장 포함)

조건부로 with 문으로 코드 블록을 시작할 수 있는 방법이 있습니까?

다음과 같은 것:

if needs_with():
    with get_stuff() as gs:

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

명확히 하기 위해, 한 시나리오는 with 문에 블록을 포함하는 반면, 다른 시나리오는 동일하지만 포함되지 않는 블록(즉, 들여놓지 않은 것처럼)입니다.

물론 초기 실험에서는 들여쓰기 오류가 발생합니다.

Python 3.3 이상

Python 3.3은 이러한 상황을 위해 도입되었습니다.필요에 따라 컨텍스트 관리자를 추가하는 "스택"을 제공합니다.이 경우 다음을 수행할 수 있습니다.

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는 자동으로 자로입다니으동▁automatic입다가 됩니다.exit마에에드막지의 에 있는 .with하지 않으면 되지 (아무 것도 입력되지 않은 경우에는 문제가 없습니다.)에서는 이예서는다같이반환다니됩이 하는 것은 .get_stuff()이라exit자동으로 편집합니다.

이전 버전의 python을 사용해야 하는 경우 모듈을 사용할 수 있지만, 이는 표준이 아닙니다.이 기능과 다른 기능을 이전 버전의 파이썬으로 지원합니다.이 방법을 사용하면 조건부 가져오기도 수행할 수 있습니다.


Python 3.7 이상

Python 3.7은 추가로 소개되었습니다(이 답변이 처음 게시된 지 몇 년 후, 그리고 그 이후로 여러 다른 답변에 언급되었습니다).@Kache는 코멘트에서 이 옵션의 가장 우아한 사용법을 지적합니다.

from contextlib import nullcontext

with get_stuff() if needs_with() else nullcontext() as gs:
    # do nearly the same large block of stuff,
    # involving gs or not, depending on needs_with()

: 만약인 경우 하세요.needs_with()이라False,그리고나서gs▁▁be 될 것입니다.None컨텍스트 블록 내부에 있습니다.네가 원한다면gs되려고something_else그 경우, 당신은 그냥 대체합니다.nullcontext()와 함께nullcontext(something_else).

이 접근 방식은 분명히 다음과 같이 유연하지 않습니다.ExitStack이고, 반면에 하면이단이선때진택기문다이이반에면지것왜은냐▁because, 에▁this,,반▁choice면▁is▁just▁binary.ExitStack하여 수있다니습할추를 최대한 할 수 .exit복잡한 논리 등으로 원하는 대로 일을 처리합니다.하지만 이것은 OP의 간단한 요구 사항에 확실히 부합합니다.

당신이 코드 3의 Python을 .contextlib.nullcontext(소개됨또경는우)또▁3경는) ▁3 3.3(우)소▁was됨▁introduced▁or)▁evencontextlib.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().

이후 버전에서 수행할 수 있는 작업은 Mike의 답변 또는 Daniel의 답변참조하십시오.

3부터는 Python 3.7을 할 수 .contextlib.nullcontext:

from contextlib import nullcontext

if needs_with():
    cm = get_stuff()
else:
    cm = nullcontext()

with cm as gs:
    # Do stuff

contextlib.nullcontext거의 무작업 상황 관리자일 뿐입니다.당신은 그것이 양보할 것이라는 주장을 전달할 수 있습니다, 만약 당신이 그 이후에 존재하는 것에 의존한다면.as:

>>> with nullcontext(5) as value:
...     print(value)
...
5

그렇지 않으면 그냥 돌아올 것입니다.None:

>>> with nullcontext() as value:
...     print(value)
...
None

매우 깔끔합니다. 여기에서 문서를 확인하십시오. https://docs.python.org/3/library/contextlib.html#contextlib.nullcontext

이를 위한 타사 옵션:
https://pypi.python.org/pypi/://pypi.python.org/pypi/conditional

from conditional import conditional

with conditional(needs_with(), get_stuff()):
    # do stuff
import contextlib

my_context = None # your context
my_condition = False # your condition

# Option 1 (Recommended)
with my_context if my_condition else contextlib.nullcontext():
    print('hello 1')

# Option 2
with my_context if my_condition else contextlib.ExitStack():
    print('hello 2')

사용할 수 있습니다.contextlib.nested를 하나의 하는 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

@farsil의 멋진 Python 3.3 원라이너를 찾는 것은 어려웠기 때문에, 다음은 자체 답변입니다.

with ExitStack() if not needs_with() else get_stuff() as gs:
     # do stuff

, 않으면 ExitStack이 먼저 . 그렇지 않으면get_stuff()평가됩니다.

그래서 이 코드를 만들었습니다.다음과 같이 호출됩니다.

with c_with(needs_with(), lambda: get_stuff()) as gs:
    ##DOESN't call get_stuff() unless needs_with is called.
    # do nearly the same large block of stuff,
    # involving gs or not, depending on needs_with()

속성:

  1. 그것은 전화하지 않습니다.get_stuff()이 한
  2. 조건이 거짓이면 더미 컨텍스트 관리자를 제공합니다.(아마도 로 대체될 수 있음)contextlib.nullcontextpython =의 에는 >= 3.7)
  3. 선택적으로 조건이 잘못된 경우 대체 컨텍스트 관리자를 보낼 수 있습니다.
    with c_with(needs_with(), lambda: get_stuff(), lambda: dont_get_stuff()) as gs:

이것이 누군가에게 도움이 되기를 바랍니다!

코드는 다음과 같습니다.

def call_if_lambda(f):
    """
    Calls f if f is a lambda function.
    From https://stackoverflow.com/a/3655857/997253
    """
    LMBD = lambda:0
    islambda=isinstance(f, type(LMBD)) and f.__name__ == LMBD.__name__
    return f() if islambda else f
import types
class _DummyClass(object):
    """
    A class that doesn't do anything when methods are called, items are set and get etc.
    I suspect this does not cover _all_ cases, but many.
    """
    def _returnself(self, *args, **kwargs):
        return self
    __getattr__=__enter__=__exit__=__call__=__getitem__=_returnself
    def __str__(self):
        return ""
    __repr__=__str__
    def __setitem__(*args,**kwargs):
        pass
    def __setattr__(*args,**kwargs):
        pass

class c_with(object):
    """
    Wrap another context manager and enter it only if condition is true.
    Parameters
    ----------
    condition:  bool
        Condition to enter contextmanager or possibly else_contextmanager
    contextmanager: contextmanager, lambda or None
        Contextmanager for entering if condition is true. A lambda function
        can be given, which will not be called unless entering the contextmanager.
    else_contextmanager: contextmanager, lambda or None
        Contextmanager for entering if condition is true. A lambda function
        can be given, which will not be called unless entering the contextmanager.
        If None is given, then a dummy contextmanager is returned.
    """
    def __init__(self, condition, contextmanager, else_contextmanager=None):
        self.condition = condition
        self.contextmanager = contextmanager
        self.else_contextmanager = _DummyClass() if else_contextmanager is None else else_contextmanager
    def __enter__(self):
        if self.condition:
            self.contextmanager=call_if_lambda(self.contextmanager)
            return self.contextmanager.__enter__()
        elif self.else_contextmanager is not None:
            self.else_contextmanager=call_if_lambda(self.else_contextmanager)
            return self.else_contextmanager.__enter__()
    def __exit__(self, *args):
        if self.condition:
            return self.contextmanager.__exit__(*args)
        elif self.else_contextmanager is not None:
            self.else_contextmanager.__exit__(*args)

#### EXAMPLE BELOW ####

from contextlib import contextmanager

def needs_with():
    return False

@contextmanager
def get_stuff():
    yield {"hello":"world"}

with c_with(needs_with(), lambda: get_stuff()) as gs:
    ## DOESN't call get_stuff() unless needs_with() returns True.
    # do nearly the same large block of stuff,
    # involving gs or not, depending on needs_with()
    print("Hello",gs['hello'])

함수의 문으로 본문을 캡슐화합니다.

다른 답에서 제안한 것처럼 모든 종류의 복잡한 묘기를 할 수 있지만, 이것들은 읽기 어렵고 불필요한 복잡성을 더합니다.선택 사항의 본문을 캡슐화하면 프로그램 구조를 따라가기가 더 쉬워집니다.with블록 자체의 기능을 수행합니다.예:

def do_stuff(a, *args, **kwargs):
    ...
    return a

a = 1
gs = "already_present_stuff"
if needs_with():
    with get_stuff() as gs:
        a = do_stuff(a, gs=gs)
else:
    a = do_stuff(a, gs=gs)

인키인수달수에 전달할 인수와 do_stuff그리고 당신이 반환하는 것은 전적으로 당신에게 달려 있습니다.에서액세는모전합니다달야해목항을 안에 있는 모든 것을 .do_stuff변경한 모든 이름과 새로 작성한 모든 이름을 반환합니다.

이러한 방식으로 작업을 수행하는 것은 스파게티 코드를 방지하는 좋은 방법이며, 따라서 해당되는 경우 pylint(함수 또는 메소드당 줄 수가 너무 많음) 또는 휴먼 코드 검토자의 불만을 피할 수 있습니다.

네, ▁where있 경우가 있습니다.contextlib필요하지만 대부분의 경우 기능을 사용하는 것이 방법입니다.

저는 @비엔트로피 답변이 불완전하다는 것을 발견했습니다.

from conditional import conditional

a = 1 # can be None

if not a is None:
  b = 1

class WithNone:
  def __enter__(self):
    return self
  def __exit__(self, type, value, tb):
    pass

def foo(x):
  print(x)
  return WithNone()

with conditional(not a is None, foo(b) if not a is None else None):
  print(123)

한 결된완된▁theconditional다음과 같은 이유로 1이 아닌 3가지 조건이 필요합니다.

  1. NameError: name 'b' is not defined정의되지 않은 경우a
  2. 라는 foo가능한 를 반환해야 . 그렇지 않으면: " 여히입가력개반합환야니해다를체능전한,▁still합니다▁enter". 그렇지 않은 경우:AttributeError: 'NoneType' object has no attribute '__enter__'

언급URL : https://stackoverflow.com/questions/27803059/conditional-with-statement-in-python

반응형