我并不是Python的新手,但有时我仍然遇到Python的逻辑或思考方式的问题。我在testclass.py文件中有以下名为TestClass的类,它有一个属性x和相应的get和set方法。
class TestClass(object):
@property
def x(self):
print('get x')
return self.__x
@x.setter
def x(self, x):
print('set x')
self.__x = x
如果我运行一个简单的例子,一切都按预期工作。因此调用get和set方法并打印其确认消息:
>>> from testclass import TestClass
>>> newObject = TestClass()
>>> newObject
<testclass.TestClass object at 0x0298B9D0>
>>> newObject.x = [1, 2, 3, 4]
set x
>>> newObject.x
get x
[1, 2, 3, 4]
我的问题是,如果我只想通过索引来修改属性,则调用get方法(我期望)从对象获取属性,但是绕过set方法(没有打印设置消息,但财产被修改了):
>>> newObject.x[1] = 99
get x
>>> newObject.x
get x
[1, 99, 3, 4]
对我来说,这种行为不太符合逻辑。我来自Matlab(这不是OOP最优雅的语言)。 Matlab中的相同结构将导致以下过程:
这只是一个小例子。在我的代码中,我需要在每次修改属性时进入set方法。有没有pythonic方式吗?
非常感谢!
答案 0 :(得分:0)
property.getter
控制绑定到实例时属性返回的内容。一旦返回该值,getter就无法控制您使用它做什么。特别是,如果对象是可变的,那么您可以在不经过property.setter
的情况下更新它。
解决这个问题的一个简单方法是让您的属性返回内部数据的视图,例如返回数据的副本而不是数据本身。
这将强制用户get
值,更新后再set
。
import copy
class TestClass(object):
@property
def x(self):
print('get x')
return copy.deepcopy(self.__x)
@x.setter
def x(self, x):
print('set x')
self.__x = copy.deepcopy(x)
newObject = TestClass()
newObject.x = [1, 2, 3, 4]
newObject.x[1] = 99
print(newObject.x)
set x
get x
get x
[1, 2, 3, 4]
尽管每次复制数据的成本很高,但另一种解决方案是将其包装在控制器类中。
这可以通过继承collections.MutableSequence
以有条不紊的方式完成,list
根据您对__delitem__
,__setitem__
,{__getitem__
的实施提供__len__
可用的常用方法{1}},insert
和list
。
结果将是一个行为与from collections import MutableSequence
class ListController(MutableSequence):
def __init__(self, data):
self._data = data
def __setitem__(self, key, value):
print('index {} is being set to {}'.format(key, value))
self._data[key] = value
def __delitem__(self, key):
print('index {} is being deleted'.format(key))
del self._data[key]
def __getitem__(self, item):
return self._data[item]
def __len__(self):
return len(self._data)
def __repr__(self):
return repr(self._data)
def insert(self, index, value):
print('item {} is being inserted at index {}'.format(value, index))
self._data.insert(index, value)
class TestClass(object):
def __init__(self):
self.__x = [1, 2, 3, 4]
@property
def x(self):
print('get x')
return ListController(self.__x)
@x.setter
def x(self, x):
print('set x')
self.__x = list(x)
完全相同的对象,但有一些钩子可以检查任何突变是否有效。
newObject = TestClass()
newObject.x[1] = 99
print(newObject.x)
get x
index 1 is being set to 99
get x
[1, 99, 3, 4]
{{1}}