我有许多不同的小类,每个小类都有几个字段,例如:
class Article:
def __init__(self, name, available):
self.name = name
self.available = available
Run Code Online (Sandbox Code Playgroud)
什么是使该name字段只读的最简单和/或最惯用的方式,所以
a = Article("Pineapple", True)
a.name = "Banana" # <-- should not be possible
Run Code Online (Sandbox Code Playgroud)
不可能了吗?
这是我到目前为止所考虑的:
使用吸气剂(呃!).
class Article:
def __init__(self, name, available):
self._name = name
self.available = available
def name(self):
return self._name
Run Code Online (Sandbox Code Playgroud)
丑陋,非pythonic - 以及许多要编写的样板代码(特别是如果我有多个字段可以进行只读).但是,它完成了工作,很容易理解为什么会这样.
用途__setattr__:
class Article:
def __init__(self, name, available):
self.name = name
self.available = available
def __setattr__(self, name, value):
if name == "name":
raise Exception("%s property is read-only" % name)
self.__dict__[name] = value
Run Code Online (Sandbox Code Playgroud)
在调用者方面看起来很漂亮,似乎是执行这项工作的惯用方法 - 但不幸的是,我有很多只有少数几个字段才能只读取每个字段.所以我需要__setattr__为所有这些添加一个实现.或者使用某种混合物?在任何情况下,我都需要决定如何在客户端尝试将值分配给只读字段时如何操作.产量有些例外,我猜 - 但是哪个?
使用实用程序功能自动定义属性(以及可选的getter).这与(1)基本相同,只是我没有明确地写出getters,而是做了类似的事情
class Article:
def __init__(self, name, available):
# This function would somehow give a '_name' field to self
# and a 'name()' getter to the 'Article' class object (if
# necessary); the getter simply returns self._name
defineField(self, "name")
self.available = available
Run Code Online (Sandbox Code Playgroud)
这样做的缺点是我甚至不知道这是否可行(或者如何实现它),因为我不熟悉Python中的运行时代码生成.:-)
到目前为止,(2)对我来说似乎最有希望,除了我需要__setattr__对我所有课程定义的事实.我希望有一种方法可以"注释"字段,以便自动发生这种情况.有没有人有更好的主意?
为了它的价值,我正在阅读Python 2.6.
更新: 感谢所有有趣的回复!到现在为止,我有这个:
def ro_property(o, name, value):
setattr(o.__class__, name, property(lambda o: o.__dict__["_" + name]))
setattr(o, "_" + name, value)
class Article(object):
def __init__(self, name, available):
ro_property(self, "name", name)
self.available = available
Run Code Online (Sandbox Code Playgroud)
这看起来效果很好.原始类所需的唯一更改是
object(无论如何,这不是一个愚蠢的事情,我猜)self._name = name到ro_property(self, "name", name).这看起来很整洁 - 任何人都可以看到它的缺点吗?
Chr*_*ris 40
我将property用作装饰器来管理你的getter name(参见Parrot文档中类的示例).例如,使用类似的东西:
class Article(object):
def __init__(self, name, available):
self._name = name
self.available = available
@property
def name(self):
return self._name
Run Code Online (Sandbox Code Playgroud)
如果你没有为name属性定义setter (使用x.setter函数周围的装饰器),那么AttributeError当你尝试重置时会抛出一个name.
注意:您必须使用Python的新式类(即在Python 2.6中必须继承根据@SvenMarnach,情况并非如此.object)才能使属性正常工作.
Sve*_*ach 14
正如其他答案所指出的,使用属性是获取只读属性的方法.克里斯回答的解决方案是最干净的:它property()以直接,简单的方式使用内置.每个熟悉Python的人都会认识到这种模式,而且没有特定领域的伏都教.
如果您不喜欢每个属性都需要三行来定义,这是另一种直截了当的方式:
from operator import attrgetter
class Article(object):
def __init__(self, name, available):
self._name = name
self.available = available
name = property(attrgetter("_name"))
Run Code Online (Sandbox Code Playgroud)
一般来说,我不喜欢定义特定于域的函数来执行可以使用标准工具轻松完成的操作.如果您不必首先习惯所有项目特定的东西,那么阅读代码会更容易.
基于克里斯答案,但可以说更pythonic:
def ro_property(field):
return property(lambda self : self.__dict__[field])
class Article(object):
name = ro_property('_name')
def __init__(self):
self._name = "banana"
Run Code Online (Sandbox Code Playgroud)
如果尝试修改属性,它将引发一个AttributeError.
a = Article()
print a.name # -> 'banana'
a.name = 'apple' # -> AttributeError: can't set attribute
Run Code Online (Sandbox Code Playgroud)
更新:关于您更新的答案,我看到的(小)问题是您每次创建实例时都在修改类中属性的定义.而且我认为这不是一个好主意.这就是为什么我将ro_property调用放在__init__函数之外
关于什么?:
def ro_property(name):
def ro_property_decorator(c):
setattr(c, name, property(lambda o: o.__dict__["_" + name]))
return c
return ro_property_decorator
@ro_property('name')
@ro_property('other')
class Article(object):
def __init__(self, name):
self._name = name
self._other = "foo"
a = Article("banana")
print a.name # -> 'banana'
a.name = 'apple' # -> AttributeError: can't set attribute
Run Code Online (Sandbox Code Playgroud)
班装饰者很有意思!
| 归档时间: |
|
| 查看次数: |
10210 次 |
| 最近记录: |