如何在设置/检索属性时自动转义属性?

时间:2013-06-26 12:37:20

标签: python

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

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

    - 更新 -

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

    此外,由于'Bundle'中的大多数属性只是代理到后端存储,如果有一种方法可以说'如果属性在此列表中,那么只需调用{{1} }”。有时虽然我希望能够覆盖该赋值,例如对要设置的值运行正则表达式。

    所以,我正在寻找一种方式来说'当设置属性时,总是调用此方法。然后,如果存在具体的@ property.setter,则调用它而不是执行self.storage.PROPERTY_NAME = VALUE'。

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

3 个答案:

答案 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本身的setterdeleterproperty方法(以允许您使用包裹属性的@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,因此应该为该属性添加一项检查类型。有很多角落案例。这就是为什么我建议装饰器解决方案更清洁 - 它明确地标记了谁接受了特殊行为并且没有令人讨厌的意外后果。