- c - 在位数组中找到第一个零
- linux - Unix 显示有关匹配两种模式之一的文件的信息
- 正则表达式替换多个文件
- linux - 隐藏来自 xtrace 的命令
我可以使用 Python 的属性系统创建一个只读列表吗?
我创建了一个 Python 类,其中有一个列表作为成员。在内部,我希望它在每次修改列表时都做一些事情。如果这是 C++,我会创建 getter 和 setter,它们允许我在调用 setter 时进行簿记,并且我会让 getter 返回一个 const
引用,这样编译器就会如果我试图通过 getter 修改列表,就会对我大喊大叫。在 Python 中,我们有属性系统,因此不再需要(谢天谢地)为每个数据成员编写普通的 getter 和 setter。但是,请考虑以下脚本:
def main():
foo = Foo()
print('foo.myList:', foo.myList)
# Here, I'm modifying the list without doing any bookkeeping.
foo.myList.append(4)
print('foo.myList:', foo.myList)
# Here, I'm modifying my "read-only" list.
foo.readOnlyList.append(8)
print('foo.readOnlyList:', foo.readOnlyList)
class Foo:
def __init__(self):
self._myList = [1, 2, 3]
self._readOnlyList = [5, 6, 7]
@property
def myList(self):
return self._myList
@myList.setter
def myList(self, rhs):
print("Insert bookkeeping here")
self._myList = rhs
@property
def readOnlyList(self):
return self._readOnlyList
if __name__ == '__main__':
main()
输出:
foo.myList: [1, 2, 3]
# Note there's no "Insert bookkeeping here" message.
foo.myList: [1, 2, 3, 4]
foo.readOnlyList: [5, 6, 7, 8]
这说明 Python 中缺少 const
的概念允许我使用 append()
方法修改我的列表,尽管我已经做了它是一个属性。这可以绕过我的簿记机制 (_myList
),或者它可以用于修改可能希望为只读的列表 (_readOnlyList
)。
一种解决方法是在 getter 方法中返回列表的深拷贝(即 return self._myList[:]
)。这可能意味着很多额外的复制,如果列表很大或者复制是在内部循环中完成的。 (但无论如何,过早的优化是万恶之源。)此外,虽然深拷贝可以防止簿记机制被绕过,但如果有人要调用 .myList.append()
,他们的更改将被默默地丢弃,这可能会产生一些痛苦的调试。如果提出异常就好了,这样他们就会知道他们在违背类的设计。
最后一个问题的解决方法是不使用属性系统,并制作“正常”的 getter 和 setter 方法:
def myList(self):
# No property decorator.
return self._myList[:]
def setMyList(self, myList):
print('Insert bookkeeping here')
self._myList = myList
如果用户尝试调用 append()
,它看起来像 foo.myList().append(8)
,那些额外的括号会提示他们他们可能会得到一份副本,而不是对内部列表数据的引用。不利的是,像这样编写 getter 和 setter 有点不符合 Pythonic,如果该类有其他列表成员,我将不得不为那些 (eww) 编写 getter 和 setter,或者创建接口(interface)不一致。 (我认为稍微不一致的界面可能是所有弊端中最少的。)
我还缺少其他解决方案吗?可以使用 Pyton 的属性系统制作一个只读列表吗?
两个主要的建议似乎是使用元组作为只读列表,或者将列表子类化。我喜欢这两种方法。从 getter 返回一个元组,或者首先使用一个元组,可以防止使用 += 运算符,这可能是一个有用的运算符,并且还可以通过调用 setter 来触发簿记机制。然而,返回一个元组是一个单行更改,如果您想进行防御性编程但认为将整个其他类添加到您的脚本可能会不必要地复杂,这很好。 (有时保持极简主义并假设您不会需要它可能是一件好事。)
这是脚本的更新版本,它说明了这两种方法,供任何通过 Google 找到它的人使用。
import collections
def main():
foo = Foo()
print('foo.myList:', foo.myList)
try:
foo.myList.append(4)
except RuntimeError:
print('Appending prevented.')
# Note that this triggers the bookkeeping, as we would like.
foo.myList += [3.14]
print('foo.myList:', foo.myList)
try:
foo.readOnlySequence.append(8)
except AttributeError:
print('Appending prevented.')
print('foo.readOnlySequence:', foo.readOnlySequence)
class UnappendableList(collections.UserList):
def __init__(self, *args, **kwargs):
data = kwargs.pop('data')
super().__init__(self, *args, **kwargs)
self.data = data
def append(self, item):
raise RuntimeError('No appending allowed.')
class Foo:
def __init__(self):
self._myList = [1, 2, 3]
self._readOnlySequence = [5, 6, 7]
@property
def myList(self):
return UnappendableList(data=self._myList)
@myList.setter
def myList(self, rhs):
print('Insert bookkeeping here')
self._myList = rhs
@property
def readOnlySequence(self):
# or just use a tuple in the first place
return tuple(self._readOnlySequence)
if __name__ == '__main__':
main()
输出:
foo.myList: [1, 2, 3]
Appending prevented.
Insert bookkeeping here
foo.myList: [1, 2, 3, 3.14]
Appending prevented.
foo.readOnlySequence: (5, 6, 7)
谢谢大家。
最佳答案
您可以让方法返回原始列表的包装器——collections.Sequence
可能有助于编写它。或者,您可以返回一个元组
——将列表复制到元组中的开销通常可以忽略不计。
但最终,如果用户想要更改基础列表,他们可以,而且您实际上无法阻止他们。 (毕竟,如果需要,他们可以直接访问 self._myList
)。
我认为执行此类操作的 pythonic 方法是记录他们不应该更改列表,如果做,那么这是他们的错当他们的程序崩溃和燃烧。
关于使用属性装饰器的 Python 只读列表,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23474648/
我之前让 dll 注入(inject)器变得简单,但我有 Windows 7,我用 C# 和 C++ 做了它,它工作得很好!但是现在当我在 Windows 8 中尝试相同的代码时,它似乎没有以正确的方
我正在尝试制作一个名为 core-splitter 的元素,该元素在 1.0 中已弃用,因为它在我们的项目中起着关键作用。 如果您不知道 core-splitter 的作用,我可以提供一个简短的描述。
我有几个不同的蜘蛛,想一次运行所有它们。基于 this和 this ,我可以在同一个进程中运行多个蜘蛛。但是,我不知道如何设计一个信号系统来在所有蜘蛛都完成后停止 react 器。 我试过了: cra
有没有办法在达到特定条件时停止扭曲 react 器。例如,如果一个变量被设置为某个值,那么 react 器应该停止吗? 最佳答案 理想情况下,您不会将变量设置为一个值并停止 react 器,而是调用
https://code.angularjs.org/1.0.0rc9/angular-1.0.0rc9.js 上面的链接定义了外部js文件,我不知道Angular-1.0.0rc9.js的注入(in
我正在尝试运行一个函数并将服务注入(inject)其中。我认为这可以使用 $injector 轻松完成.所以我尝试了以下(简化示例): angular.injector().invoke( [ "$q
在 google Guice 中,我可以使用函数 createInjector 创建基于多个模块的注入(inject)器。 因为我使用 GWT.create 在 GoogleGin 中实例化注入(in
我在 ASP.NET Core 1.1 解决方案中使用配置绑定(bind)。基本上,我在“ConfigureServices Startup”部分中有一些用于绑定(bind)的简单代码,如下所示: s
我在 Spring MVC 中设置 initBinder 时遇到一些问题。我有一个 ModelAttribute,它有一个有时会显示的字段。 public class Model { privat
我正在尝试通过jquery post发布knockoutjs View 模型 var $form = $('#barcodeTemplate form'); var data = ko.toJS(vm
如何为包含多态对象集合的复杂模型编写自定义模型绑定(bind)程序? 我有下一个模型结构: public class CustomAttributeValueViewModel { publi
您好,我正在尝试实现我在 this article 中找到的扩展方法对于简单的注入(inject)器,因为它不支持开箱即用的特定构造函数的注册。 根据这篇文章,我需要用一个假的委托(delegate)
你好,我想自动注册我的依赖项。 我现在拥有的是: public interface IRepository where T : class public interface IFolderReposi
我正在使用 Jasmine 测试一些 Angular.js 代码。为此,我需要一个 Angular 注入(inject)器: var injector = angular.injector(['ng'
我正在使用 Matlab 代码生成器。不可能包含代码风格指南。这就是为什么我正在寻找一个工具来“ reshape ”、重命名和重新格式化生成的代码,根据我的: 功能横幅约定 文件横幅约定 命名约定 等
这个问题在这里已经有了答案: Where and why do I have to put the "template" and "typename" keywords? (8 个答案) 关闭 8
我开发了一种工具,可以更改某些程序的外观。为此,我需要在某些进程中注入(inject)一个 dll。 现在我基本上使用这个 approach .问题通常是人们无法注入(inject) dll,因为他们
我想使用 swing、spring 和 hibernate 编写一个 java 应用程序。 我想使用数据绑定(bind)器用 bean 的值填充 gui,并且我还希望它反射(reflect) gui
我有这段代码,当两个蜘蛛完成后,程序仍在运行。 #!C:\Python27\python.exe from twisted.internet import reactor from scrapy.cr
要点是 Spring Batch (v2) 测试框架具有带有 @Autowired 注释的 JobLauncherTestUtils.setJob。我们的测试套件有多个 Job 类提供者。因为这个类不
我是一名优秀的程序员,十分优秀!