1

我正在寻找一种在设置某些属性时执行一些自定义转义的方法,以及在从我的一个对象中检索这些属性时执行相应的转义代码。我查看了 __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,但不影响“存储”)的方法,并且在设置/检索时会转义/取消转义值。

  1. __getattr__ 似乎不正确,因为应该为新的和现有的属性调用此代码。
  2. __setattr__ 可能有效,但我不明白它如何适合我的 @property 方法。例如,是否总是调用 __setattr__ 而不是我的自定义方法?如何从 __setattr__ 中调用我的自定义方法?有没有一种简单的方法可以只影响我的@property 属性而不存储要处理的属性列表?
  3. __getattribute__ 我尝试使用它,但最终陷入无限循环。除此之外,我的问题与 __setattr__ 大致相同,关于是否可以委托给使用 @property 声明的方法(以及如何做到这一点?),以及如何最好地说“仅适用于这些属性而不是任何属性这个类的。

解决这个问题的最佳方法是什么?

- 更新 -

详细说明:这个特定的“Bundle”类充当代理,将配置值存储在不同的后端 - 在本例中是包装 ConfigParser 的 FileConfigBackend。ConfigParser 使用 '%' 符号进行插值。但是,我想设置的一些值可以合法地包含百分号,所以我需要转义它们。但是,我不想为每个属性显式转义它们,而是希望有某种魔术方法,每次设置属性时都会调用它。然后它将replace('%', '%%')在字符串上运行。同样,我想要一种方法,每次检索属性时,都会replace('%%', '%')在返回值之前运行该值。

此外,由于 'Bundle' 中的大多数属性只是简单地代理到后端存储,因此如果有办法说'如果该属性在此列表中,则调用self.storage.PROPERTY_NAME = VALUE'会很好。有时虽然我希望能够覆盖该分配,例如对要设置的值运行正则表达式。

所以真的,我正在寻找一种方式来表达'当一个属性被设置时,总是调用这个方法。然后,如果存在具体的 @property.setter,请调用它而不是执行self.storage.key = value'.

(哦!我的意思是 __getattr__ 和 __setattr__ 不是 __get__ 和 __set__ - 更新了我的问题以反映这一点)

4

3 回答 3

2

装饰器根据描述符@property协议创建一个对象,这是,方法所在的位置。__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与包装属性类似的语法。您可以查看描述符指南以获取帮助。

也可以在此处找到一些详细信息:Python 属性如何工作?.

于 2013-06-26T13:00:16.617 回答
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你对象的方法,所以应该为属性类型添加一个检查。有很多这样的极端情况。这就是为什么我建议装饰器解决方案更清洁 - 它明确标记谁收到了特殊行为并且在路上没有令人讨厌的意外后果。

于 2013-06-26T13:39:52.603 回答
0

阅读 Google 叔叔在这方面的建议,通过反复试验,一切都变得非常清楚。在您的情况下,您可能不需要__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


您可能真正寻找的是通过覆盖和类中的方法可用的属性访问(如果您从2.7.x 派生,则可用):__setattr__()__getattr()__object

http://docs.python.org/2/reference/datamodel.html#customizing-attribute-access

于 2013-06-26T12:58:59.347 回答