Python 中的数据绑定?
Data-Binding in Python?
我欣然承认我可能用错了名字来指代它,或者想要一些我不应该需要的东西。我只是挂断了希望这成为一件事:将对象属性链接在一起,这样如果我改变一个,我就会改变另一个。总是。直到我故意做点什么来断开它们。
那么让我们直接进入一个例子:
class Example:
def __init__(self):
self.name = ["Thing1"]
a = Example()
b = Example()
c = Example()
a.name = b.name
b.name.append("Thing2")
b.name.remove("Thing1")
现在 a.name
和 b.name
已连接,因此它们都是同一个可变变量的名称,现在显示为 ["Thing2"]
。此外,c.name
是一个不同值的名称 ["Thing1"]
.
我现在已经完成了我想做的事情:a.name
和 b.name
已关联。但这是非常笨拙的。
我可以制作一个自定义列表 Class 并创建一些方法来切换列表中的内容,并且 return 在调用时成为不可变的,这样它看起来更像一个普通的不可变的。但这一切似乎都是黑客。
有没有让这个更干净的想法?还是我只是想要一件坏事?
一般情况下,当你重新定义时
x = 8
y = x
x = 4 # <-- Re-definition of x
x
和 y
将不再引用相同的东西(尝试 print id(x) == id(y)
)。
考虑重新定义的工作原理:
x = 8
y = x
x = 4
print y # Still 8
x = "foo"
y = x
x = "bar"
print y # Still 'foo'
x = [1,2,3]
y = x
x = [4,5,6]
print y # Still [1,2,3]
使用可变类型,您可以做的是更改对象"in place"。
例如,
x = [1,2,3]
y = x
现在,x
和 y
引用同一个列表 (print id(x) == id(y)
)。
现在我们可以 替换 元素,只需使用 x
:
x[:] = [4,5,6]
这里,我们不是重新定义x
,我们只是修改列表中的值。
在这种情况下,更改 将 反映在 y
:
print y # Now [4,5,6]
注:
print id(x) == id(y) # True
如果您希望能够就地修改不可变类型,您可以 "fake" 通过将其包装在列表中来实现:
x = ["Foo"] # Strings are immutable
y = x
x[0] = "Bar"
print y # ['Bar']
print id(x) == id(y) # True
这里将有 100 万 个关于可变/不可变类型的问题——您应该查看它们。
Edit 我们 可以 通过 bind
行结合可变类型和 properties:
首先,我们创建一个 "fake" 可变字符串 class:
class FakeMutableString(object):
def __init__(self, s=""):
self.s = [s]
def __str__(self):
return self.s[0]
def get(self):
return self.s[0]
def set(self, new):
self.s[0] = new
看看它是如何工作的
x = FakeMutableString("Foo")
y = x
x.set("Bar")
print y # Prints 'Bar' -- Change in x is reflected in y
请注意,我们不会 重新分配给 x
——而是使用 x.set()
方法。如果我们重新分配给 x
,我们会毁了一切(正如我们上面所说的)。
那么,我们可以将你的Example
class修改为:
class Example(object):
def __init__(self):
self._name = FakeMutableString()
@property
def name(self):
return self._name.get()
@name.setter
def name(self, new):
self._name.set(new)
Example
实例具有引用 FakeMutableString
对象的 _name
属性。
但是对于属性,我们可以假装我们提供直接属性访问,同时隐藏实际实现。
所以我们可以这样做:
# Create Example instances
a = Example()
b = Example()
c = Example()
# Set their name attributes
# ( this actually calls <Example>._name.set() )
a.name = "ThingA"
b.name = "ThingB"
c.name = "ThingC"
# Access and print their name attributes
# ( this actually calls <Example>._name.get() )
print a.name, b.name, c.name # ThingA ThingB ThingC
# We can't bind like you suggested, but we can change what b._name points to
# So here, we change b._name to point to the FakeMutableString a._name points to
b._name = a._name
# Now when we print the names, we see something different
print a.name, b.name, c.name # ThingA ThingA ThingC
# And we can make a change in a, and have it reflected in b
a.name = "CommonName"
print a.name, b.name, c.name # CommonName CommonName ThingC
# And vice-versa
b.name = "StillCommon"
print a.name, b.name, c.name # StillCommon StillCommon ThingC
我欣然承认我可能用错了名字来指代它,或者想要一些我不应该需要的东西。我只是挂断了希望这成为一件事:将对象属性链接在一起,这样如果我改变一个,我就会改变另一个。总是。直到我故意做点什么来断开它们。
那么让我们直接进入一个例子:
class Example:
def __init__(self):
self.name = ["Thing1"]
a = Example()
b = Example()
c = Example()
a.name = b.name
b.name.append("Thing2")
b.name.remove("Thing1")
现在 a.name
和 b.name
已连接,因此它们都是同一个可变变量的名称,现在显示为 ["Thing2"]
。此外,c.name
是一个不同值的名称 ["Thing1"]
.
我现在已经完成了我想做的事情:a.name
和 b.name
已关联。但这是非常笨拙的。
我可以制作一个自定义列表 Class 并创建一些方法来切换列表中的内容,并且 return 在调用时成为不可变的,这样它看起来更像一个普通的不可变的。但这一切似乎都是黑客。
有没有让这个更干净的想法?还是我只是想要一件坏事?
一般情况下,当你重新定义时
x = 8
y = x
x = 4 # <-- Re-definition of x
x
和 y
将不再引用相同的东西(尝试 print id(x) == id(y)
)。
考虑重新定义的工作原理:
x = 8
y = x
x = 4
print y # Still 8
x = "foo"
y = x
x = "bar"
print y # Still 'foo'
x = [1,2,3]
y = x
x = [4,5,6]
print y # Still [1,2,3]
使用可变类型,您可以做的是更改对象"in place"。
例如,
x = [1,2,3]
y = x
现在,x
和 y
引用同一个列表 (print id(x) == id(y)
)。
现在我们可以 替换 元素,只需使用 x
:
x[:] = [4,5,6]
这里,我们不是重新定义x
,我们只是修改列表中的值。
在这种情况下,更改 将 反映在 y
:
print y # Now [4,5,6]
注:
print id(x) == id(y) # True
如果您希望能够就地修改不可变类型,您可以 "fake" 通过将其包装在列表中来实现:
x = ["Foo"] # Strings are immutable
y = x
x[0] = "Bar"
print y # ['Bar']
print id(x) == id(y) # True
这里将有 100 万 个关于可变/不可变类型的问题——您应该查看它们。
Edit 我们 可以 通过 bind
行结合可变类型和 properties:
首先,我们创建一个 "fake" 可变字符串 class:
class FakeMutableString(object):
def __init__(self, s=""):
self.s = [s]
def __str__(self):
return self.s[0]
def get(self):
return self.s[0]
def set(self, new):
self.s[0] = new
看看它是如何工作的
x = FakeMutableString("Foo")
y = x
x.set("Bar")
print y # Prints 'Bar' -- Change in x is reflected in y
请注意,我们不会 重新分配给 x
——而是使用 x.set()
方法。如果我们重新分配给 x
,我们会毁了一切(正如我们上面所说的)。
那么,我们可以将你的Example
class修改为:
class Example(object):
def __init__(self):
self._name = FakeMutableString()
@property
def name(self):
return self._name.get()
@name.setter
def name(self, new):
self._name.set(new)
Example
实例具有引用 FakeMutableString
对象的 _name
属性。
但是对于属性,我们可以假装我们提供直接属性访问,同时隐藏实际实现。
所以我们可以这样做:
# Create Example instances
a = Example()
b = Example()
c = Example()
# Set their name attributes
# ( this actually calls <Example>._name.set() )
a.name = "ThingA"
b.name = "ThingB"
c.name = "ThingC"
# Access and print their name attributes
# ( this actually calls <Example>._name.get() )
print a.name, b.name, c.name # ThingA ThingB ThingC
# We can't bind like you suggested, but we can change what b._name points to
# So here, we change b._name to point to the FakeMutableString a._name points to
b._name = a._name
# Now when we print the names, we see something different
print a.name, b.name, c.name # ThingA ThingA ThingC
# And we can make a change in a, and have it reflected in b
a.name = "CommonName"
print a.name, b.name, c.name # CommonName CommonName ThingC
# And vice-versa
b.name = "StillCommon"
print a.name, b.name, c.name # StillCommon StillCommon ThingC