在Python方法中设置属性

时间:2011-02-22 13:26:48

标签: python class methods properties

我想在类方法中设置只读属性 我已经尝试过了:

class Foo(object):
    def __init__(self, v):
        self._set('_v', v)

    def _set(self, attr, v):
        setattr(self, attr, v)
        setattr(Foo, attr[1:], property(lambda self: getattr(self, attr)))

但它太可怕了。还有另外一种方法吗?我需要做的是设置属性:

class Foo(object):
    def __init__(self, v):
        self._v = v

    @ property
    def v(self):
        return self._v    

>>> f = Foo(42)
>>> f.v
42
>>> f.v = 41
AttributeError: can't set attribute ## This is what I want: a read-only attribute

但我需要在方法中完成。还有另外一种方法吗?

谢谢你,
魔方

P.S。我已经查过这篇文章,但它没有解决我的问题: Using Python property() inside a method

编辑:我无法使用property,因为我想在方法中设置它。我只能从外面使用property

class Foo(object):
    def __init__(self, v):
        self._v = v
    @ property
    def v(self):
        return self._v
    ## ...OR
    def getv(self):
        return self._v
    v = property(getv)

我不能这样做,因为我不知道属性名称,我必须动态设置它。像这样:

class Foo(object):
    def __init__(self, v):
        self._set_property_from_inside('v', v)
>>> f = Foo(42)
>>> f.v
42

4 个答案:

答案 0 :(得分:3)

认为你正在寻找python descriptors

class MyDescriptor(object):
    def __init__(self, protected_attr_name):
        self.attr = protected_attr_name

    def __get__(self, obj, objtype):
        return getattr(obj, self.attr)

    def __set__(self, obj, value):
        #setattr(obj, self.attr, value)
        raise AttributeError("Can't set attribute")

class Foo(object):
    def __init__(self, k, v):
        setattr(self.__class__, k, MyDescriptor("_" + k))
        setattr(self, "_" + k, v)

f = Foo("v", 42)
print f.v   # Prints 42
try:
    f.v = 32
except AttributeError:
    pass
print f.v  # Prints 42

您可以在__get____set__方法中执行任何操作来控制访问权限。如果你在__get__中调用obj.get_v,在__set__中调用obj.set_v,这非常接近于属性的实际实现,正如您在上面的链接中所看到的那样。

修改:已修复。我应该更好地阅读那个页面。引用:

  

对于对象,机制位于object.__getattribute__,将b.x转换为type(b).__dict__['x'].__get__(b, type(b))

因此,如果您将描述符放在实例的__dict__中,那么当您将该属性设置为新值时,它们就会被覆盖。

答案 1 :(得分:2)

class Foo(object):
    def __getattr__(self, name):
        return getattr(self, "_" + name)
    def __setattr__(self, name, value):
        if name.startswith('_'):
            self.__dict__[name] = value
        else:
            raise ValueError("%s is read only" % name)

然后:

>>> f = Foo()
>>> f.x = 5
Traceback (most recent call last):
  File "<input>", line 1, in <module>
  File "<input>", line 8, in __setattr__
ValueError: x is read only
>>> f._x = 5
>>> f.x
5

答案 2 :(得分:1)

property()正是这里的解决方案。为什么不能解决你的问题呢? 覆盖setter的getter方法可以让你完全控制你想要的东西:完全控制属性。

请查看

等官方文档

http://docs.python.org/library/functions.html#property

为了理解整个故事。

答案 3 :(得分:1)

我已经想到了我认为实现纯只读属性的更简洁的解决方案,如果这就是你想要的。它是tangentstorm提供的解决方案的变体,但完全不需要__getattr__方法。

class Foo(object):
    def __init__(self):
        self.readonly = set()

    def set_readonly(self, attr, value):
        setattr(self, attr, value)
        self.readonly.add(attr)

    def __setattr__(self, attr, value):
        if hasattr(self, "readonly") and attr in self.readonly:
            raise AttributeError("Read only attribute: %s" % (attr,))
        object.__setattr__(self, attr, value)

它的工作原理如下:

>>> f = Foo()
>>> f.x = 5
>>> f.set_readonly("y", 9)
>>> f.x, f.y
(5, 9)
>>> f.x = 7
>>> f.y = 1
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "ro.py", line 13, in __setattr__
    raise AttributeError("Read only attribute: %s" % (name,))
AttributeError: Read only attribute: y

再次对只读属性进行读写很容易:

    def unset_readonly(self, attr):
        self.readonly.remove(attr)

在我第一次尝试撰写此提示时,我使用的是self.__readonly而不是self.readonly,但这导致了实际设置__readonly属性的问题,因为我需要这样做un-munge“private”属性来检查它的存在(hasattr(self, "_Foo__readonly")),这是不鼓励的。