【发布时间】:2015-09-13 20:35:51
【问题描述】:
有什么办法:
-
以一种不会扼杀 Python 程序员期望的方式覆盖赋值?
port = IntContainer(80) port = 8080 # set the IntContainer contents to 8080 # instead of changing what the port name references # from the IntContainer(80) to int(8080) -
重写丰富的比较器,以便二进制比较运算符的参数顺序无关紧要?
port = IntContainer(80) port < 80 # False 80 > port # TypeError
@BrenBarn:
什么版本的 Python?我正在使用 3.4.3:
class Foo(object):
def __lt__(self, other):
return self.value < other
def __init__(self, other):
self.value = other
foo = Foo(1)
foo < 1 # False
1 > foo # raise TypeError()
为什么我想要这个:
- 我想要一个动态的 HTTP 服务器进程配置对象。对某个 URL 的某个控制端口的 PUT 和 POST 请求应该更新配置对象,并让它传播。
- 我认为轮询配置对象不是一个好主意,尤其是在并发上下文中。对于可组合性,你在哪里锁定而不会有死锁的风险?如果配置对象共享一个全局锁(这对于配置的预期突变率来说很好),每次读取仍然获取所述锁。如果每条
logging消息都需要设置Handler级别,那显然很糟糕。 - 因此,我想要一个发布-订阅配置对象。这允许配置用户在本地缓存和更新 pub 上的缓存值。这具有没有锁的并发安全的额外好处 - 发布消息始终可以在内部保持一致。 (构图需要注意,但还不错。)
我已经为 list 和 dict 容器配置类型编写了代码。我使用blinker lib 发出信号。这些对象很聪明;信号从叶子向上传播到包含它们的任何对象(不是树木或森林,因为它们是可重新组合的)。每个都向他们的订阅者广播,其中包括其他容器配置,以及实际的用户订阅者。例如:
def receive_a(signal):
print('Object 'a' received {} event at path {}; prev: {!r}, curr: {!r}'.format(
signal.type, signal.path, signal.prev, signal.curr
))
config_dict_a = ConfigDict()
config_dict_a.connect(receive_a)
def receive_b(signal):
print('Object 'b' received {} event at path {}; prev: {!r}, curr: {!r}'.format(
signal.type.name, signal.path, signal.prev, signal.curr
))
config_dict_b = ConfigDict()
config_dict_b.connect(receive_b)
config_dict_a['b'] = config_dict_b
# Object 'a' received INSERT event at path ('b',); prev: None, curr: {}
# Object 'a' received UPDATE event at path (); prev: {}, curr: {'b': {}}
config_dict_b['foo'] = 42
# Object 'b' received INSERT event at path ('foo',); prev: None, curr: 42
# Object 'b' received UPDATE event at path (); prev: {}, curr: {'foo': 42}
# Object 'a' received INSERT event at path ('b', 'foo'); prev: None, curr: 42
# Object 'a' received UPDATE event at path ('b',); prev: {}, curr: {'foo': 42}
# Object 'a' received UPDATE event at path (); prev: {'b': {}}, curr: {'b': {'foo': 42}}
del config_a['b']['foo']
# Object 'b' received DELETE event at path ('foo',); prev: 42, curr: None
# Object 'b' received UPDATE event at path (); prev: {'foo': 42}, curr: {}
# Object 'a' received DELETE event at path ('b', 'foo',); prev: 42, curr: None
# Object 'a' received UPDATE event at path ('b',); prev: {'foo': 42}, curr: {}
# Object 'a' received UPDATE event at path (); prev: {'b': {'foo': 42}}, curr: {'b': {}}
只需订阅您想要的,并聆听您想要的确切路径。当有子服务或工作人员时,这对于在 Python 进程级别进行配置特别有用。该进程可以分割其配置,重组为特定于每个组成部分的新根配置,并将其交给各个部分供他们订阅。
从低级消息组合高级消息的安全性只需要本地锁,而不需要全局锁。
问题是什么:
list 和dict 作为容器类型,本质上是一种引用类型——这就是它们可变的原因。我的Config<Container> 对象可以从外部读取,就像您在任何东西上__getitem__ 一样。它们扩展了collections.abc 的东西,所以它们可以工作。你也可以用同样的方式给他们写信,不用任何时髦的self.set(value) 符号。
但深度不可变的标量 (Hashable) 不是。这包括int、str,甚至NoneType。这些不能直接替代:
-
我不能覆盖分配,有充分的理由(这太疯狂了)。
int_a = ConfigInt() int_a = 2 # swap out what the name 'int_a' refers to, or modify the internal contents? # obviously crazy 无论如何我都需要支持
set(value),所以不管1.。-
如果不对内置类型进行子类化,我就无法进行比较。
- 扩展内置是一个谎言 - 对每种类型的期望是它们是不可变的。
-
此外,没有虚拟子类来扩展左右交换问题的相等或比较:
port = ConfigInt() port > 40 # True 40 < port # TypeError()
这些都不是不可克服的,但是好的库对你很好。我真的很想解决这个问题。特别是对于那些工作人员只需要标量的情况——比如 Flask greenlet 或其他东西——标量是顶级对象,我们不应该将它包装在 dict 中。
其他问题
- IDK。想法?有更好的方法吗?
【问题讨论】:
-
您的问题有些冗长且令人困惑。我不知道你实际上在问什么。您能否将您的问题简化为直接说明您要实现的内容,最好是使用假代码示例?另外,您在上一个示例中所说的内容不正确;如果您以适当的方式覆盖
ConfigInt上的__eq__,那么port == 80和80 == port都可以工作。 -
谢谢,@BrenBarn。抱歉,我讨厌没有上下文的问题,因为所需的解决方案通常基于不正确的假设。我会修复的。至于
__eq__,我会举一个坏的例子。 -
无法重现
__eq__的东西。我一定是在抽烟。
标签: python python-3.x concurrency publish-subscribe