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
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.
发布评论
评论(1)
通过更改
a
中的类属性,每次对象都会在本地进行a
的更改,就有一种非常丑陋的方法来完成您想要的事情:此方法使您的示例通过,甚至可以使用嵌套上下文:
请注意,在此实施中,
上下文
是在创建时记录的,因此您可能会得到令人惊讶的行为:可以使每个实例
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:This method makes your example pass, and can even use nested contexts:
Note that, in this implementation, the
context
is recorded at the time of creation, so you may get surprising behaviour:It is possible to make every instance of
A
fetch the current context it is in, by using directlyA.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
yield
s from inside aB
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.