Python在“with”上下文中处理参数

Python在“with”上下文中处理参数,python,python-2.7,Python,Python 2.7,假设我有一个python方法来获取p4中最后一个同步的变更列表。这个问题本身与p4无关,只是一个基本的python问题 def get_last_synced_changelist(p4port, client_name, p4 = None): with P4Connection(p4port) as p4: last_change_list = p4.run_changes("-m1", "@%s" % client_name) if last_cha

假设我有一个python方法来获取p4中最后一个同步的变更列表。这个问题本身与p4无关,只是一个基本的python问题

def get_last_synced_changelist(p4port, client_name, p4 = None):
    with P4Connection(p4port) as p4:
        last_change_list = p4.run_changes("-m1", "@%s" % client_name)
        if last_change_list:
            return last_change_list[0]["change"]
        else:
            return None
此方法的调用方可以提供作为字符串的p4port,也可以提供p4对象本身。我的要求是,如果提供了p4对象,我希望在没有with上下文的情况下运行方法体,也就是说,我不希望在p4上调用enter或exit方法。这是因为关闭/输入p4对象的责任现在由调用方承担。如果未提供p4对象,则需要在此程序中使用p4port字符串构造语法

有人能告诉我构建这种方法的最佳方式是什么吗?我想尽量少换身体。基本上

p4 or with P4Connection(p4port) as p4:
   p4.run_changes(...)

但是我不确定最好的语法是什么。

这不可能直接实现,因为with是一个复合语句,不能嵌入这样的表达式中。但您可以利用以下事实,即您的函数还支持借用资源:

def get_last_synced_changelist(p4port, client_name, p4 = None):
    if p4 is None:
        with P4Connection(p4port) as p4:
            return get_last_synced_changelist(p4port, client_name, p4)
    last_change_list = p4.run_changes("-m1", "@%s" % client_name)
    if last_change_list:
        return last_change_list[0]["change"]
    else:
        return None

如果两条路径有单独的函数,这种方法甚至可以工作,这在本例中可能是有意义的,因为当传入现有的P4Connection时,显然不使用p4port。

这不是直接可行的,with是一个复合语句,不能嵌入到这样的表达式中。但您可以利用以下事实,即您的函数还支持借用资源:

def get_last_synced_changelist(p4port, client_name, p4 = None):
    if p4 is None:
        with P4Connection(p4port) as p4:
            return get_last_synced_changelist(p4port, client_name, p4)
    last_change_list = p4.run_changes("-m1", "@%s" % client_name)
    if last_change_list:
        return last_change_list[0]["change"]
    else:
        return None

如果两条路径有单独的函数,这种方法甚至可以工作,这在本例中可能有意义,因为当传入现有的P4连接时,p4port显然不使用。

您可以创建一个虚拟上下文管理器:

import contextlib
@contextlib.contextmanager
def dummyctx(x):
    yield x

def get_last_synced_changelist(p4port, client_name, p4=None):
    if p4 is None:
        ctx = P4Connection(p4port)
    else:
        ctx = dummyctx(p4)
    with ctx as p4:
        last_change_list = p4.run_changes("-m1", "@%s" % client_name)
        if last_change_list:
            return last_change_list[0]["change"]
        else:
            return None

您可以创建虚拟上下文管理器:

import contextlib
@contextlib.contextmanager
def dummyctx(x):
    yield x

def get_last_synced_changelist(p4port, client_name, p4=None):
    if p4 is None:
        ctx = P4Connection(p4port)
    else:
        ctx = dummyctx(p4)
    with ctx as p4:
        last_change_list = p4.run_changes("-m1", "@%s" % client_name)
        if last_change_list:
            return last_change_list[0]["change"]
        else:
            return None

在Python2中,您可能需要创建自己的上下文管理器,或者像@glglgl在其答案中所示的那样创建一个虚拟的上下文管理器,或者创建一个包装可选连接创建的上下文管理器:

import contextlib

@contextlib.context_manager
def p4_context(p4port, p4=None):
    p4 is None:
        with P4Connection(p4port) as p4:
            yield p4
    else:
        yield p4

def get_last_synced_changelist(p4port, client_name, p4 = None):
    with p4_context(p4port, p4) as p4:
        last_change_list = p4.run_changes("-m1", "@%s" % client_name)
        if last_change_list:
            return last_change_list[0]["change"]
        else:
            return None
如果您使用的是Python 3.3或更高版本,则可以改用令人敬畏的新类:


在Python2中,您可能需要创建自己的上下文管理器,或者像@glglgl在其答案中所示的那样创建一个虚拟的上下文管理器,或者创建一个包装可选连接创建的上下文管理器:

import contextlib

@contextlib.context_manager
def p4_context(p4port, p4=None):
    p4 is None:
        with P4Connection(p4port) as p4:
            yield p4
    else:
        yield p4

def get_last_synced_changelist(p4port, client_name, p4 = None):
    with p4_context(p4port, p4) as p4:
        last_change_list = p4.run_changes("-m1", "@%s" % client_name)
        if last_change_list:
            return last_change_list[0]["change"]
        else:
            return None
如果您使用的是Python 3.3或更高版本,则可以改用令人敬畏的新类:


dummyctx相当优雅。它是可重用的,我可以称之为no_op_ctx_mgr或类似的东西,它感觉像一个空对象设计模式dummyctx非常优雅。它是可重用的,我可以称之为no_op_ctx_mgr或类似的东西,它感觉像一个空对象设计模式