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()
속성:
- 그것은 전화하지 않습니다.
get_stuff()
이 한 - 조건이 거짓이면 더미 컨텍스트 관리자를 제공합니다.(아마도 로 대체될 수 있음)
contextlib.nullcontext
python =의 에는 >= 3.7) - 선택적으로 조건이 잘못된 경우 대체 컨텍스트 관리자를 보낼 수 있습니다.
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가지 조건이 필요합니다.
NameError: name 'b' is not defined
정의되지 않은 경우a
- 라는
foo
가능한 를 반환해야 . 그렇지 않으면: " 여히입가력개반합환야니해다를체능전한,▁still합니다▁enter". 그렇지 않은 경우:AttributeError: 'NoneType' object has no attribute '__enter__'
언급URL : https://stackoverflow.com/questions/27803059/conditional-with-statement-in-python
'programing' 카테고리의 다른 글
산점도:산란을 위한 lib 색상 막대 (0) | 2023.07.25 |
---|---|
AJAX 요청의 내용 유형 및 데이터 유형은 무엇입니까? (0) | 2023.07.25 |
로컬 도커에서 호스팅되는 mariadb에 연결하는 스프링 부트 (0) | 2023.07.25 |
봄 부츠 조다 데이트 시간 연재 (0) | 2023.07.25 |
CMAKE_BUILD_TYPE= Release가 -DN DEBUG를 의미합니까? (0) | 2023.07.25 |