Python中的容器是否有与@property等价的属性?
我正在用Python为AWS模块(特别是Boto)编写一个简化的包装器类。在这个过程中,我多次使用Python中的容器是否有与@property等价的属性?,python,dictionary,properties,boto,descriptor,Python,Dictionary,Properties,Boto,Descriptor,我正在用Python为AWS模块(特别是Boto)编写一个简化的包装器类。在这个过程中,我多次使用@property来避免在我的库中使用特殊的“getter”和“setter”方法——我被告知这是一种更具python风格的方法。当使用该类时,程序员调用这些方法,就像调用简单对象一样,如下所示: myclass.myprop = 5 # sends "5" to myprop's setter function result = myclass.myprop # calls
@property
来避免在我的库中使用特殊的“getter”和“setter”方法——我被告知这是一种更具python风格的方法。当使用该类时,程序员调用这些方法,就像调用简单对象一样,如下所示:
myclass.myprop = 5 # sends "5" to myprop's setter function
result = myclass.myprop # calls myprop's getter function and stores the result
但我也在处理几组对象——例如,标记的名称/值对——我希望像访问容器一样访问它们,可能是字典或列表。以标签为例:
myclass.tags["newkey"] = "newvalue" # runs a function that applies tag in AWS
result = myclass.tags["newkey"] # accesses AWS to get value of "newkey" tag
从我所看到的情况来看,通过子类化dict
似乎可以做到这一点,但我觉得这里缺少了一些东西。创建这样一个接口的最具python风格的方法是什么
编辑:我最终使用了Silas Ray的解决方案,但对其进行了修改,以便这些类可以用于定义多个类似dict的对象。它并不完全干净,但我将在这里发布我修改过的代码和解释,以帮助其他有困难的人
class FakeDict(object):
def __init__(self, obj, getter, setter, remover, lister):
self.obj = obj
self.getter = getter
self.setter = setter
self.lister = lister
self.remover = remover
def __getitem__(self, key):
return self.getter(self.obj, key)
def __setitem__(self, key, value):
self.setter(self.obj, key, value)
def __delitem__(self, key):
self.remover(self.obj, key)
def _set(self, new_dict):
for key in self.lister(self.obj):
if key not in new_dict:
self.remover(self.obj, key)
for key, value in new_dict.iteritems():
self.setter(self.obj, key, value)
class ProxyDescriptor(object):
def __init__(self, name, klass, getter, setter, remover, lister):
self.name = name
self.proxied_class = klass
self.getter = getter
self.setter = setter
self.remover = remover
self.lister = lister
def __get__(self, obj, klass):
if not hasattr(obj, self.name):
setattr(obj, self.name, self.proxied_class(obj, self.getter, self.setter, self.remover, self.lister))
return getattr(obj, self.name)
def __set__(self, obj, value):
self.__get__(obj, obj.__class__)._set(value)
class AWS(object):
def get_tag(self, tag):
print "Ran get tag"
return "fgsfds"
# Call to AWS to get tag
def set_tag(self, tag, value):
print "Ran set tag"
# Call to AWS to set tag
def remove_tag(self, tag):
print "Ran remove tag"
# Call to AWS to remove tag
def tag_list(self):
print "Ran list tags"
# Call to AWS to retrieve all tags
def get_foo(self, foo):
print "Ran get foo"
return "fgsfds"
# Call to AWS to get tag
def set_foo(self, foo, value):
print "Ran set foo"
# Call to AWS to set tag
def remove_foo(self, tag):
print "Ran remove foo"
# Call to AWS to remove tag
def foo_list(self):
print "Ran list foo"
# Call to AWS to retrieve all tags
tags = ProxyDescriptor('_tags', FakeDict, get_tag, set_tag, remove_tag, tag_list)
foos = ProxyDescriptor('_foos', FakeDict, get_foo, set_foo, remove_foo, foo_list)
test = AWS()
tagvalue = test.tags["tag1"]
print tagvalue
test.tags["tag1"] = "value1"
del test.tags["tag1"]
foovalue = test.foos["foo1"]
print foovalue
test.foos["foo1"] = "value1"
del test.foos["foo1"]
现在来解释一下
标记
和foos
都是ProxyDescriptor的类级实例,在定义类时只实例化一次。它们已经移到了底部,因此可以引用上面的函数定义,这些函数定义用于定义各种字典操作的行为
ProxyDescriptor的大部分“魔法”都发生在\uuuu get\uuuu
方法上。任何带有test.tags
的代码都将运行描述符的\uuuu get\uuu
方法,该方法只检查test
(作为obj
传入)是否具有名为\u tags
的属性。如果没有,它将创建一个实例—以前传递给它的类的实例。这就是调用FakeDict的构造函数的地方。对于引用了tags
的AWS
的每个实例,它最终被调用并创建一次
我们通过描述符和FakeDict
的构造函数传递了四个函数集,但是在FakeDict
中使用它们有点棘手,因为上下文已经改变了。如果我们直接在AWS类的实例中使用函数(如test.get_tag
),Python会自动用所有者test
填充self
参数。但是它们不是从test
调用的-当我们将它们传递给描述符时,我们传递了类级函数,这些函数没有self
可引用。为了解决这个问题,我们将self
视为一个传统的论点obj
inFakeDict
实际上代表了我们的test
对象,因此我们可以将它作为第一个参数传递给函数
部分原因是在AWS
、ProxyDescriptor
和FakeDict
之间有很多奇怪的循环引用。如果您在理解它时遇到困难,请记住,在“ProxyDescriptor”和“FakeDict”中,obj
是已传递给它们的AWS类的实例,即使FakeDict
的实例存在于AWS类的同一实例中。实现挂钩到对象[…]
索引或项目访问:
>>> class DuplexContainer(object):
... def __init__(self):
... self._values = ['foo', 'bar', 'baz']
... def __getitem__(self, item):
... if item in self._values:
... return self._values.index(item)
... return self._values[item]
...
>>> d = DuplexContainer()
>>> d[1]
'bar'
>>> d['baz']
2
要支持项目分配,您可以实现\uuuuu setitem\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu
您还可以选择支持切片;当有人在您的自定义对象上使用时,会向\uu*item\uuux()
钩子传递一个钩子,然后您可以根据切片索引返回值、设置值或删除值:
>>> class DuplexContainer(object):
... def __init__(self):
... self._values = ['foo', 'bar', 'baz']
... def __getitem__(self, item):
... if isinstance(item, slice):
... return ['Slice-item {}'.format(self._values[i])
... for i in range(*item.indices(len(self._values)))]
... if item in self._values:
... return self._values.index(item)
... return self._values[item]
...
>>> d = DuplexContainer()
>>> d[:2]
['Slice-item foo', 'Slice-item bar']
@Martjin Pieters在\uu getitem\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu
(和)方面是正确的,但是我猜,你应该考虑写一个习惯<代码>属性实际上是描述符本身
class AWSTagsProxy(object):
def __init__(self, aws_inst):
self.aws_inst = aws_inst
def __getitem__(self, key):
return self.aws_inst.get_tag(key)
def __setitem__(self, key, value):
self.aws_inst.set_tag(key, value)
def __delitem__(self, key):
self.aws_inst.remove_tag(key)
def _set(self, tag_dict):
for tag in self.aws_inst.tag_list():
if tag not in tag_dict:
self.aws_inst.remove_tag(tag)
for tag, value in tag_dict.iteritems():
self.aws_inst.set_tag(tag, value)
class ProxyDescriptor(object):
def __init__(self, name, klass):
self.name = name
self.proxied_class = klass
def __get__(self, obj, klass):
if not hasattr(obj, self.name):
setattr(obj, self.name, self.proxied_class(obj))
return getattr(obj, self.name)
def __set__(self, obj, value):
self.__get__(obj, obj.__class__)._set(value)
class AWS(object):
tags = ProxyDescriptor('_tags', AWSTagsProxy)
def get_tag(self, tag):
# Call to AWS to get tag
def set_tag(self, tag, value):
# Call to AWS to set tag
def remove_tag(self, tag):
# Call to AWS to remove tag
def tag_list(self):
# Call to AWS to retrieve all tags
这在任何情况下都更类似于property
setter和getter方法,因为您的\uuuu setitem\uuuu
和\uuu getitem\uuuuu
可以访问包含的实例(ProxyDescriptor
实例范围中的aws\u inst
实例范围中的obj
)与属性
方法访问self
的方式类似,不要忘记\uuuuu setitem\uuuuuuuu
,也可能是\uuuu delitem\uuuuuuu
。Pythonic方式是既不使用getter和setter方法,也不使用@property
。属性访问和属性分配的语义本身就足够了。除非!除非您需要验证输入或计算输出。请注意,您可以先不使用属性,然后切换到使用@property
,而无需更改客户端代码。所以你只需要在需要的时候添加它。这非常接近我想要的,我可以在我的代码中使用它,但我不知道它实际上是如何工作的。你能更详细地解释一下下面发生了什么吗?例如,AWSTagsProxy的构造函数在哪里被调用?另外,tags对象是在类级别而不是在init上被调用的-这对类的多个实例有影响吗?有没有办法将函数传递到AWSTagsProxy,而不是硬编码为“set_tag”、“remove_tag”等。?我很想让它们成为泛型的,只是为我的包装类中的每一个类似dict的构造实例化它们。很抱歉问了这么多问题-我真的很想更好地理解描述符。从你的问题编辑中,看起来你差不多明白了。我这样组织事情的原因是