我正在寻找在设置某些属性时执行某些自定义转义的方法,以及从我的某个对象检索这些属性时相应的非转义代码。我查看了__setattr __,__ getattr__和__getattribute__,但看不出它们与属性的关系。
例如,我有一个类,如:
class Bundle(object):
def __init__(self):
self.storage = Storage()
@property
def users_name(self):
"""
Get the user's name
"""
return self.storage.users_name
@users_name.setter
def users_name(self, users_name):
"""
Set the user's name
"""
# only permit letters and spaces.
if not re.match('^[A-Za-z ]+$', users_name):
msg = "'%s' is not a valid users_name. It must only contain letters and spaces" % users_name
log.fatal(msg)
raise ConfigurationError(msg)
self.storage.users_name = users_name
...
对象上有几个这样的属性。
我想要的是一种只会影响我的某些属性(例如users_name,而不是'storage')的方法,并且在设置/检索时会转义/取消该值。
接近这个的最佳方式是什么?
- 更新 -
详细说明:这个特殊的'Bundle'类充当代理,在不同的后端存储配置值 - 在这种情况下是一个包装ConfigParser的FileConfigBackend。 ConfigParser使用'%'符号进行插值。但是,我想设置的一些值可以合法地包含百分号,所以我需要逃避它们。但是,我没有为每个属性显式地转义它们,而是希望每次设置属性时都会调用某种魔术方法。然后它将在字符串上运行replace('%', '%%')
。同样,我想要一个方法,每次检索一个属性时,都会在返回值之前对该值运行replace('%%', '%')
。
此外,由于'Bundle'中的大多数属性只是代理到后端存储,如果有一种方法可以说'如果属性在此列表中,那么只需调用{{1} }”。有时虽然我希望能够覆盖该赋值,例如对要设置的值运行正则表达式。
所以,我正在寻找一种方式来说'当设置属性时,总是调用此方法。然后,如果存在具体的@ property.setter,则调用它而不是执行self.storage.PROPERTY_NAME = VALUE
'。
(doh!我的意思是__getattr__& __setattr__而不是__get__和__set__ - 更新我的问题以反映这一点)
答案 0 :(得分:2)
@property
装饰器按照descriptor协议创建一个对象,这是__get__
,__set__
方法所在的位置。
我可以想到为某些属性添加其他行为的最佳方法是创建 你自己的装饰师。这个装饰器将遵循相同的协议,包装最初由Python创建的属性,并添加您想要的转义/转义行为。这将允许您标记“特殊”转义属性:
@escaped
@property
def users_name(self):
...
只有那些属性才能得到特殊待遇。以下是如何实现此功能的快速示例:
class escaped:
def __init__(self, property_to_wrap):
# we wrap the property object created by the other decorator
self.property = property_to_wrap
def __get__(self, instance, objtype=None):
# delegate to the original property
original_value = self.property_to_wrap.__get__(instance, objtype)
# ... change the data however you like
return frotz(original_value)
def __set__(self, instance, new_value):
actual_value = frob(new_value)
self.property.__set__(instance, actual_value)
...
对于所有描述符方法都应该重复相同的操作。您还必须委派getter
本身的setter
,deleter
,property
方法(以允许您使用包裹属性的@users_name.setter
语法。您可以查看descriptor guide以获取帮助。
这里也可以找到一些细节:How do Python properties work?。
答案 1 :(得分:0)
阅读谷歌叔叔在这方面的建议以及试验结束错误都变得非常清楚。在您的情况下,您可能不需要__getatribute__
因为它是在访问时调用而不是查看obect的__dict__
。
我不习惯装饰,因为使用property()
内置对我来说更清楚:
http://docs.python.org/2/library/functions.html#property
方法__set__()
和__get__()
用于描述符类,这意味着您需要为您的属性/ -ies定义类。很好的例子:
http://docs.python.org/2/howto/descriptor.html#descriptor-example
您实际需要的是通过覆盖您班级中的__setattr__()
和__getattr()__
方法提供的属性访问(如果您从object
派生,则可用2.7.x):
http://docs.python.org/2/reference/datamodel.html#customizing-attribute-access
答案 2 :(得分:0)
我使用装饰器提供了不同的答案,但是如果你真的更喜欢使用魔术方法,那么有一种方法:
class Magic(object):
@staticmethod
def should_be_escaped(property_name):
return not "__" in property_name
def __getattribute__(self, property):
value = object.__getattribute__(self, property)
if Magic.should_be_escaped(property):
value = value.replace("%%", "%")
return value
def __setattr__(self, property, value):
if Magic.should_be_escaped(property):
value = value.replace("%", "%%")
object.__setattr__(self, property, value)
调用object.__getattribute__
和object.__setattr__
可让您从魔术方法中获得标准行为,其中应包括解析属性。使用它们是一种避免你提到的无限循环的方法。
这是更糟糕的解决方案的原因在于should_be_escaped
,您必须根据名称决定是否应该转义属性。这很难做到正确,你必须关心特殊名称,你的storage
变量等。
例如,上面should_be_escaped
的实现非常不完整,它会尝试在您对象的方法上调用replace
,因此应该为该属性添加一项检查类型。有很多角落案例。这就是为什么我建议装饰器解决方案更清洁 - 它明确地标记了谁接受了特殊行为并且没有令人讨厌的意外后果。