通过上下文管理器调整第二类的行为

发布于 2025-02-12 23:25:56 字数 1400 浏览 0 评论 0原文

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

拔了角的鹿 2025-02-19 23:25:56

通过更改a中的类属性,每次对象都会在本地进行a的更改,就有一种非常丑陋的方法来完成您想要的事情:

class A:
    additional_amount = 0

    def __init__(self, amount):
        self.amount = amount
        self.local_additional_amount = A.additional_amount  # Remember the "context" at creation time

    def __add__(self, other):
        return self.amount + other.amount + self.local_additional_amount


class B:
    def __init__(self, added_amount):
        self.added_amount = added_amount
        self.old_additional_amount = A.additional_amount  # Could be in `__enter__`, for a slightly different behaviour

    def __enter__(self):
        # Change the additional amount for A's created inside this context
        A.additional_amount = self.added_amount
        return self

    def __exit__(self, exc_type, exc_value, exc_traceback):
        # Restore As original amount, so we can nest it:
        A.additional_amount = self.old_additional_amount

此方法使您的示例通过,甚至可以使用嵌套上下文:

if __name__ == '__main__':
    # Test default behaviour:
    assert (A(2) + A(3)) == 5

    # Simple context (example requested by OP):
    with B(1000):
        assert (A(1) + A(5)) == 1006

    # Test default behaviour is restored:
    assert (A(1) + A(5)) == 6

    # Nested contexts work
    with B(1000):
        with B(2000):
            assert (A(1) + A(5)) == 2006
        # Out of the '2000' context
        assert (A(1) + A(5)) == 1006
    assert (A(1) + A(5)) == 6

请注意,在此实施中,上下文是在创建时记录的,因此您可能会得到令人惊讶的行为:

    with B(1000):
        a = A(1)
    with B(2000):
        b = A(1)
    assert (a + b) == 1002  # `a` adds 1000
    assert (b + a) == 2002  # but `b` adds 2000

可以使每个实例a获取当前上下文是在__添加__方法中,通过直接使用a.additional_amount

无论如何,请,不要将其用作语言难题。这是非常令人惊讶的行为,并且违背了“显式胜于隐式”的python哲学。由于它基本上使用了一个全局变量,因此它将在任何类型的并发或上下文切换案例中破裂(例如,在yarts s内部的函数中。

顺便说一句,某些语言实现了与您想要的非常相似的功能。例如,看看上下文参数例如,在Scala中。

There is a very ugly way to do what you want, by changing a class attribute in A that is copied locally every time an object is instantiated:

class A:
    additional_amount = 0

    def __init__(self, amount):
        self.amount = amount
        self.local_additional_amount = A.additional_amount  # Remember the "context" at creation time

    def __add__(self, other):
        return self.amount + other.amount + self.local_additional_amount


class B:
    def __init__(self, added_amount):
        self.added_amount = added_amount
        self.old_additional_amount = A.additional_amount  # Could be in `__enter__`, for a slightly different behaviour

    def __enter__(self):
        # Change the additional amount for A's created inside this context
        A.additional_amount = self.added_amount
        return self

    def __exit__(self, exc_type, exc_value, exc_traceback):
        # Restore As original amount, so we can nest it:
        A.additional_amount = self.old_additional_amount

This method makes your example pass, and can even use nested contexts:

if __name__ == '__main__':
    # Test default behaviour:
    assert (A(2) + A(3)) == 5

    # Simple context (example requested by OP):
    with B(1000):
        assert (A(1) + A(5)) == 1006

    # Test default behaviour is restored:
    assert (A(1) + A(5)) == 6

    # Nested contexts work
    with B(1000):
        with B(2000):
            assert (A(1) + A(5)) == 2006
        # Out of the '2000' context
        assert (A(1) + A(5)) == 1006
    assert (A(1) + A(5)) == 6

Note that, in this implementation, the context is recorded at the time of creation, so you may get surprising behaviour:

    with B(1000):
        a = A(1)
    with B(2000):
        b = A(1)
    assert (a + b) == 1002  # `a` adds 1000
    assert (b + a) == 2002  # but `b` adds 2000

It is possible to make every instance of A fetch the current context it is in, by using directly A.additional_amount in the __add__ method.

In any case, please, never use it as anything more than a language puzzle. It is very surprising behaviour and goes against the Python philosophy of "Explicit is better than Implicit". As it uses essentially a global variable, it will break badly in any kind of concurrency or context switching cases (such as in a function that yields from inside a B context).

As an aside, some languages implement a feature very similar to what you want. Have a look at context parameters in Scala, for example.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文